Merge pull request #449 from future-architect/support_oval

v0.4.0
This commit is contained in:
Kota Kanbe
2017-08-25 11:20:02 +09:00
committed by GitHub
76 changed files with 9550 additions and 4712 deletions

View File

@@ -1,6 +1,5 @@
language: go
go:
- 1.7
- 1.8

View File

@@ -15,7 +15,7 @@
clean
SRCS = $(shell git ls-files '*.go')
PKGS = ./. ./config ./models ./report ./cveapi ./scan ./util ./commands ./cache
PKGS = ./. ./cache ./commands ./config ./models ./oval ./report ./scan ./util
VERSION := $(shell git describe --tags --abbrev=0)
REVISION := $(shell git rev-parse --short HEAD)
LDFLAGS := -X 'main.version=$(VERSION)' \

149
Gopkg.lock generated
View File

@@ -1,16 +1,17 @@
memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Azure/azure-storage-go"
packages = ["."]
revision = "12ccaadb081cdd217702067d28da9a7ff4305239"
name = "github.com/Azure/azure-sdk-for-go"
packages = ["storage"]
revision = "57db66900881e9fd21fd041a9d013514700ecab3"
version = "v10.3.0-beta"
[[projects]]
name = "github.com/Azure/go-autorest"
packages = ["autorest","autorest/azure","autorest/date"]
revision = "a2fdd780c9a50455cecd249b00bdc3eb73a78e31"
version = "v7.3.1"
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
revision = "77a52603f06947221c672f10275abc9bf2c7d557"
version = "v8.3.0"
[[projects]]
name = "github.com/BurntSushi/toml"
@@ -18,40 +19,35 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
revision = "b26d9c308763d68093482582cea63d69be07a0f0"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "github.com/Sirupsen/logrus"
packages = ["."]
revision = "10f801ebc38b33738c9d17d50860f484a0988ff5"
[[projects]]
name = "github.com/asaskevich/govalidator"
packages = ["."]
revision = "7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877"
version = "v5"
revision = "4918b99a7cb949bb295f3c7bbaf24b577d806e35"
version = "v6"
[[projects]]
name = "github.com/aws/aws-sdk-go"
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/request","aws/session","aws/signer/v4","private/endpoints","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","private/waiter","service/s3","service/sts"]
revision = "5b341290c488aa6bd76b335d819b4a68516ec3ab"
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"]
revision = "264af29009637e0a9e5d4a276d0969c3ed918ffd"
version = "v1.10.29"
[[projects]]
name = "github.com/boltdb/bolt"
packages = ["."]
revision = "583e8937c61f1af6513608ccc75c97b6abdf4ff9"
version = "v1.3.0"
revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
version = "v1.3.1"
[[projects]]
name = "github.com/cenkalti/backoff"
packages = ["."]
revision = "32cd0c5b3aef12c76ed64aaf678f6c79736be7dc"
version = "v1.0.0"
revision = "61153c768f31ee5f130071d08fc82b85208528de"
version = "v1.1.0"
[[projects]]
name = "github.com/cheggaaa/pb"
packages = ["."]
revision = "b6229822fa186496fcbf34111237e7a9693c6971"
version = "v1.0.13"
revision = "0d6285554e726cc0620cbecc7e6969c945dcc63b"
version = "v1.0.17"
[[projects]]
name = "github.com/dgrijalva/jwt-go"
@@ -62,8 +58,14 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
[[projects]]
name = "github.com/go-ini/ini"
packages = ["."]
revision = "e7fea39b01aea8d5671f6858f0532f56e8bff3a5"
version = "v1.27.0"
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
version = "v1.28.2"
[[projects]]
name = "github.com/go-redis/redis"
packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"]
revision = "19c1c2272e00c1aaa903cf574c746cd449f9cd3c"
version = "v6.5.7"
[[projects]]
name = "github.com/go-sql-driver/mysql"
@@ -108,22 +110,34 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
version = "0.2.2"
[[projects]]
branch = "master"
name = "github.com/jroimartin/gocui"
packages = ["."]
revision = "612b0b2987ec1a6af46d7008cef1efd4b3898346"
revision = "4e9ce9a8e26f2ef33dfe297dbdfca148733b6b9b"
version = "v0.3.0"
[[projects]]
branch = "master"
name = "github.com/k0kubun/pp"
packages = ["."]
revision = "027a6d1765d673d337e687394dbe780dd64e2a1e"
version = "v2.3.0"
revision = "d1532fc5d94ecdf2da29e24d7b99721f3287de4a"
[[projects]]
branch = "master"
name = "github.com/knqyf263/go-deb-version"
packages = ["."]
revision = "9865fe14d09b1c729188ac810466dde90f897ee3"
[[projects]]
branch = "master"
name = "github.com/knqyf263/go-rpm-version"
packages = ["."]
revision = "74609b86c936dff800c69ec89fcf4bc52d5f13a4"
[[projects]]
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
packages = ["config","db","jvn","log","models","nvd","util"]
revision = "d47709be4cc24d2c77a7be9096dcfcf211ba1d57"
revision = "c20fa7e1d07f7c700baf12c855f7fcf61525f1b6"
[[projects]]
name = "github.com/kotakanbe/go-pingscanner"
@@ -131,23 +145,35 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
revision = "641dc2cc2d3cbf295dad356667b74c69bcbd6f70"
version = "v0.1.0"
[[projects]]
branch = "master"
name = "github.com/kotakanbe/goval-dictionary"
packages = ["config","db","db/rdb","log","models"]
revision = "3523cc174e68f285d0572d07c68ffa3a9290799c"
[[projects]]
branch = "master"
name = "github.com/kotakanbe/logrus-prefixed-formatter"
packages = ["."]
revision = "e7519b8c80ba008a3bfc57ffa31232bf2a77f455"
revision = "75edb2e85a38873f0318be05a458446681d1022f"
[[projects]]
name = "github.com/labstack/gommon"
packages = ["color","log"]
revision = "779b8a8b9850a97acba6a3fe20feb628c39e17c1"
version = "0.2.2"
[[projects]]
branch = "master"
name = "github.com/lib/pq"
packages = [".","hstore","oid"]
revision = "2704adc878c21e1329f46f6e56a1c387d788ff94"
revision = "e42267488fe361b9dc034be7a6bffef5b195bceb"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "d228849504861217f796da67fae4f6e347643f15"
version = "v0.0.7"
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
@@ -183,7 +209,7 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
branch = "master"
name = "github.com/nsf/termbox-go"
packages = ["."]
revision = "7994c181db7761ca3c67a217068cf31826113f5f"
revision = "4ed959e0540971545eddb8c75514973d670cf739"
[[projects]]
name = "github.com/parnurzeal/gorequest"
@@ -200,29 +226,66 @@ memo = "404d058cf6b46d820e153afc5721e1ab2aa2b10ec345d969b9c460097f99add3"
[[projects]]
name = "github.com/rifflock/lfshook"
packages = ["."]
revision = "2adb3e0c4ddd8778c4adde609d2dfd4fbe6096ea"
version = "1.6"
revision = "6844c808343cb8fa357d7f141b1b990e05d24e41"
version = "1.7"
[[projects]]
name = "github.com/satori/uuid"
packages = ["."]
revision = "879c5887cd475cd7864858769793b2ceb0d44feb"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "84573d5f03ab3740f524c7842c3a9bf617961d32"
[[projects]]
branch = "master"
name = "github.com/valyala/bytebufferpool"
packages = ["."]
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
[[projects]]
branch = "master"
name = "github.com/valyala/fasttemplate"
packages = ["."]
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
[[projects]]
branch = "master"
name = "github.com/ymomoi/goval-parser"
packages = ["oval"]
revision = "0a0be1dd9d0855b50be0be5a10ad3085382b6d59"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
revision = "ed779e1bec0180cdfce8135ca6558067b388777b"
revision = "eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","idna","publicsuffix"]
revision = "d1e1b351919c6738fdeb9893d5c998b161464f0c"
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "f3918c30c5c2cb527c0b071a27c35120a6c0719a"
packages = ["unix","windows"]
revision = "07c182904dbd53199946ba614a412c61d3c548f5"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
revision = "f4b4367115ec2de254587813edaa901bc1c723a8"
revision = "e56139fd9c5bc7244c76116c68e500765bb6db6b"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "36d700add80d36c56484ed310b9a7e622b3e308ab22eb42bdfb02fd8f5c90407"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -1,28 +1,90 @@
[[dependencies]]
branch = "master"
name = "github.com/Azure/azure-storage-go"
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[dependencies]]
branch = "master"
[[constraint]]
name = "github.com/BurntSushi/toml"
version = "0.3.0"
[[dependencies]]
[[constraint]]
name = "github.com/asaskevich/govalidator"
version = "6.0.0"
[[constraint]]
name = "github.com/boltdb/bolt"
version = "1.3.1"
[[constraint]]
name = "github.com/cenkalti/backoff"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "github.com/Sirupsen/logrus"
name = "github.com/google/subcommands"
[[dependencies]]
name = "github.com/aws/aws-sdk-go"
revision = "5b341290c488aa6bd76b335d819b4a68516ec3ab"
[[dependencies]]
[[constraint]]
branch = "master"
name = "github.com/gosuri/uitable"
[[constraint]]
branch = "master"
name = "github.com/howeyc/gopass"
[[constraint]]
name = "github.com/jroimartin/gocui"
version = "0.3.0"
[[dependencies]]
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
name = "github.com/k0kubun/pp"
[[dependencies]]
[[constraint]]
branch = "master"
name = "github.com/knqyf263/go-deb-version"
[[constraint]]
branch = "master"
name = "github.com/knqyf263/go-rpm-version"
[[constraint]]
name = "github.com/kotakanbe/go-pingscanner"
version = "0.1.0"
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/logrus-prefixed-formatter"
[[constraint]]
name = "github.com/parnurzeal/gorequest"
version = "0.2.15"
[[constraint]]
name = "github.com/rifflock/lfshook"
version = "1.7.0"
[[constraint]]
branch = "master"
name = "github.com/sirupsen/logrus"
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"

File diff suppressed because it is too large Load Diff

766
README.md

File diff suppressed because it is too large Load Diff

2
cache/bolt.go vendored
View File

@@ -22,9 +22,9 @@ import (
"fmt"
"time"
"github.com/Sirupsen/logrus"
"github.com/boltdb/bolt"
"github.com/future-architect/vuls/util"
"github.com/sirupsen/logrus"
)
// Bolt holds a pointer of bolt.DB

6
cache/bolt_test.go vendored
View File

@@ -22,10 +22,10 @@ import (
"reflect"
"testing"
"github.com/Sirupsen/logrus"
"github.com/boltdb/bolt"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/sirupsen/logrus"
)
const path = "/tmp/vuls-test-cache-11111111.db"
@@ -37,8 +37,8 @@ var meta = Meta{
Family: "ubuntu",
Release: "16.04",
},
Packs: []models.PackageInfo{
{
Packs: models.Packages{
"apt": {
Name: "apt",
Version: "1",
},

12
cache/db.go vendored
View File

@@ -45,16 +45,6 @@ type Cache interface {
type Meta struct {
Name string
Distro config.Distro
Packs []models.PackageInfo
Packs models.Packages
CreatedAt time.Time
}
// FindPack search a PackageInfo
func (m Meta) FindPack(name string) (pack models.PackageInfo, found bool) {
for _, p := range m.Packs {
if name == p.Name {
return p, true
}
}
return pack, false
}

View File

@@ -1,21 +0,0 @@
package commands
import (
"fmt"
"github.com/howeyc/gopass"
)
func getPasswd(prompt string) (string, error) {
for {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
if err != nil {
return "", fmt.Errorf("Failed to read password")
}
if 0 < len(pass) {
return string(pass[:]), nil
}
}
}

View File

@@ -36,6 +36,7 @@ type ConfigtestCmd struct {
logDir string
askKeyPassword bool
containersOnly bool
deep bool
sshNative bool
httpProxy string
timeoutSec int
@@ -53,6 +54,7 @@ func (*ConfigtestCmd) Synopsis() string { return "Test configuration" }
func (*ConfigtestCmd) Usage() string {
return `configtest:
configtest
[-deep]
[-config=/path/to/config.toml]
[-log-dir=/path/to/log]
[-ask-key-password]
@@ -86,6 +88,8 @@ func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
"Ask ssh privatekey password before scanning",
)
f.BoolVar(&p.deep, "deep", false, "Config test for deep scan mode")
f.StringVar(
&p.httpProxy,
"http-proxy",
@@ -133,6 +137,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
c.Conf.SSHNative = p.sshNative
c.Conf.HTTPProxy = p.httpProxy
c.Conf.ContainersOnly = p.containersOnly
c.Conf.Deep = p.deep
var servernames []string
if 0 < len(f.Args()) {
@@ -169,7 +174,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
return subcommands.ExitFailure
}
util.Log.Info("Checking dependendies...")
util.Log.Info("Checking dependencies...")
scan.CheckDependencies(p.timeoutSec)
util.Log.Info("Checking sudo settings...")

View File

@@ -27,8 +27,8 @@ import (
"github.com/google/subcommands"
"github.com/Sirupsen/logrus"
ps "github.com/kotakanbe/go-pingscanner"
"github.com/sirupsen/logrus"
)
// DiscoverCmd is Subcommand of host discovery mode
@@ -87,9 +87,9 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
return subcommands.ExitSuccess
}
// Output the tmeplate of config.toml
// Output the template of config.toml
func printConfigToml(ips []string) (err error) {
const tomlTempale = `
const tomlTemplate = `
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
@@ -99,13 +99,13 @@ authUser = "username"
notifyUsers = ["@username"]
[email]
smtpAddr = "smtp.gmail.com"
smtpAddr = "smtp.example.com"
smtpPort = "587"
user = "username"
password = "password"
from = "from@address.com"
to = ["to@address.com"]
cc = ["cc@address.com"]
from = "from@example.com"
to = ["to@example.com"]
cc = ["cc@example.com"]
subjectPrefix = "[vuls]"
[default]
@@ -140,7 +140,7 @@ host = "{{$ip}}"
# ["key", "value"],
#]
#[servers.{{index $names $i}}.containers]
#type = "docker" #or "lxd" defualt: docker
#type = "docker" #or "lxd" default: docker
#includes = ["${running}"]
#excludes = ["container_name_a", "4aa37a8b63b9"]
@@ -149,7 +149,7 @@ host = "{{$ip}}"
`
var tpl *template.Template
if tpl, err = template.New("tempalte").Parse(tomlTempale); err != nil {
if tpl, err = template.New("template").Parse(tomlTemplate); err != nil {
return
}
@@ -167,7 +167,7 @@ host = "{{$ip}}"
}
a.Names = names
fmt.Println("# Create config.toml using below and then ./vuls --config=/path/to/config.toml")
fmt.Println("# Create config.toml using below and then ./vuls -config=/path/to/config.toml")
if err = tpl.Execute(os.Stdout, a); err != nil {
return
}

View File

@@ -27,6 +27,7 @@ import (
"strings"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/report"
"github.com/google/subcommands"
)
@@ -68,9 +69,8 @@ func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
c.Conf.DebugSQL = p.debugSQL
c.Conf.ResultsDir = p.resultsDir
var err error
var dirs jsonDirs
if dirs, err = lsValidJSONDirs(); err != nil {
dirs, err := report.ListValidJSONDirs()
if err != nil {
return subcommands.ExitFailure
}
for _, d := range dirs {

View File

@@ -25,8 +25,8 @@ import (
"path/filepath"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
@@ -46,9 +46,13 @@ type ReportCmd struct {
ignoreUnscoredCves bool
httpProxy string
cvedbtype string
cvedbpath string
cvedbURL string
cveDBType string
cveDBPath string
cveDBURL string
ovalDBType string
ovalDBPath string
ovalDBURL string
toSlack bool
toEMail bool
@@ -65,9 +69,10 @@ type ReportCmd struct {
gzip bool
awsProfile string
awsS3Bucket string
awsRegion string
awsProfile string
awsS3Bucket string
awsS3ResultsDir string
awsRegion string
azureAccount string
azureKey string
@@ -96,6 +101,9 @@ func (*ReportCmd) Usage() string {
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
@@ -114,7 +122,8 @@ func (*ReportCmd) Usage() string {
[-aws-profile=default]
[-aws-region=us-west-2]
[-aws-s3-bucket=bucket_name]
[-azure-account=accout]
[-aws-s3-results-dir=/bucket/path/to/results]
[-azure-account=account]
[-azure-key=key]
[-azure-container=container]
[-http-proxy=http://192.168.0.1:8080]
@@ -150,23 +159,42 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
"Refresh CVE information in JSON file under results dir")
f.StringVar(
&p.cvedbtype,
&p.cveDBType,
"cvedb-type",
"sqlite3",
"DB type for fetching CVE dictionary (sqlite3, mysql or postgres)")
defaultCveDBPath := filepath.Join(wd, "cve.sqlite3")
f.StringVar(
&p.cvedbpath,
&p.cveDBPath,
"cvedb-path",
defaultCveDBPath,
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
f.StringVar(
&p.cvedbURL,
&p.cveDBURL,
"cvedb-url",
"",
"http://cve-dictionary.com:8080 or DB connection string")
"http://cve-dictionary.com:1323 or mysql connection string")
f.StringVar(
&p.ovalDBType,
"ovaldb-type",
"sqlite3",
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
f.StringVar(
&p.ovalDBPath,
"ovaldb-path",
defaultOvalDBPath,
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
f.StringVar(
&p.ovalDBURL,
"ovaldb-url",
"",
"http://goval-dictionary.com:1324 or mysql connection string")
f.Float64Var(
&p.cvssScoreOver,
@@ -237,6 +265,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&p.awsProfile, "aws-profile", "default", "AWS profile to use")
f.StringVar(&p.awsRegion, "aws-region", "us-east-1", "AWS region to use")
f.StringVar(&p.awsS3Bucket, "aws-s3-bucket", "", "S3 bucket name")
f.StringVar(&p.awsS3ResultsDir, "aws-s3-results-dir", "", "/bucket/path/to/results")
f.BoolVar(&p.toAzureBlob,
"to-azure-blob",
@@ -273,15 +302,18 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.Lang = p.lang
c.Conf.ResultsDir = p.resultsDir
c.Conf.CveDBType = p.cvedbtype
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDBURL = p.cvedbURL
c.Conf.RefreshCve = p.refreshCve
c.Conf.Diff = p.diff
c.Conf.CveDBType = p.cveDBType
c.Conf.CveDBPath = p.cveDBPath
c.Conf.CveDBURL = p.cveDBURL
c.Conf.OvalDBType = p.ovalDBType
c.Conf.OvalDBPath = p.ovalDBPath
c.Conf.OvalDBURL = p.ovalDBURL
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.HTTPProxy = p.httpProxy
c.Conf.Pipe = p.pipe
c.Conf.FormatXML = p.formatXML
c.Conf.FormatJSON = p.formatJSON
c.Conf.FormatOneEMail = p.formatOneEMail
@@ -291,13 +323,14 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.GZIP = p.gzip
c.Conf.Diff = p.diff
c.Conf.Pipe = p.pipe
var dir string
var err error
if p.diff {
dir, err = jsonDir([]string{})
dir, err = report.JSONDir([]string{})
} else {
dir, err = jsonDir(f.Args())
dir, err = report.JSONDir(f.Args())
}
if err != nil {
util.Log.Errorf("Failed to read from JSON: %s", err)
@@ -327,6 +360,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.AwsRegion = p.awsRegion
c.Conf.AwsProfile = p.awsProfile
c.Conf.S3Bucket = p.awsS3Bucket
c.Conf.S3ResultsDir = p.awsS3ResultsDir
if err := report.CheckIfBucketExists(); err != nil {
util.Log.Errorf("Check if there is a bucket beforehand: %s, err: %s", c.Conf.S3Bucket, err)
return subcommands.ExitUsageError
@@ -347,7 +381,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.AzureContainer = p.azureContainer
if len(c.Conf.AzureContainer) == 0 {
util.Log.Error("Azure storage container name is requied with --azure-container option")
util.Log.Error("Azure storage container name is required with -azure-container option")
return subcommands.ExitUsageError
}
if err := report.CheckIfAzureContainerExists(); err != nil {
@@ -366,9 +400,9 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
if !c.Conf.ValidateOnReport() {
return subcommands.ExitUsageError
}
if ok, err := cveapi.CveClient.CheckHealth(); !ok {
if err := report.CveClient.CheckHealth(); err != nil {
util.Log.Errorf("CVE HTTP server is not running. err: %s", err)
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with --cvedb-path option")
util.Log.Errorf("Run go-cve-dictionary as server mode before reporting or run with -cvedb-path option")
return subcommands.ExitFailure
}
if c.Conf.CveDBURL != "" {
@@ -379,66 +413,25 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
}
}
var history models.ScanHistory
history, err = loadOneScanHistory(dir)
if err != nil {
if c.Conf.OvalDBURL != "" {
err := oval.Base{}.CheckHTTPHealth()
if err != nil {
util.Log.Errorf("OVAL HTTP server is not running. err: %s", err)
util.Log.Errorf("Run goval-dictionary as server mode before reporting or run with -ovaldb-path option")
return subcommands.ExitFailure
}
}
var res models.ScanResults
if res, err = report.LoadScanResults(dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
util.Log.Infof("Loaded: %s", dir)
var results []models.ScanResult
for _, r := range history.ScanResults {
if p.refreshCve || needToRefreshCve(r) {
util.Log.Debugf("need to refresh")
if c.Conf.CveDBType == "sqlite3" && c.Conf.CveDBURL == "" {
if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) {
util.Log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
c.Conf.CveDBPath)
return subcommands.ExitFailure
}
}
filled, err := fillCveInfoFromCveDB(r)
if err != nil {
util.Log.Errorf("Failed to fill CVE information: %s", err)
return subcommands.ExitFailure
}
filled.Lang = c.Conf.Lang
if err := overwriteJSONFile(dir, *filled); err != nil {
util.Log.Errorf("Failed to write JSON: %s", err)
return subcommands.ExitFailure
}
results = append(results, *filled)
} else {
util.Log.Debugf("no need to refresh")
results = append(results, r)
}
}
if p.diff {
currentHistory := models.ScanHistory{ScanResults: results}
previousHistory, err := loadPreviousScanHistory(currentHistory)
if err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
history, err = diff(currentHistory, previousHistory)
if err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
results = []models.ScanResult{}
for _, r := range history.ScanResults {
filled, _ := r.FillCveDetail()
results = append(results, *filled)
}
}
var res models.ScanResults
for _, r := range results {
res = append(res, r.FilterByCvssOver())
if res, err = report.FillCveInfos(res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
for _, w := range reports {

View File

@@ -43,6 +43,7 @@ type ScanCmd struct {
httpProxy string
askKeyPassword bool
containersOnly bool
deep bool
skipBroken bool
sshNative bool
pipe bool
@@ -60,6 +61,7 @@ func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
func (*ScanCmd) Usage() string {
return `scan:
scan
[-deep]
[-config=/path/to/config.toml]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
@@ -132,6 +134,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.deep,
"deep",
false,
"Deep scan mode. Scan accuracy improves and scanned information becomes richer. Since analysis of changelog, issue commands requiring sudo, but it may be slower and high load on the target server")
f.BoolVar(
&p.pipe,
"pipe",
@@ -149,7 +157,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
&p.scanTimeoutSec,
"timeout-scan",
120*60,
"Number of seconds for scaning vulnerabilities for all servers",
"Number of seconds for scanning vulnerabilities for all servers",
)
}
@@ -223,6 +231,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
c.Conf.SSHNative = p.sshNative
c.Conf.HTTPProxy = p.httpProxy
c.Conf.ContainersOnly = p.containersOnly
c.Conf.Deep = p.deep
c.Conf.SkipBroken = p.skipBroken
util.Log.Info("Validating config...")

View File

@@ -38,12 +38,17 @@ type TuiCmd struct {
configPath string
logDir string
resultsDir string
refreshCve bool
resultsDir string
refreshCve bool
cvedbtype string
cvedbpath string
cveDictionaryURL string
ovalDBType string
ovalDBPath string
ovalDBURL string
pipe bool
}
@@ -51,7 +56,7 @@ type TuiCmd struct {
func (*TuiCmd) Name() string { return "tui" }
// Synopsis return synopsis
func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites" }
func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabilities" }
// Usage return usage
func (*TuiCmd) Usage() string {
@@ -61,6 +66,9 @@ func (*TuiCmd) Usage() string {
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 or DB connection string]
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-refresh-cve]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
@@ -110,7 +118,26 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
&p.cveDictionaryURL,
"cvedb-url",
"",
"http://cve-dictionary.com:8080 or DB connection string")
"http://cve-dictionary.example.com:1323 or mysql connection string")
f.StringVar(
&p.ovalDBType,
"ovaldb-type",
"sqlite3",
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
f.StringVar(
&p.ovalDBPath,
"ovaldb-path",
defaultOvalDBPath,
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
f.StringVar(
&p.ovalDBURL,
"ovaldb-url",
"",
"http://goval-dictionary.example.com:1324 or mysql connection string")
f.BoolVar(
&p.pipe,
@@ -139,6 +166,9 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.CveDBType = p.cvedbtype
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDBURL = p.cveDictionaryURL
c.Conf.OvalDBType = p.ovalDBType
c.Conf.OvalDBPath = p.ovalDBPath
c.Conf.OvalDBURL = p.ovalDBURL
log.Info("Validating config...")
if !c.Conf.ValidateOnTui() {
@@ -146,44 +176,22 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
}
c.Conf.Pipe = p.pipe
jsonDir, err := jsonDir(f.Args())
dir, err := report.JSONDir(f.Args())
if err != nil {
log.Errorf("Failed to read json dir under results: %s", err)
util.Log.Errorf("Failed to read from JSON: %s", err)
return subcommands.ExitFailure
}
history, err := loadOneScanHistory(jsonDir)
if err != nil {
log.Errorf("Failed to read from JSON: %s", err)
var res models.ScanResults
if res, err = report.LoadScanResults(dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
util.Log.Infof("Loaded: %s", dir)
var results []models.ScanResult
for _, r := range history.ScanResults {
if p.refreshCve || needToRefreshCve(r) {
if c.Conf.CveDBType == "sqlite3" {
if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) {
log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s",
c.Conf.CveDBPath)
return subcommands.ExitFailure
}
}
filled, err := fillCveInfoFromCveDB(r)
if err != nil {
log.Errorf("Failed to fill CVE information: %s", err)
return subcommands.ExitFailure
}
if err := overwriteJSONFile(jsonDir, *filled); err != nil {
log.Errorf("Failed to write JSON: %s", err)
return subcommands.ExitFailure
}
results = append(results, *filled)
} else {
results = append(results, r)
}
if res, err = report.FillCveInfos(res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
history.ScanResults = results
return report.RunTui(history)
return report.RunTui(res)
}

View File

@@ -18,316 +18,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
"github.com/howeyc/gopass"
)
// jsonDirPattern is file name pattern of JSON directory
// 2016-11-16T10:43:28+09:00
// 2016-11-16T10:43:28Z
var jsonDirPattern = regexp.MustCompile(
`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`)
// JSONDirs is array of json files path.
type jsonDirs []string
// sort as recent directories are at the head
func (d jsonDirs) Len() int {
return len(d)
}
func (d jsonDirs) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
func (d jsonDirs) Less(i, j int) bool {
return d[j] < d[i]
}
// getValidJSONDirs return valid json directory as array
// Returned array is sorted so that recent directories are at the head
func lsValidJSONDirs() (dirs jsonDirs, err error) {
var dirInfo []os.FileInfo
if dirInfo, err = ioutil.ReadDir(c.Conf.ResultsDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", c.Conf.ResultsDir, err)
return
}
for _, d := range dirInfo {
if d.IsDir() && jsonDirPattern.MatchString(d.Name()) {
jsonDir := filepath.Join(c.Conf.ResultsDir, d.Name())
dirs = append(dirs, jsonDir)
}
}
sort.Sort(dirs)
return
}
// jsonDir returns
// If there is an arg, check if it is a valid format and return the corresponding path under results.
// If arg passed via PIPE (such as history subcommand), return that path.
// Otherwise, returns the path of the latest directory
func jsonDir(args []string) (string, error) {
var err error
var dirs jsonDirs
if 0 < len(args) {
if dirs, err = lsValidJSONDirs(); err != nil {
return "", err
}
path := filepath.Join(c.Conf.ResultsDir, args[0])
for _, d := range dirs {
ss := strings.Split(d, string(os.PathSeparator))
timedir := ss[len(ss)-1]
if timedir == args[0] {
return path, nil
}
}
return "", fmt.Errorf("Invalid path: %s", path)
}
// PIPE
if c.Conf.Pipe {
bytes, err := ioutil.ReadAll(os.Stdin)
func getPasswd(prompt string) (string, error) {
for {
fmt.Print(prompt)
pass, err := gopass.GetPasswdMasked()
if err != nil {
return "", fmt.Errorf("Failed to read stdin: %s", err)
return "", fmt.Errorf("Failed to read password")
}
fields := strings.Fields(string(bytes))
if 0 < len(fields) {
return filepath.Join(c.Conf.ResultsDir, fields[0]), nil
if 0 < len(pass) {
return string(pass[:]), nil
}
return "", fmt.Errorf("Stdin is invalid: %s", string(bytes))
}
// returns latest dir when no args or no PIPE
if dirs, err = lsValidJSONDirs(); err != nil {
return "", err
}
if len(dirs) == 0 {
return "", fmt.Errorf("No results under %s",
c.Conf.ResultsDir)
}
return dirs[0], nil
}
// loadOneServerScanResult read JSON data of one server
func loadOneServerScanResult(jsonFile string) (result models.ScanResult, err error) {
var data []byte
if data, err = ioutil.ReadFile(jsonFile); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonFile, err)
return
}
if json.Unmarshal(data, &result) != nil {
err = fmt.Errorf("Failed to parse %s: %s", jsonFile, err)
}
return
}
// loadOneScanHistory read JSON data
func loadOneScanHistory(jsonDir string) (scanHistory models.ScanHistory, err error) {
var results []models.ScanResult
var files []os.FileInfo
if files, err = ioutil.ReadDir(jsonDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonDir, err)
return
}
for _, f := range files {
if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") {
continue
}
var r models.ScanResult
path := filepath.Join(jsonDir, f.Name())
if r, err = loadOneServerScanResult(path); err != nil {
return
}
results = append(results, r)
}
if len(results) == 0 {
err = fmt.Errorf("There is no json file under %s", jsonDir)
return
}
scanHistory = models.ScanHistory{
ScanResults: results,
}
return
}
func fillCveInfoFromCveDB(r models.ScanResult) (*models.ScanResult, error) {
var err error
var vs []models.VulnInfo
sInfo := c.Conf.Servers[r.ServerName]
vs, err = scanVulnByCpeNames(sInfo.CpeNames, r.ScannedCves)
if err != nil {
return nil, err
}
r.ScannedCves = vs
return r.FillCveDetail()
}
func loadPreviousScanHistory(current models.ScanHistory) (previous models.ScanHistory, err error) {
var dirs jsonDirs
if dirs, err = lsValidJSONDirs(); err != nil {
return
}
for _, result := range current.ScanResults {
for _, dir := range dirs[1:] {
var r models.ScanResult
path := filepath.Join(dir, result.ServerName+".json")
if r, err = loadOneServerScanResult(path); err != nil {
continue
}
if r.Family == result.Family && r.Release == result.Release {
previous.ScanResults = append(previous.ScanResults, r)
break
}
}
}
return previous, nil
}
func diff(currentHistory, previousHistory models.ScanHistory) (diffHistory models.ScanHistory, err error) {
for _, currentResult := range currentHistory.ScanResults {
found := false
var previousResult models.ScanResult
for _, previousResult = range previousHistory.ScanResults {
if currentResult.ServerName == previousResult.ServerName {
found = true
break
}
}
if found {
currentResult.ScannedCves = getNewCves(previousResult, currentResult)
currentResult.KnownCves = []models.CveInfo{}
currentResult.UnknownCves = []models.CveInfo{}
currentResult.Packages = models.PackageInfoList{}
for _, s := range currentResult.ScannedCves {
currentResult.Packages = append(currentResult.Packages, s.Packages...)
}
currentResult.Packages = currentResult.Packages.UniqByName()
}
diffHistory.ScanResults = append(diffHistory.ScanResults, currentResult)
}
return diffHistory, err
}
func getNewCves(previousResult, currentResult models.ScanResult) (newVulninfos []models.VulnInfo) {
previousCveIDsSet := map[string]bool{}
for _, previousVulnInfo := range previousResult.ScannedCves {
previousCveIDsSet[previousVulnInfo.CveID] = true
}
for _, v := range currentResult.ScannedCves {
if previousCveIDsSet[v.CveID] {
if isCveInfoUpdated(currentResult, previousResult, v.CveID) {
newVulninfos = append(newVulninfos, v)
}
} else {
newVulninfos = append(newVulninfos, v)
}
}
return
}
func isCveInfoUpdated(currentResult, previousResult models.ScanResult, CveID string) bool {
type lastModified struct {
Nvd time.Time
Jvn time.Time
}
previousModifies := lastModified{}
for _, c := range previousResult.KnownCves {
if CveID == c.CveID {
previousModifies.Nvd = c.CveDetail.Nvd.LastModifiedDate
previousModifies.Jvn = c.CveDetail.Jvn.LastModifiedDate
}
}
currentModifies := lastModified{}
for _, c := range currentResult.KnownCves {
if CveID == c.CveDetail.CveID {
currentModifies.Nvd = c.CveDetail.Nvd.LastModifiedDate
currentModifies.Jvn = c.CveDetail.Jvn.LastModifiedDate
}
}
return !currentModifies.Nvd.Equal(previousModifies.Nvd) ||
!currentModifies.Jvn.Equal(previousModifies.Jvn)
}
func overwriteJSONFile(dir string, r models.ScanResult) error {
before := c.Conf.FormatJSON
beforeDiff := c.Conf.Diff
c.Conf.FormatJSON = true
c.Conf.Diff = false
w := report.LocalFileWriter{CurrentDir: dir}
if err := w.Write(r); err != nil {
return fmt.Errorf("Failed to write summary report: %s", err)
}
c.Conf.FormatJSON = before
c.Conf.Diff = beforeDiff
return nil
}
func scanVulnByCpeNames(cpeNames []string, scannedVulns []models.VulnInfo) ([]models.VulnInfo, error) {
// To remove duplicate
set := map[string]models.VulnInfo{}
for _, v := range scannedVulns {
set[v.CveID] = v
}
for _, name := range cpeNames {
details, err := cveapi.CveClient.FetchCveDetailsByCpeName(name)
if err != nil {
return nil, err
}
for _, detail := range details {
if val, ok := set[detail.CveID]; ok {
names := val.CpeNames
names = util.AppendIfMissing(names, name)
val.CpeNames = names
val.Confidence = models.CpeNameMatch
set[detail.CveID] = val
} else {
v := models.VulnInfo{
CveID: detail.CveID,
CpeNames: []string{name},
Confidence: models.CpeNameMatch,
}
v.NilSliceToEmpty()
set[detail.CveID] = v
}
}
}
vinfos := []models.VulnInfo{}
for key := range set {
vinfos = append(vinfos, set[key])
}
return vinfos, nil
}
func needToRefreshCve(r models.ScanResult) bool {
return r.Lang != c.Conf.Lang || len(r.KnownCves) == 0 &&
len(r.UnknownCves) == 0 &&
len(r.IgnoredCves) == 0
}

View File

@@ -16,279 +16,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package commands
import (
"testing"
"time"
"reflect"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
cve "github.com/kotakanbe/go-cve-dictionary/models"
)
func TestDiff(t *testing.T) {
atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
var tests = []struct {
inCurrent models.ScanHistory
inPrevious models.ScanHistory
out models.ScanResult
}{
{
models.ScanHistory{
ScanResults: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
CveID: "CVE-2012-6702",
Packages: models.PackageInfoList{
{
Name: "libexpat1",
Version: "2.1.0-7",
Release: "",
NewVersion: "2.1.0-7ubuntu0.16.04.2",
NewRelease: "",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
{
CveID: "CVE-2014-9761",
Packages: models.PackageInfoList{
{
Name: "libc-bin",
Version: "2.21-0ubuntu5",
Release: "",
NewVersion: "2.23-0ubuntu5",
NewRelease: "",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
KnownCves: []models.CveInfo{},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
Packages: models.PackageInfoList{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
},
models.ScanHistory{
ScanResults: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
CveID: "CVE-2012-6702",
Packages: models.PackageInfoList{
{
Name: "libexpat1",
Version: "2.1.0-7",
Release: "",
NewVersion: "2.1.0-7ubuntu0.16.04.2",
NewRelease: "",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
{
CveID: "CVE-2014-9761",
Packages: models.PackageInfoList{
{
Name: "libc-bin",
Version: "2.21-0ubuntu5",
Release: "",
NewVersion: "2.23-0ubuntu5",
NewRelease: "",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
KnownCves: []models.CveInfo{},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
Packages: models.PackageInfoList{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
},
models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
KnownCves: []models.CveInfo{},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
// Packages: models.PackageInfoList{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
{
models.ScanHistory{
ScanResults: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
CveID: "CVE-2016-6662",
Packages: models.PackageInfoList{
{
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
KnownCves: []models.CveInfo{
{
CveDetail: cve.CveDetail{
CveID: "CVE-2016-6662",
Nvd: cve.Nvd{
LastModifiedDate: time.Date(2016, 1, 1, 0, 0, 0, 0, time.Local),
},
},
VulnInfo: models.VulnInfo{
CveID: "CVE-2016-6662",
},
},
},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
},
},
},
models.ScanHistory{
ScanResults: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
CveID: "CVE-2016-6662",
Packages: models.PackageInfoList{
{
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
KnownCves: []models.CveInfo{
{
CveDetail: cve.CveDetail{
CveID: "CVE-2016-6662",
Nvd: cve.Nvd{
LastModifiedDate: time.Date(2017, 3, 15, 13, 40, 57, 0, time.Local),
},
},
VulnInfo: models.VulnInfo{
CveID: "CVE-2016-6662",
},
},
},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
},
},
},
models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
CveID: "CVE-2016-6662",
Packages: models.PackageInfoList{
{
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
},
},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
KnownCves: []models.CveInfo{},
UnknownCves: []models.CveInfo{},
IgnoredCves: []models.CveInfo{},
Packages: models.PackageInfoList{
models.PackageInfo{
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
Changelog: models.Changelog{
Contents: "",
Method: "",
},
},
},
},
},
}
var s models.ScanHistory
for _, tt := range tests {
s, _ = diff(tt.inCurrent, tt.inPrevious)
for _, actual := range s.ScanResults {
if !reflect.DeepEqual(actual, tt.out) {
h := pp.Sprint(actual)
x := pp.Sprint(tt.out)
t.Errorf("diff result : \n %s \n output result : \n %s", h, x)
}
}
}
}

View File

@@ -19,17 +19,47 @@ package config
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
valid "github.com/asaskevich/govalidator"
log "github.com/sirupsen/logrus"
)
// Conf has Configuration
var Conf Config
const (
// RedHat is
RedHat = "redhat"
// Debian is
Debian = "debian"
// Ubuntu is
Ubuntu = "ubuntu"
// CentOS is
CentOS = "centos"
// Fedora is
Fedora = "fedora"
// Amazon is
Amazon = "amazon"
// Oracle is
Oracle = "oracle"
// FreeBSD is
FreeBSD = "freebsd"
// Raspbian is
Raspbian = "raspbian"
)
//Config is struct of Configuration
type Config struct {
Debug bool
@@ -46,17 +76,25 @@ type Config struct {
SSHNative bool
ContainersOnly bool
Deep bool
SkipBroken bool
HTTPProxy string `valid:"url"`
LogDir string
ResultsDir string
CveDBType string
CveDBPath string
CveDBURL string
CveDBType string
CveDBPath string
CveDBURL string
OvalDBType string
OvalDBPath string
OvalDBURL string
CacheDBPath string
RefreshCve bool
FormatXML bool
FormatJSON bool
FormatOneEMail bool
@@ -66,12 +104,13 @@ type Config struct {
GZIP bool
AwsProfile string
AwsRegion string
S3Bucket string
AwsProfile string
AwsRegion string
S3Bucket string
S3ResultsDir string
AzureAccount string
AzureKey string
AzureKey string `json:"-"`
AzureContainer string
Pipe bool
@@ -155,26 +194,17 @@ func (c Config) ValidateOnReport() bool {
}
}
switch c.CveDBType {
case "sqlite3":
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB(CVE-Dictionary) path must be a *Absolute* file path. -cvedb-path: %s",
c.CveDBPath))
if err := validateDB("cvedb", c.CveDBType, c.CveDBPath, c.CveDBURL); err != nil {
errs = append(errs, err)
}
if c.CveDBType == "sqlite3" {
if _, err := os.Stat(c.CveDBPath); os.IsNotExist(err) {
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDBPath))
}
case "mysql":
if c.CveDBURL == "" {
errs = append(errs, fmt.Errorf(
`MySQL connection string is needed. -cvedb-url="user:pass@tcp(localhost:3306)/dbname"`))
}
case "postgres":
if c.CveDBURL == "" {
errs = append(errs, fmt.Errorf(
`PostgreSQL connection string is needed. -cvedb-url=""host=myhost user=user dbname=dbname sslmode=disable password=password""`))
}
default:
errs = append(errs, fmt.Errorf(
"CVE DB type must be either 'sqlite3', 'mysql' or 'postgres'. -cvedb-type: %s", c.CveDBType))
}
if err := validateDB("ovaldb", c.OvalDBType, c.OvalDBPath, c.OvalDBURL); err != nil {
errs = append(errs, err)
}
_, err := valid.ValidateStruct(c)
@@ -208,15 +238,12 @@ func (c Config) ValidateOnTui() bool {
}
}
if c.CveDBType != "sqlite3" && c.CveDBType != "mysql" && c.CveDBType != "postgres" {
errs = append(errs, fmt.Errorf(
"CVE DB type must be either 'sqlite3', 'mysql' or 'postgres'. -cve-dictionary-dbtype: %s", c.CveDBType))
if err := validateDB("cvedb", c.CveDBType, c.CveDBPath, c.CveDBURL); err != nil {
errs = append(errs, err)
}
if c.CveDBType == "sqlite3" {
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB(CVE-Dictionary) path must be a *Absolute* file path. -cve-dictionary-dbpath: %s", c.CveDBPath))
if _, err := os.Stat(c.CveDBPath); os.IsNotExist(err) {
errs = append(errs, fmt.Errorf("SQLite3 DB path (%s) is not exist: %s", "cvedb", c.CveDBPath))
}
}
@@ -227,13 +254,53 @@ func (c Config) ValidateOnTui() bool {
return len(errs) == 0
}
// validateDB validates configuration
// dictionaryDB name is 'cvedb' or 'ovaldb'
func validateDB(dictionaryDBName, dbType, dbPath, dbURL string) error {
switch dbType {
case "sqlite3":
if ok, _ := valid.IsFilePath(dbPath); !ok {
return fmt.Errorf(
"SQLite3 DB path (%s) must be a *Absolute* file path. -%s-path: %s",
dictionaryDBName,
dictionaryDBName,
dbPath)
}
case "mysql":
if dbURL == "" {
return fmt.Errorf(
`MySQL connection string is needed. -%s-url="user:pass@tcp(localhost:3306)/dbname"`,
dictionaryDBName)
}
case "postgres":
if dbURL == "" {
return fmt.Errorf(
`PostgreSQL connection string is needed. -%s-url="host=myhost user=user dbname=dbname sslmode=disable password=password"`,
dictionaryDBName)
}
case "redis":
if dbURL == "" {
return fmt.Errorf(
`Redis connection string is needed. -%s-url="redis://localhost/0"`,
dictionaryDBName)
}
default:
return fmt.Errorf(
"%s type must be either 'sqlite3', 'mysql', 'postgres' or 'redis'. -%s-type: %s",
dictionaryDBName,
dictionaryDBName,
dbType)
}
return nil
}
// SMTPConf is smtp config
type SMTPConf struct {
SMTPAddr string
SMTPPort string `valid:"port"`
User string
Password string
Password string `json:"-"`
From string
To []string
Cc []string
@@ -293,7 +360,7 @@ func (c *SMTPConf) Validate() (errs []error) {
// SlackConf is slack config
type SlackConf struct {
HookURL string `valid:"url"`
HookURL string `valid:"url" json:"-"`
Channel string `json:"channel"`
IconEmoji string `json:"icon_emoji"`
AuthUser string `json:"username"`
@@ -343,7 +410,7 @@ type ServerInfo struct {
Host string
Port string
KeyPath string
KeyPassword string
KeyPassword string `json:"-"`
CpeNames []string
DependencyCheckXMLPath string
@@ -357,7 +424,7 @@ type ServerInfo struct {
Optional [][]interface{}
// For CentOS, RHEL, Amazon
Enablerepo string
Enablerepo []string
// used internal
LogMsgAnsiColor string // DebugLog Color

View File

@@ -20,11 +20,10 @@ package config
import (
"fmt"
"os"
"strings"
"github.com/BurntSushi/toml"
log "github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
log "github.com/sirupsen/logrus"
)
// TOMLLoader loads config
@@ -164,7 +163,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.Enablerepo = d.Enablerepo
}
if len(s.Enablerepo) != 0 {
for _, repo := range strings.Split(s.Enablerepo, ",") {
for _, repo := range s.Enablerepo {
switch repo {
case "base", "updates":
// nop

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
)
@@ -35,18 +34,18 @@ func appendIfMissing(slice []string, str string) []string {
func Parse(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return []string{}, fmt.Errorf("Failed to open: %s", err)
return nil, fmt.Errorf("Failed to open: %s", err)
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
return []string{}, fmt.Errorf("Failed to read: %s", err)
return nil, fmt.Errorf("Failed to read: %s", err)
}
var anal analysis
if err := xml.Unmarshal(b, &anal); err != nil {
fmt.Errorf("Failed to unmarshal: %s", err)
return nil, fmt.Errorf("Failed to unmarshal: %s", err)
}
cpes := []string{}
@@ -59,6 +58,5 @@ func Parse(path string) ([]string, error) {
}
}
}
sort.Strings(cpes)
return cpes, nil
}

BIN
img/vuls-abstract.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.14.2-->
<!--Created by yEd 3.17-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
@@ -17,10 +17,10 @@
<node id="n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.cloud">
<y:Geometry height="50.0" width="80.0" x="918.6458873748779" y="138.1740214029948"/>
<y:Geometry height="50.0" width="80.0" x="927.4999999999995" y="148.1740214029948"/>
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="23.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -37,20 +37,20 @@
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="289.5891316731771" width="171.0" x="1053.145887374878" y="29.37945556640625"/>
<y:Geometry height="311.6660156250001" width="171.0" x="1053.145887374878" y="24.166992187499943"/>
<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="171.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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="171.0" x="0.0" 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="29" bottomF="28.96858723958337" left="29" leftF="29.35411262512207" right="27" rightF="26.64588737487793" top="27" topF="27.242065429687557"/>
<y:BorderInsets bottom="35" bottomF="35.31797281901038" left="29" leftF="29.35411262512207" right="27" rightF="26.64588737487793" 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 1</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
@@ -63,10 +63,10 @@
<node id="n1::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1097.5" y="205.0"/>
<y:Geometry height="70.0" width="85.0" x="1097.5" y="138.1740214029948"/>
<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
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.279296875" x="10.8603515625" y="18.8671875">JVN
(Japanese)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -81,10 +81,27 @@
<node id="n1::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1097.5" y="93.2875366210938"/>
<y:Geometry height="70.0" width="85.0" x="1097.5" y="60.83300781249994"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.69921875" x="27.650390625" y="25.93359375">NVD<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1097.5" y="215.51503499348968"/>
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="35.845703125" x="24.5771484375" y="25.93359375">OVAL<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -106,17 +123,17 @@
<y:Geometry height="311.6660156250001" width="171.61765336990447" x="1264.1911733150478" y="24.166992187499943"/>
<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="171.61765336990447" x="0.0" y="0.0">Distribution Support</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="171.61765336990447" x="0.0" y="0.0">Distribution 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="0" bottomF="0.0" left="27" leftF="26.617653369904474" right="13" rightF="13.0" top="0" topF="0.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="27" leftF="26.617653369904474" right="13" rightF="13.292458057404474" 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 2</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 2</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"/>
@@ -132,7 +149,7 @@
<y:Geometry height="50.0" width="100.0" x="1305.8088266849522" y="131.29349772135407"/>
<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="17.9208984375" y="8.8671875">apptitude
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">apptitude
changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -150,7 +167,7 @@ changelog<y:LabelModel>
<y:Geometry height="50.0" width="100.0" x="1306.8088266849522" y="60.83300781249994"/>
<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="17.9208984375" y="8.8671875">yum
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">yum
changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -165,11 +182,12 @@ changelog<y:LabelModel>
<node id="n2::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="100.0" x="1307.8088266849522" y="199.8717651367188"/>
<y:Geometry height="64.96826171875" width="99.38234663009553" x="1307.8088266849522" y="193.57912190755206"/>
<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="3.5859375" y="8.8671875">RHSA (RedHat)
ALAS (Amazon)<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="92.828125" x="3.277110815047763" y="9.284912109375">RHSA (RedHat)
ALAS (Amazon)
ELSA(Oracle)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -186,7 +204,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="100.0" x="1307.1911733150478" y="270.83300781250006"/>
<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="100.650390625" x="-0.3251953125" y="15.93359375">FreeBSD Support<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="100.650390625" x="-0.3251953125" y="15.93359375">FreeBSD Support<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -205,7 +223,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="56.554100036621094" x="1461.7229499816895" y="455.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="26.277050018310547" y="23.0">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="26.277050018310547" y="23.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -222,7 +240,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="64.96826171875" width="56.554100036621094" x="700.4821946087437" y="772.1779226918643"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="100.890625" x="-22.168262481689453" y="68.96826171875">System Operator<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -242,7 +260,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="37.0" width="109.57881927490234" x="543.9698349896031" y="806.1620535512393"/>
<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:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="52.78940963745117" y="41.0"/>
<y:SVGNodeProperties usingVisualBounds="true"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="2"/>
@@ -259,7 +277,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="293.63460286458337" width="322.9999999999998" x="574.4999999999998" y="53.505286524178246"/>
<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="322.9999999999998" 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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="322.9999999999998" x="0.0" y="0.0">go-cve-dictionary / goval-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"/>
@@ -269,7 +287,7 @@ ALAS (Amazon)<y:LabelModel>
<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 4</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 4</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"/>
@@ -285,7 +303,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="70.0" width="60.5" x="779.75" y="235.0"/>
<y:Fill color="#99CCFF" 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">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.25" y="33.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -293,7 +311,7 @@ ALAS (Amazon)<y:LabelModel>
<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: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="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -309,7 +327,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="101.0" x="626.7499999999997" y="246.92130214917827"/>
<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.933593749999972">HTTP server<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.943359375" x="13.5283203125" y="15.933593749999972">HTTP server<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -326,7 +344,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="80.0" x="800.0" y="139.68717128980336"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.796875" x="16.6015625" y="15.93359375">Fetcher<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -345,7 +363,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="116.06321112515802" width="97.39570164348925" x="443.18908027812887" y="624.5596727180784"/>
<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="46.69785082174462" y="120.06321112515798"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="46.69785082174462" y="120.06321112515798"/>
<y:Image alphaImage="true" refid="3"/>
</y:ImageNode>
</data>
@@ -356,7 +374,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="85.0" x="449.3869310998735" y="537.5912782806574"/>
<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="38.201171875" x="23.3994140625" y="8.8671875">Azure
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="38.201171875" x="23.3994140625" y="8.8671875">Azure
BLOB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -374,7 +392,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="388.72953539823004" y="542.5912782806574"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.828125" x="4.6007921144121156" y="10.93359375">.xml<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.828125" x="4.6007921144121156" y="10.93359375">.xml<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -390,7 +408,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="387.2223068773705" y="640.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="24.1328125" x="7.448448364412172" y="10.93359375">.txt<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="24.1328125" x="7.448448364412172" y="10.93359375">.txt<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -406,7 +424,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="387.69456235145407" y="591.4808135524652"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="3.3234483644121724" y="10.93359375">.json<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="3.3234483644121724" y="10.93359375">.json<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -422,7 +440,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="387.50427410872305" y="692.5912782806574"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="22.158203125" x="8.435753051912116" y="10.93359375">.gz<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.158203125" x="8.435753051912116" y="10.93359375">.gz<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -435,10 +453,10 @@ BLOB<y:LabelModel>
<node id="n13">
<data key="d6">
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
<y:Geometry height="24.0" width="35.0" x="672.5" y="723.6620535512393"/>
<y:Geometry height="24.0" width="35.0" x="665.4821946087437" y="740.5686144683882"/>
<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">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="15.5" y="28.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -464,7 +482,7 @@ BLOB<y:LabelModel>
<y:Geometry height="302.8032445617764" width="362.4815107458912" x="568.7592446270544" y="407.7653699066118"/>
<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="362.4815107458912" x="0.0" y="0.0">Vuls Reporting Server</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="362.4815107458912" x="0.0" y="0.0">Vuls Reporting Server</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"/>
@@ -474,7 +492,7 @@ BLOB<y:LabelModel>
<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="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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"/>
@@ -493,7 +511,7 @@ BLOB<y:LabelModel>
<y:Geometry height="251.1372289367764" width="332.4815107458912" x="583.7592446270544" y="444.4313855316118"/>
<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="332.4815107458912" 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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="332.4815107458912" 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"/>
@@ -503,7 +521,7 @@ BLOB<y:LabelModel>
<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 9</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 9</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"/>
@@ -519,7 +537,7 @@ BLOB<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="614.5184892541087" y="531.5686144683882"/>
<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="42.595703125" x="24.18365918339134" y="15.93359375">Report<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="24.18365918339134" y="15.93359375">Report<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -536,7 +554,7 @@ BLOB<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="716.024731352718" y="630.5686144683882"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="58.076171875" x="16.44342480839134" y="8.8671875">VulsRepo
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="58.076171875" x="16.44342480839134" y="8.8671875">VulsRepo
(WebUI)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -554,7 +572,7 @@ BLOB<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="810.2777338811629" y="630.5686144683882"/>
<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="23.359375" x="33.80182324589134" y="15.93359375">TUI<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="23.359375" x="33.80182324589134" y="15.93359375">TUI<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -574,7 +592,7 @@ BLOB<y:LabelModel>
<y:Geometry height="108.94242662355282" width="124.0" x="751.2407553729457" y="481.0974011566118"/>
<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="124.0" x="0.0" y="0.0">results dir</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="124.0" x="0.0" y="0.0">results dir</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"/>
@@ -584,7 +602,7 @@ BLOB<y:LabelModel>
<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 7</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</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"/>
@@ -600,7 +618,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="766.2407553729457" y="517.7634167816118"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -617,7 +635,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="773.2407553729457" y="527.3379464487607"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -634,7 +652,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="784.2407553729457" y="539.0398277801646"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -660,7 +678,7 @@ BLOB<y:LabelModel>
<y:Geometry height="369.38665771484386" width="427.5" x="1016.25" y="415.3066711425781"/>
<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="427.5" x="0.0" y="0.0">Scan Target Server</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="427.5" x="0.0" y="0.0">Scan Target Server</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"/>
@@ -670,7 +688,7 @@ BLOB<y:LabelModel>
<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="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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"/>
@@ -689,7 +707,7 @@ BLOB<y:LabelModel>
<y:Geometry height="115.49428304036473" width="147.0" x="1274.75" y="481.9726867675781"/>
<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="147.0" x="0.0" y="0.0">Docker/LXD</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="147.0" x="0.0" y="0.0">Docker/LXD</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"/>
@@ -699,7 +717,7 @@ BLOB<y:LabelModel>
<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:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 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"/>
@@ -715,7 +733,7 @@ BLOB<y:LabelModel>
<y:Geometry height="50.0" width="85.0" x="1311.75" y="528.4669698079429"/>
<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="60.748046875" x="12.1259765625" y="15.93359375">Container<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="60.748046875" x="12.1259765625" y="15.93359375">Container<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -734,7 +752,7 @@ BLOB<y:LabelModel>
<y:Geometry height="104.0" width="149.0" x="1279.75" y="651.4669698079429"/>
<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="105.126953125" x="21.9365234375" y="42.93359375">Package Manager<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="105.126953125" x="21.9365234375" y="42.93359375">Package Manager<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -754,7 +772,7 @@ BLOB<y:LabelModel>
<y:Geometry height="295.1606569803161" width="154.0" x="1031.25" y="467.75445641081495"/>
<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="154.0" 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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="154.0" 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"/>
@@ -764,7 +782,7 @@ BLOB<y:LabelModel>
<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="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.2431640625" x="-11.62158203125" y="0.0">Folder 10</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"/>
@@ -780,7 +798,7 @@ BLOB<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178246" x="1068.7684892541088" y="528.4669698079429"/>
<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="30.139713870891228" y="15.93359375">Scan<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="30.68359375" x="30.139713870891228" y="15.93359375">Scan<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -800,7 +818,7 @@ BLOB<y:LabelModel>
<y:Geometry height="108.94242662355293" width="124.0" x="1046.25" y="638.9726867675781"/>
<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="124.0" x="0.0" y="0.0">results dir</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="124.0" x="0.0" y="0.0">results dir</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"/>
@@ -810,7 +828,7 @@ BLOB<y:LabelModel>
<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 7</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</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"/>
@@ -826,7 +844,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="1061.25" y="675.6387023925781"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -843,7 +861,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="1068.25" y="685.2132320597271"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -860,7 +878,7 @@ BLOB<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="1079.25" y="696.9151133911311"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -877,32 +895,13 @@ BLOB<y:LabelModel>
</node>
</graph>
</node>
<edge id="e0" source="n6::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="bold" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="113.986328125" x="30.532685822272697" y="-68.65962969657895">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="51.40637506342563" distanceToCenter="true" position="left" ratio="49.539548566656244" 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="e1" source="n3" target="n2">
<edge id="e0" source="n3" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="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="-16.97942515444379" y="-79.29177619236555">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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="34.626953125" x="-16.97942515444379" y="-79.29177619236555">HTTP<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -914,38 +913,12 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" 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="17.81251335144043" y="23.136142435046395">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="e3" source="n4" target="n14::n0::n1">
<edge id="e1" source="n4" target="n14::n0::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="2.1630847029074403" ty="11.890644753476522"/>
<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="40.275390625" x="-16.05774472670157" y="-25.101374183135704">WebUI<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="124.43524707167697" distanceToCenter="true" position="center" 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>
@@ -956,7 +929,7 @@ Vulnerability data<y:LabelModel>
<y:Path sx="45.46294808585753" sy="4.081810185279039" 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="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="77.576171875" x="50.52168085731432" y="-18.006733762306112">docker exec
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="77.576171875" x="50.52168085731432" y="-18.006733762306112">docker exec
lxc exec<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -969,7 +942,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n15::n0::n0" target="n3">
<edge id="e2" source="n15::n0::n0" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -985,7 +958,7 @@ lxc exec<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: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="-56.200249246840485" y="13.590015088261055">Insert<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.10546875" x="-56.200249246840485" y="13.590015088261055">Insert<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -997,31 +970,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n5" 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="38.875" x="-5.725583803513814" y="-46.98669248029603">Notify<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="35.394351766092626" distanceToCenter="true" position="left" ratio="0.644733250412419" 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="n6::e1" source="n6::n1" target="n6::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="37.9375" x="7.031249999999773" y="20.560447548932814">Select<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.9375" x="7.031249999999773" y="20.560447548932814">Select<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1043,7 +998,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e6" source="n14::n0::n0" target="n4">
<edge id="e3" source="n14::n0::n0" target="n4">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1053,7 +1008,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n14::n0::n0" target="n5">
<edge id="e4" source="n14::n0::n0" target="n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1073,13 +1028,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n14::n0::n0" target="n7">
<edge id="e5" source="n14::n0::n0" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y: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="22.568359375" x="-69.79481320678701" y="-2.6984373210885906">Put<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.568359375" x="-69.79481320678701" y="-2.6984373210885906">Put<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1091,7 +1046,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n14::n0::n0" target="n8">
<edge id="e6" source="n14::n0::n0" target="n8">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1111,13 +1066,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n4" target="n14::n0::n2">
<edge id="e7" source="n4" target="n14::n0::n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="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="95.939453125" x="28.897803242470673" y="-60.108331548755814"> View Results
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="95.939453125" x="28.897803242470673" y="-60.108331548755814"> View Results
on Terminal<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -1136,7 +1091,7 @@ lxc exec<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: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="48.953125" x="57.01879933351279" y="77.24561396863191">os.exec<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="48.953125" x="57.01879933351279" y="77.24561396863191">os.exec<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1148,13 +1103,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n14::n0::n0" target="n6::n1">
<edge id="e8" source="n14::n0::n0" target="n6::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="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="37.28125" x="-38.41298470873414" y="-178.74336216335405">HTTP<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="37.28125" x="-38.41298470873414" y="-178.74336216335405">HTTP<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1176,13 +1131,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n15::n2::n1" target="n14::n0::n3">
<edge id="e9" source="n15::n2::n1" target="n14::n0::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: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="53.5" x="-97.64801827097926" y="-81.03347374530358">transfer<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="53.5" x="-97.64801827097926" y="-81.03347374530358">transfer<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1194,7 +1149,17 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n15::n1" target="n3">
<edge id="e10" source="n15::n1" 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="e11" source="n6::n2" target="n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.14.2-->
<!--Created by yEd 3.17-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
@@ -20,7 +20,7 @@
<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">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="23.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -37,20 +37,20 @@
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="289.5891316731771" width="171.0" x="1230.5282340049735" y="-28.09765625"/>
<y:Geometry height="292.34706624348956" width="171.0" x="1230.5282340049735" y="-30.855590820312443"/>
<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="171.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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="171.0" x="0.0" 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="29" bottomF="28.96858723958337" left="29" leftF="29.35411262512207" right="27" rightF="26.64588737487793" top="27" topF="27.242065429687557"/>
<y:BorderInsets bottom="11" bottomF="11.256123860677178" left="29" leftF="29.35411262512207" right="27" rightF="26.64588737487793" 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 1</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
@@ -63,10 +63,10 @@
<node id="n1::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1274.8823466300955" y="147.52288818359375"/>
<y:Geometry height="70.0" width="85.0" x="1274.8823466300955" y="85.52288818359375"/>
<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
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.279296875" x="10.8603515625" y="18.8671875">JVN
(Japanese)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -81,10 +81,27 @@
<node id="n1::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1274.8823466300955" y="35.81042480468756"/>
<y:Geometry height="70.0" width="85.0" x="1274.8823466300955" y="5.810424804687557"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.69921875" x="27.650390625" y="25.93359375">NVD<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="70.0" width="85.0" x="1274.8823466300955" y="165.23535156249994"/>
<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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="35.845703125" x="24.5771484375" y="25.93359375">OVAL<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -103,20 +120,20 @@
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="336.1141560872396" width="171.61765336990447" x="1227.3823466300955" y="315.76495361328136"/>
<y:Geometry height="336.82033284505223" width="171.61765336990447" x="1227.3823466300955" y="315.76495361328136"/>
<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="171.61765336990447" x="0.0" y="0.0">Distribution Support</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="171.61765336990447" x="0.0" y="0.0">Distribution 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="12" bottomF="11.879109700520985" left="27" leftF="26.617653369904474" right="13" rightF="13.0" top="13" topF="12.569030761718636"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="27" leftF="26.617653369904474" right="13" rightF="13.292458057404474" top="13" topF="12.569030761718636"/>
</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 2</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 2</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"/>
@@ -132,7 +149,7 @@
<y:Geometry height="50.0" width="100.0" x="1269.0" y="435.46048990885413"/>
<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="17.9208984375" y="8.8671875">apptitude
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">apptitude
changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -150,7 +167,7 @@ changelog<y:LabelModel>
<y:Geometry height="50.0" width="100.0" x="1270.0" y="365.0"/>
<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="17.9208984375" y="8.8671875">yum
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">yum
changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -165,11 +182,12 @@ changelog<y:LabelModel>
<node id="n2::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="100.0" x="1271.0" y="504.03875732421886"/>
<y:Geometry height="64.96826171875" width="100.0" x="1269.0" y="504.03875732421886"/>
<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="3.5859375" y="8.8671875">RHSA (RedHat)
ALAS (Amazon)<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="92.828125" x="3.5859375" y="9.284912109375">RHSA (RedHat)
ALAS (Amazon)
ELSA (Oracle)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -183,10 +201,10 @@ ALAS (Amazon)<y:LabelModel>
<node id="n2::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="100.0" x="1270.3823466300955" y="575.0"/>
<y:Geometry height="50.0" width="100.0" x="1270.3823466300955" y="587.5852864583336"/>
<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="100.650390625" x="-0.3251953125" y="15.93359375">FreeBSD Support<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="100.650390625" x="-0.3251953125" y="15.93359375">FreeBSD Support<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -205,7 +223,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="56.554100036621094" x="1141.7229499816895" y="425.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="26.277050018310547" y="23.0">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="26.277050018310547" y="23.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -225,7 +243,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="371.666015625" width="341.1769911504424" x="575.0" y="298.333984375"/>
<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="341.1769911504424" 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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="341.1769911504424" 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"/>
@@ -235,7 +253,7 @@ ALAS (Amazon)<y:LabelModel>
<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 3</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 3</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"/>
@@ -251,7 +269,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178246" x="749.634165613148" y="349.1798612773512"/>
<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="30.139713870891228" y="15.93359375">Scan<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="30.68359375" x="30.139713870891228" y="15.93359375">Scan<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -268,7 +286,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="598.6954804045512" y="455.0"/>
<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="42.595703125" x="24.18365918339134" y="15.93359375">Report<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="24.18365918339134" y="15.93359375">Report<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -285,7 +303,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="715.9609671302148" y="575.0"/>
<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="32.265625" modelName="custom" textColor="#000000" visible="true" width="58.076171875" x="16.44342480839134" y="8.8671875">VulsRepo
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="58.076171875" x="16.44342480839134" y="8.8671875">VulsRepo
(WebUI)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -303,7 +321,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="90.96302149178268" x="810.2139696586597" y="575.0"/>
<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="23.359375" x="33.80182324589134" y="15.93359375">TUI<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="23.359375" x="33.80182324589134" y="15.93359375">TUI<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -322,7 +340,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="64.96826171875" width="56.554100036621094" x="691.7229499816895" y="717.515869140625"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="100.890625" x="-22.168262481689453" y="68.96826171875">System Operator<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -342,7 +360,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="37.0" width="109.57881927490234" x="575.2105903625488" y="701.5"/>
<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:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="52.78940963745117" y="41.0"/>
<y:SVGNodeProperties usingVisualBounds="true"/>
<y:SVGModel svgBoundsPolicy="0">
<y:SVGContent refid="2"/>
@@ -356,7 +374,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="24.0" width="35.0" x="689.5518331226297" y="663.3072060682681"/>
<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">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="15.5" y="28.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -382,7 +400,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="293.63460286458337" width="322.9999999999998" x="574.4999999999998" 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="322.9999999999998" 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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="322.9999999999998" x="0.0" y="0.0">go-cve-dictionary / goval-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"/>
@@ -392,7 +410,7 @@ ALAS (Amazon)<y:LabelModel>
<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 4</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 4</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"/>
@@ -408,7 +426,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="70.0" width="60.5" x="779.75" y="130.31282871019664"/>
<y:Fill color="#99CCFF" 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">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.25" y="33.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -416,7 +434,7 @@ ALAS (Amazon)<y:LabelModel>
<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: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="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -432,7 +450,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="101.0" x="626.7499999999997" y="142.23413085937491"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -449,7 +467,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="80.0" x="800.0" y="35.0"/>
<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>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.796875" x="16.6015625" y="15.93359375">Fetcher<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -471,7 +489,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="201.49428304036473" width="161.48764324188164" 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="161.48764324188164" x="0.0" y="0.0">Docker/LXD</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="161.48764324188164" x="0.0" y="0.0">Docker/LXD</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"/>
@@ -481,7 +499,7 @@ ALAS (Amazon)<y:LabelModel>
<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:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 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"/>
@@ -497,7 +515,7 @@ ALAS (Amazon)<y:LabelModel>
<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="30.794921875" x="27.1025390625" y="15.93359375">Host<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="30.794921875" x="27.1025390625" y="15.93359375">Host<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -514,7 +532,7 @@ ALAS (Amazon)<y:LabelModel>
<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="18.1328125" modelName="custom" textColor="#000000" visible="true" width="60.748046875" x="12.1259765625" y="15.93359375">Container<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="60.748046875" x="12.1259765625" y="15.93359375">Container<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -536,7 +554,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="131.666015625" width="168.0" 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="168.0" x="0.0" y="0.0">Linux/FreeBSD</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="168.0" x="0.0" y="0.0">Linux/FreeBSD</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"/>
@@ -546,7 +564,7 @@ ALAS (Amazon)<y:LabelModel>
<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:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 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"/>
@@ -562,7 +580,7 @@ ALAS (Amazon)<y:LabelModel>
<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:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="39.865234375" x="22.5673828125" y="15.93359375">Server<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -579,7 +597,7 @@ ALAS (Amazon)<y:LabelModel>
<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:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="39.865234375" x="22.5673828125" y="15.93359375">Server<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -601,7 +619,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="108.94242662355293" width="124.0" x="739.0" y="434.05757337644707"/>
<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="124.0" x="0.0" y="0.0">results dir</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="124.0" x="0.0" y="0.0">results dir</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"/>
@@ -611,7 +629,7 @@ ALAS (Amazon)<y:LabelModel>
<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 7</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" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</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"/>
@@ -627,7 +645,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="754.0" y="470.72358900144707"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -644,7 +662,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="761.0" y="480.2981186685961"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -661,7 +679,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="36.0" width="76.0" x="772.0" y="492.0"/>
<y:Fill color="#99CCFF" 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="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -680,7 +698,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="116.06321112515802" width="97.39570164348925" x="445.4298356510746" y="571.968394437421"/>
<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="46.69785082174462" y="120.06321112515798"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="s" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="46.69785082174462" y="120.06321112515798"/>
<y:Image alphaImage="true" refid="3"/>
</y:ImageNode>
</data>
@@ -691,7 +709,7 @@ ALAS (Amazon)<y:LabelModel>
<y:Geometry height="50.0" width="85.0" x="451.6276864728192" y="485.0"/>
<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="38.201171875" x="23.3994140625" y="8.8671875">Azure
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="38.201171875" x="23.3994140625" y="8.8671875">Azure
BLOB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -709,7 +727,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="390.97029077117577" y="490.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.828125" x="4.6007921144121156" y="10.93359375">.xml<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="29.828125" x="4.6007921144121156" y="10.93359375">.xml<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -725,7 +743,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="389.4630622503162" y="587.4087217193426"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="24.1328125" x="7.448448364412172" y="10.93359375">.txt<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="24.1328125" x="7.448448364412172" y="10.93359375">.txt<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -741,7 +759,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="389.9353177243998" y="538.8895352718077"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="3.3234483644121724" y="10.93359375">.json<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="32.3828125" x="3.3234483644121724" y="10.93359375">.json<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -757,7 +775,7 @@ BLOB<y:LabelModel>
<y:Geometry height="40.0" width="39.02970922882429" x="389.7450294816688" y="640.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="22.158203125" x="8.435753051912116" y="10.93359375">.gz<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.158203125" x="8.435753051912116" y="10.93359375">.gz<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -773,7 +791,7 @@ BLOB<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="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="28.568307252002455" y="48.099340559462576">Fetch
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="106.240234375" x="28.568307252002455" y="48.099340559462576">Fetch
Vulnerability data<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -792,7 +810,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: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="-4.560574684247513" y="24.771374369551154">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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="34.626953125" x="-4.628977826813298" y="24.803594807609784">HTTP<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -810,7 +828,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: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="42.345150113104864" y="26.521800272057305">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" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="34.626953125" x="42.457454800604864" y="26.157886474797976">HTTP<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -828,14 +846,6 @@ Vulnerability data<y:LabelModel>
<y:Path sx="0.0" sy="0.0" tx="2.1630847029074403" ty="11.890644753476522"/>
<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="40.275390625" x="-14.861825373422448" y="-25.77642822265625">WebUI<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="124.43524707167697" distanceToCenter="true" position="center" 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>
@@ -846,7 +856,7 @@ Vulnerability data<y:LabelModel>
<y:Path sx="44.363642405794096" 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="76.88603771593068" y="105.56250755816848">SSH<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="27.07421875" x="76.88603771593068" y="105.56250755816848">SSH<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -864,7 +874,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: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="71.96010962283094" y="-0.07972829529296632">SSH<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="27.07421875" x="71.96010962283094" y="-0.07972829529296632">SSH<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -882,7 +892,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:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" visible="true" width="77.576171875" x="-86.46309207519198" y="-41.560991819769924">docker exec
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="77.576171875" x="-86.46309207519198" y="-41.560991819769924">docker exec
lxc exec<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -921,7 +931,7 @@ lxc exec<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: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="-56.200249246840485" y="13.59000810509832">Insert<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.10546875" x="-56.200249246840485" y="13.59000810509832">Insert<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -933,31 +943,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n6" 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="38.875" x="3.063580934131096" y="-107.12398849561202">Notify<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="100.14105708039112" distanceToCenter="true" position="left" ratio="-56.992693208601096" 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="n8::e1" source="n8::n1" target="n8::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="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -969,7 +961,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n4::n0" target="n11">
<edge id="e8" source="n4::n0" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -979,7 +971,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n4::n1" target="n11">
<edge id="e9" source="n4::n1" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -989,7 +981,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n4::n1" target="n5">
<edge id="e10" source="n4::n1" target="n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -999,7 +991,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n4::n1" target="n6">
<edge id="e11" source="n4::n1" target="n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1009,7 +1001,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n4::n1" target="n8">
<edge id="e12" source="n4::n1" target="n8">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1019,7 +1011,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n4::n2" target="n11">
<edge id="e13" source="n4::n2" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1029,13 +1021,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n4::n1" target="n12">
<edge id="e14" source="n4::n1" target="n12">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y: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="22.568359375" x="-77.34538676739476" y="14.553670286396482">Put<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.568359375" x="-77.34538676739476" y="14.553670286396482">Put<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -1047,7 +1039,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e16" source="n4::n1" target="n13">
<edge id="e15" source="n4::n1" target="n13">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1057,7 +1049,7 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e17" source="n4::n3" target="n11">
<edge id="e16" source="n4::n3" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -1067,13 +1059,13 @@ lxc exec<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e18" source="n5" target="n4::n3">
<edge id="e17" source="n5" target="n4::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: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="95.939453125" x="30.74393308155709" y="-58.42560122139275"> View Results
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="95.939453125" x="30.74393308155709" y="-58.42560122139275"> View Results
on Terminal<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

@@ -0,0 +1,415 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.17-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="graphml" id="d7" yfiles.type="resources"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d0"/>
<node id="n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n1">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.decision">
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n2">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
FreeBSD: pkg<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n3">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="630.0546766682629"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n4">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
Amazon: yum plugin security
FreeBSD: pkg audit<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n5">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="750.4705298628534"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n6" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="234.29467728596296" y="709.1901021013174"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n6:">
<node id="n6::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="416.1341210280616" y="745.8561177263174"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n6::n1">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="249.29467728596296" y="745.8561177263174"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
</graph>
</node>
<node id="n7">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n8">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.loopLimit">
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="92.54867256637169" y="376.28592169721867"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
upgradable packages<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n9">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="27.144753476611868" y="459.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
Debian/Ubuntu: aptitude changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n10">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
<y:Geometry height="50.0" width="137.0" x="92.64475347661187" y="545.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<edge id="e0" source="n2" target="n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n1" target="n4">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
<y:Point x="743.3698412698412" y="226.44247787610618"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.7796030035582084" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n0" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n5" target="n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n1" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
<y:Point x="443.6849206349206" y="658.0546766682629"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="74.6640625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">CentOS
RHEL
Ubuntu
Debian
Oracle Linux<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="59.14459455430983" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n4" target="n3">
<data key="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="e6" source="n7" target="n8">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n8" target="n9">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n9" target="n10">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n3" target="n5">
<data key="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="e10" source="n1" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="161.14475347661187" y="226.44247787610618"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-196.80057112212188" y="20.933597260871807">Raspbian<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6447921222409765" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n10" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">
<y:Point x="161.14475347661187" y="658.0546766682629"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

BIN
img/vuls-scan-flow-fast.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.14.2-->
<!--Created by yEd 3.17-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
@@ -20,7 +20,7 @@
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -36,7 +36,7 @@
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="18.0">
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="38.0" y="18.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -53,7 +53,7 @@
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" modelName="custom" textColor="#000000" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
FreeBSD: pkg<y:LabelModel>
@@ -72,7 +72,7 @@ FreeBSD: pkg<y:LabelModel>
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -89,7 +89,7 @@ Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
upgradable packages<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -106,7 +106,7 @@ upgradable packages<y:LabelModel>
<y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
Debian/Ubuntu: aptitude changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -123,7 +123,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
<y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -139,7 +139,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="194.904296875" x="36.5478515625" y="18.93359375">Select the CVE detail information<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="152.634765625" x="57.6826171875" y="18.93359375">Write results to JSON files<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -155,7 +155,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" modelName="custom" textColor="#000000" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
Amazon/RHEL: yum plugin security
FreeBSD: pkg audit<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
@@ -168,29 +168,12 @@ FreeBSD: pkg audit<y:LabelModel>
</data>
</node>
<node id="n9">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="687.385587863464"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n10">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="152.634765625" x="57.6826171875" y="11.8671875">Write results to JSON files
Reporting<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="42.595703125" x="112.7021484375" y="18.93359375">Report<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -200,14 +183,14 @@ Reporting<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n11">
<node id="n10">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="287.8409153761062"/>
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="371.39590905499364"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
CentOS: yum update --changelog<y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
yum changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -217,13 +200,13 @@ CentOS: yum update --changelog<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n12">
<node id="n11">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="373.8409153761062"/>
<y:Geometry height="56.0" width="268.0" x="309.68492063492056" y="459.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -233,6 +216,87 @@ CentOS: yum update --changelog<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n12">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="373.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="293.06640625" x="-12.533203124999886" y="11.8671875">Get all changelogs of updatable packages at once
Amazon / RHEL: yum changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n13" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="116.89483989807195" width="333.6788874841973" x="229.74083438685204" y="675.1748997511062"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="333.6788874841973" x="0.0" y="0.0">Vulnerability Database</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n13:">
<node id="n13::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="711.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n13::n1">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="244.74083438685204" y="711.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.533203125" x="40.653120308549205" y="23.548005886535975">OVAL DB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
</graph>
</node>
<edge id="e0" source="n2" target="n1">
<data key="d10">
<y:PolyLineEdge>
@@ -251,8 +315,9 @@ CentOS: yum update --changelog<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.697265625" x="-56.79057374984495" y="-34.26562148912808">Debian
Ubuntu<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-66.95987036992159" y="-48.39843398912808">Debian
Ubuntu
Raspbian<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -314,7 +379,7 @@ Ubuntu<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
RHEL
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
@@ -328,17 +393,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n8" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n0" target="n2">
<edge id="e7" source="n0" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
@@ -348,7 +403,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n7" target="n10">
<edge id="e8" source="n7" target="n9">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
@@ -358,29 +413,17 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n7" target="n9">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-134.01566143419018" sy="6.159084623893818" tx="0.0" ty="-29.333162136535975">
<y:Point x="480.0" y="660.0"/>
</y:Path>
<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="e11" source="n1" target="n11">
<edge id="e9" source="n1" target="n10">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.708984375" x="-53.35447755843876" y="11.632816010871807">CentOS<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.708984375" x="-53.35447755843876" y="5.000003510871807">CentOS<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -388,7 +431,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n11" target="n12">
<edge id="e10" source="n10" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
@@ -398,11 +441,12 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n12" target="n7">
<edge id="e11" source="n11" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">
<y:Point x="743.3698412698412" y="401.8409153761062"/>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
<y:Point x="743.3698412698412" y="487.8409153761062"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
@@ -410,6 +454,39 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n8" target="n12">
<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="n12" 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="e14" source="n9" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -29,7 +29,7 @@ import (
)
// Version of Vuls
var version = "0.3.0"
var version = "0.4.0"
// Revision of Git
var revision string

272
models/cvecontents.go Normal file
View File

@@ -0,0 +1,272 @@
/* 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 models
import (
"strings"
"time"
)
// CveContents has CveContent
type CveContents map[CveContentType]CveContent
// NewCveContents create CveContents
func NewCveContents(conts ...CveContent) CveContents {
m := CveContents{}
for _, cont := range conts {
m[cont.Type] = cont
}
return m
}
// CveContentStr has CveContentType and Value
type CveContentStr struct {
Type CveContentType
Value string
}
// Except returns CveContents except given keys for enumeration
func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents) {
values = CveContents{}
for ctype, content := range v {
found := false
for _, exceptCtype := range exceptCtypes {
if ctype == exceptCtype {
found = true
break
}
}
if !found {
values[ctype] = content
}
}
return
}
// SourceLinks returns link of source
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v[JVN]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{JVN, cont.SourceLink})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
for _, ctype := range order {
if cont, found := v[ctype]; found {
values = append(values, CveContentStr{ctype, cont.SourceLink})
}
}
if len(values) == 0 {
return []CveContentStr{{
Type: NVD,
Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
}}
}
return values
}
/*
// Severities returns Severities
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order)...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.Severity) {
values = append(values, CveContentStr{
Type: ctype,
Value: cont.Severity,
})
}
}
return
}
*/
// CveContentCpes has CveContentType and Value
type CveContentCpes struct {
Type CveContentType
Value []Cpe
}
// Cpes returns affected CPEs of this Vulnerability
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
order := CveContentTypes{NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order)...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
values = append(values, CveContentCpes{
Type: ctype,
Value: cont.Cpes,
})
}
}
return
}
// CveContentRefs has CveContentType and Cpes
type CveContentRefs struct {
Type CveContentType
Value []Reference
}
// References returns References
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
order := CveContentTypes{NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order)...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.References) {
values = append(values, CveContentRefs{
Type: ctype,
Value: cont.References,
})
}
}
return
}
// CweIDs returns related CweIDs of the vulnerability
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
order := CveContentTypes{NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order)...)...)
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.CweID) {
// RedHat's OVAL sometimes contains multiple CWE-IDs separated by spaces
for _, cweID := range strings.Fields(cont.CweID) {
values = append(values, CveContentStr{
Type: ctype,
Value: cweID,
})
}
}
}
return
}
// CveContent has abstraction of various vulnerability information
type CveContent struct {
Type CveContentType
CveID string
Title string
Summary string
Severity string
Cvss2Score float64
Cvss2Vector string
Cvss3Score float64
Cvss3Vector string
SourceLink string
Cpes []Cpe
References References
CweID string
Published time.Time
LastModified time.Time
}
// Empty checks the content is empty
func (c CveContent) Empty() bool {
return c.Summary == ""
}
// CveContentType is a source of CVE information
type CveContentType string
// NewCveContentType create CveContentType
func NewCveContentType(name string) CveContentType {
switch name {
case "nvd":
return NVD
case "jvn":
return JVN
case "redhat", "centos":
return RedHat
case "oracle":
return Oracle
case "ubuntu":
return Ubuntu
case "debian":
return Debian
default:
return Unknown
}
}
const (
// NVD is NVD
NVD CveContentType = "nvd"
// JVN is JVN
JVN CveContentType = "jvn"
// RedHat is RedHat
RedHat CveContentType = "redhat"
// Debian is Debian
Debian CveContentType = "debian"
// Ubuntu is Ubuntu
Ubuntu CveContentType = "ubuntu"
// Oracle is Oracle Linux
Oracle CveContentType = "oracle"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)
// CveContentTypes has slide of CveContentType
type CveContentTypes []CveContentType
// AllCveContetTypes has all of CveContentTypes
var AllCveContetTypes = CveContentTypes{NVD, JVN, RedHat, Debian, Ubuntu}
// Except returns CveContentTypes except for given args
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
for _, ctype := range c {
found := false
for _, except := range excepts {
if ctype == except {
found = true
break
}
}
if !found {
excepted = append(excepted, ctype)
}
}
return
}
// Cpe is Common Platform Enumeration
type Cpe struct {
CpeName string
}
// References is a slice of Reference
type References []Reference
// Reference has a related link of the CVE
type Reference struct {
Source string
Link string
RefID string
}

206
models/cvecontents_test.go Normal file
View File

@@ -0,0 +1,206 @@
/* 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 models
import (
"reflect"
"testing"
)
func TestExcept(t *testing.T) {
var tests = []struct {
in CveContents
out CveContents
}{{
in: CveContents{
RedHat: {Type: RedHat},
Ubuntu: {Type: Ubuntu},
Debian: {Type: Debian},
},
out: CveContents{
RedHat: {Type: RedHat},
},
},
}
for _, tt := range tests {
actual := tt.in.Except(Ubuntu, Debian)
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestSourceLinks(t *testing.T) {
type in struct {
lang string
cveID string
cont CveContents
}
var tests = []struct {
in in
out []CveContentStr
}{
// lang: ja
{
in: in{
lang: "ja",
cveID: "CVE-2017-6074",
cont: CveContents{
JVN: {
Type: JVN,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: JVN,
Value: "https://jvn.jp/vu/JVNVU93610402/",
},
{
Type: NVD,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
},
// lang: en
{
in: in{
lang: "en",
cveID: "CVE-2017-6074",
cont: CveContents{
JVN: {
Type: JVN,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: NVD,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
},
// lang: empty
{
in: in{
lang: "en",
cveID: "CVE-2017-6074",
cont: CveContents{},
},
out: []CveContentStr{
{
Type: NVD,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
}
for _, tt := range tests {
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestVendorLink(t *testing.T) {
type in struct {
family string
vinfo VulnInfo
}
var tests = []struct {
in in
out map[string]string
}{
{
in: in{
family: "redhat",
vinfo: VulnInfo{
CveID: "CVE-2017-6074",
CveContents: CveContents{
JVN: {
Type: JVN,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NVD: {
Type: NVD,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
},
out: map[string]string{
"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
{
in: in{
family: "ubuntu",
vinfo: VulnInfo{
CveID: "CVE-2017-6074",
CveContents: CveContents{
RedHat: {
Type: Ubuntu,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
},
},
out: map[string]string{
"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
},
},
}
for _, tt := range tests {
actual := tt.in.vinfo.VendorLinks(tt.in.family)
for k := range tt.out {
if tt.out[k] != actual[k] {
t.Errorf("\nexpected: %s\n actual: %s\n", tt.out[k], actual[k])
}
}
}
}

View File

@@ -17,535 +17,5 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package models
import (
"fmt"
"sort"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
cve "github.com/kotakanbe/go-cve-dictionary/models"
)
// ScanHistory is the history of Scanning.
type ScanHistory struct {
ScanResults ScanResults
}
// 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
}
// ScanResult has the result of scanned CVE information.
type ScanResult struct {
ScannedAt time.Time
Lang string
ServerName string // TOML Section key
Family string
Release string
Container Container
Platform Platform
// Scanned Vulns via SSH + CPE Vulns
ScannedCves []VulnInfo
KnownCves []CveInfo
UnknownCves []CveInfo
IgnoredCves []CveInfo
Packages PackageInfoList
Errors []string
Optional [][]interface{}
}
// FillCveDetail fetches CVE detailed information from
// CVE Database, and then set to fields.
func (r ScanResult) FillCveDetail() (*ScanResult, error) {
set := map[string]VulnInfo{}
var cveIDs []string
for _, v := range r.ScannedCves {
set[v.CveID] = v
cveIDs = append(cveIDs, v.CveID)
}
ds, err := cveapi.CveClient.FetchCveDetails(cveIDs)
if err != nil {
return nil, err
}
known, unknown, ignored := CveInfos{}, CveInfos{}, CveInfos{}
for _, d := range ds {
cinfo := CveInfo{
CveDetail: d,
VulnInfo: set[d.CveID],
}
cinfo.NilSliceToEmpty()
// ignored
found := false
for _, icve := range config.Conf.Servers[r.ServerName].IgnoreCves {
if icve == d.CveID {
ignored = append(ignored, cinfo)
found = true
break
}
}
if found {
continue
}
// unknown
if d.CvssScore(config.Conf.Lang) <= 0 {
unknown = append(unknown, cinfo)
continue
}
// known
known = append(known, cinfo)
}
sort.Sort(known)
sort.Sort(unknown)
sort.Sort(ignored)
r.KnownCves = known
r.UnknownCves = unknown
r.IgnoredCves = ignored
return &r, nil
}
// FilterByCvssOver is filter function.
func (r ScanResult) FilterByCvssOver() ScanResult {
cveInfos := []CveInfo{}
for _, cveInfo := range r.KnownCves {
if config.Conf.CvssScoreOver < cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
cveInfos = append(cveInfos, cveInfo)
}
}
r.KnownCves = cveInfos
return r
}
// ReportFileName returns the filename on localhost without extention
func (r ScanResult) ReportFileName() (name string) {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s", r.ServerName)
}
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
}
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
func (r ScanResult) ReportKeyName() (name string) {
timestr := r.ScannedAt.Format(time.RFC3339)
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s", timestr, r.ServerName)
}
return fmt.Sprintf("%s/%s@%s", timestr, r.Container.Name, r.ServerName)
}
// ServerInfo returns server name one line
func (r ScanResult) ServerInfo() string {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s (%s%s)",
r.ServerName, r.Family, r.Release)
}
return fmt.Sprintf(
"%s / %s (%s%s) on %s",
r.Container.Name,
r.Container.ContainerID,
r.Family,
r.Release,
r.ServerName,
)
}
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s (%s%s)",
r.ServerName, r.Family, r.Release)
}
return fmt.Sprintf(
"|-- %s (%s%s)",
r.Container.Name,
r.Family,
r.Release,
// r.Container.ContainerID,
)
}
// FormatServerName returns server and container name
func (r ScanResult) FormatServerName() string {
if len(r.Container.ContainerID) == 0 {
return r.ServerName
}
return fmt.Sprintf("%s@%s",
r.Container.Name, r.ServerName)
}
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
func (r ScanResult) CveSummary() string {
var high, medium, low, unknown int
cves := append(r.KnownCves, r.UnknownCves...)
for _, cveInfo := range cves {
score := cveInfo.CveDetail.CvssScore(config.Conf.Lang)
switch {
case 7.0 <= score:
high++
case 4.0 <= score:
medium++
case 0 < score:
low++
default:
unknown++
}
}
if config.Conf.IgnoreUnscoredCves {
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
high+medium+low, high, medium, low)
}
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
high+medium+low+unknown, high, medium, low, unknown)
}
// AllCves returns Known and Unknown CVEs
func (r ScanResult) AllCves() []CveInfo {
return append(r.KnownCves, r.UnknownCves...)
}
// NWLink has network link information.
type NWLink struct {
IPAddress string
Netmask string
DevName string
LinkState string
}
// Confidence is a ranking how confident the CVE-ID was deteted correctly
// Score: 0 - 100
type Confidence struct {
Score int
DetectionMethod string
}
func (c Confidence) String() string {
return fmt.Sprintf("%d / %s", c.Score, c.DetectionMethod)
}
const (
// CpeNameMatchStr is a String representation of CpeNameMatch
CpeNameMatchStr = "CpeNameMatch"
// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
// PkgAuditMatchStr is a String representation of PkgAuditMatch
PkgAuditMatchStr = "PkgAuditMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
ChangelogExactMatchStr = "ChangelogExactMatch"
// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
ChangelogLenientMatchStr = "ChangelogLenientMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
FailedToGetChangelog = "FailedToGetChangelog"
// FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog
FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
)
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
var CpeNameMatch = Confidence{100, CpeNameMatchStr}
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
var YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
var PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
var ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
var ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
// VulnInfos is VulnInfo list, getter/setter, sortable methods.
type VulnInfos []VulnInfo
// VulnInfo holds a vulnerability information and unsecure packages
type VulnInfo struct {
CveID string
Confidence Confidence
Packages PackageInfoList
DistroAdvisories []DistroAdvisory // for Aamazon, RHEL, FreeBSD
CpeNames []string
}
// NilSliceToEmpty set nil slice fields to empty slice to avoid null in JSON
func (v *VulnInfo) NilSliceToEmpty() {
if v.CpeNames == nil {
v.CpeNames = []string{}
}
if v.DistroAdvisories == nil {
v.DistroAdvisories = []DistroAdvisory{}
}
if v.Packages == nil {
v.Packages = PackageInfoList{}
}
}
// FindByCveID find by CVEID
func (s VulnInfos) FindByCveID(cveID string) (VulnInfo, bool) {
for _, p := range s {
if cveID == p.CveID {
return p, true
}
}
return VulnInfo{CveID: cveID}, false
}
// immutable
func (s VulnInfos) set(cveID string, v VulnInfo) VulnInfos {
for i, p := range s {
if cveID == p.CveID {
s[i] = v
return s
}
}
return append(s, v)
}
// Len implement Sort Interface
func (s VulnInfos) Len() int {
return len(s)
}
// Swap implement Sort Interface
func (s VulnInfos) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less implement Sort Interface
func (s VulnInfos) Less(i, j int) bool {
return s[i].CveID < s[j].CveID
}
// CveInfos is for sorting
type CveInfos []CveInfo
func (c CveInfos) Len() int {
return len(c)
}
func (c CveInfos) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func (c CveInfos) Less(i, j int) bool {
lang := config.Conf.Lang
if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
return c[i].CveDetail.CveID < c[j].CveDetail.CveID
}
return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
}
// CveInfo has Cve Information.
type CveInfo struct {
CveDetail cve.CveDetail
VulnInfo
}
// NilSliceToEmpty set nil slice fields to empty slice to avoid null in JSON
func (c *CveInfo) NilSliceToEmpty() {
if c.CveDetail.Nvd.Cpes == nil {
c.CveDetail.Nvd.Cpes = []cve.Cpe{}
}
if c.CveDetail.Jvn.Cpes == nil {
c.CveDetail.Jvn.Cpes = []cve.Cpe{}
}
if c.CveDetail.Nvd.References == nil {
c.CveDetail.Nvd.References = []cve.Reference{}
}
if c.CveDetail.Jvn.References == nil {
c.CveDetail.Jvn.References = []cve.Reference{}
}
}
// PackageInfoList is slice of PackageInfo
type PackageInfoList []PackageInfo
// Exists returns true if exists the name
func (ps PackageInfoList) Exists(name string) bool {
for _, p := range ps {
if p.Name == name {
return true
}
}
return false
}
// UniqByName be uniq by name.
func (ps PackageInfoList) UniqByName() (distincted PackageInfoList) {
set := make(map[string]PackageInfo)
for _, p := range ps {
set[p.Name] = p
}
//sort by key
keys := []string{}
for key := range set {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
distincted = append(distincted, set[key])
}
return
}
// FindByName search PackageInfo by name
func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found bool) {
for _, p := range ps {
if p.Name == name {
return p, true
}
}
return PackageInfo{}, false
}
// MergeNewVersion merges candidate version information to the receiver struct
func (ps PackageInfoList) MergeNewVersion(as PackageInfoList) {
for _, a := range as {
for i, p := range ps {
if p.Name == a.Name {
ps[i].NewVersion = a.NewVersion
ps[i].NewRelease = a.NewRelease
}
}
}
}
func (ps PackageInfoList) countUpdatablePacks() int {
count := 0
set := make(map[string]bool)
for _, p := range ps {
if len(p.NewVersion) != 0 && !set[p.Name] {
count++
set[p.Name] = true
}
}
return count
}
// FormatUpdatablePacksSummary returns a summary of updatable packages
func (ps PackageInfoList) FormatUpdatablePacksSummary() string {
return fmt.Sprintf("%d updatable packages",
ps.countUpdatablePacks())
}
// Find search PackageInfo by name-version-release
// func (ps PackageInfoList) find(nameVersionRelease string) (PackageInfo, bool) {
// for _, p := range ps {
// joined := p.Name
// if 0 < len(p.Version) {
// joined = fmt.Sprintf("%s-%s", joined, p.Version)
// }
// if 0 < len(p.Release) {
// joined = fmt.Sprintf("%s-%s", joined, p.Release)
// }
// if joined == nameVersionRelease {
// return p, true
// }
// }
// return PackageInfo{}, false
// }
// PackageInfosByName implements sort.Interface for []PackageInfo based on
// the Name field.
type PackageInfosByName []PackageInfo
func (a PackageInfosByName) Len() int { return len(a) }
func (a PackageInfosByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a PackageInfosByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
// PackageInfo has installed packages.
type PackageInfo struct {
Name string
Version string
Release string
NewVersion string
NewRelease string
Repository string
Changelog Changelog
}
// Changelog has contents of changelog and how to get it.
// Method: modesl.detectionMethodStr
type Changelog struct {
Contents string
Method string
}
// ToStringCurrentVersion returns package name-version-release
func (p PackageInfo) ToStringCurrentVersion() string {
str := p.Name
if 0 < len(p.Version) {
str = fmt.Sprintf("%s-%s", str, p.Version)
}
if 0 < len(p.Release) {
str = fmt.Sprintf("%s-%s", str, p.Release)
}
return str
}
// ToStringNewVersion returns package name-version-release
func (p PackageInfo) ToStringNewVersion() string {
str := p.Name
if 0 < len(p.NewVersion) {
str = fmt.Sprintf("%s-%s", str, p.NewVersion)
}
if 0 < len(p.NewRelease) {
str = fmt.Sprintf("%s-%s", str, p.NewRelease)
}
return str
}
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
type DistroAdvisory struct {
AdvisoryID string
Severity string
Issued time.Time
Updated time.Time
}
// Container has Container information
type Container struct {
ContainerID string
Name string
Image string
Type string
}
// Platform has platform information
type Platform struct {
Name string // aws or azure or gcp or other...
InstanceID string
}
// JSONVersion is JSON Version
const JSONVersion = 2

View File

@@ -16,122 +16,3 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package models
import (
"reflect"
"testing"
"github.com/k0kubun/pp"
)
func TestPackageInfoListUniqByName(t *testing.T) {
var test = struct {
in PackageInfoList
out PackageInfoList
}{
PackageInfoList{
{
Name: "hoge",
},
{
Name: "fuga",
},
{
Name: "hoge",
},
},
PackageInfoList{
{
Name: "hoge",
},
{
Name: "fuga",
},
},
}
actual := test.in.UniqByName()
for i, ePack := range test.out {
if actual[i].Name == ePack.Name {
t.Errorf("expected %#v, actual %#v", ePack.Name, actual[i].Name)
}
}
}
func TestMergeNewVersion(t *testing.T) {
var test = struct {
a PackageInfoList
b PackageInfoList
expected PackageInfoList
}{
PackageInfoList{
{
Name: "hoge",
},
},
PackageInfoList{
{
Name: "hoge",
NewVersion: "1.0.0",
NewRelease: "release1",
},
},
PackageInfoList{
{
Name: "hoge",
NewVersion: "1.0.0",
NewRelease: "release1",
},
},
}
test.a.MergeNewVersion(test.b)
if !reflect.DeepEqual(test.a, test.expected) {
e := pp.Sprintf("%v", test.a)
a := pp.Sprintf("%v", test.expected)
t.Errorf("expected %s, actual %s", e, a)
}
}
func TestVulnInfosSetGet(t *testing.T) {
var test = struct {
in []string
out []string
}{
[]string{
"CVE1",
"CVE2",
"CVE3",
"CVE1",
"CVE1",
"CVE2",
"CVE3",
},
[]string{
"CVE1",
"CVE2",
"CVE3",
},
}
// var ps packageCveInfos
var ps VulnInfos
for _, cid := range test.in {
ps = ps.set(cid, VulnInfo{CveID: cid})
}
if len(test.out) != len(ps) {
t.Errorf("length: expected %d, actual %d", len(test.out), len(ps))
}
for i, expectedCid := range test.out {
if expectedCid != ps[i].CveID {
t.Errorf("expected %s, actual %s", expectedCid, ps[i].CveID)
}
}
for _, cid := range test.in {
p, _ := ps.FindByCveID(cid)
if p.CveID != cid {
t.Errorf("expected %s, actual %s", cid, p.CveID)
}
}
}

153
models/packages.go Normal file
View File

@@ -0,0 +1,153 @@
/* 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 models
import (
"bytes"
"fmt"
"strings"
)
// Packages is Map of Package
// { "package-name": Package }
type Packages map[string]Package
// NewPackages create Packages
func NewPackages(packs ...Package) Packages {
m := Packages{}
for _, pack := range packs {
m[pack.Name] = pack
}
return m
}
// MergeNewVersion merges candidate version information to the receiver struct
func (ps Packages) MergeNewVersion(as Packages) {
for _, a := range as {
if pack, ok := ps[a.Name]; ok {
pack.NewVersion = a.NewVersion
pack.NewRelease = a.NewRelease
pack.Repository = a.Repository
ps[a.Name] = pack
}
}
}
// Merge returns merged map (immutable)
func (ps Packages) Merge(other Packages) Packages {
merged := Packages{}
for k, v := range ps {
merged[k] = v
}
for k, v := range other {
merged[k] = v
}
return merged
}
// FormatUpdatablePacksSummary returns a summary of updatable packages
func (ps Packages) FormatUpdatablePacksSummary() string {
nUpdatable := 0
for _, p := range ps {
if p.NewVersion != "" {
nUpdatable++
}
}
return fmt.Sprintf("%d updatable packages", nUpdatable)
}
// FindOne search a element by name-newver-newrel-arch
func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
for key, p := range ps {
if f(p) {
return key, p, true
}
}
return "", Package{}, false
}
// Package has installed packages.
type Package struct {
Name string
Version string
Release string
NewVersion string
NewRelease string
Arch string
Repository string
Changelog Changelog
}
// FormatVer returns package version-release
func (p Package) FormatVer() string {
ver := p.Version
if 0 < len(p.Release) {
ver = fmt.Sprintf("%s-%s", ver, p.Release)
}
return ver
}
// FormatNewVer returns package version-release
func (p Package) FormatNewVer() string {
ver := p.NewVersion
if 0 < len(p.NewRelease) {
ver = fmt.Sprintf("%s-%s", ver, p.NewRelease)
}
return ver
}
// FormatVersionFromTo formats installed and new package version
func (p Package) FormatVersionFromTo(notFixedYet bool) string {
to := p.FormatNewVer()
if notFixedYet {
to = "Not Fixed Yet"
}
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
}
// FormatChangelog formats the changelog
func (p Package) FormatChangelog() string {
buf := []string{}
packVer := fmt.Sprintf("%s-%s -> %s",
p.Name, p.FormatVer(), p.FormatNewVer())
var delim bytes.Buffer
for i := 0; i < len(packVer); i++ {
delim.WriteString("-")
}
clog := p.Changelog.Contents
if lines := strings.Split(clog, "\n"); len(lines) != 0 {
clog = strings.Join(lines[0:len(lines)-1], "\n")
}
switch p.Changelog.Method {
case FailedToGetChangelog:
clog = "No changelogs"
case FailedToFindVersionInChangelog:
clog = "Failed to parse changelogs. For detials, check yourself"
}
buf = append(buf, packVer, delim.String(), clog)
return strings.Join(buf, "\n")
}
// Changelog has contents of changelog and how to get it.
// Method: models.detectionMethodStr
type Changelog struct {
Contents string
Method DetectionMethod
}

89
models/packages_test.go Normal file
View File

@@ -0,0 +1,89 @@
/* 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 models
import (
"reflect"
"testing"
"github.com/k0kubun/pp"
)
func TestMergeNewVersion(t *testing.T) {
var test = struct {
a Packages
b Packages
expected Packages
}{
Packages{
"hoge": {
Name: "hoge",
},
},
Packages{
"hoge": {
Name: "hoge",
NewVersion: "1.0.0",
NewRelease: "release1",
},
},
Packages{
"hoge": {
Name: "hoge",
NewVersion: "1.0.0",
NewRelease: "release1",
},
},
}
test.a.MergeNewVersion(test.b)
if !reflect.DeepEqual(test.a, test.expected) {
e := pp.Sprintf("%v", test.a)
a := pp.Sprintf("%v", test.expected)
t.Errorf("expected %s, actual %s", e, a)
}
}
func TestMerge(t *testing.T) {
var test = struct {
a Packages
b Packages
expected Packages
}{
Packages{
"hoge": {Name: "hoge"},
"fuga": {Name: "fuga"},
},
Packages{
"hega": {Name: "hega"},
"hage": {Name: "hage"},
},
Packages{
"hoge": {Name: "hoge"},
"fuga": {Name: "fuga"},
"hega": {Name: "hega"},
"hage": {Name: "hage"},
},
}
actual := test.a.Merge(test.b)
if !reflect.DeepEqual(actual, test.expected) {
e := pp.Sprintf("%v", test.expected)
a := pp.Sprintf("%v", actual)
t.Errorf("expected %s, actual %s", e, a)
}
}

191
models/scanresults.go Normal file
View File

@@ -0,0 +1,191 @@
/* 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 models
import (
"bytes"
"fmt"
"time"
"github.com/future-architect/vuls/config"
)
// ScanResults is a slide of ScanResult
type ScanResults []ScanResult
// ScanResult has the result of scanned CVE information.
type ScanResult struct {
ScannedAt time.Time
ReportedAt time.Time
JSONVersion int
Lang string
ServerUUID string
ServerName string // TOML Section key
Family string
Release string
Container Container
Platform Platform
// Scanned Vulns by SSH scan + CPE + OVAL
ScannedCves VulnInfos
RunningKernel Kernel
Packages Packages
Errors []string
Optional [][]interface{}
Config struct {
Scan config.Config
Report config.Config
}
}
// Kernel has the Release, version and whether need restart
type Kernel struct {
Release string
Version string
RebootRequired bool
}
// FilterByCvssOver is filter function.
func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
v2Max := v.MaxCvss2Score()
v3Max := v.MaxCvss3Score()
max := v2Max.Value.Score
if max < v3Max.Value.Score {
max = v3Max.Value.Score
}
if over <= max {
return true
}
return false
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
}
// FilterIgnoreCves is filter function.
func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
for _, c := range cveIDs {
if v.CveID == c {
return false
}
}
return true
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
}
// ReportFileName returns the filename on localhost without extention
func (r ScanResult) ReportFileName() (name string) {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s", r.ServerName)
}
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
}
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
func (r ScanResult) ReportKeyName() (name string) {
timestr := r.ScannedAt.Format(time.RFC3339)
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s", timestr, r.ServerName)
}
return fmt.Sprintf("%s/%s@%s", timestr, r.Container.Name, r.ServerName)
}
// ServerInfo returns server name one line
func (r ScanResult) ServerInfo() string {
if len(r.Container.ContainerID) == 0 {
return fmt.Sprintf("%s (%s%s)",
r.FormatServerName(), r.Family, r.Release)
}
return fmt.Sprintf(
"%s (%s%s) on %s",
r.FormatServerName(),
r.Family,
r.Release,
r.ServerName,
)
}
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
if len(r.Container.ContainerID) == 0 {
line := fmt.Sprintf("%s (%s%s)",
r.ServerName, r.Family, r.Release)
if r.RunningKernel.RebootRequired {
return "[Reboot] " + line
}
return line
}
fmtstr := "|-- %s (%s%s)"
if r.RunningKernel.RebootRequired {
fmtstr = "|-- [Reboot] %s (%s%s)"
}
return fmt.Sprintf(fmtstr, r.Container.Name, r.Family, r.Release)
}
// FormatServerName returns server and container name
func (r ScanResult) FormatServerName() (name string) {
if len(r.Container.ContainerID) == 0 {
name = r.ServerName
} else {
name = fmt.Sprintf("%s@%s",
r.Container.Name, r.ServerName)
}
if r.RunningKernel.RebootRequired {
name = "[Reboot Required] " + name
}
return
}
// FormatTextReportHeadedr returns header of text report
func (r ScanResult) FormatTextReportHeadedr() string {
serverInfo := r.ServerInfo()
var buf bytes.Buffer
for i := 0; i < len(serverInfo); i++ {
buf.WriteString("=")
}
return fmt.Sprintf("%s\n%s\n%s\t%s\n",
r.ServerInfo(),
buf.String(),
r.ScannedCves.FormatCveSummary(),
r.Packages.FormatUpdatablePacksSummary(),
)
}
// Container has Container information
type Container struct {
ContainerID string
Name string
Image string
Type string
}
// Platform has platform information
type Platform struct {
Name string // aws or azure or gcp or other...
InstanceID string
}

257
models/scanresults_test.go Normal file
View File

@@ -0,0 +1,257 @@
/* 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 models
import (
"reflect"
"testing"
"time"
"github.com/k0kubun/pp"
)
func TestFilterByCvssOver(t *testing.T) {
type in struct {
over float64
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
over: 7.0,
rs: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0002",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
},
),
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
},
),
},
},
},
},
// OVAL Severity
{
in: in{
over: 7.0,
rs: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
},
},
},
}
for _, tt := range tests {
actual := tt.in.rs.FilterByCvssOver(tt.in.over)
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}
func TestFilterIgnoreCveIDs(t *testing.T) {
type in struct {
cves []string
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
cves: []string{"CVE-2017-0002"},
rs: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
},
},
},
},
}
for _, tt := range tests {
actual := tt.in.rs.FilterIgnoreCves(tt.in.cves)
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}

114
models/utils.go Normal file
View File

@@ -0,0 +1,114 @@
/* 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 models
import (
"fmt"
"strings"
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
)
// ConvertNvdToModel convert NVD to CveContent
func ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
var cpes []Cpe
for _, c := range nvd.Cpes {
cpes = append(cpes, Cpe{CpeName: c.CpeName})
}
var refs []Reference
for _, r := range nvd.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
validVec := true
for _, v := range []string{
nvd.AccessVector,
nvd.AccessComplexity,
nvd.Authentication,
nvd.ConfidentialityImpact,
nvd.IntegrityImpact,
nvd.AvailabilityImpact,
} {
if len(v) == 0 {
validVec = false
}
}
vector := ""
if validVec {
vector = fmt.Sprintf("AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
string(nvd.AccessVector[0]),
string(nvd.AccessComplexity[0]),
string(nvd.Authentication[0]),
string(nvd.ConfidentialityImpact[0]),
string(nvd.IntegrityImpact[0]),
string(nvd.AvailabilityImpact[0]))
}
//TODO CVSSv3
return &CveContent{
Type: NVD,
CveID: cveID,
Summary: nvd.Summary,
Cvss2Score: nvd.Score,
Cvss2Vector: vector,
Severity: "", // severity is not contained in NVD
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
Cpes: cpes,
CweID: nvd.CweID,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}
}
// ConvertJvnToModel convert JVN to CveContent
func ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
var cpes []Cpe
for _, c := range jvn.Cpes {
cpes = append(cpes, Cpe{CpeName: c.CpeName})
}
refs := []Reference{}
for _, r := range jvn.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
vector := strings.TrimSuffix(strings.TrimPrefix(jvn.Vector, "("), ")")
return &CveContent{
Type: JVN,
CveID: cveID,
Title: jvn.Title,
Summary: jvn.Summary,
Severity: jvn.Severity,
Cvss2Score: jvn.Score,
Cvss2Vector: vector,
SourceLink: jvn.JvnLink,
Cpes: cpes,
References: refs,
Published: jvn.PublishedDate,
LastModified: jvn.LastModifiedDate,
}
}

666
models/vulninfos.go Normal file
View File

@@ -0,0 +1,666 @@
/* 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 models
import (
"bytes"
"fmt"
"sort"
"strings"
"time"
"github.com/future-architect/vuls/config"
)
// VulnInfos has a map of VulnInfo
// Key: CveID
type VulnInfos map[string]VulnInfo
// Find elements that matches the function passed in argument
func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
filtered := VulnInfos{}
for _, vv := range v {
if f(vv) {
filtered[vv.CveID] = vv
}
}
return filtered
}
// FindScoredVulns return scored vulnerabilities
func (v VulnInfos) FindScoredVulns() VulnInfos {
return v.Find(func(vv VulnInfo) bool {
if 0 < vv.MaxCvss2Score().Value.Score ||
0 < vv.MaxCvss3Score().Value.Score {
return true
}
return false
})
}
// ToSortedSlice returns slice of VulnInfos that is sorted by Score, CVE-ID
func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
for k := range v {
sorted = append(sorted, v[k])
}
sort.Slice(sorted, func(i, j int) bool {
maxI := sorted[i].MaxCvssScore()
maxJ := sorted[j].MaxCvssScore()
if maxI.Value.Score != maxJ.Value.Score {
return maxJ.Value.Score < maxI.Value.Score
}
return sorted[i].CveID < sorted[j].CveID
})
return
}
// CountGroupBySeverity summarize the number of CVEs group by CVSSv2 Severity
func (v VulnInfos) CountGroupBySeverity() map[string]int {
m := map[string]int{}
for _, vInfo := range v {
score := vInfo.MaxCvss2Score().Value.Score
if score < 0.1 {
score = vInfo.MaxCvss3Score().Value.Score
}
switch {
case 7.0 <= score:
m["High"]++
case 4.0 <= score:
m["Medium"]++
case 0 < score:
m["Low"]++
default:
m["Unknown"]++
}
}
return m
}
// FormatCveSummary summarize the number of CVEs group by CVSSv2 Severity
func (v VulnInfos) FormatCveSummary() string {
m := v.CountGroupBySeverity()
if config.Conf.IgnoreUnscoredCves {
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
}
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
m["High"], m["Medium"], m["Low"], m["Unknown"])
}
// PackageStatuses is a list of PackageStatus
type PackageStatuses []PackageStatus
// Sort by Name
func (p PackageStatuses) Sort() {
sort.Slice(p, func(i, j int) bool {
return p[i].Name < p[j].Name
})
return
}
// PackageStatus has name and other status abount the package
type PackageStatus struct {
Name string
NotFixedYet bool
}
// VulnInfo has a vulnerability information and unsecure packages
type VulnInfo struct {
CveID string
Confidence Confidence
AffectedPackages PackageStatuses
DistroAdvisories []DistroAdvisory // for Aamazon, RHEL, FreeBSD
CpeNames []string
CveContents CveContents
}
// Titles returns tilte (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Title) {
values = append(values, CveContentStr{JVN, cont.Title})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
for _, ctype := range order {
// Only JVN has meaningful title. so return first 100 char of summary
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
}
}
for _, adv := range v.DistroAdvisories {
values = append(values, CveContentStr{
Type: "Vendor",
Value: strings.Replace(adv.Description, "\n", " ", -1),
})
}
if len(values) == 0 {
values = []CveContentStr{{
Type: Unknown,
Value: "-",
}}
}
return
}
// Summaries returns summaries
func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[JVN]; found && 0 < len(cont.Summary) {
summary := cont.Title
summary += "\n" + strings.Replace(
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
values = append(values, CveContentStr{JVN, summary})
}
}
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
summary := strings.Replace(cont.Summary, "\n", " ", -1)
values = append(values, CveContentStr{
Type: ctype,
Value: summary,
})
}
}
for _, adv := range v.DistroAdvisories {
values = append(values, CveContentStr{
Type: "Vendor",
Value: adv.Description,
})
}
if len(values) == 0 {
return []CveContentStr{{
Type: Unknown,
Value: "-",
}}
}
return
}
// Cvss2Scores returns CVSS V2 Scores
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
order := []CveContentType{NVD, RedHat, JVN}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss2Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
if ctype == NVD {
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
}
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: strings.ToUpper(sev),
},
})
}
}
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
values = append(values, CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Score: severityToV2ScoreRoughly(adv.Severity),
CalculatedBySeverity: true,
Vector: "-",
Severity: strings.ToUpper(adv.Severity),
},
})
}
}
return
}
// Cvss3Scores returns CVSS V3 Score
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
// TODO implement NVD
order := []CveContentType{RedHat}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < cont.Cvss3Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
values = append(values, CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: strings.ToUpper(sev),
},
})
}
}
return
}
// MaxCvss3Score returns Max CVSS V3 Score
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
// TODO implement NVD
order := []CveContentType{RedHat}
max := 0.0
value := CveContentCvss{
Type: Unknown,
Value: Cvss{Type: CVSS3},
}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss3Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS3,
Score: cont.Cvss3Score,
Vector: cont.Cvss3Vector,
Severity: sev,
},
}
max = cont.Cvss3Score
}
}
return value
}
// MaxCvssScore returns max CVSS Score
// If there is no CVSS Score, return Severity as a numerical value.
func (v VulnInfo) MaxCvssScore() CveContentCvss {
v3Max := v.MaxCvss3Score()
v2Max := v.MaxCvss2Score()
max := v3Max
if max.Type == Unknown {
return v2Max
}
if max.Value.Score < v2Max.Value.Score && !v2Max.Value.CalculatedBySeverity {
max = v2Max
}
return max
}
// MaxCvss2Score returns Max CVSS V2 Score
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
order := []CveContentType{NVD, RedHat, JVN}
max := 0.0
value := CveContentCvss{
Type: Unknown,
Value: Cvss{Type: CVSS2},
}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && max < cont.Cvss2Score {
// https://nvd.nist.gov/vuln-metrics/cvss
sev := cont.Severity
if ctype == NVD {
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
}
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: cont.Cvss2Score,
Vector: cont.Cvss2Vector,
Severity: sev,
},
}
max = cont.Cvss2Score
}
}
if 0 < max {
return value
}
// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
// Convert severity to cvss srore roughly, then returns max severity.
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
order = []CveContentType{Ubuntu, RedHat, Oracle}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Severity) {
score := severityToV2ScoreRoughly(cont.Severity)
if max < score {
value = CveContentCvss{
Type: ctype,
Value: Cvss{
Type: CVSS2,
Score: score,
CalculatedBySeverity: true,
Vector: cont.Cvss2Vector,
Severity: cont.Severity,
},
}
}
max = score
}
}
// Only RedHat, Oracle and Amazon has severity data in advisory.
for _, adv := range v.DistroAdvisories {
if adv.Severity != "" {
score := severityToV2ScoreRoughly(adv.Severity)
if max < score {
value = CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Score: score,
CalculatedBySeverity: true,
Vector: "-",
Severity: adv.Severity,
},
}
}
}
}
return value
}
// CveContentCvss has CveContentType and Cvss2
type CveContentCvss struct {
Type CveContentType
Value Cvss
}
// CvssType Represent the type of CVSS
type CvssType string
const (
// CVSS2 means CVSS vesion2
CVSS2 CvssType = "2"
// CVSS3 means CVSS vesion3
CVSS3 CvssType = "3"
)
// Cvss has CVSS Score
type Cvss struct {
Type CvssType
Score float64
CalculatedBySeverity bool
Vector string
Severity string
}
// Format CVSS Score and Vector
func (c Cvss) Format() string {
switch c.Type {
case CVSS2:
return fmt.Sprintf("%3.1f/%s", c.Score, c.Vector)
case CVSS3:
return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
}
return ""
}
func cvss2ScoreToSeverity(score float64) string {
if 7.0 <= score {
return "HIGH"
} else if 4.0 <= score {
return "MEDIUM"
}
return "LOW"
}
// Amazon Linux Security Advisory
// Critical, Important, Medium, Low
// https://alas.aws.amazon.com/
//
// RedHat, Oracle OVAL
// Critical, Important, Moderate, Low
// https://access.redhat.com/security/updates/classification
//
// Ubuntu OVAL
// Critical, High, Medium, Low
// https://wiki.ubuntu.com/Bugs/Importance
// https://people.canonical.com/~ubuntu-security/cve/priority.html
func severityToV2ScoreRoughly(severity string) float64 {
switch strings.ToUpper(severity) {
case "CRITICAL":
return 10.0
case "IMPORTANT", "HIGH":
return 8.9
case "MODERATE", "MEDIUM":
return 6.9
case "LOW":
return 3.9
}
return 0
}
// CveContentCvss3 has CveContentType and Cvss3
// type CveContentCvss3 struct {
// Type CveContentType
// Value Cvss3
// }
// Cvss3 has CVSS v3 Score, Vector and Severity
// type Cvss3 struct {
// Score float64
// Vector string
// Severity string
// }
// Format CVSS Score and Vector
// func (c Cvss3) Format() string {
// return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
// }
// func cvss3ScoreToSeverity(score float64) string {
// if 9.0 <= score {
// return "CRITICAL"
// } else if 7.0 <= score {
// return "HIGH"
// } else if 4.0 <= score {
// return "MEDIUM"
// }
// return "LOW"
// }
// FormatMaxCvssScore returns Max CVSS Score
func (v VulnInfo) FormatMaxCvssScore() string {
v2Max := v.MaxCvss2Score()
v3Max := v.MaxCvss3Score()
if v2Max.Value.Score <= v3Max.Value.Score {
return fmt.Sprintf("%3.1f %s (%s)",
v3Max.Value.Score,
strings.ToUpper(v3Max.Value.Severity),
v3Max.Type)
}
return fmt.Sprintf("%3.1f %s (%s)",
v2Max.Value.Score,
strings.ToUpper(v2Max.Value.Severity),
v2Max.Type)
}
// Cvss2CalcURL returns CVSS v2 caluclator's URL
func (v VulnInfo) Cvss2CalcURL() string {
return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
}
// Cvss3CalcURL returns CVSS v3 caluclator's URL
func (v VulnInfo) Cvss3CalcURL() string {
return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
}
// VendorLinks returns links of vendor support's URL
func (v VulnInfo) VendorLinks(family string) map[string]string {
links := map[string]string{}
switch family {
case config.RedHat, config.CentOS:
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
for _, advisory := range v.DistroAdvisories {
aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
links[advisory.AdvisoryID] = fmt.Sprintf("https://rhn.redhat.com/errata/%s.html", aidURL)
}
return links
case config.Oracle:
links["Oracle-CVE"] = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.CveID)
for _, advisory := range v.DistroAdvisories {
links[advisory.AdvisoryID] =
fmt.Sprintf("https://linux.oracle.com/errata/%s.html", advisory.AdvisoryID)
}
return links
case config.Amazon:
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
for _, advisory := range v.DistroAdvisories {
links[advisory.AdvisoryID] =
fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
}
return links
case config.Ubuntu:
links["Ubuntu-CVE"] = "http://people.ubuntu.com/~ubuntu-security/cve/" + v.CveID
return links
case config.Debian:
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
case config.FreeBSD:
for _, advisory := range v.DistroAdvisories {
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
}
return links
}
return links
}
// NilToEmpty set nil slice or map fields to empty to avoid null in JSON
func (v *VulnInfo) NilToEmpty() *VulnInfo {
if v.CpeNames == nil {
v.CpeNames = []string{}
}
if v.DistroAdvisories == nil {
v.DistroAdvisories = []DistroAdvisory{}
}
if v.AffectedPackages == nil {
v.AffectedPackages = PackageStatuses{}
}
if v.CveContents == nil {
v.CveContents = NewCveContents()
}
for key := range v.CveContents {
if v.CveContents[key].Cpes == nil {
cont := v.CveContents[key]
cont.Cpes = []Cpe{}
v.CveContents[key] = cont
}
}
return v
}
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
type DistroAdvisory struct {
AdvisoryID string
Severity string
Issued time.Time
Updated time.Time
Description string
}
// Format the distro advisory information
func (p DistroAdvisory) Format() string {
if p.AdvisoryID == "" {
return ""
}
var delim bytes.Buffer
for i := 0; i < len(p.AdvisoryID); i++ {
delim.WriteString("-")
}
buf := []string{p.AdvisoryID, delim.String(), p.Description}
return strings.Join(buf, "\n")
}
// Confidence is a ranking how confident the CVE-ID was deteted correctly
// Score: 0 - 100
type Confidence struct {
Score int
DetectionMethod DetectionMethod
}
func (c Confidence) String() string {
return fmt.Sprintf("%d / %s", c.Score, c.DetectionMethod)
}
// DetectionMethod indicates
// - How to detect the CveID
// - How to get the changelog difference between installed and candidate version
type DetectionMethod string
const (
// CpeNameMatchStr is a String representation of CpeNameMatch
CpeNameMatchStr = "CpeNameMatch"
// YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch
YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch"
// PkgAuditMatchStr is a String representation of PkgAuditMatch
PkgAuditMatchStr = "PkgAuditMatch"
// OvalMatchStr is a String representation of OvalMatch
OvalMatchStr = "OvalMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
ChangelogExactMatchStr = "ChangelogExactMatch"
// ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch
ChangelogLenientMatchStr = "ChangelogLenientMatch"
// FailedToGetChangelog is a String representation of FailedToGetChangelog
FailedToGetChangelog = "FailedToGetChangelog"
// FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog
FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
)
var (
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
CpeNameMatch = Confidence{100, CpeNameMatchStr}
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
OvalMatch = Confidence{100, OvalMatchStr}
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
)

936
models/vulninfos_test.go Normal file
View File

@@ -0,0 +1,936 @@
/* 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 models
import (
"reflect"
"testing"
)
func TestTitles(t *testing.T) {
type in struct {
lang string
cont VulnInfo
}
var tests = []struct {
in in
out []CveContentStr
}{
// lang: ja
{
in: in{
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Title: "Title1",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
},
},
},
out: []CveContentStr{
{
Type: JVN,
Value: "Title1",
},
{
Type: NVD,
Value: "Summary NVD",
},
{
Type: RedHat,
Value: "Summary RedHat",
},
},
},
// lang: en
{
in: in{
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Title: "Title1",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
},
},
},
out: []CveContentStr{
{
Type: NVD,
Value: "Summary NVD",
},
{
Type: RedHat,
Value: "Summary RedHat",
},
},
},
// lang: empty
{
in: in{
lang: "en",
cont: VulnInfo{},
},
out: []CveContentStr{
{
Type: Unknown,
Value: "-",
},
},
},
}
for _, tt := range tests {
actual := tt.in.cont.Titles(tt.in.lang, "redhat")
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestSummaries(t *testing.T) {
type in struct {
lang string
cont VulnInfo
}
var tests = []struct {
in in
out []CveContentStr
}{
// lang: ja
{
in: in{
lang: "ja",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Title: "Title JVN",
Summary: "Summary JVN",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
},
},
},
out: []CveContentStr{
{
Type: JVN,
Value: "Title JVN\nSummary JVN",
},
{
Type: NVD,
Value: "Summary NVD",
},
{
Type: RedHat,
Value: "Summary RedHat",
},
},
},
// lang: en
{
in: in{
lang: "en",
cont: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Title: "Title JVN",
Summary: "Summary JVN",
},
RedHat: {
Type: RedHat,
Summary: "Summary RedHat",
},
NVD: {
Type: NVD,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
},
},
},
},
out: []CveContentStr{
{
Type: NVD,
Value: "Summary NVD",
},
{
Type: RedHat,
Value: "Summary RedHat",
},
},
},
// lang: empty
{
in: in{
lang: "en",
cont: VulnInfo{},
},
out: []CveContentStr{
{
Type: Unknown,
Value: "-",
},
},
},
}
for _, tt := range tests {
actual := tt.in.cont.Summaries(tt.in.lang, "redhat")
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestCountGroupBySeverity(t *testing.T) {
var tests = []struct {
in VulnInfos
out map[string]int
}{
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 2.0,
},
},
},
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 5.0,
},
},
},
"CVE-2017-0005": {
CveID: "CVE-2017-0005",
},
},
out: map[string]int{
"High": 1,
"Medium": 1,
"Low": 1,
"Unknown": 1,
},
},
}
for _, tt := range tests {
actual := tt.in.CountGroupBySeverity()
for k := range tt.out {
if tt.out[k] != actual[k] {
t.Errorf("\nexpected %s: %d\n actual %d\n",
k, tt.out[k], actual[k])
}
}
}
}
func TestToSortedSlice(t *testing.T) {
var tests = []struct {
in VulnInfos
out []VulnInfo
}{
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 7.0,
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 8.0,
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 8.0,
},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 7.0,
},
},
},
},
},
// When max scores are the same, sort by CVE-ID
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 7.0,
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss2Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss3Score: 7.0,
},
},
},
},
},
// When there are no cvss scores, sort by severity
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
},
},
},
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
},
},
},
},
},
}
for _, tt := range tests {
actual := tt.in.ToSortedSlice()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestCvss2Scores(t *testing.T) {
var tests = []struct {
in VulnInfo
out []CveContentCvss
}{
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NVD: {
Type: NVD,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NIOT included in NVD
},
},
},
out: []CveContentCvss{
{
Type: NVD,
Value: Cvss{
Type: CVSS2,
Score: 8.1,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
},
{
Type: RedHat,
Value: Cvss{
Type: CVSS2,
Score: 8.0,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
},
{
Type: JVN,
Value: Cvss{
Type: CVSS2,
Score: 8.2,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
},
},
},
// Empty
{
in: VulnInfo{},
out: nil,
},
}
for i, tt := range tests {
actual := tt.in.Cvss2Scores()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestMaxCvss2Scores(t *testing.T) {
var tests = []struct {
in VulnInfo
out CveContentCvss
}{
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.2,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NVD: {
Type: NVD,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NIOT included in NVD
},
},
},
out: CveContentCvss{
Type: JVN,
Value: Cvss{
Type: CVSS2,
Score: 8.2,
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Severity: "HIGH",
},
},
},
// Severity in OVAL
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: Ubuntu,
Value: Cvss{
Type: CVSS2,
Score: 8.9,
CalculatedBySeverity: true,
Severity: "HIGH",
},
},
},
// Empty
{
in: VulnInfo{},
out: CveContentCvss{
Type: Unknown,
Value: Cvss{
Type: CVSS2,
Score: 0.0,
Vector: "",
Severity: "",
},
},
},
}
for i, tt := range tests {
actual := tt.in.MaxCvss2Score()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestCvss3Scores(t *testing.T) {
var tests = []struct {
in VulnInfo
out []CveContentCvss
}{
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
NVD: {
Type: NVD,
Cvss3Score: 8.1,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
// Severity is NIOT included in NVD
},
},
},
out: []CveContentCvss{
{
Type: RedHat,
Value: Cvss{
Type: CVSS3,
Score: 8.0,
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Severity: "HIGH",
},
},
},
},
// Empty
{
in: VulnInfo{},
out: nil,
},
}
for _, tt := range tests {
actual := tt.in.Cvss3Scores()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestMaxCvss3Scores(t *testing.T) {
var tests = []struct {
in VulnInfo
out CveContentCvss
}{
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
},
},
out: CveContentCvss{
Type: RedHat,
Value: Cvss{
Type: CVSS3,
Score: 8.0,
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Severity: "HIGH",
},
},
},
// Empty
{
in: VulnInfo{},
out: CveContentCvss{
Type: Unknown,
Value: Cvss{
Type: CVSS3,
Score: 0.0,
Vector: "",
Severity: "",
},
},
},
}
for _, tt := range tests {
actual := tt.in.MaxCvss3Score()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestMaxCvssScores(t *testing.T) {
var tests = []struct {
in VulnInfo
out CveContentCvss
}{
{
in: VulnInfo{
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 8.0,
},
},
},
out: CveContentCvss{
Type: RedHat,
Value: Cvss{
Type: CVSS2,
Score: 8.0,
},
},
},
{
in: VulnInfo{
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss3Score: 8.0,
},
},
},
out: CveContentCvss{
Type: RedHat,
Value: Cvss{
Type: CVSS3,
Score: 8.0,
},
},
},
//2
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: Ubuntu,
Value: Cvss{
Type: CVSS2,
Score: 8.9,
CalculatedBySeverity: true,
Severity: "HIGH",
},
},
},
//3
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "MEDIUM",
},
NVD: {
Type: NVD,
Cvss2Score: 7.0,
},
},
},
out: CveContentCvss{
Type: NVD,
Value: Cvss{
Type: CVSS2,
Score: 7.0,
Severity: "HIGH",
},
},
},
//4
{
in: VulnInfo{
DistroAdvisories: []DistroAdvisory{
{
Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: "Vendor",
Value: Cvss{
Type: CVSS2,
Score: 8.9,
CalculatedBySeverity: true,
Vector: "-",
Severity: "HIGH",
},
},
},
{
in: VulnInfo{
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "MEDIUM",
},
NVD: {
Type: NVD,
Cvss2Score: 4.0,
},
},
DistroAdvisories: []DistroAdvisory{
{
Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: NVD,
Value: Cvss{
Type: CVSS2,
Score: 4,
Severity: "MEDIUM",
},
},
},
// Empty
{
in: VulnInfo{},
out: CveContentCvss{
Type: Unknown,
Value: Cvss{
Type: CVSS2,
Score: 0,
},
},
},
}
for i, tt := range tests {
actual := tt.in.MaxCvssScore()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestFormatMaxCvssScore(t *testing.T) {
var tests = []struct {
in VulnInfo
out string
}{
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss3Score: 8.0,
},
NVD: {
Type: NVD,
Cvss2Score: 8.1,
// Severity is NIOT included in NVD
},
},
},
out: "8.3 HIGH (jvn)",
},
{
in: VulnInfo{
CveContents: CveContents{
JVN: {
Type: JVN,
Severity: "HIGH",
Cvss2Score: 8.3,
},
RedHat: {
Type: RedHat,
Severity: "HIGH",
Cvss2Score: 8.0,
Cvss3Score: 9.9,
},
NVD: {
Type: NVD,
Cvss2Score: 8.1,
},
},
},
out: "9.9 HIGH (redhat)",
},
}
for _, tt := range tests {
actual := tt.in.FormatMaxCvssScore()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}
func TestSortPackageStatues(t *testing.T) {
var tests = []struct {
in PackageStatuses
out PackageStatuses
}{
{
in: PackageStatuses{
{Name: "b"},
{Name: "a"},
},
out: PackageStatuses{
{Name: "a"},
{Name: "b"},
},
},
}
for _, tt := range tests {
tt.in.Sort()
if !reflect.DeepEqual(tt.in, tt.out) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, tt.in)
}
}
}

266
oval/debian.go Normal file
View File

@@ -0,0 +1,266 @@
/* 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 oval
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// DebianBase is the base struct of Debian and Ubuntu
type DebianBase struct {
Base
}
func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[defPacks.def.Debian.CveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
vinfo = models.VulnInfo{
CveID: defPacks.def.Debian.CveID,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s OVAL will be overwritten",
defPacks.def.Debian.CveID)
} else {
util.Log.Debugf("%s is also detected by OVAL",
defPacks.def.Debian.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
}
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
return &models.CveContent{
CveID: def.Debian.CveID,
Title: def.Title,
Summary: def.Description,
Severity: def.Advisory.Severity,
References: refs,
}
}
// Debian is the interface for Debian OVAL
type Debian struct {
DebianBase
}
// NewDebian creates OVAL client for Debian
func NewDebian() Debian {
return Debian{
DebianBase{
Base{
family: config.Debian,
},
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version)
linuxImage := "linux-image-" + r.RunningKernel.Release
// Add linux and set the version of running kernel to search OVAL.
if r.Container.ContainerID == "" {
r.Packages["linux"] = models.Package{
Name: "linux",
Version: r.RunningKernel.Version,
}
}
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
return err
}
}
delete(r.Packages, "linux")
for _, defPacks := range relatedDefs.entries {
// Remove linux added above to search for oval
// linux is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = true
delete(defPacks.actuallyAffectedPackNames, "linux")
for i, p := range defPacks.def.AffectedPacks {
if p.Name == "linux" {
p.Name = linuxImage
defPacks.def.AffectedPacks[i] = p
}
}
}
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.Debian]; ok {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.Debian] = cont
}
}
return nil
}
// Ubuntu is the interface for Debian OVAL
type Ubuntu struct {
DebianBase
}
// NewUbuntu creates OVAL client for Debian
func NewUbuntu() Ubuntu {
return Ubuntu{
DebianBase{
Base{
family: config.Ubuntu,
},
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
ovalKernelImageNames := []string{
"linux-aws",
"linux-azure",
"linux-flo",
"linux-gcp",
"linux-gke",
"linux-goldfish",
"linux-hwe",
"linux-hwe-edge",
"linux-kvm",
"linux-mako",
"linux-raspi2",
"linux-snapdragon",
}
linuxImage := "linux-image-" + r.RunningKernel.Release
found := false
if r.Container.ContainerID == "" {
for _, n := range ovalKernelImageNames {
if _, ok := r.Packages[n]; ok {
v, ok := r.Packages[linuxImage]
if ok {
// Set running kernel version
p := r.Packages[n]
p.Version = v.Version
p.NewVersion = v.NewVersion
r.Packages[n] = p
} else {
util.Log.Warnf("Running kernel image %s is not found: %s",
linuxImage, r.RunningKernel.Version)
}
found = true
break
}
}
if !found {
// linux-generic is described as "linux" in Ubuntu's oval.
// Add "linux" and set the version of running kernel to search OVAL.
v, ok := r.Packages[linuxImage]
if ok {
r.Packages["linux"] = models.Package{
Name: "linux",
Version: v.Version,
NewVersion: v.NewVersion,
}
} else {
util.Log.Warnf("%s is not found. Running: %s",
linuxImage, r.RunningKernel.Release)
}
}
}
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
return err
}
}
if !found {
delete(r.Packages, "linux")
}
for _, defPacks := range relatedDefs.entries {
// Remove "linux" added above to search for oval
// "linux" is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
defPacks.actuallyAffectedPackNames[linuxImage] = true
delete(defPacks.actuallyAffectedPackNames, "linux")
for i, p := range defPacks.def.AffectedPacks {
if p.Name == "linux" {
p.Name = linuxImage
defPacks.def.AffectedPacks[i] = p
}
}
}
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
vuln.CveContents[models.Ubuntu] = cont
}
}
return nil
}

75
oval/debian_test.go Normal file
View File

@@ -0,0 +1,75 @@
/* 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 oval
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestPackNamesOfUpdateDebian(t *testing.T) {
var tests = []struct {
in models.ScanResult
defPacks defPacks
out models.ScanResult
}{
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Debian: ovalmodels.Debian{
CveID: "CVE-2000-1000",
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
},
},
},
},
},
}
util.Log = util.NewCustomLogger(config.ServerInfo{})
for i, tt := range tests {
Debian{}.update(&tt.in, tt.defPacks)
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}

150
oval/oval.go Normal file
View File

@@ -0,0 +1,150 @@
/* 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 oval
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/kotakanbe/goval-dictionary/db"
ovallog "github.com/kotakanbe/goval-dictionary/log"
"github.com/parnurzeal/gorequest"
)
// Client is the interface of OVAL client.
type Client interface {
CheckHTTPHealth() error
FillWithOval(r *models.ScanResult) error
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
CheckIfOvalFetched(string, string) (bool, error)
CheckIfOvalFresh(string, string) (bool, error)
}
// Base is a base struct
type Base struct {
family string
}
// CheckHTTPHealth do health check
func (b Base) CheckHTTPHealth() error {
if !b.isFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", config.Conf.OvalDBURL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("Failed to request to OVAL server. url: %s, errs: %v",
url, errs)
}
return nil
}
// CheckIfOvalFetched checks if oval entries are in DB by family, release.
func (b Base) CheckIfOvalFetched(osFamily, release string) (fetched bool, err error) {
ovallog.Initialize(config.Conf.LogDir)
if !b.isFetchViaHTTP() {
var ovaldb db.DB
if ovaldb, err = db.NewDB(
osFamily,
config.Conf.OvalDBType,
config.Conf.OvalDBPath,
config.Conf.DebugSQL,
); err != nil {
return false, err
}
defer ovaldb.CloseDB()
count, err := ovaldb.CountDefs(osFamily, release)
if err != nil {
return false, fmt.Errorf("Failed to count OVAL defs: %s, %s, %v",
osFamily, release, err)
}
return 0 < count, nil
}
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "count", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
count := 0
if err := json.Unmarshal([]byte(body), &count); err != nil {
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
}
return 0 < count, nil
}
// CheckIfOvalFresh checks if oval entries are fresh enough
func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
ovallog.Initialize(config.Conf.LogDir)
var lastModified time.Time
if !b.isFetchViaHTTP() {
var ovaldb db.DB
if ovaldb, err = db.NewDB(
osFamily,
config.Conf.OvalDBType,
config.Conf.OvalDBPath,
config.Conf.DebugSQL,
); err != nil {
return false, err
}
defer ovaldb.CloseDB()
lastModified = ovaldb.GetLastModified(osFamily, release)
} else {
url, _ := util.URLPathJoin(config.Conf.OvalDBURL, "lastmodified", osFamily, release)
resp, body, errs := gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
return false, fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
}
}
major := strings.Split(release, ".")[0]
since := time.Now()
since = since.AddDate(0, 0, -3)
if lastModified.Before(since) {
util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
osFamily, major, lastModified)
return false, nil
}
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major)
return true, nil
}
func (b Base) isFetchViaHTTP() bool {
// Default value of OvalDBType is sqlite3
return config.Conf.OvalDBURL != "" && config.Conf.OvalDBType == "sqlite3"
}

224
oval/redhat.go Normal file
View File

@@ -0,0 +1,224 @@
/* 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 oval
import (
"fmt"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// RedHatBase is the base struct for RedHat and CentOS
type RedHatBase struct {
Base
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(
o.family, r.Release, r.Packages); err != nil {
return err
}
}
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
switch models.NewCveContentType(o.family) {
case models.RedHat:
if cont, ok := vuln.CveContents[models.RedHat]; ok {
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
vuln.CveContents[models.RedHat] = cont
}
case models.Oracle:
if cont, ok := vuln.CveContents[models.Oracle]; ok {
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
vuln.CveContents[models.Oracle] = cont
}
}
}
return nil
}
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
ctype := models.NewCveContentType(o.family)
for _, cve := range defPacks.def.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
} else {
util.Log.Debugf("%s also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages.Sort()
r.ScannedCves[cve.CveID] = vinfo
}
}
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
for _, cve := range def.Advisory.Cves {
if cve.CveID != cveID {
continue
}
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
score2, vec2 := o.parseCvss2(cve.Cvss2)
score3, vec3 := o.parseCvss3(cve.Cvss3)
severity := def.Advisory.Severity
if cve.Impact != "" {
severity = cve.Impact
}
return &models.CveContent{
Type: models.NewCveContentType(o.family),
CveID: cve.CveID,
Title: def.Title,
Summary: def.Description,
Severity: severity,
Cvss2Score: score2,
Cvss2Vector: vec2,
Cvss3Score: score3,
Cvss3Vector: vec3,
References: refs,
CweID: cve.Cwe,
Published: def.Advisory.Issued,
LastModified: def.Advisory.Updated,
}
}
return nil
}
// ParseCvss2 divide CVSSv2 string into score and vector
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string) {
var err error
ss := strings.Split(scoreVector, "/")
if 1 < len(ss) {
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
}
return 0, ""
}
// ParseCvss3 divide CVSSv3 string into score and vector
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
var err error
ss := strings.Split(scoreVector, "/CVSS:3.0/")
if 1 < len(ss) {
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
}
return 0, ""
}
// RedHat is the interface for RedhatBase OVAL
type RedHat struct {
RedHatBase
}
// NewRedhat creates OVAL client for Redhat
func NewRedhat() RedHat {
return RedHat{
RedHatBase{
Base{
family: config.RedHat,
},
},
}
}
// CentOS is the interface for CentOS OVAL
type CentOS struct {
RedHatBase
}
// NewCentOS creates OVAL client for CentOS
func NewCentOS() CentOS {
return CentOS{
RedHatBase{
Base{
family: config.CentOS,
},
},
}
}
// Oracle is the interface for CentOS OVAL
type Oracle struct {
RedHatBase
}
// NewOracle creates OVAL client for Oracle
func NewOracle() Oracle {
return Oracle{
RedHatBase{
Base{
family: config.Oracle,
},
},
}
}

145
oval/redhat_test.go Normal file
View File

@@ -0,0 +1,145 @@
/* 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 oval
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestParseCvss2(t *testing.T) {
type out struct {
score float64
vector string
}
var tests = []struct {
in string
out out
}{
{
in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P",
out: out{
score: 5.0,
vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
},
{
in: "",
out: out{
score: 0,
vector: "",
},
},
}
for _, tt := range tests {
s, v := RedHatBase{}.parseCvss2(tt.in)
if s != tt.out.score || v != tt.out.vector {
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
tt.out.score, tt.out.vector, s, v)
}
}
}
func TestParseCvss3(t *testing.T) {
type out struct {
score float64
vector string
}
var tests = []struct {
in string
out out
}{
{
in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
out: out{
score: 5.6,
vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
},
{
in: "",
out: out{
score: 0,
vector: "",
},
},
}
for _, tt := range tests {
s, v := RedHatBase{}.parseCvss3(tt.in)
if s != tt.out.score || v != tt.out.vector {
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
tt.out.score, tt.out.vector, s, v)
}
}
}
func TestPackNamesOfUpdate(t *testing.T) {
var tests = []struct {
in models.ScanResult
defPacks defPacks
out models.ScanResult
}{
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Advisory: ovalmodels.Advisory{
Cves: []ovalmodels.Cve{
{
CveID: "CVE-2000-1000",
},
},
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
},
},
},
},
},
}
util.Log = util.NewCustomLogger(config.ServerInfo{})
for i, tt := range tests {
RedHat{}.update(&tt.in, tt.defPacks)
e := tt.out.ScannedCves["CVE-2000-1000"].AffectedPackages
a := tt.in.ScannedCves["CVE-2000-1000"].AffectedPackages
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}

332
oval/util.go Normal file
View File

@@ -0,0 +1,332 @@
/* 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 oval
import (
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
debver "github.com/knqyf263/go-deb-version"
rpmver "github.com/knqyf263/go-rpm-version"
"github.com/kotakanbe/goval-dictionary/db"
ovallog "github.com/kotakanbe/goval-dictionary/log"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
"github.com/parnurzeal/gorequest"
)
type ovalResult struct {
entries []defPacks
}
type defPacks struct {
def ovalmodels.Definition
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
switch family {
case config.Ubuntu:
packNotFixedYet := map[string]bool{}
for _, p := range e.def.AffectedPacks {
packNotFixedYet[p.Name] = p.NotFixedYet
}
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
NotFixedYet: packNotFixedYet[k],
})
}
case config.CentOS, config.Debian:
// There are many packages that has been fixed in RedHat, but not been fixed in CentOS
for name := range e.actuallyAffectedPackNames {
pack, ok := packs[name]
if !ok {
util.Log.Warnf("Faild to find in Package list: %s", name)
return
}
ovalPackVer := ""
for _, p := range e.def.AffectedPacks {
if p.Name == name {
ovalPackVer = p.Version
break
}
}
if ovalPackVer == "" {
util.Log.Warnf("Faild to find in Oval Package list: %s", name)
return
}
if pack.NewVersion == "" {
// compare version: installed vs oval
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
} else {
// compare version: newVer vs oval
packNewVer := fmt.Sprintf("%s-%s", pack.NewVersion, pack.NewRelease)
vera := rpmver.NewVersion(packNewVer)
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
}
}
default:
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
})
}
}
return
}
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = true
return true
}
}
e.entries = append(e.entries, defPacks{
def: def,
actuallyAffectedPackNames: map[string]bool{packName: true},
})
return false
}
type request struct {
pack models.Package
}
type response struct {
pack *models.Package
defs []ovalmodels.Definition
}
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
relatedDefs ovalResult, err error) {
reqChan := make(chan request, len(r.Packages))
resChan := make(chan response, len(r.Packages))
errChan := make(chan error, len(r.Packages))
defer close(reqChan)
defer close(resChan)
defer close(errChan)
go func() {
for _, pack := range r.Packages {
reqChan <- request{
pack: pack,
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for range r.Packages {
tasks <- func() {
select {
case req := <-reqChan:
url, err := util.URLPathJoin(
config.Conf.OvalDBURL,
"packs",
r.Family,
r.Release,
req.pack.Name,
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, &req.pack, resChan, errChan)
}
}
}
}
timeout := time.After(2 * 60 * time.Second)
var errs []error
for range r.Packages {
select {
case res := <-resChan:
for _, def := range res.defs {
for _, p := range def.AffectedPacks {
if res.pack.Name != p.Name {
continue
}
if p.NotFixedYet {
relatedDefs.upsert(def, p.Name)
continue
}
if less, err := lessThan(r.Family, *res.pack, p); err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", *res.pack, p)
} else if less {
relatedDefs.upsert(def, p.Name)
}
}
}
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return relatedDefs, fmt.Errorf("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return relatedDefs, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
}
return
}
func httpGet(url string, pack *models.Package, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
count, retryMax := 0, 3
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 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
count++
if count == retryMax {
return nil
}
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
return
}
if count == retryMax {
errChan <- fmt.Errorf("HRetry count exceeded")
return
}
defs := []ovalmodels.Definition{}
if err := json.Unmarshal([]byte(body), &defs); err != nil {
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
return
}
resChan <- response{
pack: pack,
defs: defs,
}
}
func getDefsByPackNameFromOvalDB(family, osRelease string,
installedPacks models.Packages) (relatedDefs ovalResult, err error) {
ovallog.Initialize(config.Conf.LogDir)
path := config.Conf.OvalDBURL
if config.Conf.OvalDBType == "sqlite3" {
path = config.Conf.OvalDBPath
}
util.Log.Debugf("Open oval-dictionary db (%s): %s", config.Conf.OvalDBType, path)
var ovaldb db.DB
if ovaldb, err = db.NewDB(
family,
config.Conf.OvalDBType,
path,
config.Conf.DebugSQL,
); err != nil {
return
}
defer ovaldb.CloseDB()
for _, installedPack := range installedPacks {
definitions, err := ovaldb.GetByPackName(osRelease, installedPack.Name)
if err != nil {
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
}
for _, def := range definitions {
for _, ovalPack := range def.AffectedPacks {
if installedPack.Name != ovalPack.Name {
continue
}
if ovalPack.NotFixedYet {
relatedDefs.upsert(def, installedPack.Name)
continue
}
less, err := lessThan(family, installedPack, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", installedPack, ovalPack)
} else if less {
relatedDefs.upsert(def, installedPack.Name)
}
}
}
}
return
}
func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bool, error) {
switch family {
case config.Debian, config.Ubuntu:
vera, err := debver.NewVersion(packA.Version)
if err != nil {
return false, err
}
verb, err := debver.NewVersion(packB.Version)
if err != nil {
return false, err
}
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS, config.Oracle:
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", packA.Version, packA.Release))
verb := rpmver.NewVersion(packB.Version)
return vera.LessThan(verb), nil
}
return false, fmt.Errorf("Package version comparison not supported: %s", family)
}

246
oval/util_test.go Normal file
View File

@@ -0,0 +1,246 @@
package oval
import (
"reflect"
"sort"
"testing"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestUpsert(t *testing.T) {
var tests = []struct {
res ovalResult
def ovalmodels.Definition
packName string
upserted bool
out ovalResult
}{
//insert
{
res: ovalResult{},
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack1",
upserted: false,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
},
},
},
},
},
//update
{
res: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
},
},
},
},
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack2",
upserted: true,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
"pack2": true,
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
},
},
},
},
},
}
for i, tt := range tests {
upserted := tt.res.upsert(tt.def, tt.packName)
if tt.upserted != upserted {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
}
if !reflect.DeepEqual(tt.out, tt.res) {
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, tt.res)
}
}
}
func TestDefpacksToPackStatuses(t *testing.T) {
type in struct {
dp defPacks
family string
packs models.Packages
}
var tests = []struct {
in in
out models.PackageStatuses
}{
// Ubuntu
{
in: in{
family: "ubuntu",
packs: models.Packages{},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: true,
},
{
Name: "b",
NotFixedYet: false,
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
},
},
},
out: models.PackageStatuses{
{
Name: "a",
NotFixedYet: true,
},
{
Name: "b",
NotFixedYet: false,
},
},
},
// RedHat, Amazon, Debian
{
in: in{
family: "redhat",
packs: models.Packages{},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
},
{
Name: "b",
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
},
},
},
out: models.PackageStatuses{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
},
},
},
// CentOS
{
in: in{
family: "centos",
packs: models.Packages{
"a": {Version: "1.0.0"},
"b": {
Version: "1.0.0",
NewVersion: "2.0.0",
},
"c": {
Version: "1.0.0",
NewVersion: "1.5.0",
},
},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
Version: "1.0.1",
},
{
Name: "b",
Version: "1.5.0",
},
{
Name: "c",
Version: "2.0.0",
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
"c": true,
},
},
},
out: models.PackageStatuses{
{
Name: "a",
NotFixedYet: true,
},
{
Name: "b",
NotFixedYet: false,
},
{
Name: "c",
NotFixedYet: true,
},
},
},
}
for i, tt := range tests {
actual := tt.in.dp.toPackStatuses(tt.in.family, tt.in.packs)
sort.Slice(actual, func(i, j int) bool {
return actual[i].Name < actual[j].Name
})
if !reflect.DeepEqual(actual, tt.out) {
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}

View File

@@ -24,7 +24,7 @@ import (
"fmt"
"time"
"github.com/Azure/azure-storage-go"
storage "github.com/Azure/azure-sdk-for-go/storage"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -139,13 +139,9 @@ func createBlockBlob(cli storage.BlobStorageClient, k string, b []byte) error {
k = k + ".gz"
}
if err := cli.CreateBlockBlobFromReader(
c.Conf.AzureContainer,
k,
uint64(len(b)),
bytes.NewReader(b),
map[string]string{},
); err != nil {
ref := cli.GetContainerReference(c.Conf.AzureContainer)
blob := ref.GetBlobReference(k)
if err := blob.CreateBlockBlobFromReader(bytes.NewReader(b), nil); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",
c.Conf.AzureContainer, k, err)
}

View File

@@ -15,13 +15,12 @@ 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 cveapi
package report
import (
"encoding/json"
"fmt"
"net/http"
"sort"
"time"
"github.com/cenkalti/backoff"
@@ -32,6 +31,7 @@ import (
cveconfig "github.com/kotakanbe/go-cve-dictionary/config"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cve "github.com/kotakanbe/go-cve-dictionary/models"
log "github.com/sirupsen/logrus"
)
// CveClient is api client of CVE disctionary service.
@@ -46,10 +46,10 @@ func (api *cvedictClient) initialize() {
api.baseURL = config.Conf.CveDBURL
}
func (api cvedictClient) CheckHealth() (ok bool, err error) {
if config.Conf.CveDBURL == "" || config.Conf.CveDBType == "mysql" || config.Conf.CveDBType == "postgres" {
func (api cvedictClient) CheckHealth() error {
if !api.isFetchViaHTTP() {
util.Log.Debugf("get cve-dictionary from %s", config.Conf.CveDBType)
return true, nil
return nil
}
api.initialize()
@@ -59,9 +59,10 @@ func (api cvedictClient) CheckHealth() (ok bool, err error) {
resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v", url, errs)
return fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
url, errs)
}
return true, nil
return nil
}
type response struct {
@@ -69,9 +70,8 @@ type response struct {
CveDetail cve.CveDetail
}
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDetails, err error) {
switch config.Conf.CveDBType {
case "sqlite3", "mysql", "postgres":
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails []*cve.CveDetail, err error) {
if !api.isFetchViaHTTP() {
return api.FetchCveDetailsFromCveDB(cveIDs)
}
@@ -112,28 +112,26 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDet
select {
case res := <-resChan:
if len(res.CveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cve.CveDetail{
cveDetails = append(cveDetails, &cve.CveDetail{
CveID: res.Key,
})
} else {
cveDetails = append(cveDetails, res.CveDetail)
cveDetails = append(cveDetails, &res.CveDetail)
}
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return []cve.CveDetail{}, fmt.Errorf("Timeout Fetching CVE")
return []*cve.CveDetail{}, fmt.Errorf("Timeout Fetching CVE")
}
}
if len(errs) != 0 {
return []cve.CveDetail{},
return []*cve.CveDetail{},
fmt.Errorf("Failed to fetch CVE. err: %v", errs)
}
sort.Sort(cveDetails)
return
}
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails cve.CveDetails, err error) {
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails []*cve.CveDetail, err error) {
util.Log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
if config.Conf.CveDBType == "sqlite3" {
@@ -142,23 +140,33 @@ func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails c
cveconfig.Conf.DBPath = config.Conf.CveDBURL
}
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
if err := cvedb.OpenDB(); err != nil {
return []cve.CveDetail{},
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err := driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
for _, cveID := range cveIDs {
cveDetail := cvedb.Get(cveID)
cveDetail := driver.Get(cveID)
if len(cveDetail.CveID) == 0 {
cveDetails = append(cveDetails, cve.CveDetail{
cveDetails = append(cveDetails, &cve.CveDetail{
CveID: cveID,
})
} else {
cveDetails = append(cveDetails, cveDetail)
}
}
// order by CVE ID desc
sort.Sort(cveDetails)
return
}
@@ -170,20 +178,25 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v", errs, url, resp)
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v",
errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
util.Log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s",
t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
errChan <- fmt.Errorf("HTTP Error %s", err)
return
}
cveDetail := cve.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s",
body, err)
return
}
resChan <- response{
key,
@@ -196,29 +209,37 @@ type responseGetCveDetailByCpeName struct {
CveDetails []cve.CveDetail
}
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]cve.CveDetail, error) {
switch config.Conf.CveDBType {
case "sqlite3", "mysql", "postgres":
return api.FetchCveDetailsByCpeNameFromDB(cpeName)
func (api cvedictClient) isFetchViaHTTP() bool {
// Default value of CveDBType is sqlite3
if config.Conf.CveDBURL != "" && config.Conf.CveDBType == "sqlite3" {
return true
}
api.baseURL = config.Conf.CveDBURL
url, err := util.URLPathJoin(api.baseURL, "cpes")
if err != nil {
return []cve.CveDetail{}, err
}
query := map[string]string{"name": cpeName}
util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
return api.httpPost(cpeName, url, query)
return false
}
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]cve.CveDetail, error) {
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]*cve.CveDetail, error) {
if api.isFetchViaHTTP() {
api.baseURL = config.Conf.CveDBURL
url, err := util.URLPathJoin(api.baseURL, "cpes")
if err != nil {
return []*cve.CveDetail{}, err
}
query := map[string]string{"name": cpeName}
util.Log.Debugf("HTTP Request to %s, query: %#v", url, query)
return api.httpPost(cpeName, url, query)
}
return api.FetchCveDetailsByCpeNameFromDB(cpeName)
}
func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]*cve.CveDetail, error) {
var body string
var errs []error
var resp *http.Response
f := func() (err error) {
req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
// req := gorequest.New().SetDebug(config.Conf.Debug).Post(url)
req := gorequest.New().Post(url)
for key := range query {
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
}
@@ -233,18 +254,18 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
return []cve.CveDetail{}, fmt.Errorf("HTTP Error %s", err)
return []*cve.CveDetail{}, fmt.Errorf("HTTP Error %s", err)
}
cveDetails := []cve.CveDetail{}
cveDetails := []*cve.CveDetail{}
if err := json.Unmarshal([]byte(body), &cveDetails); err != nil {
return []cve.CveDetail{},
return []*cve.CveDetail{},
fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
}
return cveDetails, nil
}
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.CveDetail, error) {
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) (cveDetails []*cve.CveDetail, err error) {
util.Log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
if config.Conf.CveDBType == "sqlite3" {
@@ -254,9 +275,20 @@ func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.C
}
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
if err := cvedb.OpenDB(); err != nil {
return []cve.CveDetail{},
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err = driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
return cvedb.GetByCpeName(cpeName), nil
return driver.GetByCpeName(cpeName), nil
}

View File

@@ -35,14 +35,18 @@ type EMailWriter struct{}
func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf
var message string
var totalResult models.ScanResult
sender := NewEMailSender()
m := map[string]int{}
for _, r := range rs {
if conf.FormatOneEMail {
message += formatFullPlainText(r) + "\r\n\r\n"
totalResult.KnownCves = append(totalResult.KnownCves, r.KnownCves...)
totalResult.UnknownCves = append(totalResult.UnknownCves, r.UnknownCves...)
mm := r.ScannedCves.CountGroupBySeverity()
keys := []string{"High", "Medium", "Low", "Unknown"}
for _, k := range keys {
m[k] += mm[k]
}
} else {
var subject string
if len(r.Errors) != 0 {
@@ -50,7 +54,9 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
conf.EMail.SubjectPrefix, r.ServerInfo())
} else {
subject = fmt.Sprintf("%s%s %s",
conf.EMail.SubjectPrefix, r.ServerInfo(), r.CveSummary())
conf.EMail.SubjectPrefix,
r.ServerInfo(),
r.ScannedCves.FormatCveSummary())
}
message = formatFullPlainText(r)
if err := sender.Send(subject, message); err != nil {
@@ -59,6 +65,15 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
}
}
summary := ""
if config.Conf.IgnoreUnscoredCves {
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
}
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
m["High"], m["Medium"], m["Low"], m["Unknown"])
if conf.FormatOneEMail {
message = fmt.Sprintf(
`
@@ -71,9 +86,7 @@ One Line Summary
formatOneLineSummary(rs...), message)
subject := fmt.Sprintf("%s %s",
conf.EMail.SubjectPrefix,
totalResult.CveSummary(),
)
conf.EMail.SubjectPrefix, summary)
return sender.Send(subject, message)
}
return nil

View File

@@ -58,8 +58,14 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
if c.Conf.Debug {
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
} else {
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
}
if err := writeFile(p, b, 0600); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", p, err)

227
report/report.go Normal file
View File

@@ -0,0 +1,227 @@
/* 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 report
import (
"fmt"
"strings"
"time"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
)
const (
vulsOpenTag = "<vulsreport>"
vulsCloseTag = "</vulsreport>"
)
// FillCveInfos fills CVE Detailed Information
func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
var filled []models.ScanResult
reportedAt := time.Now()
for _, r := range rs {
if c.Conf.RefreshCve || needToRefreshCve(r) {
if err := FillCveInfo(&r); err != nil {
return nil, err
}
r.Lang = c.Conf.Lang
r.ReportedAt = reportedAt
r.Config.Report = c.Conf
r.Config.Report.Servers = map[string]c.ServerInfo{
r.ServerName: c.Conf.Servers[r.ServerName],
}
if err := overwriteJSONFile(dir, r); err != nil {
return nil, fmt.Errorf("Failed to write JSON: %s", err)
}
filled = append(filled, r)
} else {
util.Log.Debugf("No need to refresh")
filled = append(filled, r)
}
}
if c.Conf.Diff {
previous, err := loadPrevious(filled)
if err != nil {
return nil, err
}
diff, err := diff(filled, previous)
if err != nil {
return nil, err
}
filled = []models.ScanResult{}
for _, r := range diff {
if err := fillCveDetail(&r); err != nil {
return nil, err
}
filled = append(filled, r)
}
}
filtered := []models.ScanResult{}
for _, r := range filled {
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
r = r.FilterIgnoreCves(c.Conf.Servers[r.ServerName].IgnoreCves)
filtered = append(filtered, r)
}
return filtered, nil
}
// FillCveInfo fill scanResult with cve info.
func FillCveInfo(r *models.ScanResult) error {
util.Log.Debugf("need to refresh")
util.Log.Infof("Fill CVE detailed information with OVAL")
if err := FillWithOval(r); err != nil {
return fmt.Errorf("Failed to fill OVAL information: %s", err)
}
util.Log.Infof("Fill CVE detailed information with CVE-DB")
if err := fillWithCveDB(r); err != nil {
return fmt.Errorf("Failed to fill CVE information: %s", err)
}
for cveID := range r.ScannedCves {
vinfo := r.ScannedCves[cveID]
r.ScannedCves[cveID] = *vinfo.NilToEmpty()
}
return nil
}
// fillCveDetail fetches NVD, JVN from CVE Database, and then set to fields.
func fillCveDetail(r *models.ScanResult) error {
var cveIDs []string
for _, v := range r.ScannedCves {
cveIDs = append(cveIDs, v.CveID)
}
ds, err := CveClient.FetchCveDetails(cveIDs)
if err != nil {
return err
}
for _, d := range ds {
nvd := models.ConvertNvdToModel(d.CveID, d.Nvd)
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
for cveID, vinfo := range r.ScannedCves {
if vinfo.CveID == d.CveID {
if vinfo.CveContents == nil {
vinfo.CveContents = models.CveContents{}
}
for _, con := range []models.CveContent{*nvd, *jvn} {
if !con.Empty() {
vinfo.CveContents[con.Type] = con
}
}
r.ScannedCves[cveID] = vinfo
break
}
}
}
return nil
}
func fillWithCveDB(r *models.ScanResult) error {
sInfo := c.Conf.Servers[r.ServerName]
if err := fillVulnByCpeNames(sInfo.CpeNames, r.ScannedCves); err != nil {
return err
}
if err := fillCveDetail(r); err != nil {
return err
}
return nil
}
// FillWithOval fetches OVAL database, and then set to fields.
func FillWithOval(r *models.ScanResult) (err error) {
var ovalClient oval.Client
var ovalFamily string
// TODO
switch r.Family {
case c.Debian:
ovalClient = oval.NewDebian()
ovalFamily = c.Debian
case c.Ubuntu:
ovalClient = oval.NewUbuntu()
ovalFamily = c.Ubuntu
case c.RedHat:
ovalClient = oval.NewRedhat()
ovalFamily = c.RedHat
case c.CentOS:
ovalClient = oval.NewCentOS()
//use RedHat's OVAL
ovalFamily = c.RedHat
case c.Oracle:
ovalClient = oval.NewOracle()
ovalFamily = c.Oracle
case c.Amazon, c.Raspbian, c.FreeBSD:
return nil
default:
return fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
}
ok, err := ovalClient.CheckIfOvalFetched(ovalFamily, r.Release)
if err != nil {
return err
}
if !ok {
major := strings.Split(r.Release, ".")[0]
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, major)
return nil
}
_, err = ovalClient.CheckIfOvalFresh(ovalFamily, r.Release)
if err != nil {
return err
}
if err := ovalClient.FillWithOval(r); err != nil {
return err
}
return nil
}
func fillVulnByCpeNames(cpeNames []string, scannedVulns models.VulnInfos) error {
for _, name := range cpeNames {
details, err := CveClient.FetchCveDetailsByCpeName(name)
if err != nil {
return err
}
for _, detail := range details {
if val, ok := scannedVulns[detail.CveID]; ok {
names := val.CpeNames
names = util.AppendIfMissing(names, name)
val.CpeNames = names
val.Confidence = models.CpeNameMatch
scannedVulns[detail.CveID] = val
} else {
v := models.VulnInfo{
CveID: detail.CveID,
CpeNames: []string{name},
Confidence: models.CpeNameMatch,
}
scannedVulns[detail.CveID] = v
}
}
}
return nil
}

1
report/report_test.go Normal file
View File

@@ -0,0 +1 @@
package report

View File

@@ -22,6 +22,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"path"
"time"
"github.com/aws/aws-sdk-go/aws"
@@ -147,8 +148,8 @@ func putObject(svc *s3.S3, k string, b []byte) error {
}
if _, err := svc.PutObject(&s3.PutObjectInput{
Bucket: &c.Conf.S3Bucket,
Key: &k,
Bucket: aws.String(c.Conf.S3Bucket),
Key: aws.String(path.Join(c.Conf.S3ResultsDir, k)),
Body: bytes.NewReader(b),
}); err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s",

View File

@@ -24,11 +24,11 @@ import (
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/parnurzeal/gorequest"
log "github.com/sirupsen/logrus"
)
type field struct {
@@ -45,6 +45,7 @@ type attachment struct {
Color string `json:"color"`
Fields []*field `json:"fields"`
MrkdwnIn []string `json:"mrkdwn_in"`
Footer string `json:"footer"`
}
type message struct {
Text string `json:"text"`
@@ -69,7 +70,8 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
if 0 < len(r.Errors) {
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers)
txt := fmt.Sprintf("%s\n%s\nError: %s", notifyUsers, serverInfo, r.Errors)
txt := fmt.Sprintf("%s\n%s\nError: %s",
notifyUsers, serverInfo, r.Errors)
msg := message{
Text: txt,
Username: conf.AuthUser,
@@ -152,54 +154,73 @@ func send(msg message) error {
func msgText(r models.ScanResult) string {
notifyUsers := ""
if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) {
if 0 < len(r.ScannedCves) {
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
}
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, serverInfo, r.CveSummary())
return fmt.Sprintf("%s\n%s\n>%s",
notifyUsers,
serverInfo,
r.ScannedCves.FormatCveSummary())
}
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
cves := scanResult.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, scanResult.UnknownCves...)
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
var vinfos []models.VulnInfo
if config.Conf.IgnoreUnscoredCves {
vinfos = r.ScannedCves.FindScoredVulns().ToSortedSlice()
} else {
vinfos = r.ScannedCves.ToSortedSlice()
}
for _, cveInfo := range cves {
cveID := cveInfo.CveDetail.CveID
curentPackages := []string{}
for _, p := range cveInfo.Packages {
curentPackages = append(curentPackages, p.ToStringCurrentVersion())
for _, vinfo := range vinfos {
curent := []string{}
for _, affected := range vinfo.AffectedPackages {
if p, ok := r.Packages[affected.Name]; ok {
curent = append(curent,
fmt.Sprintf("%s-%s", p.Name, p.FormatVer()))
} else {
curent = append(curent, affected.Name)
}
}
for _, n := range cveInfo.CpeNames {
curentPackages = append(curentPackages, n)
for _, n := range vinfo.CpeNames {
curent = append(curent, n)
}
newPackages := []string{}
for _, p := range cveInfo.Packages {
newPackages = append(newPackages, p.ToStringNewVersion())
new := []string{}
for _, affected := range vinfo.AffectedPackages {
if p, ok := r.Packages[affected.Name]; ok {
if affected.NotFixedYet {
new = append(new, "Not Fixed Yet")
} else {
new = append(new, p.FormatNewVer())
}
} else {
new = append(new, "?")
}
}
for range vinfo.CpeNames {
new = append(new, "?")
}
a := attachment{
Title: cveID,
TitleLink: fmt.Sprintf("%s/%s", nvdBaseURL, cveID),
Text: attachmentText(cveInfo, scanResult.Family),
Title: vinfo.CveID,
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family),
MrkdwnIn: []string{"text", "pretext"},
Fields: []*field{
{
// Title: "Current Package/CPE",
// Title: "Current Package/CPE",
Title: "Installed",
Value: strings.Join(curentPackages, "\n"),
Value: strings.Join(curent, "\n"),
Short: true,
},
{
Title: "Candidate",
Value: strings.Join(newPackages, "\n"),
Value: strings.Join(new, "\n"),
Short: true,
},
},
Color: color(cveInfo.CveDetail.CvssScore(config.Conf.Lang)),
Color: color(vinfo.MaxCvssScore().Value.Score),
}
attaches = append(attaches, &a)
}
@@ -220,68 +241,72 @@ func color(cvssScore float64) string {
}
}
func attachmentText(cveInfo models.CveInfo, osFamily string) string {
linkText := links(cveInfo, osFamily)
switch {
case config.Conf.Lang == "ja" &&
0 < cveInfo.CveDetail.Jvn.CvssScore():
func attachmentText(vinfo models.VulnInfo, osFamily string) string {
maxCvss := vinfo.MaxCvssScore()
vectors := []string{}
for _, cvss := range vinfo.Cvss2Scores() {
calcURL := ""
switch cvss.Value.Type {
case models.CVSS2:
calcURL = fmt.Sprintf(
"https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=%s",
vinfo.CveID)
case models.CVSS3:
calcURL = fmt.Sprintf(
"https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=%s",
vinfo.CveID)
}
jvn := cveInfo.CveDetail.Jvn
return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v",
cveInfo.CveDetail.CvssScore(config.Conf.Lang),
jvn.CvssSeverity(),
fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID),
jvn.CvssVector(),
jvn.CveTitle(),
linkText,
cveInfo.VulnInfo.Confidence,
)
case 0 < cveInfo.CveDetail.CvssScore("en"):
nvd := cveInfo.CveDetail.Nvd
return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v",
cveInfo.CveDetail.CvssScore(config.Conf.Lang),
nvd.CvssSeverity(),
fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID),
nvd.CvssVector(),
nvd.CveSummary(),
linkText,
cveInfo.VulnInfo.Confidence,
)
default:
nvd := cveInfo.CveDetail.Nvd
return fmt.Sprintf("?\n%s\n%s\n*Confidence:* %v",
nvd.CveSummary(), linkText, cveInfo.VulnInfo.Confidence)
}
}
if cont, ok := vinfo.CveContents[cvss.Type]; ok {
v := fmt.Sprintf("<%s|%s> (<%s|%s>)",
calcURL,
cvss.Value.Format(),
cont.SourceLink,
cvss.Type)
vectors = append(vectors, v)
func links(cveInfo models.CveInfo, osFamily string) string {
links := []string{}
} else {
if 0 < len(vinfo.DistroAdvisories) {
links := []string{}
for k, v := range vinfo.VendorLinks(osFamily) {
links = append(links, fmt.Sprintf("<%s|%s>",
v, k))
}
cweID := cveInfo.CveDetail.CweID()
if 0 < len(cweID) {
links = append(links, fmt.Sprintf("<%s|%s>",
cweURL(cweID), cweID))
if config.Conf.Lang == "ja" {
links = append(links, fmt.Sprintf("<%s|%s(JVN)>",
cweJvnURL(cweID), cweID))
v := fmt.Sprintf("<%s|%s> (%s)",
calcURL,
cvss.Value.Format(),
strings.Join(links, ", "))
vectors = append(vectors, v)
}
}
}
cveID := cveInfo.CveDetail.CveID
if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) {
jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link())
links = append(links, jvn)
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
dlinks := distroLinks(cveInfo, osFamily)
for _, link := range dlinks {
links = append(links,
fmt.Sprintf("<%s|%s>", link.url, link.title))
}
links = append(links, fmt.Sprintf("<%s|MITRE>",
fmt.Sprintf("%s%s", mitreBaseURL, cveID)))
links = append(links, fmt.Sprintf("<%s|CVEDetails>",
fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)))
return fmt.Sprintf("*%4.1f (%s)* %s\n%s\n```%s```",
maxCvss.Value.Score,
severity,
cweIDs(vinfo, osFamily),
strings.Join(vectors, "\n"),
vinfo.Summaries(config.Conf.Lang, osFamily)[0].Value,
)
}
func cweIDs(vinfo models.VulnInfo, osFamily string) string {
links := []string{}
for _, cwe := range vinfo.CveContents.CweIDs(osFamily) {
if config.Conf.Lang == "ja" {
links = append(links, fmt.Sprintf("<%s|%s>",
cweJvnURL(cwe.Value), cwe.Value))
} else {
links = append(links, fmt.Sprintf("<%s|%s>",
cweURL(cwe.Value), cwe.Value))
}
}
return strings.Join(links, " / ")
}

View File

@@ -21,36 +21,41 @@ import (
"bytes"
"fmt"
"os"
"sort"
"strings"
"text/template"
"time"
log "github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
"github.com/gosuri/uitable"
"github.com/jroimartin/gocui"
cve "github.com/kotakanbe/go-cve-dictionary/models"
log "github.com/sirupsen/logrus"
)
var scanHistory models.ScanHistory
var scanResults models.ScanResults
var currentScanResult models.ScanResult
var currentCveInfo int
var vinfos []models.VulnInfo
var currentVinfo int
var currentDetailLimitY int
var currentChangelogLimitY int
// RunTui execute main logic
func RunTui(history models.ScanHistory) subcommands.ExitStatus {
scanHistory = history
func RunTui(results models.ScanResults) subcommands.ExitStatus {
scanResults = results
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
// g, err := gocui.NewGui(gocui.OutputNormal)
g := gocui.NewGui()
if err := g.Init(); err != nil {
log.Errorf("%s", err)
return subcommands.ExitFailure
}
defer g.Close()
g.SetManagerFunc(layout)
g.SetLayout(layout)
// g.SetManagerFunc(layout)
if err := keybindings(g); err != nil {
log.Errorf("%s", err)
return subcommands.ExitFailure
@@ -175,19 +180,19 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
}
switch v.Name() {
case "side":
_, err = g.SetCurrentView("summary")
err = g.SetCurrentView("summary")
case "summary":
_, err = g.SetCurrentView("detail")
err = g.SetCurrentView("detail")
case "detail":
_, err = g.SetCurrentView("changelog")
err = g.SetCurrentView("changelog")
case "changelog":
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
default:
_, err = g.SetCurrentView("summary")
err = g.SetCurrentView("summary")
}
return err
}
@@ -196,19 +201,19 @@ func previousView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
}
switch v.Name() {
case "side":
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
case "summary":
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
case "detail":
_, err = g.SetCurrentView("summary")
err = g.SetCurrentView("summary")
case "changelog":
_, err = g.SetCurrentView("detail")
err = g.SetCurrentView("detail")
default:
_, err = g.SetCurrentView("side")
err = g.SetCurrentView("side")
}
return err
}
@@ -216,27 +221,27 @@ func previousView(g *gocui.Gui, v *gocui.View) error {
func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
switch v.Name() {
case "side":
yLimit = len(scanHistory.ScanResults) - 1
yLimit = len(scanResults) - 1
if yLimit < nextY {
return false, yLimit
}
return true, yLimit
case "summary":
yLimit = len(currentScanResult.AllCves()) - 1
yLimit = len(currentScanResult.ScannedCves) - 1
if yLimit < nextY {
return false, yLimit
}
return true, yLimit
case "detail":
if currentDetailLimitY < nextY {
return false, currentDetailLimitY
}
// if currentDetailLimitY < nextY {
// return false, currentDetailLimitY
// }
return true, currentDetailLimitY
case "changelog":
if currentDetailLimitY < nextY {
return false, currentDetailLimitY
}
return true, currentDetailLimitY
// if currentChangelogLimitY < nextY {
// return false, currentChangelogLimitY
// }
return true, currentChangelogLimitY
default:
return true, 0
}
@@ -280,7 +285,7 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error {
// ok, := movable(v, oy+cy+1)
// _, maxY := v.Size()
ok, _ := movable(v, oy+cy+1)
// log.Info(cy, oy, maxY, yLimit)
// log.Info(cy, oy)
if !ok {
return nil
}
@@ -291,6 +296,10 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error {
}
onMovingCursorRedrawView(g, v)
}
cx, cy := v.Cursor()
ox, oy := v.Origin()
debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy))
return nil
}
@@ -387,7 +396,7 @@ func cursorPageUp(g *gocui.Gui, v *gocui.View) error {
func previousSummary(g *gocui.Gui, v *gocui.View) error {
if v != nil {
// cursor to summary
if _, err := g.SetCurrentView("summary"); err != nil {
if err := g.SetCurrentView("summary"); err != nil {
return err
}
// move next line
@@ -395,7 +404,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
return err
}
// cursor to detail
if _, err := g.SetCurrentView("detail"); err != nil {
if err := g.SetCurrentView("detail"); err != nil {
return err
}
}
@@ -405,7 +414,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
func nextSummary(g *gocui.Gui, v *gocui.View) error {
if v != nil {
// cursor to summary
if _, err := g.SetCurrentView("summary"); err != nil {
if err := g.SetCurrentView("summary"); err != nil {
return err
}
// move next line
@@ -413,7 +422,7 @@ func nextSummary(g *gocui.Gui, v *gocui.View) error {
return err
}
// cursor to detail
if _, err := g.SetCurrentView("detail"); err != nil {
if err := g.SetCurrentView("detail"); err != nil {
return err
}
}
@@ -439,9 +448,10 @@ func changeHost(g *gocui.Gui, v *gocui.View) error {
}
serverName := strings.TrimSpace(l)
for _, r := range scanHistory.ScanResults {
for _, r := range scanResults {
if serverName == strings.TrimSpace(r.ServerInfoTui()) {
currentScanResult = r
vinfos = r.ScannedCves.ToSortedSlice()
break
}
}
@@ -495,7 +505,7 @@ func getLine(g *gocui.Gui, v *gocui.View) error {
return err
}
fmt.Fprintln(v, l)
if _, err := g.SetCurrentView("msg"); err != nil {
if err := g.SetCurrentView("msg"); err != nil {
return err
}
}
@@ -510,14 +520,15 @@ func showMsg(g *gocui.Gui, v *gocui.View) error {
// maxX, maxY := v.Size()
_, maxY := v.Size()
l := fmt.Sprintf("cy: %d, oy: %d, maxY: %d, yLimit: %d, curCve %d, ok: %v", cy, oy, maxY, yLimit, currentCveInfo, ok)
l := fmt.Sprintf("cy: %d, oy: %d, maxY: %d, yLimit: %d, curCve %d, ok: %v",
cy, oy, maxY, yLimit, currentVinfo, ok)
// if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
if v, err := g.SetView("msg", 10, maxY/2, 10+50, maxY/2+2); err != nil {
if err != gocui.ErrUnknownView {
return err
}
fmt.Fprintln(v, l)
if _, err := g.SetCurrentView("msg"); err != nil {
if err := g.SetCurrentView("msg"); err != nil {
return err
}
}
@@ -528,7 +539,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
if err := g.DeleteView("msg"); err != nil {
return err
}
if _, err := g.SetCurrentView("summary"); err != nil {
if err := g.SetCurrentView("summary"); err != nil {
return err
}
return nil
@@ -551,6 +562,20 @@ func layout(g *gocui.Gui) error {
if err := setChangelogLayout(g); err != nil {
return err
}
return nil
}
func debug(g *gocui.Gui, str string) error {
if config.Conf.Debug {
maxX, maxY := g.Size()
if _, err := g.View("debug"); err != gocui.ErrUnknownView {
g.DeleteView("debug")
}
if v, err := g.SetView("debug", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
fmt.Fprintf(v, str)
}
}
return nil
}
@@ -562,14 +587,15 @@ func setSideLayout(g *gocui.Gui) error {
}
v.Highlight = true
for _, result := range scanHistory.ScanResults {
for _, result := range scanResults {
fmt.Fprintln(v, result.ServerInfoTui())
}
if len(scanHistory.ScanResults) == 0 {
if len(scanResults) == 0 {
return fmt.Errorf("No scan results")
}
currentScanResult = scanHistory.ScanResults[0]
if _, err := g.SetCurrentView("side"); err != nil {
currentScanResult = scanResults[0]
vinfos = scanResults[0].ScannedCves.ToSortedSlice()
if err := g.SetCurrentView("side"); err != nil {
return err
}
}
@@ -603,50 +629,28 @@ func summaryLines() string {
}
indexFormat := ""
if len(currentScanResult.AllCves()) < 10 {
if len(currentScanResult.ScannedCves) < 10 {
indexFormat = "[%1d]"
} else if len(currentScanResult.AllCves()) < 100 {
} else if len(currentScanResult.ScannedCves) < 100 {
indexFormat = "[%2d]"
} else {
indexFormat = "[%3d]"
}
for i, d := range currentScanResult.AllCves() {
for i, vinfo := range vinfos {
summary := vinfo.Titles(
config.Conf.Lang, currentScanResult.Family)[0].Value
cvssScore := fmt.Sprintf("| %4.1f",
vinfo.MaxCvssScore().Value.Score)
var cols []string
// packs := []string{}
// for _, pack := range d.Packages {
// packs = append(packs, pack.Name)
// }
if config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore() {
summary := d.CveDetail.Jvn.CveTitle()
cols = []string{
fmt.Sprintf(indexFormat, i+1),
d.CveDetail.CveID,
fmt.Sprintf("| %4.1f",
d.CveDetail.CvssScore(config.Conf.Lang)),
fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score),
summary,
}
} else {
summary := d.CveDetail.Nvd.CveSummary()
var cvssScore string
if d.CveDetail.CvssScore("en") <= 0 {
cvssScore = "| ?"
} else {
cvssScore = fmt.Sprintf("| %4.1f",
d.CveDetail.CvssScore(config.Conf.Lang))
}
cols = []string{
fmt.Sprintf(indexFormat, i+1),
d.CveDetail.CveID,
cvssScore,
fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score),
summary,
}
cols = []string{
fmt.Sprintf(indexFormat, i+1),
vinfo.CveID,
cvssScore,
fmt.Sprintf("| %3d |", vinfo.Confidence.Score),
summary,
}
icols := make([]interface{}, len(cols))
for j := range cols {
icols[j] = cols[j]
@@ -665,7 +669,7 @@ func setDetailLayout(g *gocui.Gui) error {
}
_, cy := summaryView.Cursor()
_, oy := summaryView.Origin()
currentCveInfo = cy + oy
currentVinfo = cy + oy
if v, err := g.SetView("detail", -1, int(float64(maxY)*0.2), int(float64(maxX)*0.5), maxY); err != nil {
if err != gocui.ErrUnknownView {
@@ -693,22 +697,27 @@ func setChangelogLayout(g *gocui.Gui) error {
}
_, cy := summaryView.Cursor()
_, oy := summaryView.Origin()
currentCveInfo = cy + oy
currentVinfo = cy + oy
if v, err := g.SetView("changelog", int(float64(maxX)*0.5), int(float64(maxY)*0.2), maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
if len(currentScanResult.Errors) != 0 || len(currentScanResult.AllCves()) == 0 {
if len(currentScanResult.Errors) != 0 || len(currentScanResult.ScannedCves) == 0 {
return nil
}
lines := []string{}
cveInfo := currentScanResult.AllCves()[currentCveInfo]
for _, pack := range cveInfo.Packages {
vinfo := vinfos[currentVinfo]
for _, adv := range vinfo.DistroAdvisories {
lines = append(lines, adv.Format())
}
for _, affected := range vinfo.AffectedPackages {
pack := currentScanResult.Packages[affected.Name]
for _, p := range currentScanResult.Packages {
if pack.Name == p.Name {
lines = append(lines, formatOneChangelog(p), "\n")
lines = append(lines, p.FormatChangelog(), "\n")
}
}
}
@@ -717,21 +726,19 @@ func setChangelogLayout(g *gocui.Gui) error {
v.Editable = false
v.Wrap = true
currentDetailLimitY = len(strings.Split(text, "\n")) - 1
currentChangelogLimitY = len(strings.Split(text, "\n")) - 1
}
return nil
}
type dataForTmpl struct {
CveID string
CvssScore string
CvssVector string
CvssSeverity string
Cvsses string
Summary string
Confidence models.Confidence
CweURL string
VulnSiteLinks []string
References []cve.Reference
Cwes []models.CveContentStr
Links []string
References []models.Reference
Packages []string
CpeNames []string
PublishedDate time.Time
@@ -739,82 +746,75 @@ type dataForTmpl struct {
}
func detailLines() (string, error) {
if len(currentScanResult.Errors) != 0 {
r := currentScanResult
if len(r.Errors) != 0 {
return "", nil
}
if len(currentScanResult.AllCves()) == 0 {
if len(r.ScannedCves) == 0 {
return "No vulnerable packages", nil
}
cveInfo := currentScanResult.AllCves()[currentCveInfo]
cveID := cveInfo.CveDetail.CveID
tmpl, err := template.New("detail").Parse(detailTemplate())
tmpl, err := template.New("detail").Parse(mdTemplate)
if err != nil {
return "", err
}
var cvssSeverity, cvssVector, summary string
var refs []cve.Reference
switch {
case config.Conf.Lang == "ja" &&
0 < cveInfo.CveDetail.Jvn.CvssScore():
jvn := cveInfo.CveDetail.Jvn
cvssSeverity = jvn.CvssSeverity()
cvssVector = jvn.CvssVector()
summary = fmt.Sprintf("%s\n%s", jvn.CveTitle(), jvn.CveSummary())
refs = jvn.VulnSiteReferences()
default:
nvd := cveInfo.CveDetail.Nvd
cvssSeverity = nvd.CvssSeverity()
cvssVector = nvd.CvssVector()
summary = nvd.CveSummary()
refs = nvd.VulnSiteReferences()
vinfo := vinfos[currentVinfo]
packsVer := []string{}
vinfo.AffectedPackages.Sort()
for _, affected := range vinfo.AffectedPackages {
// packages detected by OVAL may not be actually installed
if pack, ok := r.Packages[affected.Name]; ok {
packsVer = append(packsVer, pack.FormatVersionFromTo(affected.NotFixedYet))
}
}
sort.Strings(vinfo.CpeNames)
for _, name := range vinfo.CpeNames {
packsVer = append(packsVer, name)
}
cweURL := cweURL(cveInfo.CveDetail.CweID())
links := []string{
fmt.Sprintf("[NVD]( %s )", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)),
fmt.Sprintf("[MITRE]( %s )", fmt.Sprintf("%s%s", mitreBaseURL, cveID)),
fmt.Sprintf("[CveDetais]( %s )", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)),
fmt.Sprintf("[CVSSv2 Calc]( %s )", fmt.Sprintf(cvssV2CalcBaseURL, cveID)),
fmt.Sprintf("[CVSSv3 Calc]( %s )", fmt.Sprintf(cvssV3CalcBaseURL, cveID)),
}
dlinks := distroLinks(cveInfo, currentScanResult.Family)
for _, link := range dlinks {
links = append(links, fmt.Sprintf("[%s]( %s )", link.title, link.url))
links := []string{vinfo.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
vinfo.Cvss2CalcURL(),
vinfo.Cvss3CalcURL()}
for _, url := range vinfo.VendorLinks(r.Family) {
links = append(links, url)
}
var cvssScore string
if cveInfo.CveDetail.CvssScore(config.Conf.Lang) == -1 {
cvssScore = "?"
} else {
cvssScore = fmt.Sprintf("%4.1f", cveInfo.CveDetail.CvssScore(config.Conf.Lang))
refs := []models.Reference{}
for _, rr := range vinfo.CveContents.References(r.Family) {
for _, ref := range rr.Value {
refs = append(refs, ref)
}
}
packages := []string{}
for _, pack := range cveInfo.Packages {
packages = append(packages,
fmt.Sprintf(
"%s -> %s",
pack.ToStringCurrentVersion(),
pack.ToStringNewVersion()))
summary := vinfo.Summaries(r.Lang, r.Family)[0]
table := uitable.New()
table.MaxColWidth = maxColWidth
table.Wrap = true
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores()...)
var cols []interface{}
for _, score := range scores {
cols = []interface{}{
score.Value.Severity,
score.Value.Format(),
score.Type,
}
table.AddRow(cols...)
}
data := dataForTmpl{
CveID: cveID,
CvssScore: cvssScore,
CvssSeverity: cvssSeverity,
CvssVector: cvssVector,
Summary: summary,
Confidence: cveInfo.VulnInfo.Confidence,
CweURL: cweURL,
VulnSiteLinks: links,
References: refs,
Packages: packages,
CpeNames: cveInfo.CpeNames,
CveID: vinfo.CveID,
Cvsses: fmt.Sprintf("%s\n", table),
Summary: fmt.Sprintf("%s (%s)", summary.Value, summary.Type),
Confidence: vinfo.Confidence,
Cwes: vinfo.CveContents.CweIDs(r.Family),
Links: util.Distinct(links),
Packages: packsVer,
References: refs,
}
buf := bytes.NewBuffer(nil) // create empty buffer
@@ -825,52 +825,49 @@ func detailLines() (string, error) {
return string(buf.Bytes()), nil
}
func detailTemplate() string {
return `
const mdTemplate = `
{{.CveID}}
==============
CVSS Score
CVSS Scores
--------------
{{.CvssScore}} ({{.CvssSeverity}}) {{.CvssVector}}
{{.Cvsses }}
Summary
--------------
{{.Summary }}
Confidence
--------------
{{.Confidence }}
Links
--------------
{{range $link := .Links -}}
* {{$link}}
{{end}}
CWE
--------------
{{.CweURL }}
{{range .Cwes -}}
* {{.Value}} ({{.Type}})
{{end}}
Package/CPE
--------------
{{range $pack := .Packages -}}
* {{$pack}}
{{end -}}
{{range $name := .CpeNames -}}
* {{$name}}
{{end}}
Links
--------------
{{range $link := .VulnSiteLinks -}}
* {{$link}}
{{end}}
Confidence
--------------
{{.Confidence }}
References
--------------
{{range .References -}}
* [{{.Source}}]( {{.Link}} )
{{end}}
`
}

View File

@@ -18,12 +18,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package report
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/gosuri/uitable"
)
@@ -39,7 +46,6 @@ func formatScanSummary(rs ...models.ScanResult) string {
cols = []interface{}{
r.FormatServerName(),
fmt.Sprintf("%s%s", r.Family, r.Release),
fmt.Sprintf("%d CVEs", len(r.ScannedCves)),
r.Packages.FormatUpdatablePacksSummary(),
}
} else {
@@ -64,7 +70,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
if len(r.Errors) == 0 {
cols = []interface{}{
r.FormatServerName(),
r.CveSummary(),
r.ScannedCves.FormatCveSummary(),
r.Packages.FormatUpdatablePacksSummary(),
}
} else {
@@ -80,97 +86,69 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
}
func formatShortPlainText(r models.ScanResult) string {
stable := uitable.New()
stable.MaxColWidth = maxColWidth
stable.Wrap = true
cves := r.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, r.UnknownCves...)
}
var buf bytes.Buffer
for i := 0; i < len(r.ServerInfo()); i++ {
buf.WriteString("=")
}
header := fmt.Sprintf("%s\n%s\n%s\t%s\n\n",
r.ServerInfo(),
buf.String(),
r.CveSummary(),
r.Packages.FormatUpdatablePacksSummary(),
)
header := r.FormatTextReportHeadedr()
if len(r.Errors) != 0 {
return fmt.Sprintf(
"%s\nError: Scan with --debug to view the details\n%s\n\n",
header, r.Errors)
}
if len(cves) == 0 {
vulns := r.ScannedCves
if config.Conf.IgnoreUnscoredCves {
vulns = vulns.FindScoredVulns()
}
if len(vulns) == 0 {
return fmt.Sprintf(`
%s
No CVE-IDs are found in updatable packages.
%s
`, header, r.Packages.FormatUpdatablePacksSummary())
`, header, r.Packages.FormatUpdatablePacksSummary())
}
for _, d := range cves {
var packsVer string
for _, p := range d.Packages {
packsVer += fmt.Sprintf(
"%s -> %s\n", p.ToStringCurrentVersion(), p.ToStringNewVersion())
}
for _, n := range d.CpeNames {
packsVer += n
stable := uitable.New()
stable.MaxColWidth = maxColWidth
stable.Wrap = true
for _, vuln := range vulns.ToSortedSlice() {
summaries := vuln.Summaries(config.Conf.Lang, r.Family)
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
vlinks := []string{}
for name, url := range vuln.VendorLinks(r.Family) {
vlinks = append(vlinks, fmt.Sprintf("%s (%s)", url, name))
}
var scols []string
switch {
case config.Conf.Lang == "ja" &&
0 < d.CveDetail.Jvn.CvssScore():
summary := fmt.Sprintf("%s\n%s\n%s\n%sConfidence: %v",
d.CveDetail.Jvn.CveTitle(),
d.CveDetail.Jvn.Link(),
distroLinks(d, r.Family)[0].url,
packsVer,
d.VulnInfo.Confidence,
)
scols = []string{
d.CveDetail.CveID,
fmt.Sprintf("%-4.1f (%s)",
d.CveDetail.CvssScore(config.Conf.Lang),
d.CveDetail.Jvn.CvssSeverity(),
),
summary,
}
case 0 < d.CveDetail.CvssScore("en"):
summary := fmt.Sprintf("%s\n%s/%s\n%s\n%sConfidence: %v",
d.CveDetail.Nvd.CveSummary(),
cveDetailsBaseURL,
d.CveDetail.CveID,
distroLinks(d, r.Family)[0].url,
packsVer,
d.VulnInfo.Confidence,
)
scols = []string{
d.CveDetail.CveID,
fmt.Sprintf("%-4.1f (%s)",
d.CveDetail.CvssScore(config.Conf.Lang),
d.CveDetail.Nvd.CvssSeverity(),
),
summary,
}
default:
summary := fmt.Sprintf("%s\n%sConfidence: %v",
distroLinks(d, r.Family)[0].url, packsVer, d.VulnInfo.Confidence)
scols = []string{
d.CveDetail.CveID,
"?",
summary,
}
cvsses := ""
for _, cvss := range vuln.Cvss2Scores() {
cvsses += fmt.Sprintf("%s (%s)\n", cvss.Value.Format(), cvss.Type)
}
cvsses += vuln.Cvss2CalcURL() + "\n"
for _, cvss := range vuln.Cvss3Scores() {
cvsses += fmt.Sprintf("%s (%s)\n", cvss.Value.Format(), cvss.Type)
}
if 0 < len(vuln.Cvss3Scores()) {
cvsses += vuln.Cvss3CalcURL() + "\n"
}
maxCvss := vuln.FormatMaxCvssScore()
rightCol := fmt.Sprintf(`%s
%s
---
%s
%s
%sConfidence: %v`,
maxCvss,
summaries[0].Value,
links[0].Value,
strings.Join(vlinks, "\n"),
cvsses,
// packsVer,
vuln.Confidence,
)
leftCol := fmt.Sprintf("%s", vuln.CveID)
scols := []string{leftCol, rightCol}
cols := make([]interface{}, len(scols))
for i := range cols {
cols[i] = scols[i]
@@ -182,295 +160,79 @@ No CVE-IDs are found in updatable packages.
}
func formatFullPlainText(r models.ScanResult) string {
serverInfo := r.ServerInfo()
var buf bytes.Buffer
for i := 0; i < len(serverInfo); i++ {
buf.WriteString("=")
}
header := fmt.Sprintf("%s\n%s\n%s\t%s\n",
r.ServerInfo(),
buf.String(),
r.CveSummary(),
r.Packages.FormatUpdatablePacksSummary(),
)
header := r.FormatTextReportHeadedr()
if len(r.Errors) != 0 {
return fmt.Sprintf(
"%s\nError: Scan with --debug to view the details\n%s\n\n",
header, r.Errors)
}
if len(r.KnownCves) == 0 && len(r.UnknownCves) == 0 {
vulns := r.ScannedCves
if config.Conf.IgnoreUnscoredCves {
vulns = vulns.FindScoredVulns()
}
if len(vulns) == 0 {
return fmt.Sprintf(`
%s
No CVE-IDs are found in updatable packages.
%s
`, header, r.Packages.FormatUpdatablePacksSummary())
`, header, r.Packages.FormatUpdatablePacksSummary())
}
scoredReport, unscoredReport := []string{}, []string{}
scoredReport, unscoredReport = formatPlainTextDetails(r, r.Family)
table := uitable.New()
table.MaxColWidth = maxColWidth
table.Wrap = true
for _, vuln := range vulns.ToSortedSlice() {
table.AddRow(vuln.CveID)
table.AddRow("----------------")
table.AddRow("Max Score", vuln.FormatMaxCvssScore())
for _, cvss := range vuln.Cvss2Scores() {
table.AddRow(cvss.Type, cvss.Value.Format())
}
for _, cvss := range vuln.Cvss3Scores() {
table.AddRow(cvss.Type, cvss.Value.Format())
}
if 0 < len(vuln.Cvss2Scores()) {
table.AddRow("CVSSv2 Calc", vuln.Cvss2CalcURL())
}
if 0 < len(vuln.Cvss3Scores()) {
table.AddRow("CVSSv3 Calc", vuln.Cvss3CalcURL())
}
table.AddRow("Summary", vuln.Summaries(
config.Conf.Lang, r.Family)[0].Value)
unscored := ""
if !config.Conf.IgnoreUnscoredCves {
unscored = strings.Join(unscoredReport, "\n\n")
}
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
table.AddRow("Source", links[0].Value)
scored := strings.Join(scoredReport, "\n\n")
detail := fmt.Sprintf(`
%s
vlinks := vuln.VendorLinks(r.Family)
for name, url := range vlinks {
table.AddRow(name, url)
}
%s
`,
scored,
unscored,
)
return fmt.Sprintf("%s\n%s\n%s", header, detail, formatChangelogs(r))
}
for _, v := range vuln.CveContents.CweIDs(r.Family) {
table.AddRow(fmt.Sprintf("%s (%s)", v.Value, v.Type), cweURL(v.Value))
}
func formatPlainTextDetails(r models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) {
for _, cve := range r.KnownCves {
switch config.Conf.Lang {
case "en":
if 0 < cve.CveDetail.Nvd.CvssScore() {
scoredReport = append(
scoredReport, formatPlainTextDetailsLangEn(cve, osFamily))
} else {
scoredReport = append(
scoredReport, formatPlainTextUnknownCve(cve, osFamily))
}
case "ja":
if 0 < cve.CveDetail.Jvn.CvssScore() {
scoredReport = append(
scoredReport, formatPlainTextDetailsLangJa(cve, osFamily))
} else if 0 < cve.CveDetail.Nvd.CvssScore() {
scoredReport = append(
scoredReport, formatPlainTextDetailsLangEn(cve, osFamily))
} else {
scoredReport = append(
scoredReport, formatPlainTextUnknownCve(cve, osFamily))
packsVer := []string{}
vuln.AffectedPackages.Sort()
for _, affected := range vuln.AffectedPackages {
if pack, ok := r.Packages[affected.Name]; ok {
packsVer = append(packsVer, pack.FormatVersionFromTo(affected.NotFixedYet))
}
}
}
for _, cve := range r.UnknownCves {
unscoredReport = append(
unscoredReport, formatPlainTextUnknownCve(cve, osFamily))
}
return
}
sort.Strings(vuln.CpeNames)
for _, name := range vuln.CpeNames {
packsVer = append(packsVer, name)
}
table.AddRow("Package/CPE", strings.Join(packsVer, "\n"))
table.AddRow("Confidence", vuln.Confidence)
func formatPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
cveID := cveInfo.CveDetail.CveID
dtable := uitable.New()
dtable.MaxColWidth = maxColWidth
dtable.Wrap = true
dtable.AddRow(cveID)
dtable.AddRow("-------------")
dtable.AddRow("Score", "?")
dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID))
dlinks := distroLinks(cveInfo, osFamily)
for _, link := range dlinks {
dtable.AddRow(link.title, link.url)
}
dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID))
dtable = addPackageInfos(dtable, cveInfo.Packages)
dtable = addCpeNames(dtable, cveInfo.CpeNames)
dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence)
return fmt.Sprintf("%s", dtable)
}
func formatPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
cveDetail := cveInfo.CveDetail
cveID := cveDetail.CveID
jvn := cveDetail.Jvn
dtable := uitable.New()
dtable.MaxColWidth = maxColWidth
dtable.Wrap = true
dtable.AddRow(cveID)
dtable.AddRow("-------------")
if score := cveDetail.Jvn.CvssScore(); 0 < score {
dtable.AddRow("Score",
fmt.Sprintf("%4.1f (%s)",
cveDetail.Jvn.CvssScore(),
jvn.CvssSeverity(),
))
} else {
dtable.AddRow("Score", "?")
}
dtable.AddRow("Vector", jvn.CvssVector())
dtable.AddRow("Title", jvn.CveTitle())
dtable.AddRow("Description", jvn.CveSummary())
dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID()))
dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID()))
dtable.AddRow("JVN", jvn.Link())
dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID))
dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID))
dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID))
dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID))
dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID))
dlinks := distroLinks(cveInfo, osFamily)
for _, link := range dlinks {
dtable.AddRow(link.title, link.url)
table.AddRow("\n")
}
dtable = addPackageInfos(dtable, cveInfo.Packages)
dtable = addCpeNames(dtable, cveInfo.CpeNames)
dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence)
return fmt.Sprintf("%s", dtable)
}
func formatPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
cveDetail := d.CveDetail
cveID := cveDetail.CveID
nvd := cveDetail.Nvd
dtable := uitable.New()
dtable.MaxColWidth = maxColWidth
dtable.Wrap = true
dtable.AddRow(cveID)
dtable.AddRow("-------------")
if score := cveDetail.Nvd.CvssScore(); 0 < score {
dtable.AddRow("Score",
fmt.Sprintf("%4.1f (%s)",
cveDetail.Nvd.CvssScore(),
nvd.CvssSeverity(),
))
} else {
dtable.AddRow("Score", "?")
}
dtable.AddRow("Vector", nvd.CvssVector())
dtable.AddRow("Summary", nvd.CveSummary())
dtable.AddRow("CWE", cweURL(cveDetail.CweID()))
dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID))
dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID))
dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID))
dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID))
dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID))
links := distroLinks(d, osFamily)
for _, link := range links {
dtable.AddRow(link.title, link.url)
}
dtable = addPackageInfos(dtable, d.Packages)
dtable = addCpeNames(dtable, d.CpeNames)
dtable.AddRow("Confidence", d.VulnInfo.Confidence)
return fmt.Sprintf("%s\n", dtable)
}
type distroLink struct {
title string
url string
}
// distroLinks add Vendor URL of the CVE to table
func distroLinks(cveInfo models.CveInfo, osFamily string) []distroLink {
cveID := cveInfo.CveDetail.CveID
switch osFamily {
case "rhel", "centos":
links := []distroLink{
{
"RHEL-CVE",
fmt.Sprintf("%s/%s", redhatSecurityBaseURL, cveID),
},
}
for _, advisory := range cveInfo.DistroAdvisories {
aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
links = append(links, distroLink{
// "RHEL-errata",
advisory.AdvisoryID,
fmt.Sprintf(redhatRHSABaseBaseURL, aidURL),
})
}
return links
case "oraclelinux":
links := []distroLink{
{
"Oracle-CVE",
fmt.Sprintf(oracleSecurityBaseURL, cveID),
},
}
for _, advisory := range cveInfo.DistroAdvisories {
links = append(links, distroLink{
// "Oracle-ELSA"
advisory.AdvisoryID,
fmt.Sprintf(oracleELSABaseBaseURL, advisory.AdvisoryID),
})
}
return links
case "amazon":
links := []distroLink{
{
"RHEL-CVE",
fmt.Sprintf("%s/%s", redhatSecurityBaseURL, cveID),
},
}
for _, advisory := range cveInfo.DistroAdvisories {
links = append(links, distroLink{
// "Amazon-ALAS",
advisory.AdvisoryID,
fmt.Sprintf(amazonSecurityBaseURL, advisory.AdvisoryID),
})
}
return links
case "ubuntu":
return []distroLink{
{
"Ubuntu-CVE",
fmt.Sprintf("%s/%s", ubuntuSecurityBaseURL, cveID),
},
//TODO Ubuntu USN
}
case "debian":
return []distroLink{
{
"Debian-CVE",
fmt.Sprintf("%s/%s", debianTrackerBaseURL, cveID),
},
// TODO Debian dsa
}
case "FreeBSD":
links := []distroLink{}
for _, advisory := range cveInfo.DistroAdvisories {
links = append(links, distroLink{
"FreeBSD-VuXML",
fmt.Sprintf(freeBSDVuXMLBaseURL, advisory.AdvisoryID),
})
}
return links
default:
return []distroLink{}
}
}
// addPackageInfos add package information related the CVE to table
func addPackageInfos(table *uitable.Table, packs []models.PackageInfo) *uitable.Table {
for i, p := range packs {
var title string
if i == 0 {
title = "Package"
}
ver := fmt.Sprintf(
"%s -> %s", p.ToStringCurrentVersion(), p.ToStringNewVersion())
table.AddRow(title, ver)
}
return table
}
func addCpeNames(table *uitable.Table, names []string) *uitable.Table {
for _, n := range names {
table.AddRow("CPE", fmt.Sprintf("%s", n))
}
return table
return fmt.Sprintf("%s\n%s", header, table)
}
func cweURL(cweID string) string {
@@ -488,36 +250,263 @@ func formatChangelogs(r models.ScanResult) string {
if p.NewVersion == "" {
continue
}
clog := formatOneChangelog(p)
clog := p.FormatChangelog()
buf = append(buf, clog, "\n\n")
}
return strings.Join(buf, "\n")
}
func formatOneChangelog(p models.PackageInfo) string {
buf := []string{}
if p.NewVersion == "" {
return ""
func needToRefreshCve(r models.ScanResult) bool {
if r.Lang != config.Conf.Lang {
return true
}
packVer := fmt.Sprintf("%s -> %s",
p.ToStringCurrentVersion(), p.ToStringNewVersion())
var delim bytes.Buffer
for i := 0; i < len(packVer); i++ {
delim.WriteString("-")
for _, cve := range r.ScannedCves {
if 0 < len(cve.CveContents) {
return false
}
}
clog := p.Changelog.Contents
if lines := strings.Split(clog, "\n"); len(lines) != 0 {
clog = strings.Join(lines[0:len(lines)-1], "\n")
}
switch p.Changelog.Method {
case models.FailedToGetChangelog:
clog = "No changelogs"
case models.FailedToFindVersionInChangelog:
clog = "Failed to parse changelogs. For detials, check yourself"
}
buf = append(buf, packVer, delim.String(), clog)
return strings.Join(buf, "\n")
return true
}
func overwriteJSONFile(dir string, r models.ScanResult) error {
before := config.Conf.FormatJSON
beforeDiff := config.Conf.Diff
config.Conf.FormatJSON = true
config.Conf.Diff = false
w := LocalFileWriter{CurrentDir: dir}
if err := w.Write(r); err != nil {
return fmt.Errorf("Failed to write summary report: %s", err)
}
config.Conf.FormatJSON = before
config.Conf.Diff = beforeDiff
return nil
}
func loadPrevious(current models.ScanResults) (previous models.ScanResults, err error) {
dirs, err := ListValidJSONDirs()
if err != nil {
return
}
for _, result := range current {
for _, dir := range dirs[1:] {
var r *models.ScanResult
path := filepath.Join(dir, result.ServerName+".json")
if r, err = loadOneServerScanResult(path); err != nil {
continue
}
if r.Family == result.Family && r.Release == result.Release {
previous = append(previous, *r)
util.Log.Infof("Privious json found: %s", path)
break
}
}
}
return previous, nil
}
func diff(curResults, preResults models.ScanResults) (diffed models.ScanResults, err error) {
for _, current := range curResults {
found := false
var previous models.ScanResult
for _, r := range preResults {
if current.ServerName == r.ServerName {
found = true
previous = r
break
}
}
if found {
current.ScannedCves = getDiffCves(previous, current)
packages := models.Packages{}
for _, s := range current.ScannedCves {
for _, affected := range s.AffectedPackages {
p := current.Packages[affected.Name]
packages[affected.Name] = p
}
}
current.Packages = packages
}
diffed = append(diffed, current)
}
return diffed, err
}
func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
previousCveIDsSet := map[string]bool{}
for _, previousVulnInfo := range previous.ScannedCves {
previousCveIDsSet[previousVulnInfo.CveID] = true
}
new := models.VulnInfos{}
updated := models.VulnInfos{}
for _, v := range current.ScannedCves {
if previousCveIDsSet[v.CveID] {
if isCveInfoUpdated(v.CveID, previous, current) {
updated[v.CveID] = v
}
} else {
new[v.CveID] = v
}
}
for cveID, vuln := range new {
updated[cveID] = vuln
}
return updated
}
func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
cTypes := []models.CveContentType{
models.NVD,
models.JVN,
models.NewCveContentType(current.Family),
}
prevLastModified := map[models.CveContentType]time.Time{}
for _, c := range previous.ScannedCves {
if cveID == c.CveID {
for _, cType := range cTypes {
content, _ := c.CveContents[cType]
prevLastModified[cType] = content.LastModified
}
break
}
}
curLastModified := map[models.CveContentType]time.Time{}
for _, c := range current.ScannedCves {
if cveID == c.CveID {
for _, cType := range cTypes {
content, _ := c.CveContents[cType]
curLastModified[cType] = content.LastModified
}
break
}
}
for _, cType := range cTypes {
if equal := prevLastModified[cType].Equal(curLastModified[cType]); !equal {
return true
}
}
return false
}
// jsonDirPattern is file name pattern of JSON directory
// 2016-11-16T10:43:28+09:00
// 2016-11-16T10:43:28Z
var jsonDirPattern = regexp.MustCompile(
`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:Z|[+-]\d{2}:\d{2})$`)
// ListValidJSONDirs returns valid json directory as array
// Returned array is sorted so that recent directories are at the head
func ListValidJSONDirs() (dirs []string, err error) {
var dirInfo []os.FileInfo
if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s",
config.Conf.ResultsDir, err)
return
}
for _, d := range dirInfo {
if d.IsDir() && jsonDirPattern.MatchString(d.Name()) {
jsonDir := filepath.Join(config.Conf.ResultsDir, d.Name())
dirs = append(dirs, jsonDir)
}
}
sort.Slice(dirs, func(i, j int) bool {
return dirs[j] < dirs[i]
})
return
}
// JSONDir returns
// If there is an arg, check if it is a valid format and return the corresponding path under results.
// If arg passed via PIPE (such as history subcommand), return that path.
// Otherwise, returns the path of the latest directory
func JSONDir(args []string) (string, error) {
var err error
dirs := []string{}
if 0 < len(args) {
if dirs, err = ListValidJSONDirs(); err != nil {
return "", err
}
path := filepath.Join(config.Conf.ResultsDir, args[0])
for _, d := range dirs {
ss := strings.Split(d, string(os.PathSeparator))
timedir := ss[len(ss)-1]
if timedir == args[0] {
return path, nil
}
}
return "", fmt.Errorf("Invalid path: %s", path)
}
// PIPE
if config.Conf.Pipe {
bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return "", fmt.Errorf("Failed to read stdin: %s", err)
}
fields := strings.Fields(string(bytes))
if 0 < len(fields) {
return filepath.Join(config.Conf.ResultsDir, fields[0]), nil
}
return "", fmt.Errorf("Stdin is invalid: %s", string(bytes))
}
// returns latest dir when no args or no PIPE
if dirs, err = ListValidJSONDirs(); err != nil {
return "", err
}
if len(dirs) == 0 {
return "", fmt.Errorf("No results under %s",
config.Conf.ResultsDir)
}
return dirs[0], nil
}
// LoadScanResults read JSON data
func LoadScanResults(jsonDir string) (results models.ScanResults, err error) {
var files []os.FileInfo
if files, err = ioutil.ReadDir(jsonDir); err != nil {
return nil, fmt.Errorf("Failed to read %s: %s", jsonDir, err)
}
for _, f := range files {
if filepath.Ext(f.Name()) != ".json" || strings.HasSuffix(f.Name(), "_diff.json") {
continue
}
var r *models.ScanResult
path := filepath.Join(jsonDir, f.Name())
if r, err = loadOneServerScanResult(path); err != nil {
return nil, err
}
results = append(results, *r)
}
if len(results) == 0 {
return nil, fmt.Errorf("There is no json file under %s", jsonDir)
}
return
}
// loadOneServerScanResult read JSON data of one server
func loadOneServerScanResult(jsonFile string) (*models.ScanResult, error) {
var (
data []byte
err error
)
if data, err = ioutil.ReadFile(jsonFile); err != nil {
return nil, fmt.Errorf("Failed to read %s: %s", jsonFile, err)
}
result := &models.ScanResult{}
if err := json.Unmarshal(data, result); err != nil {
return nil, fmt.Errorf("Failed to parse %s: %s", jsonFile, err)
}
return result, nil
}

327
report/util_test.go Normal file
View File

@@ -0,0 +1,327 @@
package report
import (
"reflect"
"testing"
"time"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
)
func TestIsCveInfoUpdated(t *testing.T) {
f := "2006-01-02"
old, _ := time.Parse(f, "2015-12-15")
new, _ := time.Parse(f, "2015-12-16")
type In struct {
cveID string
cur models.ScanResult
prev models.ScanResult
}
var tests = []struct {
in In
expected bool
}{
// NVD compare non-initialized times
{
in: In{
cveID: "CVE-2017-0001",
cur: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
),
},
},
},
prev: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
),
},
},
},
},
expected: false,
},
// JVN not updated
{
in: In{
cveID: "CVE-2017-0002",
cur: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0002",
LastModified: old,
},
),
},
},
},
prev: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0002",
LastModified: old,
},
),
},
},
},
},
expected: false,
},
// OVAL updated
{
in: In{
cveID: "CVE-2017-0003",
cur: models.ScanResult{
Family: "ubuntu",
ScannedCves: models.VulnInfos{
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0002",
LastModified: new,
},
),
},
},
},
prev: models.ScanResult{
Family: "ubuntu",
ScannedCves: models.VulnInfos{
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0002",
LastModified: old,
},
),
},
},
},
},
expected: true,
},
// OVAL newly detected
{
in: In{
cveID: "CVE-2017-0004",
cur: models.ScanResult{
Family: "redhat",
ScannedCves: models.VulnInfos{
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NVD,
CveID: "CVE-2017-0002",
LastModified: old,
},
),
},
},
},
prev: models.ScanResult{
Family: "redhat",
ScannedCves: models.VulnInfos{},
},
},
expected: true,
},
}
for i, tt := range tests {
actual := isCveInfoUpdated(tt.in.cveID, tt.in.prev, tt.in.cur)
if actual != tt.expected {
t.Errorf("[%d] actual: %t, expected: %t", i, actual, tt.expected)
}
}
}
func TestDiff(t *testing.T) {
atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
var tests = []struct {
inCurrent models.ScanResults
inPrevious models.ScanResults
out models.ScanResult
}{
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
Packages: models.Packages{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
AffectedPackages: models.PackageStatuses{{Name: "libexpat1"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
AffectedPackages: models.PackageStatuses{{Name: "libc-bin"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
Packages: models.Packages{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
Packages: models.Packages{},
ScannedCves: models.VulnInfos{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
{
inCurrent: models.ScanResults{
{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
Changelog: models.Changelog{
Contents: "",
Method: "",
},
},
},
},
},
inPrevious: models.ScanResults{
{
ScannedAt: atPrevious,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{},
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
AffectedPackages: models.PackageStatuses{{Name: "mysql-libs"}},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
},
Packages: models.Packages{
"mysql-libs": {
Name: "mysql-libs",
Version: "5.1.73",
Release: "7.el6",
NewVersion: "5.1.73",
NewRelease: "8.el6_8",
Repository: "",
Changelog: models.Changelog{
Contents: "",
Method: "",
},
},
},
},
},
}
for i, tt := range tests {
diff, _ := diff(tt.inCurrent, tt.inPrevious)
for _, actual := range diff {
if !reflect.DeepEqual(actual.ScannedCves, tt.out.ScannedCves) {
h := pp.Sprint(actual.ScannedCves)
x := pp.Sprint(tt.out.ScannedCves)
t.Errorf("[%d] cves actual: \n %s \n expected: \n %s", i, h, x)
}
for j := range tt.out.Packages {
if !reflect.DeepEqual(tt.out.Packages[j], actual.Packages[j]) {
h := pp.Sprint(tt.out.Packages[j])
x := pp.Sprint(actual.Packages[j])
t.Errorf("[%d] packages actual: \n %s \n expected: \n %s", i, x, h)
}
}
}
}
}

View File

@@ -24,28 +24,6 @@ import (
"github.com/future-architect/vuls/models"
)
const (
nvdBaseURL = "https://nvd.nist.gov/vuln/detail"
mitreBaseURL = "https://cve.mitre.org/cgi-bin/cvename.cgi?name="
cveDetailsBaseURL = "http://www.cvedetails.com/cve"
cvssV2CalcBaseURL = "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=%s"
cvssV3CalcBaseURL = "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=%s"
redhatSecurityBaseURL = "https://access.redhat.com/security/cve"
redhatRHSABaseBaseURL = "https://rhn.redhat.com/errata/%s.html"
amazonSecurityBaseURL = "https://alas.aws.amazon.com/%s.html"
oracleSecurityBaseURL = "https://linux.oracle.com/cve/%s.html"
oracleELSABaseBaseURL = "https://linux.oracle.com/errata/%s.html"
ubuntuSecurityBaseURL = "http://people.ubuntu.com/~ubuntu-security/cve"
debianTrackerBaseURL = "https://security-tracker.debian.org/tracker"
freeBSDVuXMLBaseURL = "https://vuxml.freebsd.org/freebsd/%s.html"
vulsOpenTag = "<vulsreport>"
vulsCloseTag = "</vulsreport>"
)
// ResultWriter Interface
type ResultWriter interface {
Write(...models.ScanResult) error

View File

@@ -20,13 +20,12 @@ package scan
import (
"fmt"
"regexp"
"sort"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/sirupsen/logrus"
)
type base struct {
@@ -48,7 +47,7 @@ func (l *base) setServerInfo(c config.ServerInfo) {
l.ServerInfo = c
}
func (l base) getServerInfo() config.ServerInfo {
func (l *base) getServerInfo() config.ServerInfo {
return l.ServerInfo
}
@@ -64,7 +63,7 @@ func (l *base) setDistro(fam, rel string) {
l.setServerInfo(s)
}
func (l base) getDistro() config.Distro {
func (l *base) getDistro() config.Distro {
return l.Distro
}
@@ -72,11 +71,32 @@ func (l *base) setPlatform(p models.Platform) {
l.Platform = p
}
func (l base) getPlatform() models.Platform {
func (l *base) getPlatform() models.Platform {
return l.Platform
}
func (l base) allContainers() (containers []config.Container, err error) {
func (l *base) runningKernel() (release, version string, err error) {
r := l.exec("uname -r", noSudo)
if !r.isSuccess() {
return "", "", fmt.Errorf("Failed to SSH: %s", r)
}
release = strings.TrimSpace(r.Stdout)
switch l.Distro.Family {
case config.Debian:
r := l.exec("uname -a", noSudo)
if !r.isSuccess() {
return "", "", fmt.Errorf("Failed to SSH: %s", r)
}
ss := strings.Fields(r.Stdout)
if 6 < len(ss) {
version = ss[6]
}
}
return
}
func (l *base) allContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Containers.Type {
case "", "docker":
stdout, err := l.dockerPs("-a --format '{{.ID}} {{.Names}} {{.Image}}'")
@@ -213,7 +233,7 @@ func (l *base) detectPlatform() {
return
}
func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
func (l *base) detectRunningOnAws() (ok bool, instanceID string, err error) {
if r := l.exec("type curl", noSudo); r.isSuccess() {
cmd := "curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id"
r := l.exec(cmd, noSudo)
@@ -261,16 +281,11 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/resource-ids.html
var awsInstanceIDPattern = regexp.MustCompile(`^i-[0-9a-f]+$`)
func (l base) isAwsInstanceID(str string) bool {
func (l *base) isAwsInstanceID(str string) bool {
return awsInstanceIDPattern.MatchString(str)
}
func (l *base) convertToModel() models.ScanResult {
for _, p := range l.VulnInfos {
sort.Sort(models.PackageInfosByName(p.Packages))
}
sort.Sort(l.VulnInfos)
ctype := l.ServerInfo.Containers.Type
if l.ServerInfo.Container.ContainerID != "" && ctype == "" {
ctype = "docker"
@@ -287,22 +302,19 @@ func (l *base) convertToModel() models.ScanResult {
errs = append(errs, fmt.Sprintf("%s", e))
}
// Avoid null slice being null in JSON
for i := range l.VulnInfos {
l.VulnInfos[i].NilSliceToEmpty()
}
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,
ScannedAt: time.Now(),
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Platform: l.Platform,
ScannedCves: l.VulnInfos,
Packages: l.Packages,
Optional: l.ServerInfo.Optional,
Errors: errs,
JSONVersion: models.JSONVersion,
ServerName: l.ServerInfo.ServerName,
ScannedAt: time.Now(),
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Platform: l.Platform,
ScannedCves: l.VulnInfos,
RunningKernel: l.Kernel,
Packages: l.Packages,
Optional: l.ServerInfo.Optional,
Errors: errs,
}
}
@@ -310,6 +322,6 @@ func (l *base) setErrs(errs []error) {
l.errs = errs
}
func (l base) getErrs() []error {
func (l *base) getErrs() []error {
return l.errs
}

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scan
import (
"bufio"
"fmt"
"regexp"
"strconv"
@@ -28,6 +29,8 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/knqyf263/go-deb-version"
)
// inherit OsTypeInterface
@@ -37,7 +40,14 @@ type debian struct {
// NewDebian is constructor
func newDebian(c config.ServerInfo) *debian {
d := &debian{}
d := &debian{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
@@ -53,8 +63,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
return false, deb, nil
}
if r.ExitStatus == 255 {
return false, deb, fmt.Errorf(
"Unable to connect via SSH. Check SSH settings. %s", r)
return false, deb, fmt.Errorf("Unable to connect via SSH. Check SSH settings. If you have never SSH to the host to be scanned, SSH to the host before scanning in order to add the HostKey. %s@%s port: %s\n%s", c.User, c.Host, c.Port, r)
}
util.Log.Debugf("Not Debian like Linux. %s", r)
return false, deb, nil
@@ -67,7 +76,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// e.g.
// Raspbian GNU/Linux 7 \n \l
result := strings.Fields(r.Stdout)
if len(result) > 2 && result[0] == "Raspbian" {
if len(result) > 2 && result[0] == config.Raspbian {
distro := strings.ToLower(trim(result[0]))
deb.setDistro(distro, trim(result[2]))
return true, deb, nil
@@ -115,7 +124,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Debian
cmd := "cat /etc/debian_version"
if r := exec(c, cmd, noSudo); r.isSuccess() {
deb.setDistro("debian", trim(r.Stdout))
deb.setDistro(config.Debian, trim(r.Stdout))
return true, deb, nil
}
@@ -128,60 +137,112 @@ func trim(str string) string {
}
func (o *debian) checkIfSudoNoPasswd() error {
cmd := util.PrependProxyEnv("apt-get update")
o.log.Infof("Checking... sudo %s", cmd)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
if config.Conf.Deep || o.Distro.Family == config.Raspbian {
cmd := util.PrependProxyEnv("apt-get update")
o.log.Infof("Checking... sudo %s", cmd)
r := o.exec(cmd, sudo)
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
}
o.log.Infof("Sudo... Pass")
return nil
}
o.log.Infof("Sudo... Pass")
o.log.Infof("sudo ... No need")
return nil
}
func (o *debian) checkDependencies() error {
packNames := []string{}
switch o.Distro.Family {
case "ubuntu", "raspbian":
case config.Ubuntu, config.Raspbian:
o.log.Infof("Dependencies... No need")
return nil
case "debian":
// Debian needs aptitude to get changelogs.
// Because unable to get changelogs via apt-get changelog on Debian.
if r := o.exec("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
case config.Debian:
// https://askubuntu.com/a/742844
packNames = append(packNames, "reboot-notifier")
if !config.Conf.Deep {
// Debian needs aptitude to get changelogs.
// Because unable to get changelogs via apt-get changelog on Debian.
packNames = append(packNames, "aptitude")
}
o.log.Infof("Dependencies... Pass")
return nil
default:
return fmt.Errorf("Not implemented yet: %s", o.Distro)
}
}
func (o *debian) scanPackages() error {
var err error
var packs []models.PackageInfo
if packs, err = o.scanInstalledPackages(); err != nil {
o.log.Errorf("Failed to scan installed packages")
return err
for _, name := range packNames {
//TODO --show-format
cmd := "dpkg-query -W " + name
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", name)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
}
o.setPackages(packs)
var unsecurePacks []models.VulnInfo
if unsecurePacks, err = o.scanUnsecurePackages(packs); err != nil {
o.log.Errorf("Failed to scan vulnerable packages")
return err
}
o.setVulnInfos(unsecurePacks)
o.log.Infof("Dependencies... Pass")
return nil
}
func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error) {
func (o *debian) scanPackages() error {
// collect the running kernel information
release, version, err := o.runningKernel()
if err != nil {
o.log.Errorf("Failed to scan the running kernel version: %s", err)
return err
}
rebootRequired, err := o.rebootRequired()
if err != nil {
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
return err
}
o.Kernel = models.Kernel{
Version: version,
Release: release,
RebootRequired: rebootRequired,
}
installed, updatable, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
o.Packages = installed
if config.Conf.Deep || o.Distro.Family == config.Raspbian {
unsecures, err := o.scanUnsecurePackages(updatable)
if err != nil {
o.log.Errorf("Failed to scan vulnerable packages: %s", err)
return err
}
o.VulnInfos = unsecures
return nil
}
return nil
}
// https://askubuntu.com/a/742844
func (o *debian) rebootRequired() (bool, error) {
r := o.exec("test -f /var/run/reboot-required", noSudo)
switch r.ExitStatus {
case 0:
return true, nil
case 1:
return false, nil
default:
return false, fmt.Errorf("Failed to check reboot reauired: %s", r)
}
}
func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, error) {
installed, updatable := models.Packages{}, models.Packages{}
r := o.exec("dpkg-query -W", noSudo)
if !r.isSuccess() {
return packs, fmt.Errorf("Failed to SSH: %s", r)
return nil, nil, fmt.Errorf("Failed to SSH: %s", r)
}
// e.g.
@@ -192,16 +253,37 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
name, version, err := o.parseScannedPackagesLine(trimmed)
if err != nil {
return nil, fmt.Errorf(
return nil, nil, fmt.Errorf(
"Debian: Failed to parse package line: %s", line)
}
packs = append(packs, models.PackageInfo{
installed[name] = models.Package{
Name: name,
Version: version,
})
}
}
}
return
updatableNames, err := o.getUpdatablePackNames()
if err != nil {
return nil, nil, err
}
for _, name := range updatableNames {
for _, pack := range installed {
if pack.Name == name {
updatable[name] = pack
break
}
}
}
// Fill the candidate versions of upgradable packages
err = o.fillCandidateVersion(updatable)
if err != nil {
return nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
}
installed.MergeNewVersion(updatable)
return installed, updatable, nil
}
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
@@ -221,51 +303,33 @@ func (o *debian) parseScannedPackagesLine(line string) (name, version string, er
return "", "", fmt.Errorf("Unknown format: %s", line)
}
func (o *debian) scanUnsecurePackages(installed []models.PackageInfo) ([]models.VulnInfo, error) {
func (o *debian) aptGetUpdate() error {
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.exec(cmd, sudo); !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
// Convert the name of upgradable packages to PackageInfo struct
upgradableNames, err := o.GetUpgradablePackNames()
if err != nil {
return nil, err
}
var upgradablePacks []models.PackageInfo
for _, name := range upgradableNames {
for _, pack := range installed {
if pack.Name == name {
upgradablePacks = append(upgradablePacks, pack)
break
}
}
}
// Fill the candidate versions of upgradable packages
upgradablePacks, err = o.fillCandidateVersion(upgradablePacks)
if err != nil {
return nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
}
o.Packages.MergeNewVersion(upgradablePacks)
func (o *debian) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
o.aptGetUpdate()
// Setup changelog cache
current := cache.Meta{
Name: o.getServerInfo().GetServerName(),
Distro: o.getServerInfo().Distro,
Packs: upgradablePacks,
Packs: updatable,
}
o.log.Debugf("Ensure changelog cache: %s", current.Name)
var meta *cache.Meta
if meta, err = o.ensureChangelogCache(current); err != nil {
meta, err := o.ensureChangelogCache(current)
if err != nil {
return nil, err
}
// Collect CVE information of upgradable packages
vulnInfos, err := o.scanVulnInfos(upgradablePacks, meta)
vulnInfos, err := o.scanVulnInfos(updatable, meta)
if err != nil {
return nil, fmt.Errorf("Failed to scan unsecure packages. err: %s", err)
}
@@ -308,34 +372,34 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) {
return &cached, nil
}
func (o *debian) fillCandidateVersion(before models.PackageInfoList) (filled []models.PackageInfo, err error) {
func (o *debian) fillCandidateVersion(updatables models.Packages) (err error) {
names := []string{}
for _, p := range before {
names = append(names, p.Name)
for name := range updatables {
names = append(names, name)
}
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
return fmt.Errorf("Failed to SSH: %s", r)
}
packChangelog := o.splitAptCachePolicy(r.Stdout)
for k, v := range packChangelog {
ver, err := o.parseAptCachePolicy(v, k)
if err != nil {
return nil, fmt.Errorf("Failed to parse %s", err)
return fmt.Errorf("Failed to parse %s", err)
}
p, found := before.FindByName(k)
if !found {
return nil, fmt.Errorf("Not found: %s", k)
pack, ok := updatables[k]
if !ok {
return fmt.Errorf("Not found: %s", k)
}
p.NewVersion = ver.Candidate
filled = append(filled, p)
pack.NewVersion = ver.Candidate
updatables[k] = pack
}
return
}
func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
cmd := util.PrependProxyEnv("LANGUAGE=en_US.UTF-8 apt-get upgrade --dry-run")
func (o *debian) getUpdatablePackNames() (packNames []string, err error) {
cmd := util.PrependProxyEnv("LANGUAGE=en_US.UTF-8 apt-get dist-upgrade --dry-run")
r := o.exec(cmd, noSudo)
if r.isSuccess(0, 1) {
return o.parseAptGetUpgrade(r.Stdout)
@@ -345,7 +409,7 @@ func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
cmd, r.ExitStatus, r.Stdout, r.Stderr)
}
func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, err error) {
func (o *debian) parseAptGetUpgrade(stdout string) (updatableNames []string, err error) {
startRe := regexp.MustCompile(`The following packages will be upgraded:`)
stopRe := regexp.MustCompile(`^(\d+) upgraded.*`)
startLineFound, stopLineFound := false, false
@@ -360,21 +424,21 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
}
result := stopRe.FindStringSubmatch(line)
if len(result) == 2 {
numUpgradablePacks, err := strconv.Atoi(result[1])
nUpdatable, err := strconv.Atoi(result[1])
if err != nil {
return nil, fmt.Errorf(
"Failed to scan upgradable packages number. line: %s", line)
}
if numUpgradablePacks != len(upgradableNames) {
if nUpdatable != len(updatableNames) {
return nil, fmt.Errorf(
"Failed to scan upgradable packages, expected: %s, detected: %d",
result[1], len(upgradableNames))
result[1], len(updatableNames))
}
stopLineFound = true
o.log.Debugf("Found the stop line. line: %s", line)
break
}
upgradableNames = append(upgradableNames, strings.Fields(line)...)
updatableNames = append(updatableNames, strings.Fields(line)...)
}
if !startLineFound {
// no upgrades
@@ -387,19 +451,28 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
return
}
func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache.Meta) (models.VulnInfos, error) {
resChan := make(chan struct {
models.PackageInfo
DetectedCveIDs
}, len(upgradablePacks))
errChan := make(chan error, len(upgradablePacks))
reqChan := make(chan models.PackageInfo, len(upgradablePacks))
// DetectedCveID has CveID, Confidence and DetectionMethod fields
// LenientMatching will be true if this vulnerability is not detected by accurate version matching.
// see https://github.com/future-architect/vuls/pull/328
type DetectedCveID struct {
CveID string
Confidence models.Confidence
}
func (o *debian) scanVulnInfos(updatablePacks models.Packages, meta *cache.Meta) (models.VulnInfos, error) {
type response struct {
pack *models.Package
DetectedCveIDs []DetectedCveID
}
resChan := make(chan response, len(updatablePacks))
errChan := make(chan error, len(updatablePacks))
reqChan := make(chan models.Package, len(updatablePacks))
defer close(resChan)
defer close(errChan)
defer close(reqChan)
go func() {
for _, pack := range upgradablePacks {
for _, pack := range updatablePacks {
reqChan <- pack
}
}()
@@ -407,50 +480,53 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
timeout := time.After(30 * 60 * time.Second)
concurrency := 10
tasks := util.GenWorkers(concurrency)
for range upgradablePacks {
for range updatablePacks {
tasks <- func() {
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
func(p models.Package) {
changelog := o.getChangelogCache(meta, p)
if 0 < len(changelog) {
cveIDs, _ := o.getCveIDsFromChangelog(changelog, p.Name, p.Version)
resChan <- struct {
models.PackageInfo
DetectedCveIDs
}{p, cveIDs}
cveIDs, pack := o.getCveIDsFromChangelog(changelog, p.Name, p.Version)
resChan <- response{pack, cveIDs}
return
}
// if the changelog is not in cache or failed to get from local cache,
// get the changelog of the package via internet.
// After that, store it in the cache.
if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
if cveIDs, pack, err := o.scanPackageCveIDs(p); err != nil {
errChan <- err
} else {
resChan <- struct {
models.PackageInfo
DetectedCveIDs
}{p, cveIDs}
resChan <- response{pack, cveIDs}
}
}(pack)
}
}
}
// { DetectedCveID{} : [packageInfo] }
cvePackages := make(map[DetectedCveID][]models.PackageInfo)
// { DetectedCveID{} : [package] }
cvePackages := make(map[DetectedCveID][]string)
errs := []error{}
for i := 0; i < len(upgradablePacks); i++ {
for i := 0; i < len(updatablePacks); i++ {
select {
case pair := <-resChan:
pack := pair.PackageInfo
cveIDs := pair.DetectedCveIDs
for _, cveID := range cveIDs {
cvePackages[cveID] = appendPackIfMissing(cvePackages[cveID], pack)
case response := <-resChan:
if response.pack == nil {
continue
}
o.log.Infof("(%d/%d) Scanned %s-%s : %s",
i+1, len(upgradablePacks), pair.Name, pair.PackageInfo.Version, cveIDs)
o.Packages[response.pack.Name] = *response.pack
cves := response.DetectedCveIDs
for _, cve := range cves {
packNames, ok := cvePackages[cve]
if ok {
packNames = append(packNames, response.pack.Name)
} else {
packNames = []string{response.pack.Name}
}
cvePackages[cve] = packNames
}
o.log.Infof("(%d/%d) Scanned %s: %s",
i+1, len(updatablePacks), response.pack.Name, cves)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
@@ -466,17 +542,22 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
cveIDs = append(cveIDs, k)
}
o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
var vinfos models.VulnInfos
for k, v := range cvePackages {
vinfos = append(vinfos, models.VulnInfo{
CveID: k.CveID,
Confidence: k.Confidence,
Packages: v,
})
vinfos := models.VulnInfos{}
for cveID, names := range cvePackages {
affected := models.PackageStatuses{}
for _, n := range names {
affected = append(affected, models.PackageStatus{Name: n})
}
vinfos[cveID.CveID] = models.VulnInfo{
CveID: cveID.CveID,
Confidence: cveID.Confidence,
AffectedPackages: affected,
}
}
// Update meta package information of changelog cache to the latest one.
meta.Packs = upgradablePacks
meta.Packs = updatablePacks
if err := cache.DB.RefreshMeta(*meta); err != nil {
return nil, err
}
@@ -484,10 +565,10 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
return vinfos, nil
}
func (o *debian) getChangelogCache(meta *cache.Meta, pack models.PackageInfo) string {
cachedPack, found := meta.FindPack(pack.Name)
func (o *debian) getChangelogCache(meta *cache.Meta, pack models.Package) string {
cachedPack, found := meta.Packs[pack.Name]
if !found {
o.log.Debugf("Not found: %s", pack.Name)
o.log.Debugf("Not found in cache: %s", pack.Name)
return ""
}
@@ -512,12 +593,12 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.PackageInfo) st
return changelog
}
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, error) {
func (o *debian) scanPackageCveIDs(pack models.Package) ([]DetectedCveID, *models.Package, error) {
cmd := ""
switch o.Distro.Family {
case "ubuntu", "raspbian":
case config.Ubuntu, config.Raspbian:
cmd = fmt.Sprintf(`PAGER=cat apt-get -q=2 changelog %s`, pack.Name)
case "debian":
case config.Debian:
cmd = fmt.Sprintf(`PAGER=cat aptitude -q=2 changelog %s`, pack.Name)
}
cmd = util.PrependProxyEnv(cmd)
@@ -526,151 +607,146 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, er
if !r.isSuccess() {
o.log.Warnf("Failed to SSH: %s", r)
// Ignore this Error.
return nil, nil
return nil, nil, nil
}
stdout := strings.Replace(r.Stdout, "\r", "", -1)
cveIDs, clog := o.getCveIDsFromChangelog(
stdout, pack.Name, pack.Version)
cveIDs, clogFilledPack := o.getCveIDsFromChangelog(stdout, pack.Name, pack.Version)
if clog.Method != models.FailedToGetChangelog {
err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, clog.Contents)
if clogFilledPack.Changelog.Method != models.FailedToGetChangelog {
err := cache.DB.PutChangelog(
o.getServerInfo().GetServerName(), pack.Name, pack.Changelog.Contents)
if err != nil {
return nil, fmt.Errorf("Failed to put changelog into cache")
return nil, nil, fmt.Errorf("Failed to put changelog into cache")
}
}
// No error will be returned. Only logging.
return cveIDs, nil
return cveIDs, clogFilledPack, nil
}
// Debian Version Numbers
// https://readme.phys.ethz.ch/documentation/debian_version_numbers/
func (o *debian) getCveIDsFromChangelog(
changelog, name, ver string) ([]DetectedCveID, models.Changelog) {
changelog, name, ver string) ([]DetectedCveID, *models.Package) {
if cveIDs, relevant, err := o.parseChangelog(
if cveIDs, pack, err := o.parseChangelog(
changelog, name, ver, models.ChangelogExactMatch); err == nil {
return cveIDs, relevant
return cveIDs, pack
}
var verAfterColon string
var err error
splittedByColon := strings.Split(ver, ":")
if 1 < len(splittedByColon) {
verAfterColon = splittedByColon[1]
if cveIDs, relevant, err := o.parseChangelog(
if cveIDs, pack, err := o.parseChangelog(
changelog, name, verAfterColon, models.ChangelogLenientMatch); err == nil {
return cveIDs, relevant
return cveIDs, pack
}
}
delim := []string{"+", "~", "build"}
switch o.Distro.Family {
case "ubuntu":
delim = append(delim, "ubuntu")
case "debian":
case "Raspbian":
case config.Ubuntu:
delim = append(delim, config.Ubuntu)
case config.Debian:
case config.Raspbian:
}
for _, d := range delim {
ss := strings.Split(ver, d)
if 1 < len(ss) {
if cveIDs, relevant, err := o.parseChangelog(
if cveIDs, pack, err := o.parseChangelog(
changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
return cveIDs, relevant
return cveIDs, pack
}
}
ss = strings.Split(verAfterColon, d)
if 1 < len(ss) {
if cveIDs, relevant, err := o.parseChangelog(
if cveIDs, pack, err := o.parseChangelog(
changelog, name, ss[0], models.ChangelogLenientMatch); err == nil {
return cveIDs, relevant
return cveIDs, pack
}
}
}
// Only logging the error.
o.log.Error(err)
for i, p := range o.Packages {
if p.Name == name {
o.Packages[i].Changelog = models.Changelog{
Contents: "",
Method: models.FailedToFindVersionInChangelog,
}
}
}
o.log.Warnf("Failed to find the version in changelog: %s-%s", name, ver)
o.log.Debugf("Changelog of : %s-%s", name, ver, changelog)
// If the version is not in changelog, return entire changelog to put into cache
return []DetectedCveID{}, models.Changelog{
pack := o.Packages[name]
pack.Changelog = models.Changelog{
Contents: changelog,
Method: models.FailedToFindVersionInChangelog,
}
}
// DetectedCveID has CveID, Confidence and DetectionMethod fields
// LenientMatching will be true if this vulnerability is not detected by accurate version matching.
// see https://github.com/future-architect/vuls/pull/328
type DetectedCveID struct {
CveID string
Confidence models.Confidence
return []DetectedCveID{}, &pack
}
// DetectedCveIDs is a slice of DetectedCveID
type DetectedCveIDs []DetectedCveID
var cveRe = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
// Collect CVE-IDs included in the changelog.
// The version which specified in argument(versionOrLater) is excluded.
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, models.Changelog, error) {
// The version specified in argument(versionOrLater) is used to compare.
func (o *debian) parseChangelog(changelog, name, ver string, confidence models.Confidence) ([]DetectedCveID, *models.Package, error) {
installedVer, err := version.NewVersion(ver)
if err != nil {
return nil, nil, fmt.Errorf("Failed to parse installed version: %s, %s", ver, err)
}
buf, cveIDs := []string{}, []string{}
stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(ver)))
stopLineFound := false
lines := strings.Split(changelog, "\n")
for _, line := range lines {
scanner := bufio.NewScanner(strings.NewReader(changelog))
found := false
for scanner.Scan() {
line := scanner.Text()
buf = append(buf, line)
if match := stopRe.MatchString(line); match {
// o.log.Debugf("Found the stop line: %s", line)
stopLineFound = true
break
} else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
for _, m := range matches {
cveIDs = util.AppendIfMissing(cveIDs, m)
}
}
ss := strings.Fields(line)
if len(ss) < 2 {
continue
}
if !strings.HasPrefix(ss[1], "(") || !strings.HasSuffix(ss[1], ")") {
continue
}
clogVer, err := version.NewVersion(ss[1][1 : len(ss[1])-1])
if err != nil {
continue
}
if installedVer.Equal(clogVer) || installedVer.GreaterThan(clogVer) {
found = true
break
}
}
if !stopLineFound {
return nil, models.Changelog{
Contents: "",
Method: models.FailedToFindVersionInChangelog,
}, fmt.Errorf(
"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
name,
ver,
)
if !found {
pack := o.Packages[name]
pack.Changelog = models.Changelog{
Contents: "",
Method: models.FailedToFindVersionInChangelog,
}
return nil, &pack, fmt.Errorf(
"Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s",
name, ver)
}
clog := models.Changelog{
Contents: strings.Join(buf, "\n"),
Method: string(confidence.DetectionMethod),
}
for i, p := range o.Packages {
if p.Name == name {
o.Packages[i].Changelog = clog
}
Contents: strings.Join(buf[0:len(buf)-1], "\n"),
Method: confidence.DetectionMethod,
}
pack := o.Packages[name]
pack.Changelog = clog
cves := []DetectedCveID{}
for _, id := range cveIDs {
cves = append(cves, DetectedCveID{id, confidence})
}
return cves, clog, nil
return cves, &pack, nil
}
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
@@ -722,14 +798,3 @@ func (o *debian) parseAptCachePolicy(stdout, name string) (packCandidateVer, err
}
return ver, fmt.Errorf("Unknown Format: %s", stdout)
}
func appendPackIfMissing(slice []models.PackageInfo, s models.PackageInfo) []models.PackageInfo {
for _, ele := range slice {
if ele.Name == s.Name &&
ele.Version == s.Version &&
ele.Release == s.Release {
return slice
}
}
return append(slice, s)
}

View File

@@ -22,11 +22,11 @@ import (
"reflect"
"testing"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
"github.com/sirupsen/logrus"
)
func TestParseScannedPackagesLineDebian(t *testing.T) {
@@ -66,7 +66,7 @@ func TestGetCveIDsFromChangelog(t *testing.T) {
changelog models.Changelog
}{
{
// verubuntu1
//0 verubuntu1
[]string{
"systemd",
"228-4ubuntu1",
@@ -81,9 +81,9 @@ systemd (228-4) unstable; urgency=medium
systemd (228-3) unstable; urgency=medium`,
},
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogLenientMatch},
{"CVE-2015-2326", models.ChangelogLenientMatch},
{"CVE-2015-3210", models.ChangelogLenientMatch},
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
},
models.Changelog{
Contents: `systemd (229-2) unstable; urgency=medium
@@ -92,13 +92,12 @@ systemd (228-6) unstable; urgency=medium
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
systemd (228-5) unstable; urgency=medium
systemd (228-4) unstable; urgency=medium`,
Method: models.ChangelogLenientMatchStr,
systemd (228-5) unstable; urgency=medium`,
Method: models.ChangelogExactMatchStr,
},
},
{
// ver
//1 ver
[]string{
"libpcre3",
"2:8.35-7.1ubuntu1",
@@ -115,9 +114,9 @@ systemd (228-4) unstable; urgency=medium`,
pcre3 (2:8.35-7) unstable; urgency=medium`,
},
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogLenientMatch},
{"CVE-2015-2326", models.ChangelogLenientMatch},
{"CVE-2015-3210", models.ChangelogLenientMatch},
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
},
models.Changelog{
Contents: `pcre3 (2:8.38-2) unstable; urgency=low
@@ -128,13 +127,12 @@ systemd (228-4) unstable; urgency=medium`,
pcre3 (2:8.35-7.2) unstable; urgency=low
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
pcre3 (2:8.35-7.1) unstable; urgency=medium`,
Method: models.ChangelogLenientMatchStr,
CVE-2015-3210: heap buffer overflow in pcre_compile2() /`,
Method: models.ChangelogExactMatchStr,
},
},
{
// ver-ubuntu3
//2 ver-ubuntu3
[]string{
"sysvinit",
"2.88dsf-59.2ubuntu3",
@@ -168,13 +166,12 @@ systemd (228-4) unstable; urgency=medium`,
sysvinit (2.88dsf-59.3) unstable; urgency=medium
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
sysvinit (2.88dsf-59.2ubuntu3) xenial; urgency=medium`,
CVE-2015-3210: heap buffer overflow in pcre_compile2() /`,
Method: models.ChangelogExactMatchStr,
},
},
{
// 1:ver-ubuntu3
//3 1:ver-ubuntu3
[]string{
"bsdutils",
"1:2.27.1-1ubuntu3",
@@ -192,25 +189,25 @@ systemd (228-4) unstable; urgency=medium`,
util-linux (2.27-3ubuntu1) xenial; urgency=medium`,
},
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogLenientMatch},
{"CVE-2015-2326", models.ChangelogLenientMatch},
{"CVE-2015-3210", models.ChangelogLenientMatch},
{"CVE-2016-1000000", models.ChangelogLenientMatch},
// {"CVE-2015-2325", models.ChangelogLenientMatch},
// {"CVE-2015-2326", models.ChangelogLenientMatch},
// {"CVE-2015-3210", models.ChangelogLenientMatch},
// {"CVE-2016-1000000", models.ChangelogLenientMatch},
},
models.Changelog{
Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
util-linux (2.27.1-3) unstable; urgency=medium
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
util-linux (2.27.1-2) unstable; urgency=medium
util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
util-linux (2.27.1-1ubuntu3) xenial; urgency=medium`,
Method: models.ChangelogLenientMatchStr,
// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
// util-linux (2.27.1-3) unstable; urgency=medium
// CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
// CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
// CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
// util-linux (2.27.1-2) unstable; urgency=medium
// util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
// util-linux (2.27.1-1ubuntu3) xenial; urgency=medium`,
Method: models.ChangelogExactMatchStr,
},
},
{
// 1:ver-ubuntu3
//4 1:ver-ubuntu3
[]string{
"bsdutils",
"1:2.27-3ubuntu3",
@@ -228,29 +225,28 @@ systemd (228-4) unstable; urgency=medium`,
util-linux (2.27-3) xenial; urgency=medium`,
},
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogLenientMatch},
{"CVE-2015-2326", models.ChangelogLenientMatch},
{"CVE-2015-3210", models.ChangelogLenientMatch},
{"CVE-2016-1000000", models.ChangelogLenientMatch},
// {"CVE-2015-2325", models.ChangelogLenientMatch},
// {"CVE-2015-2326", models.ChangelogLenientMatch},
// {"CVE-2015-3210", models.ChangelogLenientMatch},
// {"CVE-2016-1000000", models.ChangelogLenientMatch},
},
models.Changelog{
Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
util-linux (2.27.1-3) unstable; urgency=medium
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
util-linux (2.27.1-2) unstable; urgency=medium
util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
util-linux (2.27.1-1ubuntu3) xenial; urgency=medium
util-linux (2.27.1-1ubuntu2) xenial; urgency=medium
util-linux (2.27.1-1ubuntu1) xenial; urgency=medium
util-linux (2.27.1-1) unstable; urgency=medium
util-linux (2.27-3) xenial; urgency=medium`,
Method: models.ChangelogLenientMatchStr,
// Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium
// util-linux (2.27.1-3) unstable; urgency=medium
// CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
// CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
// CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
// util-linux (2.27.1-2) unstable; urgency=medium
// util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
// util-linux (2.27.1-1ubuntu3) xenial; urgency=medium
// util-linux (2.27.1-1ubuntu2) xenial; urgency=medium
// util-linux (2.27.1-1ubuntu1) xenial; urgency=medium
// util-linux (2.27.1-1) unstable; urgency=medium`,
Method: models.ChangelogExactMatchStr,
},
},
{
// https://github.com/future-architect/vuls/pull/350
//5 https://github.com/future-architect/vuls/pull/350
[]string{
"tar",
"1.27.1-2+b1",
@@ -259,13 +255,12 @@ systemd (228-4) unstable; urgency=medium`,
tar (1.27.1-2) unstable; urgency=low`,
},
[]DetectedCveID{
{"CVE-2016-6321", models.ChangelogLenientMatch},
{"CVE-2016-6321", models.ChangelogExactMatch},
},
models.Changelog{
Contents: `tar (1.27.1-2+deb8u1) jessie-security; urgency=high
* CVE-2016-6321: Bypassing the extract path name.
tar (1.27.1-2) unstable; urgency=low`,
Method: models.ChangelogLenientMatchStr,
* CVE-2016-6321: Bypassing the extract path name.`,
Method: models.ChangelogExactMatchStr,
},
},
}
@@ -273,7 +268,7 @@ systemd (228-4) unstable; urgency=medium`,
d := newDebian(config.ServerInfo{})
d.Distro.Family = "ubuntu"
for i, tt := range tests {
aCveIDs, aClog := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
aCveIDs, aPack := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1])
if len(aCveIDs) != len(tt.cveIDs) {
t.Errorf("[%d] Len of return array are'nt same. expected %#v, actual %#v", i, tt.cveIDs, aCveIDs)
t.Errorf(pp.Sprintf("%s", tt.in))
@@ -285,12 +280,12 @@ systemd (228-4) unstable; urgency=medium`,
}
}
if aClog.Contents != tt.changelog.Contents {
t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Contents, aClog.Contents))
if aPack.Changelog.Contents != tt.changelog.Contents {
t.Error(pp.Sprintf("[%d] expected: %s, actual: %s", i, tt.changelog.Contents, aPack.Changelog.Contents))
}
if aClog.Method != tt.changelog.Method {
t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Method, aClog.Method))
if aPack.Changelog.Method != tt.changelog.Method {
t.Error(pp.Sprintf("[%d] expected: %s, actual: %s", i, tt.changelog.Method, aPack.Changelog.Method))
}
}
}
@@ -308,49 +303,7 @@ Reading state information... Done
The following packages will be upgraded:
apt ca-certificates cpio dpkg e2fslibs e2fsprogs gnupg gpgv libc-bin libc6 libcomerr2 libpcre3
libpng12-0 libss2 libssl1.0.0 libudev0 multiarch-support openssl tzdata udev upstart
21 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst dpkg [1.16.1.2ubuntu7.5] (1.16.1.2ubuntu7.7 Ubuntu:12.04/precise-updates [amd64])
Conf dpkg (1.16.1.2ubuntu7.7 Ubuntu:12.04/precise-updates [amd64])
Inst upstart [1.5-0ubuntu7.2] (1.5-0ubuntu7.3 Ubuntu:12.04/precise-updates [amd64])
Inst libc-bin [2.15-0ubuntu10.10] (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64]) [libc6:amd64 ]
Conf libc-bin (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64]) [libc6:amd64 ]
Inst libc6 [2.15-0ubuntu10.10] (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64])
Conf libc6 (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64])
Inst libudev0 [175-0ubuntu9.9] (175-0ubuntu9.10 Ubuntu:12.04/precise-updates [amd64])
Inst tzdata [2015a-0ubuntu0.12.04] (2015g-0ubuntu0.12.04 Ubuntu:12.04/precise-updates [all])
Conf tzdata (2015g-0ubuntu0.12.04 Ubuntu:12.04/precise-updates [all])
Inst e2fslibs [1.42-1ubuntu2] (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64]) [e2fsprogs:amd64 on e2fslibs:amd64] [e2fsprogs:amd64 ]
Conf e2fslibs (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64]) [e2fsprogs:amd64 ]
Inst e2fsprogs [1.42-1ubuntu2] (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Conf e2fsprogs (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Inst gpgv [1.4.11-3ubuntu2.7] (1.4.11-3ubuntu2.9 Ubuntu:12.04/precise-updates [amd64])
Conf gpgv (1.4.11-3ubuntu2.9 Ubuntu:12.04/precise-updates [amd64])
Inst gnupg [1.4.11-3ubuntu2.7] (1.4.11-3ubuntu2.9 Ubuntu:12.04/precise-updates [amd64])
Conf gnupg (1.4.11-3ubuntu2.9 Ubuntu:12.04/precise-updates [amd64])
Inst apt [0.8.16~exp12ubuntu10.22] (0.8.16~exp12ubuntu10.26 Ubuntu:12.04/precise-updates [amd64])
Conf apt (0.8.16~exp12ubuntu10.26 Ubuntu:12.04/precise-updates [amd64])
Inst libcomerr2 [1.42-1ubuntu2] (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Conf libcomerr2 (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Inst libss2 [1.42-1ubuntu2] (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Conf libss2 (1.42-1ubuntu2.3 Ubuntu:12.04/precise-updates [amd64])
Inst libssl1.0.0 [1.0.1-4ubuntu5.21] (1.0.1-4ubuntu5.34 Ubuntu:12.04/precise-updates [amd64])
Conf libssl1.0.0 (1.0.1-4ubuntu5.34 Ubuntu:12.04/precise-updates [amd64])
Inst libpcre3 [8.12-4] (8.12-4ubuntu0.1 Ubuntu:12.04/precise-updates [amd64])
Inst libpng12-0 [1.2.46-3ubuntu4] (1.2.46-3ubuntu4.2 Ubuntu:12.04/precise-updates [amd64])
Inst multiarch-support [2.15-0ubuntu10.10] (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64])
Conf multiarch-support (2.15-0ubuntu10.13 Ubuntu:12.04/precise-updates [amd64])
Inst cpio [2.11-7ubuntu3.1] (2.11-7ubuntu3.2 Ubuntu:12.04/precise-updates [amd64])
Inst udev [175-0ubuntu9.9] (175-0ubuntu9.10 Ubuntu:12.04/precise-updates [amd64])
Inst openssl [1.0.1-4ubuntu5.33] (1.0.1-4ubuntu5.34 Ubuntu:12.04/precise-updates [amd64])
Inst ca-certificates [20141019ubuntu0.12.04.1] (20160104ubuntu0.12.04.1 Ubuntu:12.04/precise-updates [all])
Conf libudev0 (175-0ubuntu9.10 Ubuntu:12.04/precise-updates [amd64])
Conf upstart (1.5-0ubuntu7.3 Ubuntu:12.04/precise-updates [amd64])
Conf libpcre3 (8.12-4ubuntu0.1 Ubuntu:12.04/precise-updates [amd64])
Conf libpng12-0 (1.2.46-3ubuntu4.2 Ubuntu:12.04/precise-updates [amd64])
Conf cpio (2.11-7ubuntu3.2 Ubuntu:12.04/precise-updates [amd64])
Conf udev (175-0ubuntu9.10 Ubuntu:12.04/precise-updates [amd64])
Conf openssl (1.0.1-4ubuntu5.34 Ubuntu:12.04/precise-updates [amd64])
Conf ca-certificates (20160104ubuntu0.12.04.1 Ubuntu:12.04/precise-updates [all])`,
21 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.`,
[]string{
"apt",
"ca-certificates",
@@ -391,124 +344,6 @@ The following packages will be upgraded:
ntpdate passwd python3.4 python3.4-minimal rsyslog sudo sysv-rc
sysvinit-utils tzdata udev util-linux
59 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Inst base-files [7.2ubuntu5.2] (7.2ubuntu5.4 Ubuntu:14.04/trusty-updates [amd64])
Conf base-files (7.2ubuntu5.4 Ubuntu:14.04/trusty-updates [amd64])
Inst coreutils [8.21-1ubuntu5.1] (8.21-1ubuntu5.3 Ubuntu:14.04/trusty-updates [amd64])
Conf coreutils (8.21-1ubuntu5.3 Ubuntu:14.04/trusty-updates [amd64])
Inst dpkg [1.17.5ubuntu5.3] (1.17.5ubuntu5.5 Ubuntu:14.04/trusty-updates [amd64])
Conf dpkg (1.17.5ubuntu5.5 Ubuntu:14.04/trusty-updates [amd64])
Inst libc-bin [2.19-0ubuntu6.5] (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Inst libc6 [2.19-0ubuntu6.5] (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Inst libgcc1 [1:4.9.1-0ubuntu1] (1:4.9.3-0ubuntu4 Ubuntu:14.04/trusty-updates [amd64]) []
Inst gcc-4.9-base [4.9.1-0ubuntu1] (4.9.3-0ubuntu4 Ubuntu:14.04/trusty-updates [amd64])
Conf gcc-4.9-base (4.9.3-0ubuntu4 Ubuntu:14.04/trusty-updates [amd64])
Conf libgcc1 (1:4.9.3-0ubuntu4 Ubuntu:14.04/trusty-updates [amd64])
Conf libc6 (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Conf libc-bin (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Inst e2fslibs [1.42.9-3ubuntu1] (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64]) [e2fsprogs:amd64 on e2fslibs:amd64] [e2fsprogs:amd64 ]
Conf e2fslibs (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64]) [e2fsprogs:amd64 ]
Inst e2fsprogs [1.42.9-3ubuntu1] (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Conf e2fsprogs (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Inst login [1:4.1.5.1-1ubuntu9] (1:4.1.5.1-1ubuntu9.2 Ubuntu:14.04/trusty-updates [amd64])
Conf login (1:4.1.5.1-1ubuntu9.2 Ubuntu:14.04/trusty-updates [amd64])
Inst mount [2.20.1-5.1ubuntu20.4] (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf mount (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst tzdata [2015a-0ubuntu0.14.04] (2015g-0ubuntu0.14.04 Ubuntu:14.04/trusty-updates [all])
Conf tzdata (2015g-0ubuntu0.14.04 Ubuntu:14.04/trusty-updates [all])
Inst sysvinit-utils [2.88dsf-41ubuntu6] (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [amd64])
Inst sysv-rc [2.88dsf-41ubuntu6] (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [all])
Conf sysv-rc (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [all])
Conf sysvinit-utils (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [amd64])
Inst util-linux [2.20.1-5.1ubuntu20.4] (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf util-linux (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst gcc-4.8-base [4.8.2-19ubuntu1] (4.8.4-2ubuntu1~14.04.1 Ubuntu:14.04/trusty-updates [amd64]) [libstdc++6:amd64 ]
Conf gcc-4.8-base (4.8.4-2ubuntu1~14.04.1 Ubuntu:14.04/trusty-updates [amd64]) [libstdc++6:amd64 ]
Inst libstdc++6 [4.8.2-19ubuntu1] (4.8.4-2ubuntu1~14.04.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libstdc++6 (4.8.4-2ubuntu1~14.04.1 Ubuntu:14.04/trusty-updates [amd64])
Inst libapt-pkg4.12 [1.0.1ubuntu2.6] (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Conf libapt-pkg4.12 (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Inst gpgv [1.4.16-1ubuntu2.1] (1.4.16-1ubuntu2.3 Ubuntu:14.04/trusty-updates [amd64])
Conf gpgv (1.4.16-1ubuntu2.3 Ubuntu:14.04/trusty-updates [amd64])
Inst gnupg [1.4.16-1ubuntu2.1] (1.4.16-1ubuntu2.3 Ubuntu:14.04/trusty-updates [amd64])
Conf gnupg (1.4.16-1ubuntu2.3 Ubuntu:14.04/trusty-updates [amd64])
Inst apt [1.0.1ubuntu2.6] (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Conf apt (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Inst bsdutils [1:2.20.1-5.1ubuntu20.4] (1:2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf bsdutils (1:2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst passwd [1:4.1.5.1-1ubuntu9] (1:4.1.5.1-1ubuntu9.2 Ubuntu:14.04/trusty-updates [amd64])
Conf passwd (1:4.1.5.1-1ubuntu9.2 Ubuntu:14.04/trusty-updates [amd64])
Inst libuuid1 [2.20.1-5.1ubuntu20.4] (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf libuuid1 (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst libblkid1 [2.20.1-5.1ubuntu20.4] (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf libblkid1 (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst libcomerr2 [1.42.9-3ubuntu1] (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libcomerr2 (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Inst libmount1 [2.20.1-5.1ubuntu20.4] (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Conf libmount1 (2.20.1-5.1ubuntu20.7 Ubuntu:14.04/trusty-updates [amd64])
Inst libpcre3 [1:8.31-2ubuntu2] (1:8.31-2ubuntu2.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libpcre3 (1:8.31-2ubuntu2.1 Ubuntu:14.04/trusty-updates [amd64])
Inst libss2 [1.42.9-3ubuntu1] (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libss2 (1.42.9-3ubuntu1.3 Ubuntu:14.04/trusty-updates [amd64])
Inst libapt-inst1.5 [1.0.1ubuntu2.6] (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Inst libexpat1 [2.1.0-4ubuntu1] (2.1.0-4ubuntu1.1 Ubuntu:14.04/trusty-updates [amd64])
Inst libffi6 [3.1~rc1+r3.0.13-12] (3.1~rc1+r3.0.13-12ubuntu0.1 Ubuntu:14.04/trusty-updates [amd64])
Inst libgcrypt11 [1.5.3-2ubuntu4.1] (1.5.3-2ubuntu4.3 Ubuntu:14.04/trusty-updates [amd64])
Inst libtasn1-6 [3.4-3ubuntu0.1] (3.4-3ubuntu0.3 Ubuntu:14.04/trusty-updates [amd64])
Inst libgnutls-openssl27 [2.12.23-12ubuntu2.1] (2.12.23-12ubuntu2.4 Ubuntu:14.04/trusty-updates [amd64]) []
Inst libgnutls26 [2.12.23-12ubuntu2.1] (2.12.23-12ubuntu2.4 Ubuntu:14.04/trusty-updates [amd64])
Inst libsqlite3-0 [3.8.2-1ubuntu2] (3.8.2-1ubuntu2.1 Ubuntu:14.04/trusty-updates [amd64])
Inst python3.4 [3.4.0-2ubuntu1] (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64]) []
Inst libpython3.4-stdlib [3.4.0-2ubuntu1] (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64]) []
Inst python3.4-minimal [3.4.0-2ubuntu1] (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64]) []
Inst libssl1.0.0 [1.0.1f-1ubuntu2.8] (1.0.1f-1ubuntu2.16 Ubuntu:14.04/trusty-updates [amd64]) []
Inst libpython3.4-minimal [3.4.0-2ubuntu1] (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64])
Inst ntpdate [1:4.2.6.p5+dfsg-3ubuntu2.14.04.2] (1:4.2.6.p5+dfsg-3ubuntu2.14.04.8 Ubuntu:14.04/trusty-updates [amd64])
Inst libdrm2 [2.4.56-1~ubuntu2] (2.4.64-1~ubuntu14.04.1 Ubuntu:14.04/trusty-updates [amd64])
Inst libpng12-0 [1.2.50-1ubuntu2] (1.2.50-1ubuntu2.14.04.2 Ubuntu:14.04/trusty-updates [amd64])
Inst initscripts [2.88dsf-41ubuntu6] (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [amd64])
Inst libcgmanager0 [0.24-0ubuntu7.3] (0.24-0ubuntu7.5 Ubuntu:14.04/trusty-updates [amd64])
Inst udev [204-5ubuntu20.10] (204-5ubuntu20.18 Ubuntu:14.04/trusty-updates [amd64]) []
Inst libudev1 [204-5ubuntu20.10] (204-5ubuntu20.18 Ubuntu:14.04/trusty-updates [amd64])
Inst multiarch-support [2.19-0ubuntu6.5] (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Conf multiarch-support (2.19-0ubuntu6.7 Ubuntu:14.04/trusty-updates [amd64])
Inst apt-utils [1.0.1ubuntu2.6] (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Inst dh-python [1.20140128-1ubuntu8] (1.20140128-1ubuntu8.2 Ubuntu:14.04/trusty-updates [all])
Inst iproute2 [3.12.0-2] (3.12.0-2ubuntu1 Ubuntu:14.04/trusty-updates [amd64])
Inst ifupdown [0.7.47.2ubuntu4.1] (0.7.47.2ubuntu4.3 Ubuntu:14.04/trusty-updates [amd64])
Inst isc-dhcp-client [4.2.4-7ubuntu12] (4.2.4-7ubuntu12.4 Ubuntu:14.04/trusty-updates [amd64]) []
Inst isc-dhcp-common [4.2.4-7ubuntu12] (4.2.4-7ubuntu12.4 Ubuntu:14.04/trusty-updates [amd64])
Inst rsyslog [7.4.4-1ubuntu2.5] (7.4.4-1ubuntu2.6 Ubuntu:14.04/trusty-updates [amd64])
Inst sudo [1.8.9p5-1ubuntu1] (1.8.9p5-1ubuntu1.2 Ubuntu:14.04/trusty-updates [amd64])
Inst cpio [2.11+dfsg-1ubuntu1.1] (2.11+dfsg-1ubuntu1.2 Ubuntu:14.04/trusty-updates [amd64])
Conf libapt-inst1.5 (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Conf libexpat1 (2.1.0-4ubuntu1.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libffi6 (3.1~rc1+r3.0.13-12ubuntu0.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libgcrypt11 (1.5.3-2ubuntu4.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libtasn1-6 (3.4-3ubuntu0.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libgnutls26 (2.12.23-12ubuntu2.4 Ubuntu:14.04/trusty-updates [amd64])
Conf libgnutls-openssl27 (2.12.23-12ubuntu2.4 Ubuntu:14.04/trusty-updates [amd64])
Conf libsqlite3-0 (3.8.2-1ubuntu2.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libssl1.0.0 (1.0.1f-1ubuntu2.16 Ubuntu:14.04/trusty-updates [amd64])
Conf libpython3.4-minimal (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64])
Conf python3.4-minimal (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libpython3.4-stdlib (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64])
Conf python3.4 (3.4.3-1ubuntu1~14.04.3 Ubuntu:14.04/trusty-updates [amd64])
Conf ntpdate (1:4.2.6.p5+dfsg-3ubuntu2.14.04.8 Ubuntu:14.04/trusty-updates [amd64])
Conf libdrm2 (2.4.64-1~ubuntu14.04.1 Ubuntu:14.04/trusty-updates [amd64])
Conf libpng12-0 (1.2.50-1ubuntu2.14.04.2 Ubuntu:14.04/trusty-updates [amd64])
Conf initscripts (2.88dsf-41ubuntu6.3 Ubuntu:14.04/trusty-updates [amd64])
Conf libcgmanager0 (0.24-0ubuntu7.5 Ubuntu:14.04/trusty-updates [amd64])
Conf libudev1 (204-5ubuntu20.18 Ubuntu:14.04/trusty-updates [amd64])
Conf udev (204-5ubuntu20.18 Ubuntu:14.04/trusty-updates [amd64])
Conf apt-utils (1.0.1ubuntu2.11 Ubuntu:14.04/trusty-updates [amd64])
Conf dh-python (1.20140128-1ubuntu8.2 Ubuntu:14.04/trusty-updates [all])
Conf iproute2 (3.12.0-2ubuntu1 Ubuntu:14.04/trusty-updates [amd64])
Conf ifupdown (0.7.47.2ubuntu4.3 Ubuntu:14.04/trusty-updates [amd64])
Conf isc-dhcp-common (4.2.4-7ubuntu12.4 Ubuntu:14.04/trusty-updates [amd64])
Conf isc-dhcp-client (4.2.4-7ubuntu12.4 Ubuntu:14.04/trusty-updates [amd64])
Conf rsyslog (7.4.4-1ubuntu2.6 Ubuntu:14.04/trusty-updates [amd64])
Conf sudo (1.8.9p5-1ubuntu1.2 Ubuntu:14.04/trusty-updates [amd64])
Conf cpio (2.11+dfsg-1ubuntu1.2 Ubuntu:14.04/trusty-updates [amd64])
`,
[]string{
"apt",
@@ -613,7 +448,7 @@ Calculating upgrade... Done
func TestGetChangelogCache(t *testing.T) {
const servername = "server1"
pack := models.PackageInfo{
pack := models.Package{
Name: "apt",
Version: "1.0.0",
NewVersion: "1.0.1",
@@ -624,7 +459,9 @@ func TestGetChangelogCache(t *testing.T) {
Family: "ubuntu",
Release: "16.04",
},
Packs: []models.PackageInfo{pack},
Packs: models.Packages{
"apt": pack,
},
}
const path = "/tmp/vuls-test-cache-11111111.db"

View File

@@ -33,10 +33,10 @@ import (
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"github.com/Sirupsen/logrus"
"github.com/cenkalti/backoff"
conf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/util"
"github.com/sirupsen/logrus"
)
type execResult struct {
@@ -148,6 +148,9 @@ func parallelExec(fn func(osTypeInterface) error, timeoutSec ...int) {
}
func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result execResult) {
logger := getSSHLogger(log...)
logger.Debugf("Executing... %s", strings.Replace(cmd, "\n", "", -1))
if c.Port == "local" &&
(c.Host == "127.0.0.1" || c.Host == "localhost") {
result = localExec(c, cmd, sudo)
@@ -157,7 +160,6 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul
result = sshExecExternal(c, cmd, sudo)
}
logger := getSSHLogger(log...)
logger.Debug(result)
return
}
@@ -165,7 +167,7 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul
func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) {
cmdstr = decorateCmd(c, cmdstr, sudo)
var cmd *ex.Cmd
if c.Distro.Family == "FreeBSD" {
if c.Distro.Family == conf.FreeBSD {
cmd = ex.Command("/bin/sh", "-c", cmdstr)
} else {
cmd = ex.Command("/bin/bash", "-c", cmdstr)

View File

@@ -33,7 +33,14 @@ type bsd struct {
// NewBSD constructor
func newBsd(c config.ServerInfo) *bsd {
d := &bsd{}
d := &bsd{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
@@ -44,13 +51,13 @@ func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
// Prevent from adding `set -o pipefail` option
c.Distro = config.Distro{Family: "FreeBSD"}
c.Distro = config.Distro{Family: config.FreeBSD}
if r := exec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(r.Stdout, "FreeBSD") == true {
if strings.Contains(strings.ToLower(r.Stdout), config.FreeBSD) == true {
if b := exec(c, "freebsd-version", noSudo); b.isSuccess() {
rel := strings.TrimSpace(b.Stdout)
bsd.setDistro("FreeBSD", rel)
bsd.setDistro(config.FreeBSD, rel)
return true, bsd
}
}
@@ -66,28 +73,54 @@ func (o *bsd) checkIfSudoNoPasswd() error {
}
func (o *bsd) checkDependencies() error {
o.log.Infof("Dependencies... No need")
return nil
}
func (o *bsd) scanPackages() error {
var err error
var packs []models.PackageInfo
if packs, err = o.scanInstalledPackages(); err != nil {
o.log.Errorf("Failed to scan installed packages")
// collect the running kernel information
release, version, err := o.runningKernel()
if err != nil {
o.log.Errorf("Failed to scan the running kernel version: %s", err)
return err
}
o.setPackages(packs)
o.Kernel = models.Kernel{
Release: release,
Version: version,
}
var vinfos []models.VulnInfo
if vinfos, err = o.scanUnsecurePackages(); err != nil {
o.log.Errorf("Failed to scan vulnerable packages")
rebootRequired, err := o.rebootRequired()
if err != nil {
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
return err
}
o.setVulnInfos(vinfos)
o.Kernel.RebootRequired = rebootRequired
packs, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
o.Packages = packs
unsecures, err := o.scanUnsecurePackages()
if err != nil {
o.log.Errorf("Failed to scan vulnerable packages: %s", err)
return err
}
o.VulnInfos = unsecures
return nil
}
func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
func (o *bsd) rebootRequired() (bool, error) {
r := o.exec("freebsd-version -k", noSudo)
if !r.isSuccess() {
return false, fmt.Errorf("Failed to SSH: %s", r)
}
return o.Kernel.Release != strings.TrimSpace(r.Stdout), nil
}
func (o *bsd) scanInstalledPackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("pkg version -v")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
@@ -96,7 +129,7 @@ func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
return o.parsePkgVersion(r.Stdout), nil
}
func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
const vulndbPath = "/tmp/vuln.db"
cmd := "rm -f " + vulndbPath
r := o.exec(cmd, noSudo)
@@ -111,7 +144,7 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
}
if r.ExitStatus == 0 {
// no vulnerabilities
return []models.VulnInfo{}, nil
return nil, nil
}
var packAdtRslt []pkgAuditResult
@@ -121,7 +154,7 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
if len(cveIDs) == 0 {
continue
}
pack, found := o.Packages.FindByName(name)
pack, found := o.Packages[name]
if !found {
return nil, fmt.Errorf("Vulnerable package: %s is not found", name)
}
@@ -142,30 +175,38 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
}
}
for k := range cveIDAdtMap {
packs := []models.PackageInfo{}
for _, r := range cveIDAdtMap[k] {
packs = append(packs, r.pack)
vinfos := models.VulnInfos{}
for cveID := range cveIDAdtMap {
packs := models.Packages{}
for _, r := range cveIDAdtMap[cveID] {
packs[r.pack.Name] = r.pack
}
disAdvs := []models.DistroAdvisory{}
for _, r := range cveIDAdtMap[k] {
for _, r := range cveIDAdtMap[cveID] {
disAdvs = append(disAdvs, models.DistroAdvisory{
AdvisoryID: r.vulnIDCveIDs.vulnID,
})
}
vulnInfos = append(vulnInfos, models.VulnInfo{
CveID: k,
Packages: packs,
affected := models.PackageStatuses{}
for name := range packs {
affected = append(affected, models.PackageStatus{
Name: name,
})
}
vinfos[cveID] = models.VulnInfo{
CveID: cveID,
AffectedPackages: affected,
DistroAdvisories: disAdvs,
Confidence: models.PkgAuditMatch,
})
}
}
return
return vinfos, nil
}
func (o *bsd) parsePkgVersion(stdout string) (packs []models.PackageInfo) {
func (o *bsd) parsePkgVersion(stdout string) models.Packages {
packs := models.Packages{}
lines := strings.Split(stdout, "\n")
for _, l := range lines {
fields := strings.Fields(l)
@@ -180,20 +221,26 @@ func (o *bsd) parsePkgVersion(stdout string) (packs []models.PackageInfo) {
switch fields[1] {
case "?", "=":
packs = append(packs, models.PackageInfo{
packs[name] = models.Package{
Name: name,
Version: ver,
})
}
case "<":
candidate := strings.TrimSuffix(fields[6], ")")
packs = append(packs, models.PackageInfo{
packs[name] = models.Package{
Name: name,
Version: ver,
NewVersion: candidate,
})
}
case ">":
o.log.Warn("The installed version of the %s is newer than the current version. *This situation can arise with an out of date index file, or when testing new ports.*", name)
packs[name] = models.Package{
Name: name,
Version: ver,
}
}
}
return
return packs
}
type vulnIDCveIDs struct {
@@ -202,7 +249,7 @@ type vulnIDCveIDs struct {
}
type pkgAuditResult struct {
pack models.PackageInfo
pack models.Package
vulnIDCveIDs vulnIDCveIDs
}

View File

@@ -12,7 +12,7 @@ import (
func TestParsePkgVersion(t *testing.T) {
var tests = []struct {
in string
expected []models.PackageInfo
expected models.Packages
}{
{
`Updating FreeBSD repository catalogue...
@@ -21,27 +21,32 @@ All repositories are up-to-date.
bash-4.2.45 < needs updating (remote has 4.3.42_1)
gettext-0.18.3.1 < needs updating (remote has 0.19.7)
tcl84-8.4.20_2,1 = up-to-date with remote
ntp-4.2.8p8_1 > succeeds port (port has 4.2.8p6)
teTeX-base-3.0_25 ? orphaned: print/teTeX-base`,
[]models.PackageInfo{
{
models.Packages{
"bash": {
Name: "bash",
Version: "4.2.45",
NewVersion: "4.3.42_1",
},
{
"gettext": {
Name: "gettext",
Version: "0.18.3.1",
NewVersion: "0.19.7",
},
{
"tcl84": {
Name: "tcl84",
Version: "8.4.20_2,1",
},
{
"teTeX-base": {
Name: "teTeX-base",
Version: "3.0_25",
},
"ntp": {
Name: "ntp",
Version: "4.2.8p8_1",
},
},
},
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -59,18 +59,13 @@ type osTypeInterface interface {
// osPackages is included by base struct
type osPackages struct {
// installed packages
Packages models.PackageInfoList
Packages models.Packages
// unsecure packages
VulnInfos models.VulnInfos
}
func (p *osPackages) setPackages(pi models.PackageInfoList) {
p.Packages = pi
}
func (p *osPackages) setVulnInfos(vi []models.VulnInfo) {
p.VulnInfos = vi
// kernel information
Kernel models.Kernel
}
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
@@ -421,9 +416,15 @@ func setupChangelogCache() error {
needToSetupCache := false
for _, s := range servers {
switch s.getDistro().Family {
case "ubuntu", "debian", "raspbian":
case config.Raspbian:
needToSetupCache = true
break
case config.Ubuntu, config.Debian:
//TODO changelopg cache for RedHat, Oracle, Amazon, CentOS is not implemented yet.
if config.Conf.Deep {
needToSetupCache = true
}
break
}
}
if needToSetupCache {
@@ -443,6 +444,7 @@ func scanVulns(jsonDir string, scannedAt time.Time, timeoutSec int) error {
for _, s := range append(servers, errServers...) {
r := s.convertToModel()
r.ScannedAt = scannedAt
r.Config.Scan = config.Conf
results = append(results, r)
}

View File

@@ -26,7 +26,7 @@ func (o *unknown) checkIfSudoNoPasswd() error {
return nil
}
func (o unknown) checkDependencies() error {
func (o *unknown) checkDependencies() error {
return nil
}

View File

@@ -6,6 +6,8 @@ This is the Git repo of the official Docker image for vuls.
- go-cve-dictionary
- [`latest` (*go-cve-dictionary:latest Dockerfile*)]()
- goval-dictionary
- [`latest` (*goval-dictionary:latest Dockerfile*)]()
- vuls
- [`latest` (*vuls:latest Dockerfile*)]()
- vulsrepo
@@ -28,6 +30,14 @@ $ docker run --rm vuls/go-cve-dictionary -v
go-cve-dictionary v0.0.xxx xxxx
```
- goval-dictionary
```console
$ docker run --rm vuls/goval-dictionary -v
goval-dictionary v0.0.xxx xxxx
```
- vuls
```console
@@ -44,6 +54,12 @@ vuls v0.0.xxx xxxx
$ docker rmi vuls/go-cve-dictionary
```
- goval-dictionary
```
$ docker rmi vuls/goval-dictionary
```
- vuls
```
@@ -58,6 +74,12 @@ $ docker rmi vuls/vuls
$ docker pull vuls/go-cve-dictionary
```
- goval-dictionary
```
$ docker pull vuls/goval-dictionary
```
- vuls
```
@@ -72,6 +94,12 @@ $ docker run --rm vuls/go-cve-dictionary -v
go-cve-dictionary v0.1.xxx xxxx
```
```console
$ docker run --rm vuls/goval-dictionary -v
goval-dictionary v0.1.xxx xxxx
```
- vuls
```console
@@ -84,6 +112,7 @@ vuls v0.1.xxx xxxx
# How to use this image
1. fetch nvd (vuls/go-cve-dictionary)
1. fetch oval (vuls/goval-dictionary)
1. configuration (vuls/vuls)
1. configtest (vuls/vuls)
1. scan (vuls/vuls)
@@ -100,6 +129,19 @@ $ for i in `seq 2002 $(date +"%Y")`; do \
done
```
- To fetch JVN(Japanese), See [README](https://github.com/kotakanbe/go-cve-dictionary#usage-fetch-jvn-data)
## Step2. Fetch OVAL (e.g. redhat)
```console
$ docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-redhat 5 6 7
```
- To fetch other OVAL, See [README](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat)
## Step2. Configuration
Create config.toml referring to [this](https://github.com/future-architect/vuls#configuration).
@@ -149,6 +191,7 @@ $ docker run --rm -it \
-v /etc/localtime:/etc/localtime:ro \
vuls/vuls report \
-cvedb-path=/vuls/cve.sqlite3 \
-ovaldb-path=/vuls/oval.sqlite3 \
-format-short-text \
-config=./config.toml # path to config.toml in docker
```

View File

@@ -0,0 +1,19 @@
FROM golang:latest
MAINTAINER sadayuki-matsuno
ENV REPOSITORY github.com/kotakanbe/goval-dictionary
ENV LOGDIR /var/log/vuls
ENV WORKDIR /vuls
# goval-dictionary install
RUN git clone https://$REPOSITORY.git $GOPATH/src/$REPOSITORY \
&& cd $GOPATH/src/$REPOSITORY \
&& make install \
&& mkdir -p $LOGDIR
VOLUME [$WORKDIR, $LOGDIR]
WORKDIR $WORKDIR
ENV PWD $WORKDIR
ENTRYPOINT ["goval-dictionary"]
CMD ["--help"]

View File

@@ -0,0 +1,125 @@
# goval-dictionary-Docker
This is the Git repo of the official Docker image for goval-dictionary.
See the [Hub page](https://hub.docker.com/r/vuls/goval-dictionary/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
# Supported tags and respective `Dockerfile` links
- [`latest` (*goval-dictionary:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/goval-dictionary/latest/Dockerfile)
# Caution
This image is built per commit.
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
- Remove old docker image
```
$ docker rmi vuls/goval-dictionary
```
- Pull new docker image
```
$ docker pull vuls/goval-dictionary
```
# What is goval-dictionary?
This is tool to build a local copy of the OVAL. The local copy is generated in sqlite format, and the tool has a server mode for easy querying.
# How to use this image
## check vuls version
```
$ docker run --rm vuls/goval-dictionary -v
```
## fetch-redhat
```console
$ for i in `seq 5 7`; do \
docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-redhat $i; \
done
```
## fetch-debian
```console
$ for i in `seq 7 10`; do \
docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-debian $i; \
done
```
## fetch-ubuntu
```console
$ for i in `seq 12 2 16`; do \
docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-ubuntu $i; \
done
```
## fetch-suse
```console
$ docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-suse -opensuse 13.2
```
## fetch-oracle
```console
$ docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
vuls/goval-dictionary fetch-oracle
```
## server
```console
$ docker run -dt \
--name goval-dictionary \
-v $PWD:/vuls \
-v $PWD/goval-dictionary-log:/var/log/vuls \
--expose 1324 \
-p 1324:1324 \
vuls/goval-dictionary server --bind=0.0.0.0
```
Prease refer to [this](https://hub.docker.com/r/vuls/goval-dictionary).
## vuls
Please refer to [this](https://hub.docker.com/r/vuls/vuls/).
# User Feedback
## Documentation
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
## Contributing
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
1. get original code: go get github.com/future-architect/vuls
1. work on original code
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
1. push your changes: git push myfork
1. create a new Pull Request

View File

@@ -22,8 +22,8 @@ import (
"path/filepath"
"runtime"
"github.com/Sirupsen/logrus"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"github.com/future-architect/vuls/config"
formatter "github.com/kotakanbe/logrus-prefixed-formatter"

View File

@@ -135,3 +135,15 @@ func Truncate(str string, length int) string {
}
return str
}
// Distinct a slice
func Distinct(ss []string) (distincted []string) {
m := map[string]bool{}
for _, s := range ss {
if _, found := m[s]; !found {
m[s] = true
distincted = append(distincted, s)
}
}
return
}