Compare commits

...

75 Commits

Author SHA1 Message Date
Kota Kanbe
83d1f80959 chore(report): remove stride and hipchat support (#1104) 2020-12-26 08:52:45 +09:00
Kota Kanbe
a33cff8f13 fix(reprot): use SQLite3 in current dir if not specified (#1103) 2020-12-26 08:24:17 +09:00
Kota Kanbe
8679759f60 chore: fix typo (#1102) 2020-12-26 08:23:02 +09:00
Kota Kanbe
53deaee3d7 refactor(config): remove DependencyCheckXMLPath in config.toml (#1100) 2020-12-25 06:38:00 +09:00
Kota Kanbe
5a14a58fe4 refactor(nvdxml): Remove codes related to NVD xml(deprecated) (#1099) 2020-12-25 06:16:14 +09:00
Kota Kanbe
fb1fbf8f95 feat(report): Add NVD as a source for mitigations, primarySrc URL and Patch URL (#1097)
* feat(report): Add NVD as a src for mitigations.

* feat(report): display "Vendor Advisory" URL in NVD

* feat(report): display patch urls in report, tui
2020-12-24 08:37:10 +09:00
Kota Kanbe
cfbf779f9b feat(exploit): add exploit link in NVD as a source (#1096)
Added Refs information with NVD's Expoit tag as an information source
for Exploit.
2020-12-16 07:10:18 +09:00
Kota Kanbe
d576b6c6c1 refactor(report): around FillCveInfo (#1095)
* refactor(report): around FillCveInfo

* refacotr(report): around FillCveInfo
2020-12-15 15:48:23 +09:00
Kota Kanbe
514eb71482 fix(server): make config loading same as scan (#1091)
* fix(server): make config loading same as scan

* also remove from report, tui
2020-12-15 04:33:14 +09:00
Kota Kanbe
43ed904db1 fix(deps): update dependencies (#1094)
* fix(dpes): update dependencies

* update go ver

* update go ver

* update go

* update go
2020-12-15 04:32:23 +09:00
Kota Kanbe
0a440ca629 fix(saas): add saas subcmd (#1093) 2020-12-11 16:19:36 +09:00
Kota Kanbe
eff1dbf95b feat(scanner): vuls-scanner binary on release archive (#1092) 2020-12-11 11:05:48 +09:00
Kota Kanbe
9a32a94806 refactor: fix build warnings (#1090) 2020-12-11 06:45:39 +09:00
Shigechika AIKAWA
2534098509 fix(report): wpvulndb poor versioning(#1088) (#1089) 2020-12-11 05:53:41 +09:00
sadayuki-matsuno
9497365758 update pkg (#1087) 2020-12-04 15:57:02 +09:00
Kota Kanbe
101c44c9c0 Change .goreleaser to build binaries for arm, 386, amd64 at release. (#1082)
* fix go-releaser

* add vuls-scanner
2020-11-28 06:39:52 +09:00
Kota Kanbe
ffd745c004 fix a compile error #1083 (#1084) 2020-11-27 15:14:04 +09:00
Kota Kanbe
5fea4eaef8 feat(nocgo): enable to build with CGO_ENABLED=0 (#1080) 2020-11-27 09:55:09 +09:00
Kota Kanbe
1f610043cf feat(scan): IgnoredJSONKyes to clear values in result json #1071 (#1078) 2020-11-20 10:36:36 +09:00
Kota Kanbe
3f8de02683 fix(portscan): to keep backward compatibility before v0.13.0 (#1076) 2020-11-19 16:54:36 +09:00
Kota Kanbe
d02535d053 fix(debian): false negative of kernel cves with rdb backend (#1075)
* fix(debian): false negative of kernel cves with rdb backend

* update golangci.yml

* add --timeout=10m to golangci.yml
2020-11-18 10:32:37 +09:00
Kota Kanbe
75fceff5f7 refactor(report): format-csv (#1072) 2020-11-05 21:10:35 +09:00
gy741
ebd3834a35 add(report) -format-csv option (#1034) 2020-11-05 20:56:19 +09:00
Kota Kanbe
93059b74c3 feat(report): IgnoredJSONKyes to clear values in result json (#1071)
* feat(report): IgnoredJSONKyes to clear values in result json

* fix(report): marshal indent in JSON everytime
2020-11-05 20:13:09 +09:00
Kota Kanbe
2fc3462d35 fix(libscan): update trivy deps (#1070) 2020-11-05 15:38:12 +09:00
Kota Kanbe
f78dab50cb fix(fast-root): affectedProcs, ports bug (#1067) 2020-10-31 14:21:11 +09:00
Norihiro NAKAOKA
edb324c3d9 fix(portscan): ignore loopback address on remote scan (#1062)
* change ignore loop back address on remote scan

* fix test case

* change append simple

* fix format

* set golangci-lint timeout

* Revert "set golangci-lint timeout"

This reverts commit 56b1c7089a.
2020-10-23 16:40:03 +09:00
Norihiro NAKAOKA
83bcca6e66 experimental: add smart(fast, minimum ports, silently) TCP port scanner (#1060)
* add struct ListenPorts

* change parse to models.ListenPorts from string

* change support models.ListenPorts in TUI

* add scanPort template , detectScanDest

* add Test_detectScanDest

* change impl scanPorts template

* fix build error

* change collect scan success address

* add Test_matchListenPorts

* add Test_updatePortStatus

* change display port scan result on tui

* change display scan emoji on report

* Revert "change display scan emoji on report"

This reverts commit e281882cc6.

* add continue

* change display format

* change no use loop label

* remove comment code

* change display

* fix padding

* change refactoring var , fn name

* fix var name

* fix var name

* change eye icon

* change icon

* delete unuse mod
2020-10-19 17:47:20 +09:00
Kota Kanbe
a124518d78 fix: hard-coded version #1057 (#1059) 2020-10-16 20:42:31 +09:00
Alexander Stein
94bf630e29 Expand negative grep match for any error for lib scans. (#1056)
Many thanks 👍 

Sure, that's better.

Note: FreeBSD
find: `find: /var/run/ppp: Permission denied`
2020-10-12 11:30:11 +09:00
shopper
31bb33fd90 ignore apk warning (#1052) 2020-10-12 10:40:01 +09:00
Kota Kanbe
4b680b9960 fix(scan-freebsd): also get installed with pkg info #1042 (#1051)
* fix(scan-freebsd): also get installed with `pkg info` #1042

* fix test
2020-09-12 05:08:41 +09:00
Kota Kanbe
8a8ab8cb18 feat(libscan): enable to scan vulns of libs with pseudo #1035 (#1050) 2020-09-11 13:09:59 +09:00
Kota Kanbe
8146f5fd1b update readme (#1049) 2020-09-11 10:26:57 +09:00
shopper
425c585e47 Support for smtp LOGIN authentication (#1048)
* finished to implement new mail client

* delete email_test.go
2020-09-04 15:45:29 +09:00
Kota Kanbe
4f1578b2d6 [WIP]fix(scan): collect a running version of kernel-devel (#1044)
* fix(scan): collect a running kernel-devel version

* refactor
2020-09-01 14:37:40 +09:00
Norihiro NAKAOKA
7969b343b0 Raspberry Pi OS(Raspbian) scanning using OVAL DB (#1019)
* change: never refer to ChangeLog

* change raspberry pi os use debian oval at report

* change do not use r.Family

* change gost do not use r.Family

* change use r.Family because family has a large impact

* change replace MaineK00n/goval-dictionary@raspberrypi-oval

* note Raspbian Scan Policy

* add Raspbian Changelog support policy

* change grep Package for Raspbian at fast-scan mode

* add changelog preprocessing for Raspbian

* add take note of TODO

* change Changelog fetch part to function

* change error handling

* change solve one TODO

* change make ChangelogDir once

* add comment

* fix oval support Amazon Linux :refs #824

* change to useScannedCves from ovalSupproted

* change confidence for Raspbian

* change skip package for raspbian in OVAL DB

* change separate raspbian implementation from util

* change error, log format

* change print format

* change log format(delete newline)

* change support changelog.(Debian.)gz

* Revert "change support changelog.(Debian.)gz"

This reverts commit 2265a72c67.

* change test chnage.(Debian.)gz

* change support raspbian package(*raspberry*)

* change error format

* fix regexp pattern

* fix typo

* fix changelog cache

* change rename function name

* add TestParseChangelog

* change changelog lenient match for raspbian

* fix test case

* change clog dir support symbolic link, clog save dir name append suffix

* change remove more package for raspberry pi

* fix error handling

* change module update

* change refactoring around identifying raspbian package

* update go module

* update scan image

* update scan image

* change clarify scan mode

* change raspiPackNamePattern and add test case
2020-08-25 14:11:34 +09:00
Kota Kanbe
58cf1f4c8e refactor(typo): fix typos (#1041) 2020-08-24 16:34:32 +09:00
Norihiro NAKAOKA
a5b87af862 delete unnecessary images (#1036)
* delete unnecessary images

* Revert "delete unnecessary images"

This reverts commit 0967e1c522.

* delete unnecessary images
2020-08-21 17:07:20 +09:00
Kota Kanbe
a0e592b934 fix(report): fix segfault while uploading to s3 (#1033) 2020-08-07 10:31:43 +09:00
Kota Kanbe
7eccc538bb fix(msfdb): udpate go-msfdb-deps (#1032) 2020-08-06 16:54:14 +09:00
Kota Kanbe
59daa8570a fix(gost): suppress err logging when unsupported debian (#1031) 2020-08-05 20:05:50 +09:00
Kota Kanbe
3f52d318bc fix(log): suppress err msg if no access priv to logfile (#1029) 2020-07-31 16:55:12 +09:00
takuzoo
11a7a0c934 Display metasploit module information for each detected CVE-IDs (#1011)
* add metasploit

* fix go deps

* fix msf report

* fix msfdb server port number

* delete non-unique msfdb url from fulltext report

* fix(report): validate msfdb config on report (#1)

* fix(msfdb): update deps (go-msfdb)

* version up go-msfdb v0.1.0

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2020-07-03 14:05:07 +09:00
sadayuki-matsuno
89f49b0e29 Fix trivy parser test (#1014)
* fix trivy parser test

* fixed parser data
2020-06-24 17:14:43 +09:00
Kota Kanbe
72457cbf8e bump up version 2020-06-24 10:57:39 +09:00
Kota Kanbe
c11ba27509 fix(libscan): include a lockfile path of libs (#1012) 2020-06-24 10:46:00 +09:00
segatomo
8a611f9ba6 add diff-mode info (#1008) 2020-06-19 16:07:14 +09:00
Kota Kanbe
4a73875e4d bump up version (#1007) 2020-06-17 12:21:26 +09:00
shopper
d9d5e612ff Support ProxyJump option when using ssh command (#1004)
* Add proxyjump func

* Run go mod tidy

* Run make fmt
2020-06-17 12:15:12 +09:00
Kota Kanbe
4d8599e4fc update deps (#1006)
see https://github.com/knqyf263/go-apk-version/pull/1
2020-06-16 07:48:07 +09:00
Norihiro NAKAOKA
59c7061d29 Fix SSH failure due to .ssh/config owner (#1005)
* use -F option, success configtest and scan

* add sshConfigPath in config.toml

* Use sshConfigPath in config.toml when using ssh -F

* change -ssh-config to deprecated

* fix typo

* add sshConfigPath in tomltemplate
2020-06-16 05:48:31 +09:00
segatomo
996557c667 support alpine3.11 (#1002) 2020-06-12 13:42:11 +09:00
ahulab
519fb19a77 Added ReportedAt time for server mode reports (#996)
- Fixes #928
2020-06-11 11:42:04 +09:00
kazuminn
36456cb151 feat(wordpress): Cache WpVulnDB (#989)
* add wpVulnCache

* fix bug

* add test

* fmt

* fix bug

* refactor

* fix bug
2020-06-05 16:08:28 +09:00
sadayuki-matsuno
4ae87cc36c Fix releaser (#988)
* fix releaser

* fix releaser

* fix releaser

* fix releaser

* add 32 bit releaser and add exit code  in cmd

* delete 32 bit releaser

* fix
2020-06-05 15:04:06 +09:00
shopper
b37df89fb1 Support SMTPS when using report -to-email (#991)
* Add smtps func

* Add SMTPS implementation

* fix error message
2020-06-05 14:42:01 +09:00
sadayuki-matsuno
d18e7a751d add trivy parser (#981)
* add trivy parser

* fix test

* format

* add title and summary

* add trivy parse command

* add uploader

* set args by env

* add README

* add err check

* fix

* fix

* fix

* fix test

* update trivy

* refactor

* delete require uuid

* delete uuid from trivy parser

Co-authored-by: Kota Kanbe <kotakanbe@gmail.com>
2020-05-29 18:06:45 +09:00
kazuminn
8d5ea98e50 add -wp-ignore-inactive flag which ignores inactive plugin or themes (#974)
* command

* config

* ignore inactive

* fix

* add test

* fmt

* add unset test

* rename

* add test

* refactor

* fix

* refactor

* refactor

* fix golangci-lint error
2020-05-29 15:27:47 +09:00
Kota Kanbe
835dc08049 fix .golangci.yml 2020-05-27 20:33:57 +09:00
Kota Kanbe
62c9409fe9 add a github actions config (#985)
* add a github actions config

* fix(log): Don't create a log dir when testing

* remove a meaningless test case

* Thanks for everything, Mr, Travys.

* add golangci

* add goreleaser.yml

* add tidy.yml

* add golang-ci

* fix many lint warnings
2020-05-27 20:11:24 +09:00
Kota Kanbe
2374f578ed Bump up version 2020-05-26 09:32:10 +09:00
shopper
34e2f033d8 add kernelnames ubuntu20.04 (#982) 2020-05-22 12:19:07 +09:00
kazuminn
420825cacc remove append (#978) 2020-05-20 13:55:07 +09:00
Kota Kanbe
466ec93d8e bump up version 2020-05-08 17:15:25 +09:00
Kota Kanbe
3f5bb6ab29 fix(scan): alpine detection #965 (#966)
* fix(scan): alpine detection #965

* use knqyf263/go-apk-version
2020-05-08 16:12:01 +09:00
Kota Kanbe
ebe5f858c8 update trivy, and unsupport image scanning feature (#971)
* update trivy, fanal. unsupport image scanning

* Update models/library.go

Co-authored-by: Teppei Fukuda <teppei@elab.ic.i.u-tokyo.ac.jp>

* add -no-progress flag to report/tui cmd

* Display trivy vuln info to tui/report

* add detection method to vulninfo detected by trivy

* fix(uuid): change uuid lib to go-uuid #929 (#969)

* update trivy, fanal. unsupport image scanning

* Update models/library.go

Co-authored-by: Teppei Fukuda <teppei@elab.ic.i.u-tokyo.ac.jp>

* add -no-progress flag to report/tui cmd

* Display trivy vuln info to tui/report

* add detection method to vulninfo detected by trivy

* unique ref links in TUI

* download trivy DB only when lock file is specified in config.toml

Co-authored-by: Teppei Fukuda <teppei@elab.ic.i.u-tokyo.ac.jp>
2020-05-08 15:24:39 +09:00
Kota Kanbe
9dd025437b fix(uuid): change uuid lib to go-uuid #929 (#969) 2020-05-06 14:14:07 +09:00
Wagde Zabit
c0ebac305a composer.lock insteaad of composer.json (#973)
Co-authored-by: Wagde Zabit <wagde@orcasecurity.io>
2020-05-01 15:20:33 +09:00
Kota Kanbe
1f23ab7ba4 Bump up version 2020-04-28 14:27:46 +09:00
Kota Kanbe
ea3b63998d fix(report): GitHub Security Alerts Integration (#970) 2020-04-28 14:26:37 +09:00
Kota Kanbe
3093426458 fix(logging): panic if no write permission #949 (#968) 2020-04-27 17:37:30 +09:00
Kota Kanbe
37716feac7 refactor(lint): fix lint warnings (#967) 2020-04-27 17:02:27 +09:00
Kota Kanbe
56b12c38d2 fix(config): not working with empty config #962 (#963) 2020-04-23 10:50:35 +09:00
Kota Kanbe
749ead5d4a update go mod (#960) 2020-04-20 21:33:11 +09:00
127 changed files with 11193 additions and 8446 deletions

29
.github/workflows/golangci.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: golangci-lint
on:
push:
tags:
- v*
branches:
- master
pull_request:
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.32
args: --timeout=10m
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true

31
.github/workflows/goreleaser.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: goreleaser
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Unshallow
run: git fetch --prune --unshallow
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

21
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Test
on: [pull_request]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.15.x
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test
run: make test

22
.github/workflows/tidy.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: go-mod-tidy-pr
on:
schedule:
- cron: "0 0 * * 1" # Weekly build
jobs:
go-mod-tidy-pr:
name: go-mod-tidy-pr
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run go-mod-tidy-pr
uses: sue445/go-mod-tidy-pr@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
git_user_name: kotakanbe
git_user_email: kotakanbe@gmail.com
go_version: 1.15.6

3
.gitignore vendored
View File

@@ -1,4 +1,3 @@
vuls
.vscode
*.txt
*.json
@@ -15,4 +14,4 @@ results/
!setup/docker/*
.DS_Store
dist/
.idea
.idea

17
.golangci.yml Normal file
View File

@@ -0,0 +1,17 @@
name: golang-ci
linters-settings:
errcheck:
#exclude: /path/to/file.txt
linters:
disable-all: true
enable:
- goimports
- golint
- govet
- misspell
- errcheck
- staticcheck
- prealloc
- ineffassign

View File

@@ -6,19 +6,105 @@ release:
owner: future-architect
name: vuls
builds:
- goos:
- id: vuls
goos:
- linux
goarch:
- amd64
main: .
main: ./cmd/vuls/main.go
flags:
- -a
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
- -a
ldflags:
- -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
binary: vuls
archive:
- id: vuls-scanner
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- 386
- amd64
- arm
- arm64
main: ./cmd/scanner/main.go
flags:
- -a
- -tags=scanner
ldflags:
- -s -w -X github.com/future-architect/vuls/config.Version={{.Version}} -X github.com/future-architect/vuls/config.Revision={{.Commit}}-{{ .CommitDate }}
binary: vuls-scanner
- id: trivy-to-vuls
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- 386
- amd64
- arm
- arm64
main: ./contrib/trivy/cmd/main.go
binary: trivy-to-vuls
- id: future-vuls
env:
- CGO_ENABLED=0
goos:
- linux
goarch:
- 386
- amd64
- arm
- arm64
flags:
- -a
- -tags=scanner
main: ./contrib/future-vuls/cmd/main.go
binary: future-vuls
archives:
- id: vuls
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
builds:
- vuls
format: tar.gz
files:
- LICENSE
- NOTICE
- README*
- CHANGELOG.md
- id: vuls-scanner
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
builds:
- vuls-scanner
format: tar.gz
files:
- LICENSE
- NOTICE
- README*
- CHANGELOG.md
- id: trivy-to-vuls
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
builds:
- trivy-to-vuls
format: tar.gz
files:
- LICENSE
- NOTICE
- README*
- CHANGELOG.md
- id: future-vuls
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
builds:
- future-vuls
format: tar.gz
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- LICENSE
- NOTICE

View File

@@ -1,7 +0,0 @@
language: go
go:
- "1.13.x"
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

View File

@@ -11,7 +11,7 @@ COPY . $GOPATH/src/$REPOSITORY
RUN cd $GOPATH/src/$REPOSITORY && make install
FROM alpine:3.7
FROM alpine:3.11
MAINTAINER hikachan sadayuki-matsuno

View File

@@ -20,19 +20,26 @@ BUILDTIME := $(shell date "+%Y%m%d_%H%M%S")
LDFLAGS := -X 'github.com/future-architect/vuls/config.Version=$(VERSION)' \
-X 'github.com/future-architect/vuls/config.Revision=build-$(BUILDTIME)_$(REVISION)'
GO := GO111MODULE=on go
CGO_UNABLED := CGO_ENABLED=0 go
GO_OFF := GO111MODULE=off go
all: build
build: main.go pretest fmt
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls $<
build: ./cmd/vuls/main.go pretest fmt
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
b: main.go pretest fmt
$(GO) build -ldflags "$(LDFLAGS)" -o vuls $<
b: ./cmd/vuls/main.go
$(GO) build -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/vuls
install: main.go pretest
$(GO) install -ldflags "$(LDFLAGS)"
install: ./cmd/vuls/main.go pretest fmt
$(GO) install -ldflags "$(LDFLAGS)" ./cmd/vuls
build-scanner: ./cmd/scanner/main.go pretest fmt
$(CGO_UNABLED) build -tags=scanner -a -ldflags "$(LDFLAGS)" -o vuls ./cmd/scanner
install-scanner: ./cmd/scanner/main.go pretest fmt
$(CGO_UNABLED) install -tags=scanner -ldflags "$(LDFLAGS)" ./cmd/scanner
lint:
$(GO_OFF) get -u golang.org/x/lint/golint
@@ -66,3 +73,10 @@ cov:
clean:
echo $(PKGS) | xargs go clean || exit;
# trivy-to-vuls
build-trivy-to-vuls: pretest fmt
$(GO) build -o trivy-to-vuls contrib/trivy/cmd/*.go
# future-vuls
build-future-vuls: pretest fmt
$(GO) build -o future-vuls contrib/future-vuls/cmd/*.go

View File

@@ -9,7 +9,7 @@
![Vuls-logo](img/vuls_logo.png)
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
Vulnerability scanner for Linux/FreeBSD, agent-less, written in Go.
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
Twitter: [@vuls_en](https://twitter.com/vuls_en)
@@ -23,20 +23,6 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)
----
## NEWS
| Version | Main Feature | Date |
|:------------|:---------------------------------|:--------------------|
| [v0.8.0](https://github.com/future-architect/vuls/releases/tag/v0.8.0) | secret | Coming soon |
| [v0.7.0](https://github.com/future-architect/vuls/releases/tag/v0.7.0) | WordPress Vulnerability Scan | 2019/Apr/8 |
| [v0.6.3](https://github.com/future-architect/vuls/releases/tag/v0.6.3) | GitHub Integration | 2019/Feb/20 |
| [v0.6.2](https://github.com/future-architect/vuls/releases/tag/v0.6.2) | Add US-CERT/JPCERT Alerts as VulnSrc | 2019/Jan/23 |
| [v0.6.1](https://github.com/future-architect/vuls/releases/tag/v0.6.1) | BugFix | 2018/Nov/16 |
| [v0.6.0](https://github.com/future-architect/vuls/releases/tag/v0.6.0) | Add ExploitDB as VulnSrc | 2018/Nov/3 |
| [v0.5.0](https://github.com/future-architect/vuls/releases/tag/v0.5.0) | Scan accuracy improvement | 2018/Aug/27 |
----
## Abstract
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
@@ -66,36 +52,47 @@ Vuls is a tool created to solve the problems listed above. It has the following
- Alpine, Amazon Linux, CentOS, Debian, Oracle Linux, Raspbian, RHEL, SUSE Enterprise Linux, and Ubuntu
- FreeBSD
- Cloud, on-premise, Docker Container and Docker Image
- Cloud, on-premise, Running Docker Container
### High-quality scan
Vuls uses multiple vulnerability databases
- Vulnerability Database
- [NVD](https://nvd.nist.gov/)
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
- [NVD](https://nvd.nist.gov/)
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
- OVAL
- [Red Hat](https://www.redhat.com/security/data/oval/)
- [Debian](https://www.debian.org/security/oval/)
- [Oracle Linux](https://linux.oracle.com/security/oval/)
- [RedHat](https://www.redhat.com/security/data/oval/)
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
- [Ubuntu](https://people.canonical.com/~ubuntu-security/oval/)
- [SUSE](http://ftp.suse.com/pub/projects/security/oval/)
- [Oracle Linux](https://linux.oracle.com/security/oval/)
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
- Commands (yum, zypper, and pkg-audit)
- RHSA/ALAS/ELSA/FreeBSD-SA
- [Exploit Database](https://www.exploit-db.com/)
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
- [WPVulnDB](https://wpvulndb.com/api)
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
- Changelog
- Security Advisory
- [Alpine-secdb](https://git.alpinelinux.org/cgit/alpine-secdb/)
- [Red Hat Security Advisories](https://access.redhat.com/security/security-updates/)
- [Debian Security Bug Tracker](https://security-tracker.debian.org/tracker/)
- Commands(yum, zypper, pkg-audit)
- RHSA / ALAS / ELSA / FreeBSD-SA
- Changelog
- PoC, Exploit
- [Exploit Database](https://www.exploit-db.com/)
- [Metasploit-Framework modules](https://www.rapid7.com/db/?q=&type=metasploit)
- CERT
- [US-CERT](https://www.us-cert.gov/ncas/alerts)
- [JPCERT](http://www.jpcert.or.jp/at/2019.html)
- Libraries
- [Node.js Security Working Group](https://github.com/nodejs/security-wg)
- [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
- [Safety DB(Python)](https://github.com/pyupio/safety-db)
- [PHP Security Advisories Database](https://github.com/FriendsOfPHP/security-advisories)
- [RustSec Advisory Database](https://github.com/RustSec/advisory-db)
- WordPress
- [WPVulnDB](https://wpvulndb.com/api)
### Scan mode
@@ -134,17 +131,6 @@ Vuls uses multiple vulnerability databases
- It is possible to acquire the state of the server by connecting via SSH and executing the command.
- Vuls warns when the scan target server was updated the kernel etc. but not restarting it.
### **Static** Analysis
Vuls v0.8.0 can scan Docker images using [knqyf263/trivy](https://github.com/knqyf263/trivy).
Following Registry supported.
- ECR
- GCR
- Local Image
For details, see [Scan docker image](https://vuls.io/docs/en/tutorial-scan-docker-image.html)
### Scan vulnerabilities of non-OS-packages
- Libraries of programming language
@@ -182,7 +168,7 @@ Vuls has some options to detect the vulnerabilities
## Document
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)
[日本語翻訳ドキュメント](https://vuls.io/ja/)
----
@@ -193,12 +179,6 @@ kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these
----
## Change Log
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
----
## Stargazers over time
[![Stargazers over time](https://starcharts.herokuapp.com/future-architect/vuls.svg)](https://starcharts.herokuapp.com/future-architect/vuls)

2
cache/bolt.go vendored
View File

@@ -141,7 +141,7 @@ func (b Bolt) PrettyPrint(meta Meta) error {
})
}
// GetChangelog get the changelgo of specified packName from the Bucket
// GetChangelog get the changelog of specified packName from the Bucket
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
err = b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))

4
cache/bolt_test.go vendored
View File

@@ -46,7 +46,7 @@ func TestSetupBolt(t *testing.T) {
t.Errorf("Failed to open bolt: %s", err)
}
db.View(func(tx *bolt.Tx) error {
_ = db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(metabucket))
if bkt == nil {
t.Errorf("Meta bucket nof found")
@@ -87,7 +87,7 @@ func TestEnsureBuckets(t *testing.T) {
if err != nil {
t.Errorf("Failed to open bolt: %s", err)
}
db.View(func(tx *bolt.Tx) error {
_ = db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
t.Errorf("Meta bucket nof found")

36
cmd/scanner/main.go Normal file
View File

@@ -0,0 +1,36 @@
package main
import (
"flag"
"fmt"
"os"
"context"
"github.com/future-architect/vuls/config"
commands "github.com/future-architect/vuls/subcmds"
"github.com/google/subcommands"
)
func main() {
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.Register(subcommands.FlagsCommand(), "")
subcommands.Register(subcommands.CommandsCommand(), "")
subcommands.Register(&commands.DiscoverCmd{}, "discover")
subcommands.Register(&commands.ScanCmd{}, "scan")
subcommands.Register(&commands.HistoryCmd{}, "history")
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
subcommands.Register(&commands.SaaSCmd{}, "saas")
var v = flag.Bool("v", false, "Show version")
flag.Parse()
if *v {
fmt.Printf("vuls %s %s\n", config.Version, config.Revision)
os.Exit(int(subcommands.ExitSuccess))
}
ctx := context.Background()
os.Exit(int(subcommands.Execute(ctx)))
}

View File

@@ -7,8 +7,8 @@ import (
"context"
"github.com/future-architect/vuls/commands"
"github.com/future-architect/vuls/config"
commands "github.com/future-architect/vuls/subcmds"
"github.com/google/subcommands"
)

View File

@@ -10,14 +10,13 @@ import (
"strings"
syslog "github.com/RackSec/srslog"
"github.com/aquasecurity/fanal/types"
valid "github.com/asaskevich/govalidator"
log "github.com/sirupsen/logrus"
"golang.org/x/xerrors"
)
// Version of Vuls
var Version = "0.9.3"
var Version = "`make build` or `make install` will show the version"
// Revision of Git
var Revision string
@@ -76,7 +75,7 @@ const (
)
const (
// ServerTypePseudo is used for ServerInfo.Type
// ServerTypePseudo is used for ServerInfo.Type, r.Family
ServerTypePseudo = "pseudo"
)
@@ -90,6 +89,7 @@ type Config struct {
ResultsDir string `json:"resultsDir,omitempty"`
Pipe bool `json:"pipe,omitempty"`
Quiet bool `json:"quiet,omitempty"`
NoProgress bool `json:"noProgress,omitempty"`
Default ServerInfo `json:"default,omitempty"`
Servers map[string]ServerInfo `json:"servers,omitempty"`
@@ -103,20 +103,22 @@ type Config struct {
SSHConfig bool `json:"sshConfig,omitempty"`
ContainersOnly bool `json:"containersOnly,omitempty"`
ImagesOnly bool `json:"imagesOnly,omitempty"`
LibsOnly bool `json:"libsOnly,omitempty"`
WordPressOnly bool `json:"wordpressOnly,omitempty"`
SkipBroken bool `json:"skipBroken,omitempty"`
CacheDBPath string `json:"cacheDBPath,omitempty"`
Vvv bool `json:"vvv,omitempty"`
UUID bool `json:"uuid,omitempty"`
DetectIPS bool `json:"detectIps,omitempty"`
CacheDBPath string `json:"cacheDBPath,omitempty"`
TrivyCacheDBDir string `json:"trivyCacheDBDir,omitempty"`
CveDict GoCveDictConf `json:"cveDict,omitempty"`
OvalDict GovalDictConf `json:"ovalDict,omitempty"`
Gost GostConf `json:"gost,omitempty"`
Exploit ExploitConf `json:"exploit,omitempty"`
SkipBroken bool `json:"skipBroken,omitempty"`
Vvv bool `json:"vvv,omitempty"`
UUID bool `json:"uuid,omitempty"`
DetectIPS bool `json:"detectIps,omitempty"`
CveDict GoCveDictConf `json:"cveDict,omitempty"`
OvalDict GovalDictConf `json:"ovalDict,omitempty"`
Gost GostConf `json:"gost,omitempty"`
Exploit ExploitConf `json:"exploit,omitempty"`
Metasploit MetasploitConf `json:"metasploit,omitempty"`
Slack SlackConf `json:"-"`
EMail SMTPConf `json:"-"`
@@ -124,16 +126,12 @@ type Config struct {
Syslog SyslogConf `json:"-"`
AWS AWS `json:"-"`
Azure Azure `json:"-"`
Stride StrideConf `json:"-"`
HipChat HipChatConf `json:"-"`
ChatWork ChatWorkConf `json:"-"`
Telegram TelegramConf `json:"-"`
Saas SaasConf `json:"-"`
RefreshCve bool `json:"refreshCve,omitempty"`
ToSlack bool `json:"toSlack,omitempty"`
ToStride bool `json:"toStride,omitempty"`
ToHipChat bool `json:"toHipChat,omitempty"`
ToChatWork bool `json:"toChatWork,omitempty"`
ToTelegram bool `json:"ToTelegram,omitempty"`
ToEmail bool `json:"toEmail,omitempty"`
@@ -149,8 +147,10 @@ type Config struct {
FormatOneLineText bool `json:"formatOneLineText,omitempty"`
FormatList bool `json:"formatList,omitempty"`
FormatFullText bool `json:"formatFullText,omitempty"`
FormatCsvList bool `json:"formatCsvList,omitempty"`
GZIP bool `json:"gzip,omitempty"`
Diff bool `json:"diff,omitempty"`
WpIgnoreInactive bool `json:"wpIgnoreInactive,omitempty"`
}
// ValidateOnConfigtest validates
@@ -243,6 +243,10 @@ func (c Config) ValidateOnReportDB() bool {
errs = append(errs, err)
}
if err := validateDB("msfdb", c.Metasploit.Type, c.Metasploit.SQLite3Path, c.Metasploit.URL); err != nil {
errs = append(errs, err)
}
for _, err := range errs {
log.Error(err)
}
@@ -274,18 +278,10 @@ func (c Config) ValidateOnReport() bool {
errs = append(errs, slackerrs...)
}
if hipchaterrs := c.HipChat.Validate(); 0 < len(hipchaterrs) {
errs = append(errs, hipchaterrs...)
}
if chatworkerrs := c.ChatWork.Validate(); 0 < len(chatworkerrs) {
errs = append(errs, chatworkerrs...)
}
if strideerrs := c.Stride.Validate(); 0 < len(strideerrs) {
errs = append(errs, strideerrs...)
}
if telegramerrs := c.Telegram.Validate(); 0 < len(telegramerrs) {
errs = append(errs, telegramerrs...)
}
@@ -433,33 +429,6 @@ func (c *SMTPConf) Validate() (errs []error) {
return
}
// StrideConf is stride config
type StrideConf struct {
HookURL string `json:"-"`
AuthToken string `json:"-"`
}
// Validate validates configuration
func (c *StrideConf) Validate() (errs []error) {
if !Conf.ToStride {
return
}
if len(c.HookURL) == 0 {
errs = append(errs, xerrors.New("stride.HookURL must not be empty"))
}
if len(c.AuthToken) == 0 {
errs = append(errs, xerrors.New("stride.AuthToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}
// SlackConf is slack config
type SlackConf struct {
HookURL string `valid:"url" json:"-" toml:"hookURL,omitempty"`
@@ -503,32 +472,6 @@ func (c *SlackConf) Validate() (errs []error) {
return
}
// HipChatConf is HipChat config
type HipChatConf struct {
AuthToken string `json:"-"`
Room string `json:"-"`
}
// Validate validates configuration
func (c *HipChatConf) Validate() (errs []error) {
if !Conf.ToHipChat {
return
}
if len(c.Room) == 0 {
errs = append(errs, xerrors.New("hipcaht.room must not be empty"))
}
if len(c.AuthToken) == 0 {
errs = append(errs, xerrors.New("hipcaht.AuthToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
}
return
}
// ChatWorkConf is ChatWork config
type ChatWorkConf struct {
APIToken string `json:"-"`
@@ -541,11 +484,11 @@ func (c *ChatWorkConf) Validate() (errs []error) {
return
}
if len(c.Room) == 0 {
errs = append(errs, xerrors.New("chatworkcaht.room must not be empty"))
errs = append(errs, xerrors.New("chatWorkConf.room must not be empty"))
}
if len(c.APIToken) == 0 {
errs = append(errs, xerrors.New("chatworkcaht.ApiToken must not be empty"))
errs = append(errs, xerrors.New("chatWorkConf.ApiToken must not be empty"))
}
_, err := valid.ValidateStruct(c)
@@ -581,9 +524,9 @@ func (c *TelegramConf) Validate() (errs []error) {
return
}
// SaasConf is stride config
// SaasConf is FutureVuls config
type SaasConf struct {
GroupID int `json:"-"`
GroupID int64 `json:"-"`
Token string `json:"-"`
URL string `json:"-"`
}
@@ -752,16 +695,15 @@ func (c *HTTPConf) Validate() (errs []error) {
const httpKey = "VULS_HTTP_URL"
// Overwrite set options with the following priority.
// 1. Command line option
// 2. Environment variable
// 3. config.toml
func (c *HTTPConf) Overwrite(cmdOpt HTTPConf) {
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (c *HTTPConf) Init(toml HTTPConf) {
if os.Getenv(httpKey) != "" {
c.URL = os.Getenv(httpKey)
}
if cmdOpt.URL != "" {
c.URL = cmdOpt.URL
if toml.URL != "" {
c.URL = toml.URL
}
}
@@ -791,11 +733,10 @@ const cveDBType = "CVEDB_TYPE"
const cveDBURL = "CVEDB_URL"
const cveDBPATH = "CVEDB_SQLITE3_PATH"
// Overwrite set options with the following priority.
// 1. Command line option
// 2. Environment variable
// 3. config.toml
func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GoCveDictConf) Init() {
if os.Getenv(cveDBType) != "" {
cnf.Type = os.Getenv(cveDBType)
}
@@ -805,16 +746,6 @@ func (cnf *GoCveDictConf) Overwrite(cmdOpt GoCveDictConf) {
if os.Getenv(cveDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(cveDBPATH)
}
if cmdOpt.Type != "" {
cnf.Type = cmdOpt.Type
}
if cmdOpt.URL != "" {
cnf.URL = cmdOpt.URL
}
if cmdOpt.SQLite3Path != "" {
cnf.SQLite3Path = cmdOpt.SQLite3Path
}
cnf.setDefault()
}
@@ -850,11 +781,10 @@ const govalType = "OVALDB_TYPE"
const govalURL = "OVALDB_URL"
const govalPATH = "OVALDB_SQLITE3_PATH"
// Overwrite set options with the following priority.
// 1. Command line option
// 2. Environment variable
// 3. config.toml
func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GovalDictConf) Init() {
if os.Getenv(govalType) != "" {
cnf.Type = os.Getenv(govalType)
}
@@ -864,16 +794,6 @@ func (cnf *GovalDictConf) Overwrite(cmdOpt GovalDictConf) {
if os.Getenv(govalPATH) != "" {
cnf.SQLite3Path = os.Getenv(govalPATH)
}
if cmdOpt.Type != "" {
cnf.Type = cmdOpt.Type
}
if cmdOpt.URL != "" {
cnf.URL = cmdOpt.URL
}
if cmdOpt.SQLite3Path != "" {
cnf.SQLite3Path = cmdOpt.SQLite3Path
}
cnf.setDefault()
}
@@ -908,11 +828,10 @@ const gostDBType = "GOSTDB_TYPE"
const gostDBURL = "GOSTDB_URL"
const gostDBPATH = "GOSTDB_SQLITE3_PATH"
// Overwrite set options with the following priority.
// 1. Command line option
// 2. Environment variable
// 3. config.toml
func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *GostConf) Init() {
if os.Getenv(gostDBType) != "" {
cnf.Type = os.Getenv(gostDBType)
}
@@ -922,16 +841,6 @@ func (cnf *GostConf) Overwrite(cmdOpt GostConf) {
if os.Getenv(gostDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(gostDBPATH)
}
if cmdOpt.Type != "" {
cnf.Type = cmdOpt.Type
}
if cmdOpt.URL != "" {
cnf.URL = cmdOpt.URL
}
if cmdOpt.SQLite3Path != "" {
cnf.SQLite3Path = cmdOpt.SQLite3Path
}
cnf.setDefault()
}
@@ -966,11 +875,10 @@ const exploitDBType = "EXPLOITDB_TYPE"
const exploitDBURL = "EXPLOITDB_URL"
const exploitDBPATH = "EXPLOITDB_SQLITE3_PATH"
// Overwrite set options with the following priority.
// 1. Command line option
// 2. Environment variable
// 3. config.toml
func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *ExploitConf) Init() {
if os.Getenv(exploitDBType) != "" {
cnf.Type = os.Getenv(exploitDBType)
}
@@ -980,16 +888,6 @@ func (cnf *ExploitConf) Overwrite(cmdOpt ExploitConf) {
if os.Getenv(exploitDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(exploitDBPATH)
}
if cmdOpt.Type != "" {
cnf.Type = cmdOpt.Type
}
if cmdOpt.URL != "" {
cnf.URL = cmdOpt.URL
}
if cmdOpt.SQLite3Path != "" {
cnf.SQLite3Path = cmdOpt.SQLite3Path
}
cnf.setDefault()
}
@@ -998,6 +896,53 @@ func (cnf *ExploitConf) IsFetchViaHTTP() bool {
return Conf.Exploit.Type == "http"
}
// MetasploitConf is metasploit config
type MetasploitConf struct {
// DB type for metasploit dictionary (sqlite3, mysql, postgres or redis)
Type string
// http://metasploit-dictionary.com:1324 or DB connection string
URL string `json:"-"`
// /path/to/metasploit.sqlite3
SQLite3Path string `json:"-"`
}
func (cnf *MetasploitConf) setDefault() {
if cnf.Type == "" {
cnf.Type = "sqlite3"
}
if cnf.URL == "" && cnf.SQLite3Path == "" {
wd, _ := os.Getwd()
cnf.SQLite3Path = filepath.Join(wd, "go-msfdb.sqlite3")
}
}
const metasploitDBType = "METASPLOITDB_TYPE"
const metasploitDBURL = "METASPLOITDB_URL"
const metasploitDBPATH = "METASPLOITDB_SQLITE3_PATH"
// Init set options with the following priority.
// 1. Environment variable
// 2. config.toml
func (cnf *MetasploitConf) Init() {
if os.Getenv(metasploitDBType) != "" {
cnf.Type = os.Getenv(metasploitDBType)
}
if os.Getenv(metasploitDBURL) != "" {
cnf.URL = os.Getenv(metasploitDBURL)
}
if os.Getenv(metasploitDBPATH) != "" {
cnf.SQLite3Path = os.Getenv(metasploitDBPATH)
}
cnf.setDefault()
}
// IsFetchViaHTTP returns wether fetch via http
func (cnf *MetasploitConf) IsFetchViaHTTP() bool {
return Conf.Metasploit.Type == "http"
}
// AWS is aws config
type AWS struct {
// AWS profile to use
@@ -1030,44 +975,42 @@ type Azure struct {
// ServerInfo has SSH Info, additional CPE packages to scan.
type ServerInfo struct {
ServerName string `toml:"-" json:"serverName,omitempty"`
User string `toml:"user,omitempty" json:"user,omitempty"`
Host string `toml:"host,omitempty" json:"host,omitempty"`
Port string `toml:"port,omitempty" json:"port,omitempty"`
KeyPath string `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
KeyPassword string `json:"-,omitempty" toml:"-"`
CpeNames []string `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
ScanMode []string `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
DependencyCheckXMLPath string `toml:"dependencyCheckXMLPath,omitempty" json:"-"` // TODO Deprecated remove in near future
OwaspDCXMLPath string `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"`
ContainersIncluded []string `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
ContainersExcluded []string `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
ContainerType string `toml:"containerType,omitempty" json:"containerType,omitempty"`
Containers map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"`
IgnoreCves []string `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
IgnorePkgsRegexp []string `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
Images map[string]Image `toml:"images" json:"images,omitempty"`
UUIDs map[string]string `toml:"uuids,omitempty" json:"uuids,omitempty"`
Memo string `toml:"memo,omitempty" json:"memo,omitempty"`
Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
Optional map[string]interface{} `toml:"optional,omitempty" json:"optional,omitempty"` // Optional key-value set that will be outputted to JSON
Lockfiles []string `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"` // ie) path/to/package-lock.json
FindLock bool `toml:"findLock,omitempty" json:"findLock,omitempty"`
Type string `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
ServerName string `toml:"-" json:"serverName,omitempty"`
User string `toml:"user,omitempty" json:"user,omitempty"`
Host string `toml:"host,omitempty" json:"host,omitempty"`
JumpServer []string `toml:"jumpServer,omitempty" json:"jumpServer,omitempty"`
Port string `toml:"port,omitempty" json:"port,omitempty"`
SSHConfigPath string `toml:"sshConfigPath,omitempty" json:"sshConfigPath,omitempty"`
KeyPath string `toml:"keyPath,omitempty" json:"keyPath,omitempty"`
KeyPassword string `json:"-,omitempty" toml:"-"`
CpeNames []string `toml:"cpeNames,omitempty" json:"cpeNames,omitempty"`
ScanMode []string `toml:"scanMode,omitempty" json:"scanMode,omitempty"`
OwaspDCXMLPath string `toml:"owaspDCXMLPath,omitempty" json:"owaspDCXMLPath,omitempty"`
ContainersIncluded []string `toml:"containersIncluded,omitempty" json:"containersIncluded,omitempty"`
ContainersExcluded []string `toml:"containersExcluded,omitempty" json:"containersExcluded,omitempty"`
ContainerType string `toml:"containerType,omitempty" json:"containerType,omitempty"`
Containers map[string]ContainerSetting `toml:"containers" json:"containers,omitempty"`
IgnoreCves []string `toml:"ignoreCves,omitempty" json:"ignoreCves,omitempty"`
IgnorePkgsRegexp []string `toml:"ignorePkgsRegexp,omitempty" json:"ignorePkgsRegexp,omitempty"`
GitHubRepos map[string]GitHubConf `toml:"githubs" json:"githubs,omitempty"` // key: owner/repo
UUIDs map[string]string `toml:"uuids,omitempty" json:"uuids,omitempty"`
Memo string `toml:"memo,omitempty" json:"memo,omitempty"`
Enablerepo []string `toml:"enablerepo,omitempty" json:"enablerepo,omitempty"` // For CentOS, RHEL, Amazon
Optional map[string]interface{} `toml:"optional,omitempty" json:"optional,omitempty"` // Optional key-value set that will be outputted to JSON
Lockfiles []string `toml:"lockfiles,omitempty" json:"lockfiles,omitempty"` // ie) path/to/package-lock.json
FindLock bool `toml:"findLock,omitempty" json:"findLock,omitempty"`
Type string `toml:"type,omitempty" json:"type,omitempty"` // "pseudo" or ""
WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
IgnoredJSONKeys []string `toml:"ignoredJSONKeys,omitempty" json:"ignoredJSONKeys,omitempty"`
WordPress WordPressConf `toml:"wordpress,omitempty" json:"wordpress,omitempty"`
// used internal
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
Container Container `toml:"-" json:"-"`
Image Image `toml:"-" json:"-"`
Distro Distro `toml:"-" json:"-"`
Mode ScanMode `toml:"-" json:"-"`
// internal use
IPv4Addrs []string `toml:"-" json:"ipv4Addrs,omitempty"`
IPv6Addrs []string `toml:"-" json:"ipv6Addrs,omitempty"`
IPSIdentifiers map[IPS]string `toml:"-" json:"ipsIdentifiers,omitempty"`
LogMsgAnsiColor string `toml:"-" json:"-"` // DebugLog Color
Container Container `toml:"-" json:"-"`
Distro Distro `toml:"-" json:"-"`
Mode ScanMode `toml:"-" json:"-"`
}
// ContainerSetting is used for loading container setting in config.toml
@@ -1087,25 +1030,6 @@ type WordPressConf struct {
IgnoreInactive bool `json:"ignoreInactive,omitempty"`
}
// Image is a scan container image info
type Image struct {
Name string `json:"name"`
Tag string `json:"tag"`
Digest string `json:"digest"`
DockerOption types.DockerOption `json:"dockerOption,omitempty"`
Cpes []string `json:"cpes,omitempty"`
OwaspDCXMLPath string `json:"owaspDCXMLPath"`
IgnorePkgsRegexp []string `json:"ignorePkgsRegexp,omitempty"`
IgnoreCves []string `json:"ignoreCves,omitempty"`
}
func (i *Image) GetFullName() string {
if i.Digest != "" {
return i.Name + "@" + i.Digest
}
return i.Name + ":" + i.Tag
}
// GitHubConf is used for GitHub integration
type GitHubConf struct {
Token string `json:"-"`
@@ -1185,7 +1109,7 @@ const (
)
// GetServerName returns ServerName if this serverInfo is about host.
// If this serverInfo is abount a container, returns containerID@ServerName
// If this serverInfo is about a container, returns containerID@ServerName
func (s ServerInfo) GetServerName() string {
if len(s.Container.ContainerID) == 0 {
return s.ServerName
@@ -1204,21 +1128,18 @@ func (l Distro) String() string {
}
// MajorVersion returns Major version
func (l Distro) MajorVersion() (ver int, err error) {
func (l Distro) MajorVersion() (int, error) {
if l.Family == Amazon {
ss := strings.Fields(l.Release)
if len(ss) == 1 {
return 1, nil
}
ver, err = strconv.Atoi(ss[0])
return
return strconv.Atoi(ss[0])
}
if 0 < len(l.Release) {
ver, err = strconv.Atoi(strings.Split(l.Release, ".")[0])
} else {
err = xerrors.New("Release is empty")
return strconv.Atoi(strings.Split(l.Release, ".")[0])
}
return
return 0, xerrors.New("Release is empty")
}
// IsContainer returns whether this ServerInfo is about container

View File

@@ -63,7 +63,7 @@ func TestSyslogConfValidate(t *testing.T) {
}
}
func TestMajorVersion(t *testing.T) {
func TestDistro_MajorVersion(t *testing.T) {
var tests = []struct {
in Distro
out int

View File

@@ -21,8 +21,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
Conf.EMail = conf.EMail
Conf.Slack = conf.Slack
Conf.Stride = conf.Stride
Conf.HipChat = conf.HipChat
Conf.ChatWork = conf.ChatWork
Conf.Telegram = conf.Telegram
Conf.Saas = conf.Saas
@@ -35,6 +33,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
Conf.OvalDict = conf.OvalDict
Conf.Gost = conf.Gost
Conf.Exploit = conf.Exploit
Conf.Metasploit = conf.Metasploit
d := conf.Default
Conf.Default = d
@@ -44,29 +43,24 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
d.KeyPassword = keyPass
}
i := 0
index := 0
for serverName, v := range conf.Servers {
if 0 < len(v.KeyPassword) {
return xerrors.Errorf("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE: %s", serverName)
}
s := ServerInfo{ServerName: serverName}
s.Images = make(map[string]Image)
// image are able to set any server type
for name, image := range v.Images {
if err := IsValidImage(image); err != nil {
return err
}
s.Images[name] = image
}
if v.Type != ServerTypePseudo {
s.Host = v.Host
if len(s.Host) == 0 {
return xerrors.Errorf("%s is invalid. host is empty", serverName)
}
s.JumpServer = v.JumpServer
if len(s.JumpServer) == 0 {
s.JumpServer = d.JumpServer
}
switch {
case v.Port != "":
s.Port = v.Port
@@ -87,6 +81,11 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
}
s.SSHConfigPath = v.SSHConfigPath
if len(s.SSHConfigPath) == 0 {
s.SSHConfigPath = d.SSHConfigPath
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
@@ -115,7 +114,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
case "offline":
s.Mode.Set(Offline)
default:
return xerrors.Errorf("scanMode: %s of %s is invalie. Specify -fast, -fast-root, -deep or offline", m, serverName)
return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline", m, serverName)
}
}
if err := s.Mode.validate(); err != nil {
@@ -163,10 +162,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.Containers[contName] = cont
}
if len(v.DependencyCheckXMLPath) != 0 || len(d.DependencyCheckXMLPath) != 0 {
return xerrors.Errorf("[DEPRECATED] dependencyCheckXMLPath IS DEPRECATED. USE owaspDCXMLPath INSTEAD: %s", serverName)
}
s.OwaspDCXMLPath = v.OwaspDCXMLPath
if len(s.OwaspDCXMLPath) == 0 {
s.OwaspDCXMLPath = d.OwaspDCXMLPath
@@ -207,14 +202,14 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
for _, reg := range s.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return xerrors.Errorf("Faild to parse %s in %s. err: %w", reg, serverName, err)
return xerrors.Errorf("Failed to parse %s in %s. err: %w", reg, serverName, err)
}
}
for contName, cont := range s.Containers {
for _, reg := range cont.IgnorePkgsRegexp {
_, err := regexp.Compile(reg)
if err != nil {
return xerrors.Errorf("Faild to parse %s in %s@%s. err: %w",
return xerrors.Errorf("Failed to parse %s in %s@%s. err: %w",
reg, contName, serverName, err)
}
}
@@ -267,12 +262,23 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s.WordPress.OSUser = v.WordPress.OSUser
s.WordPress.IgnoreInactive = v.WordPress.IgnoreInactive
s.LogMsgAnsiColor = Colors[i%len(Colors)]
i++
s.IgnoredJSONKeys = v.IgnoredJSONKeys
if len(s.IgnoredJSONKeys) == 0 {
s.IgnoredJSONKeys = d.IgnoredJSONKeys
}
s.LogMsgAnsiColor = Colors[index%len(Colors)]
index++
servers[serverName] = s
}
Conf.Servers = servers
Conf.CveDict.Init()
Conf.OvalDict.Init()
Conf.Gost.Init()
Conf.Exploit.Init()
Conf.Metasploit.Init()
return nil
}
@@ -290,19 +296,5 @@ func toCpeURI(cpename string) (string, error) {
}
return naming.BindToURI(wfn), nil
}
return "", xerrors.Errorf("Unknow CPE format: %s", cpename)
}
// IsValidImage checks a container configuration
func IsValidImage(c Image) error {
if c.Name == "" {
return xerrors.New("Invalid arguments : no image name")
}
if c.Tag == "" && c.Digest == "" {
return xerrors.New("Invalid arguments : no image tag and digest")
}
if c.Tag != "" && c.Digest != "" {
return xerrors.New("Invalid arguments : you can either set image tag or digest")
}
return nil
return "", xerrors.Errorf("Unknown CPE format: %s", cpename)
}

View File

@@ -42,62 +42,3 @@ func TestToCpeURI(t *testing.T) {
}
}
}
func TestIsValidImage(t *testing.T) {
var tests = []struct {
name string
img Image
errOccur bool
}{
{
name: "ok with tag",
img: Image{
Name: "ok",
Tag: "ok",
},
errOccur: false,
},
{
name: "ok with digest",
img: Image{
Name: "ok",
Digest: "ok",
},
errOccur: false,
},
{
name: "no image name with tag",
img: Image{
Tag: "ok",
},
errOccur: true,
},
{
name: "no image name with digest",
img: Image{
Digest: "ok",
},
errOccur: true,
},
{
name: "no tag and digest",
img: Image{
Name: "ok",
},
errOccur: true,
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := IsValidImage(tt.img)
actual := err != nil
if actual != tt.errOccur {
t.Errorf("[%d] act: %v, exp: %v",
i, actual, tt.errOccur)
}
})
}
}

View File

@@ -0,0 +1,38 @@
# future-vuls
## Main Features
- upload vuls results json to future-vuls
## Installation
```
git clone https://github.com/future-architect/vuls.git
make build-future-vuls
```
## Command Reference
```
Upload to FutureVuls
Usage:
future-vuls upload [flags]
Flags:
--config string config file (default is $HOME/.cobra.yaml)
-g, --group-id int future vuls group id, ENV: VULS_GROUP_ID
-h, --help help for upload
-s, --stdin input from stdin. ENV: VULS_STDIN
-t, --token string future vuls token
--url string future vuls upload url
--uuid string server uuid. ENV: VULS_SERVER_UUID
```
## Usage
- update results json
```
cat results.json | future-vuls upload --stdin --token xxxx --url https://xxxx --group-id 1 --uuid xxxx
```

View File

@@ -0,0 +1,98 @@
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"os"
"strconv"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/saas"
"github.com/spf13/cobra"
)
var (
configFile string
stdIn bool
jsonDir string
serverUUID string
groupID int64
token string
url string
)
func main() {
var err error
var cmdFvulsUploader = &cobra.Command{
Use: "upload",
Short: "Upload to FutureVuls",
Long: `Upload to FutureVuls`,
Run: func(cmd *cobra.Command, args []string) {
if len(serverUUID) == 0 {
serverUUID = os.Getenv("VULS_SERVER_UUID")
}
if groupID == 0 {
envGroupID := os.Getenv("VULS_GROUP_ID")
if groupID, err = strconv.ParseInt(envGroupID, 10, 64); err != nil {
fmt.Printf("Invalid GroupID: %s\n", envGroupID)
return
}
}
if len(url) == 0 {
url = os.Getenv("VULS_URL")
}
if len(token) == 0 {
token = os.Getenv("VULS_TOKEN")
}
var scanResultJSON []byte
if stdIn {
reader := bufio.NewReader(os.Stdin)
buf := new(bytes.Buffer)
if _, err = buf.ReadFrom(reader); err != nil {
return
}
scanResultJSON = buf.Bytes()
} else {
fmt.Println("use --stdin option")
os.Exit(1)
return
}
var scanResult models.ScanResult
if err = json.Unmarshal(scanResultJSON, &scanResult); err != nil {
fmt.Println("Failed to parse json", err)
os.Exit(1)
return
}
scanResult.ServerUUID = serverUUID
config.Conf.Saas.GroupID = groupID
config.Conf.Saas.Token = token
config.Conf.Saas.URL = url
if err = (saas.Writer{}).Write(scanResult); err != nil {
fmt.Println(err)
os.Exit(1)
return
}
return
},
}
cmdFvulsUploader.PersistentFlags().StringVar(&serverUUID, "uuid", "", "server uuid. ENV: VULS_SERVER_UUID")
cmdFvulsUploader.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
cmdFvulsUploader.PersistentFlags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin. ENV: VULS_STDIN")
// TODO Read JSON file from directory
// cmdFvulsUploader.Flags().StringVarP(&jsonDir, "results-dir", "d", "./", "vuls scan results json dir")
cmdFvulsUploader.PersistentFlags().Int64VarP(&groupID, "group-id", "g", 0, "future vuls group id, ENV: VULS_GROUP_ID")
cmdFvulsUploader.PersistentFlags().StringVarP(&token, "token", "t", "", "future vuls token")
cmdFvulsUploader.PersistentFlags().StringVar(&url, "url", "", "future vuls upload url")
var rootCmd = &cobra.Command{Use: "future-vuls"}
rootCmd.AddCommand(cmdFvulsUploader)
if err = rootCmd.Execute(); err != nil {
fmt.Println("Failed to execute command", err)
}
}

View File

@@ -16,11 +16,11 @@ type analysis struct {
}
type dependency struct {
Identifiers []vulnerabilityId `xml:"identifiers>vulnerabilityIds"`
Identifiers []vulnerabilityID `xml:"identifiers>vulnerabilityIds"`
}
type vulnerabilityId struct {
Id string `xml:"id"`
type vulnerabilityID struct {
ID string `xml:"id"`
}
func appendIfMissing(slice []string, str string) []string {
@@ -55,7 +55,7 @@ func Parse(path string) ([]string, error) {
cpes := []string{}
for _, d := range anal.Dependencies {
for _, ident := range d.Identifiers {
id := ident.Id // Start with cpe:2.3:
id := ident.ID // Start with cpe:2.3:
// Convert from CPE 2.3 to CPE 2.2
if strings.HasPrefix(id, "cpe:2.3:") {
wfn, err := naming.UnbindFS(id)

35
contrib/trivy/README.md Normal file
View File

@@ -0,0 +1,35 @@
# trivy-to-vuls
## Main Features
- convert trivy's results json to vuls's report json
## Installation
```
git clone https://github.com/future-architect/vuls.git
make build-trivy-to-vuls
```
## Command Reference
```
Parse trivy json to vuls results
Usage:
trivy-to-vuls parse [flags]
Flags:
-h, --help help for parse
-s, --stdin input from stdin
-d, --trivy-json-dir string trivy json dir (default "./")
-f, --trivy-json-file-name string trivy json file name (default "results.json")
```
## Usage
- use trivy output
```
trivy -q image -f=json python:3.4-alpine | trivy-to-vuls parse --stdin
```

78
contrib/trivy/cmd/main.go Normal file
View File

@@ -0,0 +1,78 @@
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/future-architect/vuls/contrib/trivy/parser"
"github.com/future-architect/vuls/models"
"github.com/spf13/cobra"
)
var (
serverUUID string
stdIn bool
jsonDir string
jsonFileName string
)
func main() {
var err error
var cmdTrivyToVuls = &cobra.Command{
Use: "parse",
Short: "Parse trivy json to vuls results",
Long: `Parse trivy json to vuls results`,
Run: func(cmd *cobra.Command, args []string) {
jsonFilePath := filepath.Join(jsonDir, jsonFileName)
var trivyJSON []byte
if stdIn {
reader := bufio.NewReader(os.Stdin)
buf := new(bytes.Buffer)
if _, err = buf.ReadFrom(reader); err != nil {
os.Exit(1)
return
}
trivyJSON = buf.Bytes()
} else {
if trivyJSON, err = ioutil.ReadFile(jsonFilePath); err != nil {
fmt.Println("Failed to read file", err)
os.Exit(1)
return
}
}
scanResult := &models.ScanResult{
JSONVersion: models.JSONVersion,
ScannedCves: models.VulnInfos{},
}
if scanResult, err = parser.Parse(trivyJSON, scanResult); err != nil {
fmt.Println("Failed to execute command", err)
os.Exit(1)
return
}
var resultJSON []byte
if resultJSON, err = json.MarshalIndent(scanResult, "", " "); err != nil {
fmt.Println("Failed to create json", err)
os.Exit(1)
return
}
fmt.Println(string(resultJSON))
return
},
}
cmdTrivyToVuls.Flags().BoolVarP(&stdIn, "stdin", "s", false, "input from stdin")
cmdTrivyToVuls.Flags().StringVarP(&jsonDir, "trivy-json-dir", "d", "./", "trivy json dir")
cmdTrivyToVuls.Flags().StringVarP(&jsonFileName, "trivy-json-file-name", "f", "results.json", "trivy json file name")
var rootCmd = &cobra.Command{Use: "trivy-to-vuls"}
rootCmd.AddCommand(cmdTrivyToVuls)
if err = rootCmd.Execute(); err != nil {
fmt.Println("Failed to execute command", err)
os.Exit(1)
}
}

View File

@@ -0,0 +1,164 @@
package parser
import (
"encoding/json"
"sort"
"time"
"github.com/aquasecurity/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/future-architect/vuls/models"
)
// Parse :
func Parse(vulnJSON []byte, scanResult *models.ScanResult) (result *models.ScanResult, err error) {
var trivyResults report.Results
if err = json.Unmarshal(vulnJSON, &trivyResults); err != nil {
return nil, err
}
pkgs := models.Packages{}
vulnInfos := models.VulnInfos{}
uniqueLibraryScannerPaths := map[string]models.LibraryScanner{}
for _, trivyResult := range trivyResults {
for _, vuln := range trivyResult.Vulnerabilities {
if _, ok := vulnInfos[vuln.VulnerabilityID]; !ok {
vulnInfos[vuln.VulnerabilityID] = models.VulnInfo{
CveID: vuln.VulnerabilityID,
Confidences: models.Confidences{
{
Score: 100,
DetectionMethod: models.TrivyMatchStr,
},
},
AffectedPackages: models.PackageFixStatuses{},
CveContents: models.CveContents{},
LibraryFixedIns: models.LibraryFixedIns{},
// VulnType : "",
}
}
vulnInfo := vulnInfos[vuln.VulnerabilityID]
var notFixedYet bool
fixState := ""
if len(vuln.FixedVersion) == 0 {
notFixedYet = true
fixState = "Affected"
}
var references models.References
for _, reference := range vuln.References {
references = append(references, models.Reference{
Source: "trivy",
Link: reference,
})
}
sort.Slice(references, func(i, j int) bool {
return references[i].Link < references[j].Link
})
vulnInfo.CveContents = models.CveContents{
models.Trivy: models.CveContent{
Cvss3Severity: vuln.Severity,
References: references,
Title: vuln.Title,
Summary: vuln.Description,
},
}
// do only if image type is Vuln
if IsTrivySupportedOS(trivyResult.Type) {
pkgs[vuln.PkgName] = models.Package{
Name: vuln.PkgName,
Version: vuln.InstalledVersion,
}
vulnInfo.AffectedPackages = append(vulnInfo.AffectedPackages, models.PackageFixStatus{
Name: vuln.PkgName,
NotFixedYet: notFixedYet,
FixState: fixState,
FixedIn: vuln.FixedVersion,
})
// overwrite every time if os package
scanResult.Family = trivyResult.Type
scanResult.ServerName = trivyResult.Target
scanResult.Optional = map[string]interface{}{
"trivy-target": trivyResult.Target,
}
scanResult.ScannedAt = time.Now()
scanResult.ScannedBy = "trivy"
scanResult.ScannedVia = "trivy"
} else {
// LibraryScanの結果
vulnInfo.LibraryFixedIns = append(vulnInfo.LibraryFixedIns, models.LibraryFixedIn{
Key: trivyResult.Type,
Name: vuln.PkgName,
Path: trivyResult.Target,
FixedIn: vuln.FixedVersion,
})
libScanner := uniqueLibraryScannerPaths[trivyResult.Target]
libScanner.Libs = append(libScanner.Libs, types.Library{
Name: vuln.PkgName,
Version: vuln.InstalledVersion,
})
uniqueLibraryScannerPaths[trivyResult.Target] = libScanner
}
vulnInfos[vuln.VulnerabilityID] = vulnInfo
}
}
// flatten and unique libraries
libraryScanners := make([]models.LibraryScanner, 0, len(uniqueLibraryScannerPaths))
for path, v := range uniqueLibraryScannerPaths {
uniqueLibrary := map[string]types.Library{}
for _, lib := range v.Libs {
uniqueLibrary[lib.Name+lib.Version] = lib
}
var libraries []types.Library
for _, library := range uniqueLibrary {
libraries = append(libraries, library)
}
sort.Slice(libraries, func(i, j int) bool {
return libraries[i].Name < libraries[j].Name
})
libscanner := models.LibraryScanner{
Path: path,
Libs: libraries,
}
libraryScanners = append(libraryScanners, libscanner)
}
sort.Slice(libraryScanners, func(i, j int) bool {
return libraryScanners[i].Path < libraryScanners[j].Path
})
scanResult.ScannedCves = vulnInfos
scanResult.Packages = pkgs
scanResult.LibraryScanners = libraryScanners
return scanResult, nil
}
// IsTrivySupportedOS :
func IsTrivySupportedOS(family string) bool {
supportedFamilies := []string{
os.RedHat,
os.Debian,
os.Ubuntu,
os.CentOS,
os.Fedora,
os.Amazon,
os.Oracle,
os.Windows,
os.OpenSUSE,
os.OpenSUSELeap,
os.OpenSUSETumbleweed,
os.SLES,
os.Photon,
os.Alpine,
}
for _, supportedFamily := range supportedFamilies {
if family == supportedFamily {
return true
}
}
return false
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,5 +29,5 @@ var SansTopTwentyfive = map[string]string{
"759": "25",
}
// SansTopTwentyfiveURL
// SansTopTwentyfiveURL is a URL of sans 25
var SansTopTwentyfiveURL = "https://www.sans.org/top25-software-errors/"

View File

@@ -1,3 +1,5 @@
// +build !scanner
package exploit
import (

View File

@@ -5,18 +5,17 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/errof"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/k0kubun/pp"
"golang.org/x/oauth2"
)
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch scurity alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
// FillGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult.
// https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/
func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (nCVEs int, err error) {
src := oauth2.StaticTokenSource(
@@ -25,8 +24,9 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
httpClient := oauth2.NewClient(context.Background(), src)
// TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint
// Memo : https://developer.github.com/v4/explorer/
const jsonfmt = `{"query":
"query { repository(owner:\"%s\", name:\"%s\") { url, vulnerabilityAlerts(first: %d, %s) { pageInfo{ endCursor, hasNextPage, startCursor}, edges { node { id, externalIdentifier, externalReference, fixedIn, packageName, dismissReason, dismissedAt } } } } }"}`
"query { repository(owner:\"%s\", name:\"%s\") { url vulnerabilityAlerts(first: %d, %s) { pageInfo { endCursor hasNextPage startCursor } edges { node { id dismissReason dismissedAt securityVulnerability{ package { name ecosystem } severity vulnerableVersionRange firstPatchedVersion { identifier } } securityAdvisory { description ghsaId permalink publishedAt summary updatedAt withdrawnAt origin severity references { url } identifiers { type value } } } } } } } "}`
after := ""
for {
@@ -43,7 +43,7 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
// To toggle this preview and access data, need to provide a custom media type in the Accept header:
// MEMO: I tried to get the affected version via GitHub API. Bit it seems difficult to determin the affected version if there are multiple dependency files such as package.json.
// TODO remove this header if it is no longer preview status in the future.
req.Header.Set("Accept", "application/vnd.github.vixen-preview+json")
req.Header.Set("Accept", "application/vnd.github.package-deletes-preview+json")
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
@@ -51,16 +51,23 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
return 0, err
}
defer resp.Body.Close()
alerts := SecurityAlerts{}
if json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
util.Log.Debugf("%s", pp.Sprint(alerts))
alerts := SecurityAlerts{}
if err := json.Unmarshal(body, &alerts); err != nil {
return 0, err
}
// util.Log.Debugf("%s", pp.Sprint(alerts))
// util.Log.Debugf("%s", string(body))
if alerts.Data.Repository.URL == "" {
return 0, errof.New(
errof.ErrFailedToAccessGithubAPI,
fmt.Sprintf("Failed to access to GitHub API. Response: %#v", alerts),
fmt.Sprintf("Failed to access to GitHub API. Response: %s", string(body)),
)
}
@@ -70,31 +77,45 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
}
pkgName := fmt.Sprintf("%s %s",
alerts.Data.Repository.URL, v.Node.PackageName)
alerts.Data.Repository.URL, v.Node.SecurityVulnerability.Package.Name)
m := models.GitHubSecurityAlert{
PackageName: pkgName,
FixedIn: v.Node.FixedIn,
AffectedRange: v.Node.AffectedRange,
FixedIn: v.Node.SecurityVulnerability.FirstPatchedVersion.Identifier,
AffectedRange: v.Node.SecurityVulnerability.VulnerableVersionRange,
Dismissed: len(v.Node.DismissReason) != 0,
DismissedAt: v.Node.DismissedAt,
DismissReason: v.Node.DismissReason,
}
cveID := v.Node.ExternalIdentifier
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
cveIDs, other := []string{}, []string{}
for _, identifier := range v.Node.SecurityAdvisory.Identifiers {
if identifier.Type == "CVE" {
cveIDs = append(cveIDs, identifier.Value)
} else {
other = append(other, identifier.Value)
}
}
// If CVE-ID has not been assigned, use the GHSA ID etc as a ID.
if len(cveIDs) == 0 {
cveIDs = other
}
for _, cveID := range cveIDs {
if val, ok := r.ScannedCves[cveID]; ok {
val.GitHubSecurityAlerts = val.GitHubSecurityAlerts.Add(m)
r.ScannedCves[cveID] = val
nCVEs++
} else {
v := models.VulnInfo{
CveID: cveID,
Confidences: models.Confidences{models.GitHubMatch},
GitHubSecurityAlerts: models.GitHubSecurityAlerts{m},
}
r.ScannedCves[cveID] = v
nCVEs++
}
r.ScannedCves[cveID] = v
nCVEs++
}
}
if !alerts.Data.Repository.VulnerabilityAlerts.PageInfo.HasNextPage {
@@ -109,26 +130,50 @@ func FillGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string) (
type SecurityAlerts struct {
Data struct {
Repository struct {
URL string `json:"url,omitempty"`
URL string `json:"url"`
VulnerabilityAlerts struct {
PageInfo struct {
EndCursor string `json:"endCursor,omitempty"`
HasNextPage bool `json:"hasNextPage,omitempty"`
StartCursor string `json:"startCursor,omitempty"`
} `json:"pageInfo,omitempty"`
EndCursor string `json:"endCursor"`
HasNextPage bool `json:"hasNextPage"`
StartCursor string `json:"startCursor"`
} `json:"pageInfo"`
Edges []struct {
Node struct {
ID string `json:"id,omitempty"`
ExternalIdentifier string `json:"externalIdentifier,omitempty"`
ExternalReference string `json:"externalReference,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
AffectedRange string `json:"affectedRange,omitempty"`
PackageName string `json:"packageName,omitempty"`
DismissReason string `json:"dismissReason,omitempty"`
DismissedAt time.Time `json:"dismissedAt,omitempty"`
} `json:"node,omitempty"`
} `json:"edges,omitempty"`
} `json:"vulnerabilityAlerts,omitempty"`
} `json:"repository,omitempty"`
} `json:"data,omitempty"`
ID string `json:"id"`
DismissReason string `json:"dismissReason"`
DismissedAt time.Time `json:"dismissedAt"`
SecurityVulnerability struct {
Package struct {
Name string `json:"name"`
Ecosystem string `json:"ecosystem"`
} `json:"package"`
Severity string `json:"severity"`
VulnerableVersionRange string `json:"vulnerableVersionRange"`
FirstPatchedVersion struct {
Identifier string `json:"identifier"`
} `json:"firstPatchedVersion"`
} `json:"securityVulnerability"`
SecurityAdvisory struct {
Description string `json:"description"`
GhsaID string `json:"ghsaId"`
Permalink string `json:"permalink"`
PublishedAt time.Time `json:"publishedAt"`
Summary string `json:"summary"`
UpdatedAt time.Time `json:"updatedAt"`
WithdrawnAt time.Time `json:"withdrawnAt"`
Origin string `json:"origin"`
Severity string `json:"severity"`
References []struct {
URL string `json:"url"`
} `json:"references"`
Identifiers []struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"identifiers"`
} `json:"securityAdvisory"`
} `json:"node"`
} `json:"edges"`
} `json:"vulnerabilityAlerts"`
} `json:"repository"`
} `json:"data"`
}

90
go.mod
View File

@@ -1,56 +1,80 @@
module github.com/future-architect/vuls
go 1.13
go 1.15
replace (
github.com/genuinetools/reg => github.com/tomoyamachi/reg v0.16.1-0.20190706172545-2a2250fd7c00
gopkg.in/mattn/go-colorable.v0 => github.com/mattn/go-colorable v0.1.0
gopkg.in/mattn/go-isatty.v0 => github.com/mattn/go-isatty v0.0.6
)
require (
github.com/Azure/azure-sdk-for-go v33.2.0+incompatible
github.com/Azure/go-autorest/autorest v0.9.1 // indirect
github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
github.com/Azure/azure-sdk-for-go v49.1.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.15 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.10 // indirect
github.com/BurntSushi/toml v0.3.1
github.com/RackSec/srslog v0.0.0-20180709174129-a4725f04ec91
github.com/aquasecurity/fanal v0.0.0-20200124194549-91468b8e0460
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy v0.1.6
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/aws/aws-sdk-go v1.25.31
github.com/aquasecurity/fanal v0.0.0-20201218050947-981a0510f9cb
github.com/aquasecurity/trivy v0.14.0
github.com/aquasecurity/trivy-db v0.0.0-20201220084758-2d91316c83fa
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
github.com/aws/aws-sdk-go v1.36.12
github.com/boltdb/bolt v1.3.1
github.com/briandowns/spinner v1.12.0 // indirect
github.com/caarlos0/env/v6 v6.4.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/dnaeon/go-vcr v1.0.1 // indirect
github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 // indirect
github.com/google/subcommands v1.0.1
github.com/gosuri/uitable v0.0.3
github.com/hashicorp/go-version v1.2.0
github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
github.com/emersion/go-smtp v0.14.0
github.com/goccy/go-yaml v1.8.4 // indirect
github.com/golang/protobuf v1.4.3 // indirect
github.com/google/subcommands v1.2.0
github.com/google/wire v0.4.0 // indirect
github.com/gosuri/uitable v0.0.4
github.com/grokify/html-strip-tags-go v0.0.0-20200923094847-079d207a09f1 // indirect
github.com/hashicorp/go-uuid v1.0.2
github.com/hashicorp/go-version v1.2.1
github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c
github.com/jroimartin/gocui v0.4.0
github.com/jesseduffield/gocui v0.3.0
github.com/k0kubun/pp v3.0.1+incompatible
github.com/knqyf263/go-cpe v0.0.0-20180327054844-659663f6eca2
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
github.com/knqyf263/go-cpe v0.0.0-20201213041631-54f6ab28673f
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/knqyf263/go-version v1.1.1
github.com/knqyf263/gost v0.1.2
github.com/kotakanbe/go-cve-dictionary v0.4.1
github.com/knqyf263/gost v0.1.7
github.com/kotakanbe/go-cve-dictionary v0.5.6
github.com/kotakanbe/go-pingscanner v0.1.0
github.com/kotakanbe/goval-dictionary v0.2.3
github.com/kotakanbe/goval-dictionary v0.2.16
github.com/kotakanbe/logrus-prefixed-formatter v0.0.0-20180123152602-928f7356cb96
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/magiconair/properties v1.8.4 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mozqnet/go-exploitdb v0.0.0-20190911093644-f647f17ea8ca
github.com/mitchellh/mapstructure v1.4.0 // indirect
github.com/mozqnet/go-exploitdb v0.1.2
github.com/nlopes/slack v0.6.0
github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect
github.com/olekukonko/tablewriter v0.0.2-0.20190607075207-195002e6e56a
github.com/parnurzeal/gorequest v0.2.15
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/parnurzeal/gorequest v0.2.16
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
github.com/sirupsen/logrus v1.7.0
github.com/spf13/afero v1.5.1
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1 // indirect
github.com/takuzoo3868/go-msfdb v0.1.3
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0 // indirect
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
honnef.co/go/tools v0.1.0 // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
)

1168
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
// +build !scanner
package gost
import (

View File

@@ -1,3 +1,5 @@
// +build !scanner
package gost
import (
@@ -21,8 +23,23 @@ type packCves struct {
cves []models.CveContent
}
func (deb Debian) supported(major string) bool {
_, ok := map[string]string{
"8": "jessie",
"9": "stretch",
"10": "buster",
}[major]
return ok
}
// DetectUnfixed fills cve information that has in Gost
func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCVEs int, err error) {
if !deb.supported(major(r.Release)) {
// only logging
util.Log.Warnf("Debian %s is not supported yet", r.Release)
return 0, nil
}
linuxImage := "linux-image-" + r.RunningKernel.Release
// Add linux and set the version of running kernel to search OVAL.
if r.Container.ContainerID == "" {
@@ -37,9 +54,17 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
}
}
// Debian Security Tracker does not support Package for Raspbian, so skip it.
var scanResult models.ScanResult
if r.Family != config.Raspbian {
scanResult = *r
} else {
scanResult = r.RemoveRaspbianPackFromResult()
}
packCvesList := []packCves{}
if config.Conf.Gost.IsFetchViaHTTP() {
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(r.Release), "pkgs")
url, _ := util.URLPathJoin(config.Conf.Gost.URL, "debian", major(scanResult.Release), "pkgs")
responses, err := getAllUnfixedCvesViaHTTP(r, url)
if err != nil {
return 0, err
@@ -64,8 +89,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
if driver == nil {
return 0, nil
}
for _, pack := range r.Packages {
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
for _, pack := range scanResult.Packages {
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
cves := []models.CveContent{}
for _, cveDeb := range cveDebs {
cves = append(cves, *deb.ConvertToModel(&cveDeb))
@@ -78,8 +103,8 @@ func (deb Debian) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (nCV
}
// SrcPack
for _, pack := range r.SrcPackages {
cveDebs := driver.GetUnfixedCvesDebian(major(r.Release), pack.Name)
for _, pack := range scanResult.SrcPackages {
cveDebs := driver.GetUnfixedCvesDebian(major(scanResult.Release), pack.Name)
cves := []models.CveContent{}
for _, cveDeb := range cveDebs {
cves = append(cves, *deb.ConvertToModel(&cveDeb))

61
gost/debian_test.go Normal file
View File

@@ -0,0 +1,61 @@
package gost
import "testing"
func TestDebian_Supported(t *testing.T) {
type fields struct {
Base Base
}
type args struct {
major string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "8 is supported",
args: args{
major: "8",
},
want: true,
},
{
name: "9 is supported",
args: args{
major: "9",
},
want: true,
},
{
name: "10 is supported",
args: args{
major: "10",
},
want: true,
},
{
name: "11 is not supported yet",
args: args{
major: "11",
},
want: false,
},
{
name: "empty string is not supported yet",
args: args{
major: "",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
deb := Debian{}
if got := deb.supported(tt.args.major); got != tt.want {
t.Errorf("Debian.Supported() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package gost
import (
@@ -23,7 +25,7 @@ func NewClient(family string) Client {
switch family {
case cnf.RedHat, cnf.CentOS:
return RedHat{}
case cnf.Debian:
case cnf.Debian, cnf.Raspbian:
return Debian{}
case cnf.Windows:
return Microsoft{}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package gost
import (
@@ -18,7 +20,7 @@ func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (n
if driver == nil {
return 0, nil
}
var cveIDs []string
cveIDs := []string{}
for cveID := range r.ScannedCves {
cveIDs = append(cveIDs, cveID)
}
@@ -26,19 +28,20 @@ func (ms Microsoft) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (n
if _, ok := r.ScannedCves[cveID]; !ok {
continue
}
cveCont := ms.ConvertToModel(&msCve)
cveCont, mitigations := ms.ConvertToModel(&msCve)
v, _ := r.ScannedCves[cveID]
if v.CveContents == nil {
v.CveContents = models.CveContents{}
}
v.CveContents[models.Microsoft] = *cveCont
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[cveID] = v
}
return len(cveIDs), nil
}
// ConvertToModel converts gost model to vuls model
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveContent {
func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) (*models.CveContent, []models.Mitigation) {
v3score := 0.0
var v3Vector string
for _, scoreSet := range cve.ScoreSets {
@@ -72,7 +75,7 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
if 0 < len(cve.Workaround) {
option["workaround"] = cve.Workaround
}
var kbids []string
kbids := []string{}
for _, kbid := range cve.KBIDs {
kbids = append(kbids, kbid.KBID)
}
@@ -80,6 +83,18 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
option["kbids"] = strings.Join(kbids, ",")
}
vendorURL := "https://msrc.microsoft.com/update-guide/vulnerability/" + cve.CveID
mitigations := []models.Mitigation{}
if cve.Mitigation != "" {
mitigations = []models.Mitigation{
{
CveContentType: models.Microsoft,
Mitigation: cve.Mitigation,
URL: vendorURL,
},
}
}
return &models.CveContent{
Type: models.Microsoft,
CveID: cve.CveID,
@@ -90,10 +105,9 @@ func (ms Microsoft) ConvertToModel(cve *gostmodels.MicrosoftCVE) *models.CveCont
Cvss3Severity: v3Severity,
References: refs,
CweIDs: cwe,
Mitigation: cve.Mitigation,
Published: cve.PublishDate,
LastModified: cve.LastUpdateDate,
SourceLink: "https://portal.msrc.microsoft.com/ja-jp/security-guidance/advisory/" + cve.CveID,
SourceLink: vendorURL,
Optional: option,
}
}, mitigations
}

View File

@@ -1,9 +1,10 @@
// +build !scanner
package gost
import (
"github.com/future-architect/vuls/models"
"github.com/knqyf263/gost/db"
"strings"
)
// Pseudo is Gost client except for RedHat family and Debian
@@ -15,7 +16,3 @@ type Pseudo struct {
func (pse Pseudo) DetectUnfixed(driver db.DB, r *models.ScanResult, _ bool) (int, error) {
return 0, nil
}
func major(osVer string) (majorVersion string) {
return strings.Split(osVer, ".")[0]
}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package gost
import (
@@ -23,7 +25,7 @@ func (red RedHat) DetectUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNo
}
func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
var cveIDs []string
cveIDs := []string{}
for cveID, vuln := range r.ScannedCves {
if _, ok := vuln.CveContents[models.RedHatAPI]; ok {
continue
@@ -46,7 +48,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
if redCve.ID == 0 {
continue
}
cveCont := red.ConvertToModel(&redCve)
cveCont, mitigations := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[res.request.cveID]
if ok {
if v.CveContents == nil {
@@ -61,6 +63,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[res.request.cveID] = v
}
} else {
@@ -71,7 +74,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
if len(redCve.Name) == 0 {
continue
}
cveCont := red.ConvertToModel(&redCve)
cveCont, mitigations := red.ConvertToModel(&redCve)
v, ok := r.ScannedCves[cveID]
if ok {
if v.CveContents == nil {
@@ -86,6 +89,7 @@ func (red RedHat) fillFixed(driver db.DB, r *models.ScanResult) error {
Confidences: models.Confidences{models.RedHatAPIMatch},
}
}
v.Mitigations = append(v.Mitigations, mitigations...)
r.ScannedCves[cveID] = v
}
}
@@ -109,7 +113,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
}
for _, cve := range cves {
cveCont := red.ConvertToModel(&cve)
cveCont, mitigations := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
@@ -125,6 +129,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
}
nCVEs++
}
v.Mitigations = append(v.Mitigations, mitigations...)
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
@@ -139,10 +144,9 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
}
for _, pack := range r.Packages {
// CVE-ID: RedhatCVE
cves := map[string]gostmodels.RedhatCVE{}
cves = driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
cves := driver.GetUnfixedCvesRedhat(major(r.Release), pack.Name, ignoreWillNotFix)
for _, cve := range cves {
cveCont := red.ConvertToModel(&cve)
cveCont, mitigations := red.ConvertToModel(&cve)
v, ok := r.ScannedCves[cve.Name]
if ok {
if v.CveContents == nil {
@@ -158,7 +162,7 @@ func (red RedHat) fillUnfixed(driver db.DB, r *models.ScanResult, ignoreWillNotF
}
nCVEs++
}
v.Mitigations = append(v.Mitigations, mitigations...)
pkgStats := red.mergePackageStates(v,
cve.PackageState, r.Packages, r.Release)
if 0 < len(pkgStats) {
@@ -219,7 +223,7 @@ func (red RedHat) parseCwe(str string) (cwes []string) {
}
// ConvertToModel converts gost model to vuls model
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) (*models.CveContent, []models.Mitigation) {
cwes := red.parseCwe(cve.Cwe)
details := []string{}
@@ -245,11 +249,23 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
v3severity = cve.ThreatSeverity
}
var refs []models.Reference
refs := []models.Reference{}
for _, r := range cve.References {
refs = append(refs, models.Reference{Link: r.Reference})
}
vendorURL := "https://access.redhat.com/security/cve/" + cve.Name
mitigations := []models.Mitigation{}
if cve.Mitigation != "" {
mitigations = []models.Mitigation{
{
CveContentType: models.RedHatAPI,
Mitigation: cve.Mitigation,
URL: vendorURL,
},
}
}
return &models.CveContent{
Type: models.RedHatAPI,
CveID: cve.Name,
@@ -263,8 +279,7 @@ func (red RedHat) ConvertToModel(cve *gostmodels.RedhatCVE) *models.CveContent {
Cvss3Severity: v3severity,
References: refs,
CweIDs: cwes,
Mitigation: cve.Mitigation,
Published: cve.PublicDate,
SourceLink: "https://access.redhat.com/security/cve/" + cve.Name,
}
SourceLink: vendorURL,
}, mitigations
}

View File

@@ -2,6 +2,7 @@ package gost
import (
"net/http"
"strings"
"time"
"github.com/cenkalti/backoff"
@@ -181,3 +182,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
json: body,
}
}
func major(osVer string) (majorVersion string) {
return strings.Split(osVer, ".")[0]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -1,265 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.14.2-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="graphml" id="d7" yfiles.type="resources"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d0"/>
<node id="n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="478.6165008544913" y="1358.206868489578"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1043.7241210937468"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="38.623046875" x="17.3134765625" y="15.93359375">Nginx<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="711.9623756408686" y="1287.206868489578"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.7890625" x="15.23046875" y="15.93359375">MySQL<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="101.666015625" width="291.7208747863772" x="602.72693824768" y="1146.2994791666624"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n3:">
<node id="n3::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="640.72693824768" y="1182.9654947916624"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="723.4623756408686" y="1182.9654947916624"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="806.1978130340572" y="1182.9654947916624"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="821.1978130340572" y="1287.206868489578"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="35.412109375" x="18.9189453125" y="15.93359375">Redis<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<edge id="e0" source="n3" target="n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n3" target="n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n0" target="n3::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n0" target="n3::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n0" target="n3::n2">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n3" target="n4">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e6" source="n0" target="n4">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n0" target="n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n0" target="n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="dashed" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,194 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yEd 3.14.2-->
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key for="graphml" id="d7" yfiles.type="resources"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d0"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="508.30825042724564" y="1132.4827473958312"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="28.87890625" x="22.185546875" y="15.93359375">Vuls<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="993.2413736979156"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="23.8046875" x="24.72265625" y="15.93359375">ELB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="749.6541252136229" y="1236.7241210937468"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="27.0390625" x="23.10546875" y="15.93359375">RDS<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="101.666015625" width="291.7208747863772" x="640.4186878204343" y="1095.8167317708312"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="291.7208747863772" x="0.0" y="0.0">Web/App</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="23" leftF="23.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n3:">
<node id="n3::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="678.4186878204343" y="1132.4827473958312"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="761.1541252136229" y="1132.4827473958312"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n3::n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="73.25" x="843.8895626068115" y="1132.4827473958312"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="31.26953125" x="20.990234375" y="15.93359375">Rails<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<edge id="e0" source="n3" target="n1">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n3" target="n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n0" target="n3::n0">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,33 +1,112 @@
package libmanager
import (
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"context"
db2 "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/github"
"github.com/aquasecurity/trivy/pkg/indicator"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/spf13/afero"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// FillLibrary fills LibraryScanner informations
func FillLibrary(r *models.ScanResult) (totalCnt int, err error) {
// DetectLibsCves fills LibraryScanner information
func DetectLibsCves(r *models.ScanResult) (err error) {
totalCnt := 0
if len(r.LibraryScanners) == 0 {
return
}
// initialize trivy's logger and db
err = log.InitLogger(false, false)
if err != nil {
return 0, err
return err
}
if err := db.Init(); err != nil {
return 0, err
util.Log.Info("Updating library db...")
if err := downloadDB(config.Version, config.Conf.TrivyCacheDBDir, config.Conf.NoProgress, false, false); err != nil {
return err
}
if err := db2.Init(config.Conf.TrivyCacheDBDir); err != nil {
return err
}
defer db2.Close()
for _, lib := range r.LibraryScanners {
vinfos, err := lib.Scan()
if err != nil {
return 0, err
return err
}
for _, vinfo := range vinfos {
r.ScannedCves[vinfo.CveID] = vinfo
vinfo.Confidences.AppendIfMissing(models.TrivyMatch)
if v, ok := r.ScannedCves[vinfo.CveID]; !ok {
r.ScannedCves[vinfo.CveID] = vinfo
} else {
v.LibraryFixedIns = append(v.LibraryFixedIns, vinfo.LibraryFixedIns...)
r.ScannedCves[vinfo.CveID] = v
}
}
totalCnt += len(vinfos)
}
db.Close()
return totalCnt, nil
util.Log.Infof("%s: %d CVEs are detected with Library",
r.FormatServerName(), totalCnt)
return nil
}
func downloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
client := initializeDBClient(cacheDir, quiet)
ctx := context.Background()
needsUpdate, err := client.NeedsUpdate(appVersion, light, skipUpdate)
if err != nil {
return xerrors.Errorf("database error: %w", err)
}
if needsUpdate {
util.Log.Info("Need to update DB")
util.Log.Info("Downloading DB...")
if err := client.Download(ctx, cacheDir, light); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
if err = client.UpdateMetadata(cacheDir); err != nil {
return xerrors.Errorf("unable to update database metadata: %w", err)
}
}
// for debug
if err := showDBInfo(cacheDir); err != nil {
return xerrors.Errorf("failed to show database info: %w", err)
}
return nil
}
func initializeDBClient(cacheDir string, quiet bool) db.Client {
config := db2.Config{}
client := github.NewClient()
progressBar := indicator.NewProgressBar(quiet)
realClock := clock.RealClock{}
fs := afero.NewOsFs()
metadata := db.NewMetadata(fs, cacheDir)
dbClient := db.NewClient(config, client, progressBar, realClock, metadata)
return dbClient
}
func showDBInfo(cacheDir string) error {
m := db.NewMetadata(afero.NewOsFs(), cacheDir)
metadata, err := m.Get()
if err != nil {
return xerrors.Errorf("something wrong with DB: %w", err)
}
util.Log.Debugf("DB Schema: %d, Type: %d, UpdatedAt: %s, NextUpdate: %s",
metadata.Version, metadata.Type, metadata.UpdatedAt, metadata.NextUpdate)
return nil
}

View File

@@ -3,7 +3,7 @@ package models
import (
"time"
"github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
)
// CveContents has CveContent
@@ -42,15 +42,23 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
return
}
// SourceLinks returns link of source
func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{Jvn, cont.SourceLink})
// PrimarySrcURLs returns link of source
func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string) (values []CveContentStr) {
if cveID == "" {
return
}
if cont, found := v[Nvd]; found {
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Vendor Advisory" {
values = append(values, CveContentStr{Nvd, r.Link})
}
}
}
}
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
order := CveContentTypes{Nvd, NewCveContentType(myFamily)}
for _, ctype := range order {
if cont, found := v[ctype]; found {
if cont.SourceLink == "" {
@@ -60,6 +68,12 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
}
}
if lang == "ja" {
if cont, found := v[Jvn]; found && 0 < len(cont.SourceLink) {
values = append(values, CveContentStr{Jvn, cont.SourceLink})
}
}
if len(values) == 0 {
return []CveContentStr{{
Type: Nvd,
@@ -69,6 +83,22 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
return values
}
// PatchURLs returns link of patch
func (v CveContents) PatchURLs() (urls []string) {
cont, found := v[Nvd]
if !found {
return
}
for _, r := range cont.References {
for _, t := range r.Tags {
if t == "Patch" {
urls = append(urls, r.Link)
}
}
}
return
}
/*
// Severities returns Severities
func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
@@ -184,7 +214,6 @@ type CveContent struct {
CweIDs []string `json:"cweIDs,omitempty"`
Published time.Time `json:"published"`
LastModified time.Time `json:"lastModified"`
Mitigation string `json:"mitigation"` // RedHat API
Optional map[string]string `json:"optional,omitempty"`
}
@@ -199,8 +228,6 @@ type CveContentType string
// NewCveContentType create CveContentType
func NewCveContentType(name string) CveContentType {
switch name {
case "nvdxml":
return NvdXML
case "nvd":
return Nvd
case "jvn":
@@ -223,26 +250,15 @@ func NewCveContentType(name string) CveContentType {
return WPVulnDB
case "amazon":
return Amazon
case vulnerability.NodejsSecurityWg:
return NodeSec
case vulnerability.PythonSafetyDB:
return PythonSec
case vulnerability.RustSec:
return RustSec
case vulnerability.PhpSecurityAdvisories:
return PhpSec
case vulnerability.RubySec:
return RubySec
case "trivy":
return Trivy
default:
return Unknown
}
}
const (
// NvdXML is NvdXML
NvdXML CveContentType = "nvdxml"
// Nvd is Nvd
// Nvd is Nvd JSON
Nvd CveContentType = "nvd"
// Jvn is Jvn
@@ -254,7 +270,7 @@ const (
// RedHatAPI is RedHat
RedHatAPI CveContentType = "redhat_api"
// DebianSecurityTracker is Debian Secury tracker
// DebianSecurityTracker is Debian Security tracker
DebianSecurityTracker CveContentType = "debian_security_tracker"
// Debian is Debian
@@ -278,20 +294,8 @@ const (
// WPVulnDB is WordPress
WPVulnDB CveContentType = "wpvulndb"
// NodeSec : for JS
NodeSec CveContentType = "node"
// PythonSec : for PHP
PythonSec CveContentType = "python"
// PhpSec : for PHP
PhpSec CveContentType = "php"
// RubySec : for Ruby
RubySec CveContentType = "ruby"
// RustSec : for Rust
RustSec CveContentType = "rust"
// Trivy is Trivy
Trivy CveContentType = "trivy"
// Unknown is Unknown
Unknown CveContentType = "unknown"
@@ -303,7 +307,6 @@ type CveContentTypes []CveContentType
// AllCveContetTypes has all of CveContentTypes
var AllCveContetTypes = CveContentTypes{
Nvd,
NvdXML,
Jvn,
RedHat,
RedHatAPI,
@@ -313,11 +316,7 @@ var AllCveContetTypes = CveContentTypes{
SUSE,
DebianSecurityTracker,
WPVulnDB,
NodeSec,
PythonSec,
PhpSec,
RubySec,
RustSec,
Trivy,
}
// Except returns CveContentTypes except for given args
@@ -348,7 +347,8 @@ type References []Reference
// Reference has a related link of the CVE
type Reference struct {
Source string `json:"source"`
Link string `json:"link"`
RefID string `json:"refID"`
Link string `json:"link,omitempty"`
Source string `json:"source,omitempty"`
RefID string `json:"refID,omitempty"`
Tags []string `json:"tags,omitempty"`
}

View File

@@ -52,25 +52,43 @@ func TestSourceLinks(t *testing.T) {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
References: []Reference{
{
Link: "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
Source: "",
RefID: "",
Tags: []string{"Vendor Advisory"},
},
{
Link: "http://yahoo.com",
Source: "",
RefID: "",
Tags: []string{"Vendor"},
},
},
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: Jvn,
Value: "https://jvn.jp/vu/JVNVU93610402/",
Type: Nvd,
Value: "https://lists.apache.org/thread.html/765be3606d865de513f6df9288842c3cf58b09a987c617a535f2b99d@%3Cusers.tapestry.apache.org%3E",
},
{
Type: NvdXML,
Type: Nvd,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
{
Type: Jvn,
Value: "https://jvn.jp/vu/JVNVU93610402/",
},
},
},
// lang: en
@@ -87,17 +105,9 @@ func TestSourceLinks(t *testing.T) {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
out: []CveContentStr{
{
Type: NvdXML,
Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
{
Type: RedHat,
Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
@@ -120,71 +130,9 @@ func TestSourceLinks(t *testing.T) {
},
}
for i, tt := range tests {
actual := tt.in.cont.SourceLinks(tt.in.lang, "redhat", tt.in.cveID)
actual := tt.in.cont.PrimarySrcURLs(tt.in.lang, "redhat", tt.in.cveID)
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestVendorLink(t *testing.T) {
type in struct {
family string
vinfo VulnInfo
}
var tests = []struct {
in in
out map[string]string
}{
{
in: in{
family: "redhat",
vinfo: VulnInfo{
CveID: "CVE-2017-6074",
CveContents: CveContents{
Jvn: {
Type: Jvn,
SourceLink: "https://jvn.jp/vu/JVNVU93610402/",
},
RedHat: {
Type: RedHat,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
NvdXML: {
Type: NvdXML,
SourceLink: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
},
},
},
},
out: map[string]string{
"RHEL-CVE": "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
{
in: in{
family: "ubuntu",
vinfo: VulnInfo{
CveID: "CVE-2017-6074",
CveContents: CveContents{
RedHat: {
Type: Ubuntu,
SourceLink: "https://access.redhat.com/security/cve/CVE-2017-6074",
},
},
},
},
out: map[string]string{
"Ubuntu-CVE": "http://people.ubuntu.com/~ubuntu-security/cve/CVE-2017-6074",
},
},
}
for _, tt := range tests {
actual := tt.in.vinfo.VendorLinks(tt.in.family)
for k := range tt.out {
if tt.out[k] != actual[k] {
t.Errorf("\nexpected: %s\n actual: %s\n", tt.out[k], actual[k])
}
}
}
}

View File

@@ -3,15 +3,33 @@ package models
import (
"path/filepath"
"github.com/aquasecurity/trivy/pkg/scanner/library"
"github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy-db/pkg/db"
trivyDBTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/detector/library"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/future-architect/vuls/util"
"golang.org/x/xerrors"
"github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/knqyf263/go-version"
// "github.com/aquasecurity/go-dep-parser/pkg/types"
)
// LibraryScanners is an array of LibraryScanner
type LibraryScanners []LibraryScanner
// Find : find by name
func (lss LibraryScanners) Find(path, name string) map[string]types.Library {
filtered := map[string]types.Library{}
for _, ls := range lss {
for _, lib := range ls.Libs {
if ls.Path == path && lib.Name == name {
filtered[ls.Path] = lib
break
}
}
}
return filtered
}
// LibraryScanner has libraries information
type LibraryScanner struct {
Path string
@@ -20,29 +38,19 @@ type LibraryScanner struct {
// Scan : scan target library
func (s LibraryScanner) Scan() ([]VulnInfo, error) {
scanner := library.NewScanner(filepath.Base(string(s.Path)))
if scanner == nil {
return nil, xerrors.New("unknown file type")
}
util.Log.Info("Updating library db...")
err := scanner.UpdateDB()
scanner, err := library.DriverFactory{}.NewDriver(filepath.Base(string(s.Path)))
if err != nil {
return nil, xerrors.Errorf("failed to update %s advisories: %w", scanner.Type(), err)
return nil, xerrors.Errorf("Failed to new a library driver: %w", err)
}
var vulnerabilities []VulnInfo
var vulnerabilities = []VulnInfo{}
for _, pkg := range s.Libs {
v, err := version.NewVersion(pkg.Version)
if err != nil {
util.Log.Debugf("new version cant detected %s@%s", pkg.Name, pkg.Version)
continue
}
tvulns, err := scanner.Detect(pkg.Name, v)
tvulns, err := scanner.Detect(pkg.Name, pkg.Version)
if err != nil {
return nil, xerrors.Errorf("failed to detect %s vulnerabilities: %w", scanner.Type(), err)
}
if len(tvulns) == 0 {
continue
}
vulns := s.convertFanalToVuln(tvulns)
vulnerabilities = append(vulnerabilities, vulns...)
@@ -51,68 +59,55 @@ func (s LibraryScanner) Scan() ([]VulnInfo, error) {
return vulnerabilities, nil
}
func (s LibraryScanner) convertFanalToVuln(tvulns []vulnerability.DetectedVulnerability) (vulns []VulnInfo) {
func (s LibraryScanner) convertFanalToVuln(tvulns []types.DetectedVulnerability) (vulns []VulnInfo) {
for _, tvuln := range tvulns {
vinfo, _ := s.getVulnDetail(tvuln)
vinfo, err := s.getVulnDetail(tvuln)
if err != nil {
util.Log.Debugf("failed to getVulnDetail. err: %s, tvuln: %#v", err, tvuln)
continue
}
vulns = append(vulns, vinfo)
}
return vulns
}
func (s LibraryScanner) getVulnDetail(tvuln vulnerability.DetectedVulnerability) (vinfo VulnInfo, err error) {
details, err := vulnerability.Get(tvuln.VulnerabilityID)
func (s LibraryScanner) getVulnDetail(tvuln types.DetectedVulnerability) (vinfo VulnInfo, err error) {
vul, err := db.Config{}.GetVulnerability(tvuln.VulnerabilityID)
if err != nil {
return vinfo, err
} else if len(details) == 0 {
return vinfo, xerrors.Errorf("Unknown vulnID : %s", tvuln.VulnerabilityID)
}
vinfo.CveID = tvuln.VulnerabilityID
vinfo.CveContents = getCveContents(details)
if tvuln.FixedVersion != "" {
vinfo.CveID = tvuln.VulnerabilityID
vinfo.CveContents = getCveContents(tvuln.VulnerabilityID, vul)
if tvuln.FixedVersion != "" {
vinfo.LibraryFixedIns = []LibraryFixedIn{
{
Key: s.GetLibraryKey(),
Name: tvuln.PkgName,
FixedIn: tvuln.FixedVersion,
Path: s.Path,
},
}
}
return vinfo, nil
}
func getCveContents(details map[string]vulnerability.Vulnerability) (contents map[CveContentType]CveContent) {
func getCveContents(cveID string, vul trivyDBTypes.Vulnerability) (contents map[CveContentType]CveContent) {
contents = map[CveContentType]CveContent{}
for source, detail := range details {
refs := []Reference{}
for _, refURL := range detail.References {
refs = append(refs, Reference{Source: refURL, Link: refURL})
}
content := CveContent{
Type: NewCveContentType(source),
CveID: detail.ID,
Title: detail.Title,
Summary: detail.Description,
Cvss3Score: detail.CvssScoreV3,
Cvss3Severity: string(detail.SeverityV3),
Cvss2Score: detail.CvssScore,
Cvss2Severity: string(detail.Severity),
References: refs,
//SourceLink string `json:"sourceLink"`
//Cvss2Vector string `json:"cvss2Vector"`
//Cvss3Vector string `json:"cvss3Vector"`
//Cvss3Severity string `json:"cvss3Severity"`
//Cpes []Cpe `json:"cpes,omitempty"`
//CweIDs []string `json:"cweIDs,omitempty"`
//Published time.Time `json:"published"`
//LastModified time.Time `json:"lastModified"`
//Mitigation string `json:"mitigation"` // RedHat API
//Optional map[string]string `json:"optional,omitempty"`
}
contents[NewCveContentType(source)] = content
refs := []Reference{}
for _, refURL := range vul.References {
refs = append(refs, Reference{Source: "trivy", Link: refURL})
}
content := CveContent{
Type: Trivy,
CveID: cveID,
Title: vul.Title,
Summary: vul.Description,
Cvss3Severity: string(vul.Severity),
References: refs,
}
contents[Trivy] = content
return contents
}
@@ -122,7 +117,7 @@ var LibraryMap = map[string]string{
"yarn.lock": "node",
"Gemfile.lock": "ruby",
"Cargo.lock": "rust",
"composer.json": "php",
"composer.lock": "php",
"Pipfile.lock": "python",
"poetry.lock": "python",
}
@@ -138,4 +133,5 @@ type LibraryFixedIn struct {
Key string `json:"key,omitempty"`
Name string `json:"name,omitempty"`
FixedIn string `json:"fixedIn,omitempty"`
Path string `json:"path,omitempty"`
}

View File

@@ -1,52 +1,96 @@
package models
import (
"reflect"
"testing"
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestScan(t *testing.T) {
var tests = []struct {
func TestLibraryScanners_Find(t *testing.T) {
type args struct {
path string
pkgs []godeptypes.Library
name string
}
tests := []struct {
name string
lss LibraryScanners
args args
want map[string]types.Library
}{
{
path: "app/package-lock.json",
pkgs: []godeptypes.Library{
name: "single file",
lss: LibraryScanners{
{
Name: "jquery",
Version: "2.2.4",
Path: "/pathA",
Libs: []types.Library{
{
Name: "libA",
Version: "1.0.0",
},
},
},
{
Name: "@babel/traverse",
Version: "7.4.4",
},
args: args{"/pathA", "libA"},
want: map[string]types.Library{
"/pathA": {
Name: "libA",
Version: "1.0.0",
},
},
},
{
name: "multi file",
lss: LibraryScanners{
{
Path: "/pathA",
Libs: []types.Library{
{
Name: "libA",
Version: "1.0.0",
},
},
},
{
Path: "/pathB",
Libs: []types.Library{
{
Name: "libA",
Version: "1.0.5",
},
},
},
},
args: args{"/pathA", "libA"},
want: map[string]types.Library{
"/pathA": {
Name: "libA",
Version: "1.0.0",
},
},
},
{
name: "miss",
lss: LibraryScanners{
{
Path: "/pathA",
Libs: []types.Library{
{
Name: "libA",
Version: "1.0.0",
},
},
},
},
args: args{"/pathA", "libB"},
want: map[string]types.Library{},
},
}
if err := log.InitLogger(false, false); err != nil {
t.Errorf("trivy logger failed")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.lss.Find(tt.args.path, tt.args.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("LibraryScanners.Find() = %v, want %v", got, tt.want)
}
})
}
if err := db.Init(); err != nil {
t.Errorf("trivy db.Init failed")
}
for _, v := range tests {
lib := LibraryScanner{
Path: v.path,
Libs: v.pkgs,
}
actual, err := lib.Scan()
if err != nil {
t.Errorf("error occurred")
}
if len(actual) == 0 {
t.Errorf("no vuln found : actual: %v\n", actual)
}
}
db.Close()
}

View File

@@ -3,6 +3,7 @@ package models
import (
"bytes"
"fmt"
"regexp"
"strings"
"golang.org/x/xerrors"
@@ -173,9 +174,44 @@ type Changelog struct {
// AffectedProcess keep a processes information affected by software update
type AffectedProcess struct {
PID string `json:"pid,omitempty"`
Name string `json:"name,omitempty"`
ListenPorts []string `json:"listenPorts,omitempty"`
PID string `json:"pid,omitempty"`
Name string `json:"name,omitempty"`
ListenPorts []string `json:"listenPorts,omitempty"`
ListenPortStats []PortStat `json:"listenPortStats,omitempty"`
}
// PortStat has the result of parsing the port information to the address and port.
type PortStat struct {
BindAddress string `json:"bindAddress"`
Port string `json:"port"`
PortReachableTo []string `json:"portReachableTo"`
}
// NewPortStat create a PortStat from ipPort str
func NewPortStat(ipPort string) (*PortStat, error) {
if ipPort == "" {
return &PortStat{}, nil
}
sep := strings.LastIndex(ipPort, ":")
if sep == -1 {
return nil, xerrors.Errorf("Failed to parse IP:Port: %s", ipPort)
}
return &PortStat{
BindAddress: ipPort[:sep],
Port: ipPort[sep+1:],
}, nil
}
// HasReachablePort checks if Package.AffectedProcs has PortReachableTo
func (p Package) HasReachablePort() bool {
for _, ap := range p.AffectedProcs {
for _, lp := range ap.ListenPortStats {
if len(lp.PortReachableTo) > 0 {
return true
}
}
}
return false
}
// NeedRestartProcess keep a processes information affected by software update
@@ -227,3 +263,28 @@ func (s SrcPackages) FindByBinName(name string) (*SrcPackage, bool) {
}
return nil, false
}
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the package name.
// e.g. libraspberrypi-dev, rpi-eeprom, python3-rpi.gpio, pi-bluetooth
var raspiPackNamePattern = regexp.MustCompile(`(.*raspberry.*|^rpi.*|.*-rpi.*|^pi-.*)`)
// raspiPackNamePattern is a regular expression pattern to detect the Raspberry Pi specific package from the version.
// e.g. ffmpeg 7:4.1.4-1+rpt7~deb10u1, vlc 3.0.10-0+deb10u1+rpt2
var raspiPackVersionPattern = regexp.MustCompile(`.+\+rp(t|i)\d+`)
// raspiPackNameList is a package name array of Raspberry Pi specific packages that are difficult to detect with regular expressions.
var raspiPackNameList = []string{"piclone", "pipanel", "pishutdown", "piwiz", "pixflat-icons"}
// IsRaspbianPackage judges whether it is a package related to Raspberry Pi from the package name and version
func IsRaspbianPackage(name, version string) bool {
if raspiPackNamePattern.MatchString(name) || raspiPackVersionPattern.MatchString(version) {
return true
}
for _, n := range raspiPackNameList {
if n == name {
return true
}
}
return false
}

View File

@@ -297,3 +297,134 @@ func TestPackage_FormatVersionFromTo(t *testing.T) {
})
}
}
func Test_IsRaspbianPackage(t *testing.T) {
type args struct {
name string
ver string
}
tests := []struct {
name string
in []args
expect []bool
}{
{
name: "nameRegExp",
in: []args{
{
name: "libraspberrypi-dev",
ver: "1.20200811-1",
},
{
name: "rpi-eeprom",
ver: "7.10-1",
},
{
name: "python3-rpi.gpio",
ver: "0.7.0-0.1~bpo10+1",
},
{
name: "arping",
ver: "2.19-6",
},
{
name: "pi-bluetooth",
ver: "0.1.14",
},
},
expect: []bool{true, true, true, false, true, false},
},
{
name: "verRegExp",
in: []args{
{
name: "ffmpeg",
ver: "7:4.1.6-1~deb10u1+rpt1",
},
{
name: "gcc",
ver: "4:8.3.0-1+rpi2",
},
},
expect: []bool{true, true},
},
{
name: "nameList",
in: []args{
{
name: "piclone",
ver: "0.16",
},
},
expect: []bool{true},
},
{
name: "debianPackage",
in: []args{
{
name: "apt",
ver: "1.8.2.1",
},
},
expect: []bool{false},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for i, p := range tt.in {
ret := IsRaspbianPackage(p.name, p.ver)
if !reflect.DeepEqual(ret, tt.expect[i]) {
t.Errorf("[%s->%s] expected: %t, actual: %t, in: %#v", tt.name, tt.in[i].name, tt.expect[i], ret, tt.in[i])
}
}
})
}
}
func Test_parseListenPorts(t *testing.T) {
tests := []struct {
name string
args string
expect PortStat
}{{
name: "empty",
args: "",
expect: PortStat{
BindAddress: "",
Port: "",
},
}, {
name: "normal",
args: "127.0.0.1:22",
expect: PortStat{
BindAddress: "127.0.0.1",
Port: "22",
},
}, {
name: "asterisk",
args: "*:22",
expect: PortStat{
BindAddress: "*",
Port: "22",
},
}, {
name: "ipv6_loopback",
args: "[::1]:22",
expect: PortStat{
BindAddress: "[::1]",
Port: "22",
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
listenPort, err := NewPortStat(tt.args)
if err != nil {
t.Errorf("unexpected error occurred: %s", err)
} else if !reflect.DeepEqual(*listenPort, tt.expect) {
t.Errorf("base.parseListenPorts() = %v, want %v", *listenPort, tt.expect)
}
})
}
}

View File

@@ -3,6 +3,7 @@ package models
import (
"bytes"
"fmt"
"reflect"
"regexp"
"strings"
"time"
@@ -24,7 +25,6 @@ type ScanResult struct {
Family string `json:"family"`
Release string `json:"release"`
Container Container `json:"container"`
Image Image `json:"image"`
Platform Platform `json:"platform"`
IPv4Addrs []string `json:"ipv4Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
IPv6Addrs []string `json:"ipv6Addrs,omitempty"` // only global unicast address (https://golang.org/pkg/net/#IP.IsGlobalUnicast)
@@ -49,7 +49,7 @@ type ScanResult struct {
Packages Packages `json:"packages"`
SrcPackages SrcPackages `json:",omitempty"`
WordPressPackages *WordPressPackages `json:",omitempty"`
LibraryScanners []LibraryScanner `json:"libScanners"`
LibraryScanners LibraryScanners `json:"libraries,omitempty"`
CweDict CweDict `json:"cweDict,omitempty"`
Optional map[string]interface{} `json:",omitempty"`
Config struct {
@@ -196,7 +196,7 @@ func (r ScanResult) FilterUnfixed() ScanResult {
// FilterIgnorePkgs is filter function.
func (r ScanResult) FilterIgnorePkgs() ScanResult {
ignorePkgsRegexps := []string{}
var ignorePkgsRegexps []string
if len(r.Container.Name) == 0 {
ignorePkgsRegexps = config.Conf.Servers[r.ServerName].IgnorePkgsRegexp
} else {
@@ -217,7 +217,7 @@ func (r ScanResult) FilterIgnorePkgs() ScanResult {
for _, pkgRegexp := range ignorePkgsRegexps {
re, err := regexp.Compile(pkgRegexp)
if err != nil {
util.Log.Errorf("Faild to parse %s. err: %+v", pkgRegexp, err)
util.Log.Errorf("Failed to parse %s. err: %+v", pkgRegexp, err)
continue
} else {
regexps = append(regexps, re)
@@ -340,20 +340,21 @@ func (r ScanResult) FormatServerName() (name string) {
return
}
// FormatTextReportHeadedr returns header of text report
func (r ScanResult) FormatTextReportHeadedr() string {
// FormatTextReportHeader returns header of text report
func (r ScanResult) FormatTextReportHeader() string {
var buf bytes.Buffer
for i := 0; i < len(r.ServerInfo()); i++ {
buf.WriteString("=")
}
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s\n",
return fmt.Sprintf("%s\n%s\n%s, %s, %s, %s, %s, %s\n",
r.ServerInfo(),
buf.String(),
r.ScannedCves.FormatCveSummary(),
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary(),
r.FormatExploitCveSummary(),
r.FormatMetasploitCveSummary(),
r.FormatAlertSummary(),
)
}
@@ -389,7 +390,18 @@ func (r ScanResult) FormatExploitCveSummary() string {
return fmt.Sprintf("%d exploits", nExploitCve)
}
// FormatAlertSummary returns a summary of XCERT alerts
// FormatMetasploitCveSummary returns a summary of exploit cve
func (r ScanResult) FormatMetasploitCveSummary() string {
nMetasploitCve := 0
for _, vuln := range r.ScannedCves {
if 0 < len(vuln.Metasploits) {
nMetasploitCve++
}
}
return fmt.Sprintf("%d modules", nMetasploitCve)
}
// FormatAlertSummary returns a summary of CERT alerts
func (r ScanResult) FormatAlertSummary() string {
jaCnt := 0
enCnt := 0
@@ -405,6 +417,10 @@ func (r ScanResult) FormatAlertSummary() string {
}
func (r ScanResult) isDisplayUpdatableNum() bool {
if r.Family == config.FreeBSD {
return false
}
var mode config.ScanMode
s, _ := config.Conf.Servers[r.ServerName]
mode = s.Mode
@@ -435,11 +451,6 @@ func (r ScanResult) IsContainer() bool {
return 0 < len(r.Container.ContainerID)
}
// IsImage returns whether this ServerInfo is about container
func (r ScanResult) IsImage() bool {
return 0 < len(r.Image.Name)
}
// IsDeepScanMode checks if the scan mode is deep scan mode.
func (r ScanResult) IsDeepScanMode() bool {
for _, s := range r.Config.Scan.Servers {
@@ -461,15 +472,56 @@ type Container struct {
UUID string `json:"uuid"`
}
// Image has Container information
type Image struct {
Name string `json:"name"`
Tag string `json:"tag"`
Digest string `json:"digest"`
}
// Platform has platform information
type Platform struct {
Name string `json:"name"` // aws or azure or gcp or other...
InstanceID string `json:"instanceID"`
}
// RemoveRaspbianPackFromResult is for Raspberry Pi and removes the Raspberry Pi dedicated package from ScanResult.
func (r ScanResult) RemoveRaspbianPackFromResult() ScanResult {
if r.Family != config.Raspbian {
return r
}
result := r
packs := make(Packages)
for _, pack := range r.Packages {
if !IsRaspbianPackage(pack.Name, pack.Version) {
packs[pack.Name] = pack
}
}
srcPacks := make(SrcPackages)
for _, pack := range r.SrcPackages {
if !IsRaspbianPackage(pack.Name, pack.Version) {
srcPacks[pack.Name] = pack
}
}
result.Packages = packs
result.SrcPackages = srcPacks
return result
}
// ClearFields clears a given fields of ScanResult
func (r ScanResult) ClearFields(targetTagNames []string) ScanResult {
if len(targetTagNames) == 0 {
return r
}
target := map[string]bool{}
for _, n := range targetTagNames {
target[strings.ToLower(n)] = true
}
t := reflect.ValueOf(r).Type()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
jsonValue := strings.Split(f.Tag.Get("json"), ",")[0]
if ok := target[strings.ToLower(jsonValue)]; ok {
vv := reflect.New(f.Type).Elem().Interface()
reflect.ValueOf(&r).Elem().FieldByName(f.Name).Set(reflect.ValueOf(vv))
}
}
return r
}

View File

@@ -27,7 +27,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -38,7 +38,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0002",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -49,7 +49,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -71,7 +71,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
@@ -82,7 +82,7 @@ func TestFilterByCvssOver(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NvdXML,
Type: Nvd,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
@@ -688,7 +688,7 @@ func TestIsDisplayUpdatableNum(t *testing.T) {
{
mode: []byte{config.Fast},
family: config.FreeBSD,
expected: true,
expected: false,
},
{
mode: []byte{config.Fast},

View File

@@ -1,3 +1,5 @@
// +build !scanner
package models
import (
@@ -6,60 +8,18 @@ import (
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
)
// ConvertNvdXMLToModel convert NVD to CveContent
func ConvertNvdXMLToModel(cveID string, nvd *cvedict.NvdXML) *CveContent {
if nvd == nil {
return nil
}
var cpes []Cpe
for _, c := range nvd.Cpes {
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
}
var refs []Reference
for _, r := range nvd.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
})
}
cweIDs := []string{}
for _, cid := range nvd.Cwes {
cweIDs = append(cweIDs, cid.CweID)
}
return &CveContent{
Type: Nvd,
CveID: cveID,
Summary: nvd.Summary,
Cvss2Score: nvd.Cvss2.BaseScore,
Cvss2Vector: nvd.Cvss2.VectorString,
Cvss2Severity: nvd.Cvss2.Severity,
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
// Cpes: cpes,
CweIDs: cweIDs,
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}
}
// ConvertJvnToModel convert JVN to CveContent
func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
if jvn == nil {
return nil
}
var cpes []Cpe
for _, c := range jvn.Cpes {
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
}
// var cpes = []Cpe{}
// for _, c := range jvn.Cpes {
// cpes = append(cpes, Cpe{
// FormattedString: c.FormattedString,
// URI: c.URI,
// })
// }
refs := []Reference{}
for _, r := range jvn.References {
@@ -89,24 +49,41 @@ func ConvertJvnToModel(cveID string, jvn *cvedict.Jvn) *CveContent {
}
// ConvertNvdJSONToModel convert NVD to CveContent
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) (*CveContent, []Exploit, []Mitigation) {
if nvd == nil {
return nil
}
var cpes []Cpe
for _, c := range nvd.Cpes {
cpes = append(cpes, Cpe{
FormattedString: c.FormattedString,
URI: c.URI,
})
return nil, nil, nil
}
// var cpes = []Cpe{}
// for _, c := range nvd.Cpes {
// cpes = append(cpes, Cpe{
// FormattedString: c.FormattedString,
// URI: c.URI,
// })
// }
var refs []Reference
refs := []Reference{}
exploits := []Exploit{}
mitigations := []Mitigation{}
for _, r := range nvd.References {
refs = append(refs, Reference{
Link: r.Link,
Source: r.Source,
Tags: strings.Split(r.Tags, ","),
})
if strings.Contains(r.Tags, "Exploit") {
exploits = append(exploits, Exploit{
//TODO Add const to here
// https://github.com/vulsio/go-exploitdb/blob/master/models/exploit.go#L13-L18
ExploitType: "nvd",
URL: r.Link,
})
}
if strings.Contains(r.Tags, "Mitigation") {
mitigations = append(mitigations, Mitigation{
CveContentType: Nvd,
URL: r.Link,
})
}
}
cweIDs := []string{}
@@ -135,5 +112,5 @@ func ConvertNvdJSONToModel(cveID string, nvd *cvedict.NvdJSON) *CveContent {
References: refs,
Published: nvd.PublishedDate,
LastModified: nvd.LastModifiedDate,
}
}, exploits, mitigations
}

View File

@@ -134,7 +134,7 @@ func (ps PackageFixStatuses) Sort() {
return
}
// PackageFixStatus has name and other status abount the package
// PackageFixStatus has name and other status about the package
type PackageFixStatus struct {
Name string `json:"name,omitempty"`
NotFixedYet bool `json:"notFixedYet,omitempty"`
@@ -147,9 +147,11 @@ type VulnInfo struct {
CveID string `json:"cveID,omitempty"`
Confidences Confidences `json:"confidences,omitempty"`
AffectedPackages PackageFixStatuses `json:"affectedPackages,omitempty"`
DistroAdvisories DistroAdvisories `json:"distroAdvisories,omitempty"` // for Aamazon, RHEL, FreeBSD
DistroAdvisories DistroAdvisories `json:"distroAdvisories,omitempty"` // for Amazon, RHEL, FreeBSD
CveContents CveContents `json:"cveContents,omitempty"`
Exploits []Exploit `json:"exploits,omitempty"`
Metasploits []Metasploit `json:"metasploits,omitempty"`
Mitigations []Mitigation `json:"mitigations,omitempty"`
AlertDict AlertDict `json:"alertDict,omitempty"`
CpeURIs []string `json:"cpeURIs,omitempty"` // CpeURIs related to this CVE defined in config.toml
GitHubSecurityAlerts GitHubSecurityAlerts `json:"gitHubSecurityAlerts,omitempty"`
@@ -159,7 +161,7 @@ type VulnInfo struct {
VulnType string `json:"vulnType,omitempty"`
}
// Alert has XCERT alert information
// Alert has CERT alert information
type Alert struct {
URL string `json:"url,omitempty"`
Title string `json:"title,omitempty"`
@@ -200,6 +202,14 @@ type GitHubSecurityAlert struct {
// LibraryFixedIns is a list of Library's FixedIn
type LibraryFixedIns []LibraryFixedIn
// Names return a slice of names
func (lfs LibraryFixedIns) Names() (names []string) {
for _, lf := range lfs {
names = append(names, lf.Name)
}
return names
}
// WpPackageFixStats is a list of WpPackageFixStatus
type WpPackageFixStats []WpPackageFixStatus
@@ -224,7 +234,7 @@ func (g WpPackages) Add(pkg WpPackage) WpPackages {
return append(g, pkg)
}
// Titles returns tilte (TUI)
// Titles returns title (TUI)
func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
if lang == "ja" {
if cont, found := v.CveContents[Jvn]; found && 0 < len(cont.Title) {
@@ -237,7 +247,7 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
values = append(values, CveContentStr{RedHatAPI, cont.Title})
}
order := CveContentTypes{Nvd, NvdXML, NewCveContentType(myFamily)}
order := CveContentTypes{Trivy, Nvd, NewCveContentType(myFamily)}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
// Only JVN has meaningful title. so return first 100 char of summary
@@ -277,7 +287,7 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
}
}
order := CveContentTypes{NewCveContentType(myFamily), Nvd, NvdXML}
order := CveContentTypes{Trivy, NewCveContentType(myFamily), Nvd}
order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Summary) {
@@ -313,30 +323,9 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
return
}
// Mitigations returns mitigations
func (v VulnInfo) Mitigations(myFamily string) (values []CveContentStr) {
order := CveContentTypes{RedHatAPI}
for _, ctype := range order {
if cont, found := v.CveContents[ctype]; found && 0 < len(cont.Mitigation) {
values = append(values, CveContentStr{
Type: ctype,
Value: cont.Mitigation,
})
}
}
if len(values) == 0 {
return []CveContentStr{{
Type: Unknown,
Value: "-",
}}
}
return
}
// Cvss2Scores returns CVSS V2 Scores
func (v VulnInfo) Cvss2Scores(myFamily string) (values []CveContentCvss) {
order := []CveContentType{Nvd, NvdXML, RedHatAPI, RedHat, Jvn}
order := []CveContentType{Nvd, RedHatAPI, RedHat, Jvn}
if myFamily != config.RedHat && myFamily != config.CentOS {
order = append(order, NewCveContentType(myFamily))
}
@@ -415,6 +404,18 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
})
}
}
if cont, found := v.CveContents[Trivy]; found && cont.Cvss3Severity != "" {
values = append(values, CveContentCvss{
Type: Trivy,
Value: Cvss{
Type: CVSS3,
Score: severityToV2ScoreRoughly(cont.Cvss3Severity),
Severity: strings.ToUpper(cont.Cvss3Severity),
},
})
}
return
}
@@ -462,7 +463,7 @@ func (v VulnInfo) MaxCvssScore() CveContentCvss {
// MaxCvss2Score returns Max CVSS V2 Score
func (v VulnInfo) MaxCvss2Score() CveContentCvss {
order := []CveContentType{Nvd, NvdXML, RedHat, RedHatAPI, Jvn}
order := []CveContentType{Nvd, RedHat, RedHatAPI, Jvn}
max := 0.0
value := CveContentCvss{
Type: Unknown,
@@ -488,7 +489,7 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
}
// If CVSS score isn't on NVD, RedHat and JVN, use OVAL and advisory Severity.
// Convert severity to cvss srore roughly, then returns max severity.
// Convert severity to cvss score roughly, then returns max severity.
// Only Ubuntu, RedHat and Oracle have severity data in OVAL.
order = []CveContentType{Ubuntu, RedHat, Oracle}
for _, ctype := range order {
@@ -594,10 +595,10 @@ type CveContentCvss struct {
type CvssType string
const (
// CVSS2 means CVSS vesion2
// CVSS2 means CVSS version2
CVSS2 CvssType = "2"
// CVSS3 means CVSS vesion3
// CVSS3 means CVSS version3
CVSS3 CvssType = "3"
)
@@ -624,15 +625,6 @@ func (c Cvss) Format() string {
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/
@@ -668,70 +660,6 @@ func (v VulnInfo) FormatMaxCvssScore() string {
max.Type)
}
// Cvss2CalcURL returns CVSS v2 caluclator's URL
func (v VulnInfo) Cvss2CalcURL() string {
return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
}
// Cvss3CalcURL returns CVSS v3 caluclator's URL
func (v VulnInfo) Cvss3CalcURL() string {
return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
}
// VendorLinks returns links of vendor support's URL
func (v VulnInfo) VendorLinks(family string) map[string]string {
links := map[string]string{}
if strings.HasPrefix(v.CveID, "WPVDBID") {
links["WPVulnDB"] = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s",
strings.TrimPrefix(v.CveID, "WPVDBID-"))
return links
}
switch family {
case config.RedHat, config.CentOS:
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
for _, advisory := range v.DistroAdvisories {
aidURL := strings.Replace(advisory.AdvisoryID, ":", "-", -1)
links[advisory.AdvisoryID] = fmt.Sprintf("https://rhn.redhat.com/errata/%s.html", aidURL)
}
return links
case config.Oracle:
links["Oracle-CVE"] = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.CveID)
for _, advisory := range v.DistroAdvisories {
links[advisory.AdvisoryID] =
fmt.Sprintf("https://linux.oracle.com/errata/%s.html", advisory.AdvisoryID)
}
return links
case config.Amazon:
links["RHEL-CVE"] = "https://access.redhat.com/security/cve/" + v.CveID
for _, advisory := range v.DistroAdvisories {
if strings.HasPrefix(advisory.AdvisoryID, "ALAS2") {
links[advisory.AdvisoryID] =
fmt.Sprintf("https://alas.aws.amazon.com/AL2/%s.html",
strings.Replace(advisory.AdvisoryID, "ALAS2", "ALAS", -1))
} else {
links[advisory.AdvisoryID] =
fmt.Sprintf("https://alas.aws.amazon.com/%s.html", advisory.AdvisoryID)
}
}
return links
case config.Ubuntu:
links["Ubuntu-CVE"] = "http://people.ubuntu.com/~ubuntu-security/cve/" + v.CveID
return links
case config.Debian:
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
case config.SUSEEnterpriseServer:
links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
case config.FreeBSD:
for _, advisory := range v.DistroAdvisories {
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
}
return links
}
return links
}
// DistroAdvisories is a list of DistroAdvisory
type DistroAdvisories []DistroAdvisory
@@ -780,7 +708,22 @@ type Exploit struct {
BinaryURL *string `json:"binaryURL,omitempty"`
}
// AlertDict has target cve's JPCERT and USCERT alert data
// Metasploit :
type Metasploit struct {
Name string `json:"name"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
URLs []string `json:",omitempty"`
}
// Mitigation has a link and content
type Mitigation struct {
CveContentType CveContentType `json:"cveContentType,omitempty"`
Mitigation string `json:"mitigation,omitempty"`
URL string `json:"url,omitempty"`
}
// AlertDict has target cve JPCERT and USCERT alert data
type AlertDict struct {
Ja []Alert `json:"ja"`
En []Alert `json:"en"`
@@ -801,7 +744,7 @@ func (a AlertDict) FormatSource() string {
// Confidences is a list of Confidence
type Confidences []Confidence
// AppendIfMissing appends confidence to the list if missiong
// AppendIfMissing appends confidence to the list if missing
func (cs *Confidences) AppendIfMissing(confidence Confidence) {
for _, c := range *cs {
if c.DetectionMethod == confidence.DetectionMethod {
@@ -819,7 +762,7 @@ func (cs Confidences) SortByConfident() Confidences {
return cs
}
// Confidence is a ranking how confident the CVE-ID was deteted correctly
// Confidence is a ranking how confident the CVE-ID was detected correctly
// Score: 0 - 100
type Confidence struct {
Score int `json:"score"`
@@ -855,6 +798,9 @@ const (
// DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch
DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch"
// TrivyMatchStr is a String representation of Trivy
TrivyMatchStr = "TrivyMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
ChangelogExactMatchStr = "ChangelogExactMatch"
@@ -875,33 +821,36 @@ const (
)
var (
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
// CpeNameMatch is a ranking how confident the CVE-ID was detected correctly
CpeNameMatch = Confidence{100, CpeNameMatchStr, 1}
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was detected correctly
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr, 2}
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
// PkgAuditMatch is a ranking how confident the CVE-ID was detected correctly
PkgAuditMatch = Confidence{100, PkgAuditMatchStr, 2}
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
// OvalMatch is a ranking how confident the CVE-ID was detected correctly
OvalMatch = Confidence{100, OvalMatchStr, 0}
// RedHatAPIMatch ranking how confident the CVE-ID was deteted correctly
// RedHatAPIMatch ranking how confident the CVE-ID was detected correctly
RedHatAPIMatch = Confidence{100, RedHatAPIStr, 0}
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was deteted correctly
// DebianSecurityTrackerMatch ranking how confident the CVE-ID was detected correctly
DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0}
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
// TrivyMatch ranking how confident the CVE-ID was detected correctly
TrivyMatch = Confidence{100, TrivyMatchStr, 0}
// ChangelogExactMatch is a ranking how confident the CVE-ID was detected correctly
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr, 3}
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
// ChangelogLenientMatch is a ranking how confident the CVE-ID was detected correctly
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr, 4}
// GitHubMatch is a ranking how confident the CVE-ID was deteted correctly
// GitHubMatch is a ranking how confident the CVE-ID was detected correctly
GitHubMatch = Confidence{97, GitHubMatchStr, 2}
// WPVulnDBMatch is a ranking how confident the CVE-ID was deteted correctly
// WPVulnDBMatch is a ranking how confident the CVE-ID was detected correctly
WPVulnDBMatch = Confidence{100, WPVulnDBMatchStr, 0}
)

View File

@@ -28,10 +28,10 @@ func TestTitles(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
@@ -42,7 +42,7 @@ func TestTitles(t *testing.T) {
Value: "Title1",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
{
@@ -65,17 +65,17 @@ func TestTitles(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
},
out: []CveContentStr{
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
{
@@ -130,10 +130,10 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
@@ -148,7 +148,7 @@ func TestSummaries(t *testing.T) {
Value: "Summary RedHat",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
},
@@ -168,10 +168,10 @@ func TestSummaries(t *testing.T) {
Type: RedHat,
Summary: "Summary RedHat",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Summary: "Summary NVD",
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
@@ -182,7 +182,7 @@ func TestSummaries(t *testing.T) {
Value: "Summary RedHat",
},
{
Type: NvdXML,
Type: Nvd,
Value: "Summary NVD",
},
},
@@ -219,8 +219,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -232,8 +232,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 2.0,
},
},
@@ -241,8 +241,8 @@ func TestCountGroupBySeverity(t *testing.T) {
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 5.0,
},
},
@@ -280,8 +280,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -293,8 +293,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
@@ -308,8 +308,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
},
RedHat: {
@@ -321,8 +321,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -339,8 +339,8 @@ func TestToSortedSlice(t *testing.T) {
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -372,8 +372,8 @@ func TestToSortedSlice(t *testing.T) {
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 6.0,
},
RedHat: {
@@ -456,8 +456,8 @@ func TestCvss2Scores(t *testing.T) {
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss2Severity: "HIGH",
@@ -466,7 +466,7 @@ func TestCvss2Scores(t *testing.T) {
},
out: []CveContentCvss{
{
Type: NvdXML,
Type: Nvd,
Value: Cvss{
Type: CVSS2,
Score: 8.1,
@@ -528,11 +528,11 @@ func TestMaxCvss2Scores(t *testing.T) {
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
@@ -602,8 +602,8 @@ func TestCvss3Scores(t *testing.T) {
Cvss3Score: 8.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
Cvss2Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
Cvss2Severity: "HIGH",
@@ -611,6 +611,13 @@ func TestCvss3Scores(t *testing.T) {
},
},
out: []CveContentCvss{
{
Type: Nvd,
Value: Cvss{
Type: CVSS3,
Score: 0.0,
},
},
{
Type: RedHat,
Value: Cvss{
@@ -692,8 +699,8 @@ func TestMaxCvssScores(t *testing.T) {
{
in: VulnInfo{
CveContents: CveContents{
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss3Score: 7.0,
},
RedHat: {
@@ -755,15 +762,15 @@ func TestMaxCvssScores(t *testing.T) {
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 7.0,
Cvss2Severity: "HIGH",
},
},
},
out: CveContentCvss{
Type: NvdXML,
Type: Nvd,
Value: Cvss{
Type: CVSS2,
Score: 7.0,
@@ -798,8 +805,8 @@ func TestMaxCvssScores(t *testing.T) {
Type: Ubuntu,
Cvss2Severity: "MEDIUM",
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 4.0,
Cvss2Severity: "MEDIUM",
},
@@ -811,7 +818,7 @@ func TestMaxCvssScores(t *testing.T) {
},
},
out: CveContentCvss{
Type: NvdXML,
Type: Nvd,
Value: Cvss{
Type: CVSS2,
Score: 4,
@@ -857,10 +864,10 @@ func TestFormatMaxCvssScore(t *testing.T) {
Cvss2Severity: "HIGH",
Cvss3Score: 8.0,
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
// Severity is NIOT included in NVD
// Severity is NOT included in NVD
},
},
},
@@ -881,8 +888,8 @@ func TestFormatMaxCvssScore(t *testing.T) {
Cvss3Severity: "HIGH",
Cvss3Score: 9.9,
},
NvdXML: {
Type: NvdXML,
Nvd: {
Type: Nvd,
Cvss2Score: 8.1,
},
},
@@ -922,7 +929,7 @@ func TestSortPackageStatues(t *testing.T) {
}
}
func TestStorePackageStatueses(t *testing.T) {
func TestStorePackageStatuses(t *testing.T) {
var tests = []struct {
pkgstats PackageFixStatuses
in PackageFixStatus
@@ -985,7 +992,7 @@ func TestAppendIfMissing(t *testing.T) {
}
}
func TestSortByConfiden(t *testing.T) {
func TestSortByConfident(t *testing.T) {
var tests = []struct {
in Confidences
out Confidences

1
msf/empty.go Normal file
View File

@@ -0,0 +1 @@
package msf

75
msf/msf.go Normal file
View File

@@ -0,0 +1,75 @@
// +build !scanner
package msf
import (
"fmt"
"net/http"
cnf "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/parnurzeal/gorequest"
"github.com/takuzoo3868/go-msfdb/db"
metasploitmodels "github.com/takuzoo3868/go-msfdb/models"
"golang.org/x/xerrors"
)
// FillWithMetasploit fills metasploit module information that has in module
func FillWithMetasploit(driver db.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
if driver == nil {
return 0, nil
}
for cveID, vuln := range r.ScannedCves {
if cveID == "" {
continue
}
ms := driver.GetModuleByCveID(cveID)
if len(ms) == 0 {
continue
}
modules := ConvertToModels(ms)
vuln.Metasploits = modules
r.ScannedCves[cveID] = vuln
nMetasploitCve++
}
return nMetasploitCve, nil
}
// ConvertToModels converts gost model to vuls model
func ConvertToModels(ms []*metasploitmodels.Metasploit) (modules []models.Metasploit) {
for _, m := range ms {
var links []string
if 0 < len(m.References) {
for _, u := range m.References {
links = append(links, u.Link)
}
}
module := models.Metasploit{
Name: m.Name,
Title: m.Title,
Description: m.Description,
URLs: links,
}
modules = append(modules, module)
}
return modules
}
// CheckHTTPHealth do health check
func CheckHTTPHealth() error {
if !cnf.Conf.Metasploit.IsFetchViaHTTP() {
return nil
}
url := fmt.Sprintf("%s/health", cnf.Conf.Metasploit.URL)
var errs []error
var resp *http.Response
resp, _, errs = gorequest.New().Get(url).End()
// resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return xerrors.Errorf("Failed to connect to metasploit server. url: %s, errs: %w", url, errs)
}
return nil
}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -38,7 +40,13 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
defPacks.def.Debian.CveID)
cveContents = models.CveContents{}
}
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
if r.Family != config.Raspbian {
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
} else {
if len(vinfo.Confidences) == 0 {
vinfo.Confidences.AppendIfMissing(models.OvalMatch)
}
}
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
@@ -53,7 +61,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
}
// Update package status of source packages.
// In the case of Debian based Linux, sometimes source package name is difined as affected package in OVAL.
// In the case of Debian based Linux, sometimes source package name is defined as affected package in OVAL.
// To display binary package name showed in apt-get, need to convert source name to binary name.
for binName := range defPacks.binpkgFixstat {
if srcPack, ok := r.SrcPackages.FindByBinName(binName); ok {
@@ -76,7 +84,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
}
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
var refs []models.Reference
refs := []models.Reference{}
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
@@ -132,12 +140,28 @@ func (o Debian) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
var relatedDefs ovalResult
if config.Conf.OvalDict.IsFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return 0, err
if r.Family != config.Raspbian {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return 0, err
}
} else {
// OVAL does not support Package for Raspbian, so skip it.
result := r.RemoveRaspbianPackFromResult()
if relatedDefs, err = getDefsByPackNameViaHTTP(&result); err != nil {
return 0, err
}
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
if r.Family != config.Raspbian {
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, r); err != nil {
return 0, err
}
} else {
// OVAL does not support Package for Raspbian, so skip it.
result := r.RemoveRaspbianPackFromResult()
if relatedDefs, err = getDefsByPackNameFromOvalDB(driver, &result); err != nil {
return 0, err
}
}
}
@@ -293,6 +317,34 @@ func (o Ubuntu) FillWithOval(driver db.DB, r *models.ScanResult) (nCVEs int, err
"linux",
}
return o.fillWithOval(driver, r, kernelNamesInOval)
case "20":
kernelNamesInOval := []string{
"linux-aws",
"linux-azure",
"linux-gcp",
"linux-kvm",
"linux-meta",
"linux-meta-aws",
"linux-meta-azure",
"linux-meta-gcp",
"linux-meta-kvm",
"linux-meta-oem-5.6",
"linux-meta-oracle",
"linux-meta-raspi",
"linux-meta-riscv",
"linux-oem-5.6",
"linux-oracle",
"linux-raspi",
"linux-raspi2",
"linux-riscv",
"linux-signed",
"linux-signed-azure",
"linux-signed-gcp",
"linux-signed-oem-5.6",
"linux-signed-oracle",
"linux",
}
return o.fillWithOval(driver, r, kernelNamesInOval)
}
return 0, fmt.Errorf("Ubuntu %s is not support for now", r.Release)
}
@@ -309,7 +361,7 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
if v, ok := r.Packages[linuxImage]; ok {
runningKernelVersion = v.Version
} else {
util.Log.Warnf("Unable to detect vulns of running kernel because the version of the runnning kernel is unknown. server: %s",
util.Log.Warnf("Unable to detect vulns of running kernel because the version of the running kernel is unknown. server: %s",
r.ServerName)
}
@@ -337,13 +389,13 @@ func (o Ubuntu) fillWithOval(driver db.DB, r *models.ScanResult, kernelNamesInOv
}
for srcPackName, srcPack := range r.SrcPackages {
copiedSourcePkgs[srcPackName] = srcPack
targetBianryNames := []string{}
targetBinaryNames := []string{}
for _, n := range srcPack.BinaryNames {
if n == kernelPkgInOVAL || !strings.HasPrefix(n, "linux-") {
targetBianryNames = append(targetBianryNames, n)
targetBinaryNames = append(targetBinaryNames, n)
}
}
srcPack.BinaryNames = targetBianryNames
srcPack.BinaryNames = targetBinaryNames
r.SrcPackages[srcPackName] = srcPack
}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -34,7 +36,7 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
},
},
binpkgFixstat: map[string]fixStat{
"packB": fixStat{
"packB": {
notFixedYet: true,
fixedIn: "1.0.0",
},

1
oval/empty.go Normal file
View File

@@ -0,0 +1 @@
package oval

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -65,7 +67,7 @@ func (b Base) CheckIfOvalFetched(driver db.DB, osFamily, release string) (fetche
}
count := 0
if err := json.Unmarshal([]byte(body), &count); err != nil {
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
}
return 0 < count, nil
}
@@ -83,7 +85,7 @@ func (b Base) CheckIfOvalFresh(driver db.DB, osFamily, release string) (ok bool,
}
if err := json.Unmarshal([]byte(body), &lastModified); err != nil {
return false, xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
return false, xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
}
}

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -100,7 +102,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) (nCVEs int)
cveContents := vinfo.CveContents
if v, ok := vinfo.CveContents[ctype]; ok {
if v.LastModified.After(ovalContent.LastModified) {
util.Log.Debugf("%s, OvalID: %d ignroed: ",
util.Log.Debugf("%s, OvalID: %d ignored: ",
cve.CveID, defPacks.def.ID)
} else {
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -111,7 +113,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
},
},
binpkgFixstat: map[string]fixStat{
"packB": fixStat{
"packB": {
notFixedYet: true,
fixedIn: "1.0.0",
},

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -86,7 +88,7 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
}
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
var refs []models.Reference
refs := []models.Reference{}
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -11,6 +13,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
apkver "github.com/knqyf263/go-apk-version"
debver "github.com/knqyf263/go-deb-version"
rpmver "github.com/knqyf263/go-rpm-version"
"github.com/kotakanbe/goval-dictionary/db"
@@ -211,7 +214,7 @@ func httpGet(url string, req request, resChan chan<- response, errChan chan<- er
defs := []ovalmodels.Definition{}
if err := json.Unmarshal([]byte(body), &defs); err != nil {
errChan <- xerrors.Errorf("Failed to Unmarshall. body: %s, err: %w", body, err)
errChan <- xerrors.Errorf("Failed to Unmarshal. body: %s, err: %w", body, err)
return
}
resChan <- response{
@@ -275,6 +278,9 @@ func getDefsByPackNameFromOvalDB(driver db.DB, r *models.ScanResult) (relatedDef
}
func major(version string) string {
if version == "" {
return ""
}
ss := strings.SplitN(version, ":", 2)
ver := ""
if len(ss) == 1 {
@@ -326,13 +332,14 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
config.Amazon,
config.SUSEEnterpriseServer,
config.Debian,
config.Ubuntu:
config.Ubuntu,
config.Raspbian:
// Use fixed state in OVAL for these distros.
return true, false, ovalPack.Version
}
// But CentOS can't judge whether fixed or unfixed.
// Because fixed state in RHEL's OVAL is different.
// Because fixed state in RHEL OVAL is different.
// So, it have to be judged version comparison.
// `offline` or `fast` scan mode can't get a updatable version.
@@ -358,15 +365,27 @@ func isOvalDefAffected(def ovalmodels.Definition, req request, family string, ru
var centosVerPattern = regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
var esVerPattern = regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
func lessThan(family, newVer string, packInOVAL ovalmodels.Package) (bool, error) {
switch family {
case config.Debian,
config.Ubuntu:
vera, err := debver.NewVersion(versionRelease)
config.Ubuntu,
config.Raspbian:
vera, err := debver.NewVersion(newVer)
if err != nil {
return false, err
}
verb, err := debver.NewVersion(packB.Version)
verb, err := debver.NewVersion(packInOVAL.Version)
if err != nil {
return false, err
}
return vera.LessThan(verb), nil
case config.Alpine:
vera, err := apkver.NewVersion(newVer)
if err != nil {
return false, err
}
verb, err := apkver.NewVersion(packInOVAL.Version)
if err != nil {
return false, err
}
@@ -374,16 +393,15 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er
case config.Oracle,
config.SUSEEnterpriseServer,
config.Alpine,
config.Amazon:
vera := rpmver.NewVersion(versionRelease)
verb := rpmver.NewVersion(packB.Version)
vera := rpmver.NewVersion(newVer)
verb := rpmver.NewVersion(packInOVAL.Version)
return vera.LessThan(verb), nil
case config.RedHat,
config.CentOS:
vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(versionRelease, ".el$1"))
verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packB.Version, ".el$1"))
vera := rpmver.NewVersion(centosVerPattern.ReplaceAllString(newVer, ".el$1"))
verb := rpmver.NewVersion(esVerPattern.ReplaceAllString(packInOVAL.Version, ".el$1"))
return vera.LessThan(verb), nil
default:

View File

@@ -1,3 +1,5 @@
// +build !scanner
package oval
import (
@@ -16,7 +18,7 @@ func TestUpsert(t *testing.T) {
def ovalmodels.Definition
packName string
fixStat fixStat
upserted bool
upsert bool
out ovalResult
}{
//insert
@@ -30,7 +32,7 @@ func TestUpsert(t *testing.T) {
notFixedYet: true,
fixedIn: "1.0.0",
},
upserted: false,
upsert: false,
out: ovalResult{
[]defPacks{
{
@@ -38,7 +40,7 @@ func TestUpsert(t *testing.T) {
DefinitionID: "1111",
},
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
"pack1": {
notFixedYet: true,
fixedIn: "1.0.0",
},
@@ -56,7 +58,7 @@ func TestUpsert(t *testing.T) {
DefinitionID: "1111",
},
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
"pack1": {
notFixedYet: true,
fixedIn: "1.0.0",
},
@@ -67,7 +69,7 @@ func TestUpsert(t *testing.T) {
DefinitionID: "2222",
},
binpkgFixstat: map[string]fixStat{
"pack3": fixStat{
"pack3": {
notFixedYet: true,
fixedIn: "2.0.0",
},
@@ -83,7 +85,7 @@ func TestUpsert(t *testing.T) {
notFixedYet: false,
fixedIn: "3.0.0",
},
upserted: true,
upsert: true,
out: ovalResult{
[]defPacks{
{
@@ -91,11 +93,11 @@ func TestUpsert(t *testing.T) {
DefinitionID: "1111",
},
binpkgFixstat: map[string]fixStat{
"pack1": fixStat{
"pack1": {
notFixedYet: true,
fixedIn: "1.0.0",
},
"pack2": fixStat{
"pack2": {
notFixedYet: false,
fixedIn: "3.0.0",
},
@@ -106,7 +108,7 @@ func TestUpsert(t *testing.T) {
DefinitionID: "2222",
},
binpkgFixstat: map[string]fixStat{
"pack3": fixStat{
"pack3": {
notFixedYet: true,
fixedIn: "2.0.0",
},
@@ -117,9 +119,9 @@ func TestUpsert(t *testing.T) {
},
}
for i, tt := range tests {
upserted := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
if tt.upserted != upserted {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
upsert := tt.res.upsert(tt.def, tt.packName, tt.fixStat)
if tt.upsert != upsert {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upsert, upsert)
}
if !reflect.DeepEqual(tt.out, tt.res) {
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, tt.res)
@@ -155,12 +157,12 @@ func TestDefpacksToPackStatuses(t *testing.T) {
},
},
binpkgFixstat: map[string]fixStat{
"a": fixStat{
"a": {
notFixedYet: true,
fixedIn: "1.0.0",
isSrcPack: false,
},
"b": fixStat{
"b": {
notFixedYet: true,
fixedIn: "1.0.0",
isSrcPack: true,
@@ -1089,11 +1091,15 @@ func TestIsOvalDefAffected(t *testing.T) {
}
}
func TestMajor(t *testing.T) {
func Test_major(t *testing.T) {
var tests = []struct {
in string
expected string
}{
{
in: "",
expected: "",
},
{
in: "4.1",
expected: "4",

View File

@@ -1,3 +1,5 @@
// +build !scanner
package report
import (

View File

@@ -1,3 +1,5 @@
// +build !scanner
package report
import (
@@ -9,24 +11,27 @@ import (
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
"golang.org/x/xerrors"
)
// DBClient is a dictionarie's db client for reporting
// DBClient is DB client for reporting
type DBClient struct {
CveDB cvedb.DB
OvalDB ovaldb.DB
GostDB gostdb.DB
ExploitDB exploitdb.DB
CveDB cvedb.DB
OvalDB ovaldb.DB
GostDB gostdb.DB
ExploitDB exploitdb.DB
MetasploitDB metasploitdb.DB
}
// DBClientConf has a configuration of Vulnerability DBs
type DBClientConf struct {
CveDictCnf config.GoCveDictConf
OvalDictCnf config.GovalDictConf
GostCnf config.GostConf
ExploitCnf config.ExploitConf
DebugSQL bool
CveDictCnf config.GoCveDictConf
OvalDictCnf config.GovalDictConf
GostCnf config.GostConf
ExploitCnf config.ExploitConf
MetasploitCnf config.MetasploitConf
DebugSQL bool
}
// NewDBClient returns db clients
@@ -66,11 +71,21 @@ func NewDBClient(cnf DBClientConf) (dbclient *DBClient, locked bool, err error)
cnf.ExploitCnf.SQLite3Path, err)
}
metasploitdb, locked, err := NewMetasploitDB(cnf)
if locked {
return nil, true, xerrors.Errorf("metasploitDB is locked: %s",
cnf.MetasploitCnf.SQLite3Path)
} else if err != nil {
util.Log.Warnf("Unable to use metasploitDB: %s, err: %s",
cnf.MetasploitCnf.SQLite3Path, err)
}
return &DBClient{
CveDB: cveDriver,
OvalDB: ovaldb,
GostDB: gostdb,
ExploitDB: exploitdb,
CveDB: cveDriver,
OvalDB: ovaldb,
GostDB: gostdb,
ExploitDB: exploitdb,
MetasploitDB: metasploitdb,
}, false, nil
}
@@ -177,6 +192,32 @@ func NewExploitDB(cnf DBClientConf) (driver exploitdb.DB, locked bool, err error
return driver, false, nil
}
// NewMetasploitDB returns db client for Metasploit
func NewMetasploitDB(cnf DBClientConf) (driver metasploitdb.DB, locked bool, err error) {
if config.Conf.Metasploit.IsFetchViaHTTP() {
return nil, false, nil
}
path := cnf.MetasploitCnf.URL
if cnf.MetasploitCnf.Type == "sqlite3" {
path = cnf.MetasploitCnf.SQLite3Path
if _, err := os.Stat(path); os.IsNotExist(err) {
util.Log.Warnf("--msfdb-path=%s file not found. Fetch go-msfdb before reporting if you want to display metasploit modules of detected CVE-IDs. For details, see `https://github.com/takuzoo3868/go-msfdb`", path)
return nil, false, nil
}
}
util.Log.Debugf("Open metasploit db (%s): %s", cnf.MetasploitCnf.Type, path)
if driver, locked, err = metasploitdb.NewDB(cnf.MetasploitCnf.Type, path, cnf.DebugSQL, false); err != nil {
if locked {
util.Log.Errorf("metasploitDB is locked. err: %+v", err)
return nil, true, err
}
return nil, false, err
}
return driver, false, nil
}
// CloseDB close dbs
func (d DBClient) CloseDB() {
if d.CveDB != nil {

View File

@@ -1,13 +1,15 @@
package report
import (
"crypto/tls"
"fmt"
"net"
"net/mail"
"net/smtp"
"strings"
"time"
sasl "github.com/emersion/go-sasl"
smtp "github.com/emersion/go-smtp"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"golang.org/x/xerrors"
@@ -20,7 +22,6 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf
var message string
sender := NewEMailSender()
m := map[string]int{}
for _, r := range rs {
if conf.FormatOneEMail {
@@ -54,14 +55,15 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
}
}
}
summary := ""
var summary string
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"])
} else {
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"])
}
summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
m["High"], m["Medium"], m["Low"], m["Unknown"])
origmessage := message
if conf.FormatOneEMail {
message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(rs...))
@@ -83,7 +85,75 @@ type EMailSender interface {
type emailSender struct {
conf config.SMTPConf
send func(string, smtp.Auth, string, []string, []byte) error
}
func (e *emailSender) sendMail(smtpServerAddr, message string) (err error) {
var c *smtp.Client
var auth sasl.Client
emailConf := e.conf
//TLS Config
tlsConfig := &tls.Config{
ServerName: emailConf.SMTPAddr,
}
switch emailConf.SMTPPort {
case "465":
//New TLS connection
c, err = smtp.DialTLS(smtpServerAddr, tlsConfig)
if err != nil {
return xerrors.Errorf("Failed to create TLS connection to SMTP server: %w", err)
}
default:
c, err = smtp.Dial(smtpServerAddr)
if err != nil {
return xerrors.Errorf("Failed to create connection to SMTP server: %w", err)
}
}
defer c.Close()
if err = c.Hello("localhost"); err != nil {
return xerrors.Errorf("Failed to send Hello command: %w", err)
}
if ok, _ := c.Extension("STARTTLS"); ok {
if err := c.StartTLS(tlsConfig); err != nil {
return xerrors.Errorf("Failed to STARTTLS: %w", err)
}
}
if ok, param := c.Extension("AUTH"); ok {
authList := strings.Split(param, " ")
auth = e.newSaslClient(authList)
}
if err = c.Auth(auth); err != nil {
return xerrors.Errorf("Failed to authenticate: %w", err)
}
if err = c.Mail(emailConf.From, nil); err != nil {
return xerrors.Errorf("Failed to send Mail command: %w", err)
}
for _, to := range emailConf.To {
if err = c.Rcpt(to); err != nil {
return xerrors.Errorf("Failed to send Rcpt command: %w", err)
}
}
w, err := c.Data()
if err != nil {
return xerrors.Errorf("Failed to send Data command: %w", err)
}
_, err = w.Write([]byte(message))
if err != nil {
return xerrors.Errorf("Failed to write EMail message: %w", err)
}
err = w.Close()
if err != nil {
return xerrors.Errorf("Failed to close Writer: %w", err)
}
err = c.Quit()
if err != nil {
return xerrors.Errorf("Failed to close connection: %w", err)
}
return nil
}
func (e *emailSender) Send(subject, body string) (err error) {
@@ -112,30 +182,13 @@ func (e *emailSender) Send(subject, body string) (err error) {
smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
if emailConf.User != "" && emailConf.Password != "" {
err = e.send(
smtpServer,
smtp.PlainAuth(
"",
emailConf.User,
emailConf.Password,
emailConf.SMTPAddr,
),
emailConf.From,
mailAddresses,
[]byte(message),
)
err = e.sendMail(smtpServer, message)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
return nil
}
err = e.send(
smtpServer,
nil,
emailConf.From,
mailAddresses,
[]byte(message),
)
err = e.sendMail(smtpServer, message)
if err != nil {
return xerrors.Errorf("Failed to send emails: %w", err)
}
@@ -144,5 +197,19 @@ func (e *emailSender) Send(subject, body string) (err error) {
// NewEMailSender creates emailSender
func NewEMailSender() EMailSender {
return &emailSender{config.Conf.EMail, smtp.SendMail}
return &emailSender{config.Conf.EMail}
}
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
for _, v := range authList {
switch v {
case "PLAIN":
auth := sasl.NewPlainClient("", e.conf.User, e.conf.Password)
return auth
case "LOGIN":
auth := sasl.NewLoginClient(e.conf.User, e.conf.Password)
return auth
}
}
return nil
}

View File

@@ -1,115 +0,0 @@
package report
import (
"net/smtp"
"reflect"
"strings"
"testing"
"github.com/future-architect/vuls/config"
)
type emailRecorder struct {
addr string
auth smtp.Auth
from string
to []string
body string
}
type mailTest struct {
in config.SMTPConf
out emailRecorder
}
var mailTests = []mailTest{
{
config.SMTPConf{
SMTPAddr: "127.0.0.1",
SMTPPort: "25",
From: "from@address.com",
To: []string{"to@address.com"},
Cc: []string{"cc@address.com"},
},
emailRecorder{
addr: "127.0.0.1:25",
auth: smtp.PlainAuth("", "", "", "127.0.0.1"),
from: "from@address.com",
to: []string{"to@address.com", "cc@address.com"},
body: "body",
},
},
{
config.SMTPConf{
SMTPAddr: "127.0.0.1",
SMTPPort: "25",
User: "vuls",
Password: "password",
From: "from@address.com",
To: []string{"to1@address.com", "to2@address.com"},
Cc: []string{"cc1@address.com", "cc2@address.com"},
},
emailRecorder{
addr: "127.0.0.1:25",
auth: smtp.PlainAuth(
"",
"vuls",
"password",
"127.0.0.1",
),
from: "from@address.com",
to: []string{"to1@address.com", "to2@address.com",
"cc1@address.com", "cc2@address.com"},
body: "body",
},
},
}
func TestSend(t *testing.T) {
for i, test := range mailTests {
f, r := mockSend(nil)
sender := &emailSender{conf: test.in, send: f}
subject := "subject"
body := "body"
if err := sender.Send(subject, body); err != nil {
t.Errorf("unexpected error: %s", err)
}
if r.addr != test.out.addr {
t.Errorf("#%d: wrong 'addr' field.\r\nexpected: %s\n got: %s", i, test.out.addr, r.addr)
}
if !reflect.DeepEqual(r.auth, test.out.auth) && r.auth != nil {
t.Errorf("#%d: wrong 'auth' field.\r\nexpected: %v\n got: %v", i, test.out.auth, r.auth)
}
if r.from != test.out.from {
t.Errorf("#%d: wrong 'from' field.\r\nexpected: %v\n got: %v", i, test.out.from, r.from)
}
if !reflect.DeepEqual(r.to, test.out.to) {
t.Errorf("#%d: wrong 'to' field.\r\nexpected: %v\n got: %v", i, test.out.to, r.to)
}
if r.body != test.out.body {
t.Errorf("#%d: wrong 'body' field.\r\nexpected: %v\n got: %v", i, test.out.body, r.body)
}
}
}
func mockSend(errToReturn error) (func(string, smtp.Auth, string, []string, []byte) error, *emailRecorder) {
r := new(emailRecorder)
return func(addr string, a smtp.Auth, from string, to []string, msg []byte) error {
// Split into header and body
messages := strings.Split(string(msg), "\r\n\r\n")
body := messages[1]
*r = emailRecorder{addr, a, from, to, body}
return errToReturn
}, r
}

View File

@@ -1,74 +0,0 @@
package report
import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// HipChatWriter send report to HipChat
type HipChatWriter struct{}
func (w HipChatWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.HipChat
for _, r := range rs {
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
if err = postMessage(conf.Room, conf.AuthToken, serverInfo); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message := fmt.Sprintf(`<a href="https://nvd.nist.gov/vuln/detail\%s"> %s </a> <br/>%s (%s)<br/>%s`,
vinfo.CveID,
vinfo.CveID,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = postMessage(conf.Room, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func postMessage(room, token, message string) error {
uri := fmt.Sprintf("https://api.hipchat.com/v2/room/%s/notification?auth_token=%s", room, token)
payload := url.Values{
"color": {"purple"},
"message_format": {"html"},
"message": {message},
}
reqs, err := http.NewRequest("POST", uri, strings.NewReader(payload.Encode()))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -17,7 +17,9 @@ type HTTPRequestWriter struct{}
func (w HTTPRequestWriter) Write(rs ...models.ScanResult) (err error) {
for _, r := range rs {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(r)
if err := json.NewEncoder(b).Encode(r); err != nil {
return err
}
_, err = http.Post(c.Conf.HTTP.URL, "application/json; charset=utf-8", b)
if err != nil {
return err
@@ -35,7 +37,7 @@ type HTTPResponseWriter struct {
func (w HTTPResponseWriter) Write(rs ...models.ScanResult) (err error) {
res, err := json.Marshal(rs)
if err != nil {
return xerrors.Errorf("Failed to marshal scah results: %w", err)
return xerrors.Errorf("Failed to marshal scan results: %w", err)
}
w.Writer.Header().Set("Content-Type", "application/json")
_, err = w.Writer.Write(res)

View File

@@ -41,14 +41,8 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
var b []byte
if c.Conf.Debug {
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
} else {
if b, err = json.Marshal(r); err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return xerrors.Errorf("Failed to Marshal to JSON: %w", err)
}
if err := writeFile(p, b, 0600); err != nil {
return xerrors.Errorf("Failed to write JSON. path: %s, err: %w", p, err)
@@ -102,6 +96,17 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err)
}
}
if c.Conf.FormatCsvList {
p := path + "_short.csv"
if c.Conf.Diff {
p = path + "_short_diff.csv"
}
if err := formatCsvList(r, p); err != nil {
return xerrors.Errorf("Failed to write CSV: %s, %w", p, err)
}
}
}
return nil
}

View File

@@ -1,19 +1,15 @@
// +build !scanner
package report
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"reflect"
"regexp"
"sort"
"strings"
"time"
"github.com/future-architect/vuls/libmanager"
gostdb "github.com/knqyf263/gost/db"
"github.com/BurntSushi/toml"
"github.com/future-architect/vuls/config"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
@@ -22,117 +18,128 @@ import (
"github.com/future-architect/vuls/github"
"github.com/future-architect/vuls/gost"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/msf"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/util"
"github.com/future-architect/vuls/wordpress"
"github.com/hashicorp/uuid"
gostdb "github.com/knqyf263/gost/db"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cvemodels "github.com/kotakanbe/go-cve-dictionary/models"
ovaldb "github.com/kotakanbe/goval-dictionary/db"
exploitdb "github.com/mozqnet/go-exploitdb/db"
metasploitdb "github.com/takuzoo3868/go-msfdb/db"
"golang.org/x/xerrors"
)
const (
vulsOpenTag = "<vulsreport>"
vulsCloseTag = "</vulsreport>"
)
// FillCveInfos fills CVE Detailed Information
func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
var filledResults []models.ScanResult
// Use the same reportedAt for all rs
reportedAt := time.Now()
hostname, _ := os.Hostname()
for _, r := range rs {
if c.Conf.RefreshCve || needToRefreshCve(r) {
if ovalSupported(&r) {
r.ScannedCves = models.VulnInfos{}
}
cpeURIs := []string{}
for i, r := range rs {
if !c.Conf.RefreshCve && !needToRefreshCve(r) {
util.Log.Info("No need to refresh")
continue
}
if len(r.Container.ContainerID) == 0 {
cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerName, owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
} else {
// runningContainer
if s, ok := c.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
cpeURIs = con.Cpes
owaspDCXMLPath := con.OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerInfo(), owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
}
if !reuseScannedCves(&r) {
r.ScannedCves = models.VulnInfos{}
}
cpeURIs := []string{}
if len(r.Container.ContainerID) == 0 {
cpeURIs = c.Conf.Servers[r.ServerName].CpeNames
owaspDCXMLPath := c.Conf.Servers[r.ServerName].OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerName, owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
// Integrations
githubInts := GithubSecurityAlerts(c.Conf.Servers[r.ServerName].GitHubRepos)
wpOpt := WordPressOption{c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken}
if err := FillCveInfo(dbclient,
&r,
cpeURIs,
true,
githubInts,
wpOpt); err != nil {
return nil, err
}
r.Lang = c.Conf.Lang
r.ReportedAt = reportedAt
r.ReportedVersion = c.Version
r.ReportedRevision = c.Revision
r.ReportedBy = hostname
r.Config.Report = c.Conf
r.Config.Report.Servers = map[string]c.ServerInfo{
r.ServerName: c.Conf.Servers[r.ServerName],
}
if err := overwriteJSONFile(dir, r); err != nil {
return nil, xerrors.Errorf("Failed to write JSON: %w", err)
}
filledResults = append(filledResults, r)
} else {
util.Log.Debugf("No need to refresh")
filledResults = append(filledResults, r)
// runningContainer
if s, ok := c.Conf.Servers[r.ServerName]; ok {
if con, ok := s.Containers[r.Container.Name]; ok {
cpeURIs = con.Cpes
owaspDCXMLPath := con.OwaspDCXMLPath
if owaspDCXMLPath != "" {
cpes, err := parser.Parse(owaspDCXMLPath)
if err != nil {
return nil, xerrors.Errorf("Failed to read OWASP Dependency Check XML on %s, `%s`, err: %w",
r.ServerInfo(), owaspDCXMLPath, err)
}
cpeURIs = append(cpeURIs, cpes...)
}
}
}
}
if err := libmanager.DetectLibsCves(&r); err != nil {
return nil, xerrors.Errorf("Failed to fill with Library dependency: %w", err)
}
if err := DetectPkgCves(dbclient, &r); err != nil {
return nil, xerrors.Errorf("Failed to detect Pkg CVE: %w", err)
}
if err := DetectCpeURIsCves(dbclient.CveDB, &r, cpeURIs); err != nil {
return nil, xerrors.Errorf("Failed to detect CVE of `%s`: %w", cpeURIs, err)
}
if err := DetectGitHubCves(&r); err != nil {
return nil, xerrors.Errorf("Failed to detect GitHub Cves: %w", err)
}
if err := DetectWordPressCves(&r); err != nil {
return nil, xerrors.Errorf("Failed to detect WordPress Cves: %w", err)
}
if err := FillCveInfo(dbclient, &r); err != nil {
return nil, err
}
r.ReportedBy, _ = os.Hostname()
r.Lang = c.Conf.Lang
r.ReportedAt = reportedAt
r.ReportedVersion = c.Version
r.ReportedRevision = c.Revision
r.Config.Report = c.Conf
r.Config.Report.Servers = map[string]c.ServerInfo{
r.ServerName: c.Conf.Servers[r.ServerName],
}
rs[i] = r
}
// Overwrite the json file every time to clear the fields specified in config.IgnoredJSONKeys
for _, r := range rs {
if s, ok := c.Conf.Servers[r.ServerName]; ok {
r = r.ClearFields(s.IgnoredJSONKeys)
}
if err := overwriteJSONFile(dir, r); err != nil {
return nil, xerrors.Errorf("Failed to write JSON: %w", err)
}
}
if c.Conf.Diff {
prevs, err := loadPrevious(filledResults)
prevs, err := loadPrevious(rs)
if err != nil {
return nil, err
}
diff, err := diff(filledResults, prevs)
diff, err := diff(rs, prevs)
if err != nil {
return nil, err
}
filledResults = []models.ScanResult{}
for _, r := range diff {
if err := fillCveDetail(dbclient.CveDB, &r); err != nil {
for i, r := range diff {
if err := fillCvesWithNvdJvn(dbclient.CveDB, &r); err != nil {
return nil, err
}
filledResults = append(filledResults, r)
rs[i] = r
}
}
filtered := []models.ScanResult{}
for _, r := range filledResults {
for i, r := range rs {
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
r = r.FilterIgnoreCves()
r = r.FilterUnfixed()
@@ -141,28 +148,31 @@ func FillCveInfos(dbclient DBClient, rs []models.ScanResult, dir string) ([]mode
if c.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
}
filtered = append(filtered, r)
rs[i] = r
}
return filtered, nil
return rs, nil
}
// FillCveInfo fill scanResult with cve info.
func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, ignoreWillNotFix bool, integrations ...Integration) error {
util.Log.Debugf("need to refresh")
// DetectPkgCves detects OS pkg cves
func DetectPkgCves(dbclient DBClient, r *models.ScanResult) error {
// Pkg Scan
if r.Release != "" {
// OVAL
if err := detectPkgsCvesWithOval(dbclient.OvalDB, r); err != nil {
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
}
nCVEs, err := libmanager.FillLibrary(r)
if err != nil {
return xerrors.Errorf("Failed to fill with Library dependency: %w", err)
// gost
if err := detectPkgsCvesWithGost(dbclient.GostDB, r); err != nil {
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
}
} else if reuseScannedCves(r) {
util.Log.Infof("r.Release is empty. Use CVEs as it as.")
} else if r.Family == config.ServerTypePseudo {
util.Log.Infof("pseudo type. Skip OVAL and gost detection")
} else {
return xerrors.Errorf("Failed to fill CVEs. r.Release is empty")
}
util.Log.Infof("%s: %d CVEs are detected with Library",
r.FormatServerName(), nCVEs)
nCVEs, err = FillWithOval(dbclient.OvalDB, r)
if err != nil {
return xerrors.Errorf("Failed to fill with OVAL: %w", err)
}
util.Log.Infof("%s: %d CVEs are detected with OVAL",
r.FormatServerName(), nCVEs)
for i, v := range r.ScannedCves {
for j, p := range v.AffectedPackages {
@@ -173,47 +183,106 @@ func FillCveInfo(dbclient DBClient, r *models.ScanResult, cpeURIs []string, igno
}
}
nCVEs, err = fillVulnByCpeURIs(dbclient.CveDB, r, cpeURIs)
if err != nil {
return xerrors.Errorf("Failed to detect vulns of `%s`: %w", cpeURIs, err)
}
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
ints := &integrationResults{}
for _, o := range integrations {
if err = o.apply(r, ints); err != nil {
return xerrors.Errorf("Failed to fill with integration: %w", err)
// To keep backward compatibility
// Newer versions use ListenPortStats,
// but older versions of Vuls are set to ListenPorts.
// Set ListenPorts to ListenPortStats to allow newer Vuls to report old results.
for i, pkg := range r.Packages {
for j, proc := range pkg.AffectedProcs {
for _, ipPort := range proc.ListenPorts {
ps, err := models.NewPortStat(ipPort)
if err != nil {
util.Log.Warnf("Failed to parse ip:port: %s, err:%+v", ipPort, err)
continue
}
r.Packages[i].AffectedProcs[j].ListenPortStats = append(
r.Packages[i].AffectedProcs[j].ListenPortStats, *ps)
}
}
}
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts", r.FormatServerName(), ints.GithubAlertsCveCounts)
nCVEs, err = FillWithGost(dbclient.GostDB, r, ignoreWillNotFix)
if err != nil {
return nil
}
// DetectGitHubCves fetches CVEs from GitHub Security Alerts
func DetectGitHubCves(r *models.ScanResult) error {
repos := c.Conf.Servers[r.ServerName].GitHubRepos
if len(repos) == 0 {
return nil
}
githubInts := GithubSecurityAlerts(repos)
ints := &integrationResults{}
for _, o := range []Integration{githubInts} {
if err := o.apply(r, ints); err != nil {
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
}
}
util.Log.Infof("%s: %d CVEs are detected with GitHub Security Alerts",
r.FormatServerName(), ints.GithubAlertsCveCounts)
return nil
}
// DetectWordPressCves detects CVEs of WordPress
func DetectWordPressCves(r *models.ScanResult) error {
token := c.Conf.Servers[r.ServerName].WordPress.WPVulnDBToken
if token == "" {
return nil
}
wpVulnCaches := map[string]string{}
wpOpt := WordPressOption{
token,
&wpVulnCaches,
}
ints := &integrationResults{}
for _, o := range []Integration{wpOpt} {
if err := o.apply(r, ints); err != nil {
return xerrors.Errorf("Failed to detect CVE with integration: %w", err)
}
}
util.Log.Infof("%s: %d CVEs are detected with wpscan API",
r.FormatServerName(), ints.WordPressCveCounts)
return nil
}
// FillCveInfo fill scanResult with cve info.
func FillCveInfo(dbclient DBClient, r *models.ScanResult) error {
util.Log.Infof("Fill CVE detailed with gost")
if err := gost.NewClient(r.Family).FillCVEsWithRedHat(dbclient.GostDB, r); err != nil {
return xerrors.Errorf("Failed to fill with gost: %w", err)
}
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
r.FormatServerName(), nCVEs)
util.Log.Infof("Fill CVE detailed information with CVE-DB")
if err := fillCveDetail(dbclient.CveDB, r); err != nil {
util.Log.Infof("Fill CVE detailed with CVE-DB")
if err := fillCvesWithNvdJvn(dbclient.CveDB, r); err != nil {
return xerrors.Errorf("Failed to fill with CVE: %w", err)
}
util.Log.Infof("Fill exploit information with Exploit-DB")
nExploitCve, err := FillWithExploit(dbclient.ExploitDB, r)
util.Log.Infof("Fill exploit with Exploit-DB")
nExploitCve, err := fillWithExploitDB(dbclient.ExploitDB, r)
if err != nil {
return xerrors.Errorf("Failed to fill with exploit: %w", err)
}
util.Log.Infof("%s: %d exploits are detected",
r.FormatServerName(), nExploitCve)
util.Log.Infof("Fill metasploit module with Metasploit-DB")
nMetasploitCve, err := fillWithMetasploit(dbclient.MetasploitDB, r)
if err != nil {
return xerrors.Errorf("Failed to fill with metasploit: %w", err)
}
util.Log.Infof("%s: %d modules are detected",
r.FormatServerName(), nMetasploitCve)
util.Log.Infof("Fill CWE with NVD")
fillCweDict(r)
return nil
}
// fillCveDetail fetches NVD, JVN from CVE Database
func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
var cveIDs []string
// fillCvesWithNvdJvn fills CVE detail with NVD, JVN
func fillCvesWithNvdJvn(driver cvedb.DB, r *models.ScanResult) error {
cveIDs := []string{}
for _, v := range r.ScannedCves {
cveIDs = append(cveIDs, v.CveID)
}
@@ -223,10 +292,7 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
return err
}
for _, d := range ds {
nvd := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
if nvd == nil {
nvd = models.ConvertNvdXMLToModel(d.CveID, d.NvdXML)
}
nvd, exploits, mitigations := models.ConvertNvdJSONToModel(d.CveID, d.NvdJSON)
jvn := models.ConvertJvnToModel(d.CveID, d.Jvn)
alerts := fillCertAlerts(&d)
@@ -241,6 +307,8 @@ func fillCveDetail(driver cvedb.DB, r *models.ScanResult) error {
}
}
vinfo.AlertDict = alerts
vinfo.Exploits = append(vinfo.Exploits, exploits...)
vinfo.Mitigations = append(vinfo.Mitigations, mitigations...)
r.ScannedCves[cveID] = vinfo
break
}
@@ -271,13 +339,13 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
return dict
}
// FillWithOval fetches OVAL database
func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error) {
// detectPkgsCvesWithOval fetches OVAL database
func detectPkgsCvesWithOval(driver ovaldb.DB, r *models.ScanResult) error {
var ovalClient oval.Client
var ovalFamily string
switch r.Family {
case c.Debian:
case c.Debian, c.Raspbian:
ovalClient = oval.NewDebian()
ovalFamily = c.Debian
case c.Ubuntu:
@@ -303,73 +371,81 @@ func FillWithOval(driver ovaldb.DB, r *models.ScanResult) (nCVEs int, err error)
case c.Amazon:
ovalClient = oval.NewAmazon()
ovalFamily = c.Amazon
case c.Raspbian, c.FreeBSD, c.Windows:
return 0, nil
case c.FreeBSD, c.Windows:
return nil
case c.ServerTypePseudo:
return 0, nil
return nil
default:
if r.Family == "" {
return 0, xerrors.New("Probably an error occurred during scanning. Check the error message")
return xerrors.New("Probably an error occurred during scanning. Check the error message")
}
return 0, xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
return xerrors.Errorf("OVAL for %s is not implemented yet", r.Family)
}
if !c.Conf.OvalDict.IsFetchViaHTTP() {
if driver == nil {
return 0, xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
return xerrors.Errorf("You have to fetch OVAL data for %s before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", r.Family)
}
if err = driver.NewOvalDB(ovalFamily); err != nil {
return 0, xerrors.Errorf("Failed to New Oval DB. err: %w", err)
if err := driver.NewOvalDB(ovalFamily); err != nil {
return xerrors.Errorf("Failed to New Oval DB. err: %w", err)
}
}
util.Log.Debugf("Check whether oval fetched: %s %s", ovalFamily, r.Release)
ok, err := ovalClient.CheckIfOvalFetched(driver, ovalFamily, r.Release)
if err != nil {
return 0, err
return err
}
if !ok {
return 0, xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", ovalFamily, r.Release)
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/kotakanbe/goval-dictionary#usage`", ovalFamily, r.Release)
}
_, err = ovalClient.CheckIfOvalFresh(driver, ovalFamily, r.Release)
if err != nil {
return 0, err
return err
}
return ovalClient.FillWithOval(driver, r)
}
// FillWithGost fills CVEs with gost dataabase
// https://github.com/knqyf263/gost
func FillWithGost(driver gostdb.DB, r *models.ScanResult, ignoreWillNotFix bool) (nCVEs int, err error) {
gostClient := gost.NewClient(r.Family)
// TODO chekc if fetched
// TODO chekc if fresh enough
if nCVEs, err = gostClient.DetectUnfixed(driver, r, ignoreWillNotFix); err != nil {
return
nCVEs, err := ovalClient.FillWithOval(driver, r)
if err != nil {
return err
}
return nCVEs, gostClient.FillCVEsWithRedHat(driver, r)
util.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), nCVEs)
return nil
}
// FillWithExploit fills Exploits with exploit dataabase
func detectPkgsCvesWithGost(driver gostdb.DB, r *models.ScanResult) error {
nCVEs, err := gost.NewClient(r.Family).DetectUnfixed(driver, r, true)
util.Log.Infof("%s: %d unfixed CVEs are detected with gost",
r.FormatServerName(), nCVEs)
return err
}
// fillWithExploitDB fills Exploits with exploit dataabase
// https://github.com/mozqnet/go-exploitdb
func FillWithExploit(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
// TODO chekc if fetched
// TODO chekc if fresh enough
func fillWithExploitDB(driver exploitdb.DB, r *models.ScanResult) (nExploitCve int, err error) {
return exploit.FillWithExploit(driver, r)
}
func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) (nCVEs int, err error) {
// fillWithMetasploit fills metasploit modules with metasploit database
// https://github.com/takuzoo3868/go-msfdb
func fillWithMetasploit(driver metasploitdb.DB, r *models.ScanResult) (nMetasploitCve int, err error) {
return msf.FillWithMetasploit(driver, r)
}
// DetectCpeURIsCves detects CVEs of given CPE-URIs
func DetectCpeURIsCves(driver cvedb.DB, r *models.ScanResult, cpeURIs []string) error {
nCVEs := 0
if len(cpeURIs) != 0 && driver == nil && !config.Conf.CveDict.IsFetchViaHTTP() {
return 0, xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary beofre reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
return xerrors.Errorf("cpeURIs %s specified, but cve-dictionary DB not found. Fetch cve-dictionary before reporting. For details, see `https://github.com/kotakanbe/go-cve-dictionary#deploy-go-cve-dictionary`",
cpeURIs)
}
for _, name := range cpeURIs {
details, err := CveClient.FetchCveDetailsByCpeName(driver, name)
if err != nil {
return 0, err
return err
}
for _, detail := range details {
if val, ok := r.ScannedCves[detail.CveID]; ok {
@@ -389,7 +465,8 @@ func fillVulnByCpeURIs(driver cvedb.DB, r *models.ScanResult, cpeURIs []string)
}
}
}
return nCVEs, nil
util.Log.Infof("%s: %d CVEs are detected with CPE", r.FormatServerName(), nCVEs)
return nil
}
type integrationResults struct {
@@ -432,14 +509,15 @@ func (g GithubSecurityAlertOption) apply(r *models.ScanResult, ints *integration
// WordPressOption :
type WordPressOption struct {
token string
token string
wpVulnCaches *map[string]string
}
func (g WordPressOption) apply(r *models.ScanResult, ints *integrationResults) (err error) {
if g.token == "" {
return nil
}
n, err := wordpress.FillWordPress(r, g.token)
n, err := wordpress.FillWordPress(r, g.token, g.wpVulnCaches)
if err != nil {
return xerrors.Errorf("Failed to fetch from WPVulnDB. Check the WPVulnDBToken in config.toml. err: %w", err)
}
@@ -501,259 +579,3 @@ func fillCweDict(r *models.ScanResult) {
r.CweDict = dict
return
}
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
// Scanning with the -containers-only, -images-only flag at scan time, the UUID of Container Host may not be generated,
// so check it. Otherwise create a UUID of the Container Host and set it.
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string) {
if id, ok := server.UUIDs[r.ServerName]; !ok {
serverUUID = uuid.GenerateUUID()
} else {
matched, err := regexp.MatchString(reUUID, id)
if !matched || err != nil {
serverUUID = uuid.GenerateUUID()
}
}
return serverUUID
}
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
// And then set the generated UUID to config.toml and scan results.
func EnsureUUIDs(configPath string, results models.ScanResults) error {
// Sort Host->Container
sort.Slice(results, func(i, j int) bool {
if results[i].ServerName == results[j].ServerName {
return results[i].Container.ContainerID < results[j].Container.ContainerID
}
return results[i].ServerName < results[j].ServerName
})
for i, r := range results {
server := c.Conf.Servers[r.ServerName]
if server.UUIDs == nil {
server.UUIDs = map[string]string{}
}
name := ""
if r.IsContainer() {
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
if uuid := getOrCreateServerUUID(r, server); uuid != "" {
server.UUIDs[r.ServerName] = uuid
}
} else if r.IsImage() {
name = fmt.Sprintf("%s%s@%s", r.Image.Tag, r.Image.Digest, r.ServerName)
if uuid := getOrCreateServerUUID(r, server); uuid != "" {
server.UUIDs[r.ServerName] = uuid
}
} else {
name = r.ServerName
}
if id, ok := server.UUIDs[name]; ok {
matched, err := regexp.MatchString(reUUID, id)
if !matched || err != nil {
util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
} else {
if r.IsContainer() {
results[i].Container.UUID = id
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = id
}
// continue if the UUID has already assigned and valid
continue
}
}
// Generate a new UUID and set to config and scan result
id := uuid.GenerateUUID()
server.UUIDs[name] = id
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[r.ServerName] = server
if r.IsContainer() {
results[i].Container.UUID = id
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = id
}
}
for name, server := range c.Conf.Servers {
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[name] = server
}
email := &c.Conf.EMail
if email.SMTPAddr == "" {
email = nil
}
slack := &c.Conf.Slack
if slack.HookURL == "" {
slack = nil
}
cveDict := &c.Conf.CveDict
ovalDict := &c.Conf.OvalDict
gost := &c.Conf.Gost
exploit := &c.Conf.Exploit
http := &c.Conf.HTTP
if http.URL == "" {
http = nil
}
syslog := &c.Conf.Syslog
if syslog.Host == "" {
syslog = nil
}
aws := &c.Conf.AWS
if aws.S3Bucket == "" {
aws = nil
}
azure := &c.Conf.Azure
if azure.AccountName == "" {
azure = nil
}
stride := &c.Conf.Stride
if stride.HookURL == "" {
stride = nil
}
hipChat := &c.Conf.HipChat
if hipChat.AuthToken == "" {
hipChat = nil
}
chatWork := &c.Conf.ChatWork
if chatWork.APIToken == "" {
chatWork = nil
}
saas := &c.Conf.Saas
if saas.GroupID == 0 {
saas = nil
}
c := struct {
CveDict *c.GoCveDictConf `toml:"cveDict"`
OvalDict *c.GovalDictConf `toml:"ovalDict"`
Gost *c.GostConf `toml:"gost"`
Exploit *c.ExploitConf `toml:"exploit"`
Slack *c.SlackConf `toml:"slack"`
Email *c.SMTPConf `toml:"email"`
HTTP *c.HTTPConf `toml:"http"`
Syslog *c.SyslogConf `toml:"syslog"`
AWS *c.AWS `toml:"aws"`
Azure *c.Azure `toml:"azure"`
Stride *c.StrideConf `toml:"stride"`
HipChat *c.HipChatConf `toml:"hipChat"`
ChatWork *c.ChatWorkConf `toml:"chatWork"`
Saas *c.SaasConf `toml:"saas"`
Default c.ServerInfo `toml:"default"`
Servers map[string]c.ServerInfo `toml:"servers"`
}{
CveDict: cveDict,
OvalDict: ovalDict,
Gost: gost,
Exploit: exploit,
Slack: slack,
Email: email,
HTTP: http,
Syslog: syslog,
AWS: aws,
Azure: azure,
Stride: stride,
HipChat: hipChat,
ChatWork: chatWork,
Saas: saas,
Default: c.Conf.Default,
Servers: c.Conf.Servers,
}
// rename the current config.toml to config.toml.bak
info, err := os.Lstat(configPath)
if err != nil {
return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
}
realPath := configPath
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if realPath, err = os.Readlink(configPath); err != nil {
return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
}
}
if err := os.Rename(realPath, realPath+".bak"); err != nil {
return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
}
var buf bytes.Buffer
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
return xerrors.Errorf("Failed to encode to toml: %w", err)
}
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
str = fmt.Sprintf("%s\n\n%s",
"# See REAME for details: https://vuls.io/docs/en/usage-settings.html",
str)
return ioutil.WriteFile(realPath, []byte(str), 0600)
}
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
if reflect.DeepEqual(server.Optional, def.Optional) {
server.Optional = nil
}
if def.User == server.User {
server.User = ""
}
if def.Host == server.Host {
server.Host = ""
}
if def.Port == server.Port {
server.Port = ""
}
if def.KeyPath == server.KeyPath {
server.KeyPath = ""
}
if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
server.ScanMode = nil
}
if def.Type == server.Type {
server.Type = ""
}
if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
server.CpeNames = nil
}
if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
server.OwaspDCXMLPath = ""
}
if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
server.IgnoreCves = nil
}
if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
server.Enablerepo = nil
}
for k, v := range def.Optional {
if vv, ok := server.Optional[k]; ok && v == vv {
delete(server.Optional, k)
}
}
return server
}

View File

@@ -23,16 +23,24 @@ import (
// S3Writer writes results to S3
type S3Writer struct{}
func getS3() *s3.S3 {
Config := &aws.Config{
func getS3() (*s3.S3, error) {
ses, err := session.NewSession()
if err != nil {
return nil, err
}
config := &aws.Config{
Region: aws.String(c.Conf.AWS.Region),
Credentials: credentials.NewChainCredentials([]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: c.Conf.AWS.Profile},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(session.New())},
&ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(ses)},
}),
}
return s3.New(session.New(Config))
s, err := session.NewSession(config)
if err != nil {
return nil, err
}
return s3.New(s), nil
}
// Write results to S3
@@ -42,7 +50,10 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
return nil
}
svc := getS3()
svc, err := getS3()
if err != nil {
return err
}
if c.Conf.FormatOneLineText {
timestr := rs[0].ScannedAt.Format(time.RFC3339)
@@ -99,7 +110,11 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) {
// CheckIfBucketExists check the existence of S3 bucket
func CheckIfBucketExists() error {
svc := getS3()
svc, err := getS3()
if err != nil {
return err
}
result, err := svc.ListBuckets(&s3.ListBucketsInput{})
if err != nil {
return xerrors.Errorf(

View File

@@ -16,12 +16,6 @@ import (
"golang.org/x/xerrors"
)
type field struct {
Title string `json:"title"`
Value string `json:"value"`
Short bool `json:"short"`
}
type message struct {
Text string `json:"text"`
Username string `json:"username"`
@@ -283,9 +277,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
} else {
if 0 < len(vinfo.DistroAdvisories) {
links := []string{}
for k, v := range vinfo.VendorLinks(osFamily) {
links = append(links, fmt.Sprintf("<%s|%s>",
v, k))
for _, v := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, osFamily, vinfo.CveID) {
links = append(links, fmt.Sprintf("<%s|%s>", v.Value, v.Type))
}
v := fmt.Sprintf("<%s|%s> %s (%s)",
@@ -309,9 +302,8 @@ func attachmentText(vinfo models.VulnInfo, osFamily string, cweDict map[string]m
}
mitigation := ""
if vinfo.Mitigations(osFamily)[0].Type != models.Unknown {
mitigation = fmt.Sprintf("\nMitigation:\n```%s```\n",
vinfo.Mitigations(osFamily)[0].Value)
for _, m := range vinfo.Mitigations {
mitigation = fmt.Sprintf("\nMitigation:\n<%s|%s>", m.URL, m.CveContentType)
}
return fmt.Sprintf("*%4.1f (%s)* %s %s\n%s\n```\n%s\n```%s\n%s\n",

View File

@@ -13,7 +13,7 @@ type StdoutWriter struct{}
// WriteScanSummary prints Scan summary at the end of scan
func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) {
fmt.Printf("\n\n")
fmt.Println("One Line Summary")
fmt.Println("Scan Summary")
fmt.Println("================")
fmt.Printf("%s\n", formatScanSummary(rs...))
}
@@ -27,7 +27,7 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error {
fmt.Print("\n")
}
if c.Conf.FormatList {
if c.Conf.FormatList || c.Conf.FormatCsvList {
for _, r := range rs {
fmt.Println(formatList(r))
}

View File

@@ -1,81 +0,0 @@
package report
import (
"bytes"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// StrideWriter send report to Stride
type StrideWriter struct{}
type strideSender struct{}
func (w StrideWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Stride
for _, r := range rs {
w := strideSender{}
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
message := fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
serverInfo,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
for _, vinfo := range r.ScannedCves {
maxCvss := vinfo.MaxCvssScore()
severity := strings.ToUpper(maxCvss.Value.Severity)
if severity == "" {
severity = "?"
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s ","marks": [ { "type": "link", "attrs": { "href": "https://nvd.nist.gov/vuln/detail/%s", "title": "cve" } } ]}]}]}}`,
vinfo.CveID,
vinfo.CveID,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s (%s) "}]}]}}`,
strconv.FormatFloat(maxCvss.Value.Score, 'f', 1, 64),
severity,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
message = fmt.Sprintf(`{"body":{"version":1,"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":" %s "}]}]}}`,
vinfo.Summaries(config.Conf.Lang, r.Family)[0].Value,
)
if err = w.sendMessage(conf.HookURL, conf.AuthToken, message); err != nil {
return err
}
}
}
return nil
}
func (w strideSender) sendMessage(uri, token, jsonStr string) error {
reqs, err := http.NewRequest("POST", uri, bytes.NewBuffer([]byte(jsonStr)))
if err != nil {
return err
}
reqs.Header.Add("Content-Type", "application/json")
reqs.Header.Add("Authorization", "Bearer "+token)
client := &http.Client{}
resp, err := client.Do(reqs)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

View File

@@ -28,7 +28,7 @@ func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
for _, r := range rs {
messages := w.encodeSyslog(r)
for _, m := range messages {
if _, err = fmt.Fprintf(sysLog, m); err != nil {
if _, err = fmt.Fprint(sysLog, m); err != nil {
return err
}
}
@@ -69,7 +69,7 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
kvPairs = append(kvPairs, fmt.Sprintf(`cvss_vector_%s_v3="%s"`, cvss.Type, cvss.Value.Vector))
}
if content, ok := vinfo.CveContents[models.NvdXML]; ok {
if content, ok := vinfo.CveContents[models.Nvd]; ok {
cwes := strings.Join(content.CweIDs, ",")
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
if config.Conf.Syslog.Verbose {

View File

@@ -33,11 +33,14 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
models.PackageFixStatus{Name: "pkg4"},
},
CveContents: models.CveContents{
models.NvdXML: models.CveContent{
models.Nvd: models.CveContent{
Cvss2Score: 5.0,
Cvss2Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss2Severity: "MEDIUM",
CweIDs: []string{"CWE-20"},
Cvss3Score: 9.8,
Cvss3Vector: "AV:L/AC:L/Au:N/C:N/I:N/A:C",
Cvss3Severity: "HIGH",
},
},
},
@@ -45,7 +48,7 @@ func TestSyslogWriterEncodeSyslog(t *testing.T) {
},
expectedMessages: []string{
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg1,pkg2" cve_id="CVE-2017-0001"`,
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvdxml_v2="5.00" cvss_vector_nvdxml_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
`scanned_at="2018-06-13 16:10:00 +0000 UTC" server_name="teste01" os_family="ubuntu" os_release="16.04" ipv4_addr="192.168.0.1,10.0.2.15" ipv6_addr="" packages="pkg3,pkg4" cve_id="CVE-2017-0002" cvss_score_nvd_v2="5.00" cvss_vector_nvd_v2="AV:L/AC:L/Au:N/C:N/I:N/A:C" cvss_score_nvd_v3="9.80" cvss_vector_nvd_v3="AV:L/AC:L/Au:N/C:N/I:N/A:C" cwe_ids="CWE-20"`,
},
},
{

View File

@@ -16,7 +16,7 @@ import (
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
"github.com/gosuri/uitable"
"github.com/jroimartin/gocui"
"github.com/jesseduffield/gocui"
)
var scanResults models.ScanResults
@@ -36,14 +36,15 @@ func RunTui(results models.ScanResults) subcommands.ExitStatus {
return scanResults[i].ServerName < scanResults[j].ServerName
})
g, err := gocui.NewGui(gocui.OutputNormal)
g := gocui.NewGui()
err := g.Init()
if err != nil {
util.Log.Errorf("%+v", err)
return subcommands.ExitFailure
}
defer g.Close()
g.SetManagerFunc(layout)
g.SetLayout(layout)
if err := keybindings(g); err != nil {
util.Log.Errorf("%+v", err)
return subcommands.ExitFailure
@@ -100,7 +101,6 @@ func keybindings(g *gocui.Gui) (err error) {
errs = append(errs, g.SetKeybinding("summary", gocui.KeySpace, gocui.ModNone, cursorPageDown))
errs = append(errs, g.SetKeybinding("summary", gocui.KeyBackspace, gocui.ModNone, cursorPageUp))
errs = append(errs, g.SetKeybinding("summary", gocui.KeyBackspace2, gocui.ModNone, cursorPageUp))
// errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlM, gocui.ModNone, cursorMoveMiddle))
errs = append(errs, g.SetKeybinding("summary", gocui.KeyEnter, gocui.ModNone, nextView))
errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlN, gocui.ModNone, nextSummary))
errs = append(errs, g.SetKeybinding("summary", gocui.KeyCtrlP, gocui.ModNone, previousSummary))
@@ -168,19 +168,19 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
_, err = g.SetCurrentView("side")
return 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
}
@@ -189,19 +189,19 @@ func previousView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
_, err = g.SetCurrentView("side")
return 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
}
@@ -282,31 +282,15 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error {
return err
}
}
onMovingCursorRedrawView(g, v)
err := onMovingCursorRedrawView(g, v)
if err != nil {
return err
}
}
cx, cy := v.Cursor()
ox, oy := v.Origin()
debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy))
return nil
}
func cursorMoveTop(g *gocui.Gui, v *gocui.View) error {
if v != nil {
cx, _ := v.Cursor()
v.SetCursor(cx, 0)
}
onMovingCursorRedrawView(g, v)
return nil
}
func cursorMoveBottom(g *gocui.Gui, v *gocui.View) error {
if v != nil {
_, maxY := v.Size()
cx, _ := v.Cursor()
v.SetCursor(cx, maxY-1)
}
onMovingCursorRedrawView(g, v)
_ = debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy))
return nil
}
@@ -314,9 +298,13 @@ func cursorMoveMiddle(g *gocui.Gui, v *gocui.View) error {
if v != nil {
_, maxY := v.Size()
cx, _ := v.Cursor()
v.SetCursor(cx, maxY/2)
if err := v.SetCursor(cx, maxY/2); err != nil {
return err
}
}
if err := onMovingCursorRedrawView(g, v); err != nil {
return err
}
onMovingCursorRedrawView(g, v)
return nil
}
@@ -331,23 +319,25 @@ func cursorPageDown(g *gocui.Gui, v *gocui.View) error {
if !ok {
if yLimit < maxY {
v.SetCursor(cx, yLimit)
_ = v.SetCursor(cx, yLimit)
} else {
v.SetCursor(cx, maxY-1)
v.SetOrigin(ox, yLimit-maxY+1)
_ = v.SetCursor(cx, maxY-1)
_ = v.SetOrigin(ox, yLimit-maxY+1)
}
} else if yLimit < oy+jump+maxY {
if yLimit < maxY {
v.SetCursor(cx, yLimit)
_ = v.SetCursor(cx, yLimit)
} else {
v.SetOrigin(ox, yLimit-maxY+1)
v.SetCursor(cx, maxY-1)
_ = v.SetOrigin(ox, yLimit-maxY+1)
_ = v.SetCursor(cx, maxY-1)
}
} else {
v.SetCursor(cx, cy)
v.SetOrigin(ox, oy+jump)
_ = v.SetCursor(cx, cy)
if err := v.SetOrigin(ox, oy+jump); err != nil {
return err
}
}
onMovingCursorRedrawView(g, v)
_ = onMovingCursorRedrawView(g, v)
}
return nil
}
@@ -362,7 +352,7 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
}
}
}
onMovingCursorRedrawView(g, v)
_ = onMovingCursorRedrawView(g, v)
return nil
}
@@ -372,11 +362,13 @@ func cursorPageUp(g *gocui.Gui, v *gocui.View) error {
cx, _ := v.Cursor()
ox, oy := v.Origin()
if err := v.SetOrigin(ox, oy-jump); err != nil {
v.SetOrigin(ox, 0)
v.SetCursor(cx, 0)
if err := v.SetOrigin(ox, 0); err != nil {
return err
}
_ = v.SetCursor(cx, 0)
}
onMovingCursorRedrawView(g, v)
_ = onMovingCursorRedrawView(g, v)
}
return nil
}
@@ -384,7 +376,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
@@ -392,7 +384,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
}
}
@@ -402,7 +394,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
@@ -410,7 +402,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
}
}
@@ -484,7 +476,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
}
}
@@ -507,7 +499,7 @@ func showMsg(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
}
}
@@ -518,7 +510,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
if err := g.DeleteView("msg"); err != nil {
return err
}
_, err := g.SetCurrentView("summary")
err := g.SetCurrentView("summary")
return err
}
@@ -543,10 +535,12 @@ 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 err := g.DeleteView("debug"); err != nil {
return err
}
}
if v, err := g.SetView("debug", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
fmt.Fprintf(v, str)
fmt.Fprint(v, str)
}
}
return nil
@@ -568,7 +562,7 @@ func setSideLayout(g *gocui.Gui) error {
}
currentScanResult = scanResults[0]
vinfos = scanResults[0].ScannedCves.ToSortedSlice()
if _, err := g.SetCurrentView("side"); err != nil {
if err := g.SetCurrentView("side"); err != nil {
return err
}
}
@@ -583,7 +577,7 @@ func setSummaryLayout(g *gocui.Gui) error {
}
lines := summaryLines(currentScanResult)
fmt.Fprintf(v, lines)
fmt.Fprint(v, lines)
v.Highlight = true
v.Editable = false
@@ -621,9 +615,20 @@ func summaryLines(r models.ScanResult) string {
pkgNames = append(pkgNames, vinfo.CpeURIs...)
pkgNames = append(pkgNames, vinfo.GitHubSecurityAlerts.Names()...)
pkgNames = append(pkgNames, vinfo.WpPackageFixStats.Names()...)
pkgNames = append(pkgNames, vinfo.LibraryFixedIns.Names()...)
av := vinfo.AttackVector()
for _, pname := range vinfo.AffectedPackages.Names() {
if r.Packages[pname].HasReachablePort() {
av = fmt.Sprintf("%s ◉", av)
break
}
}
exploits := ""
if 0 < len(vinfo.Exploits) {
if 0 < len(vinfo.Metasploits) {
exploits = "EXP"
} else if 0 < len(vinfo.Exploits) {
exploits = "POC"
}
@@ -632,7 +637,7 @@ func summaryLines(r models.ScanResult) string {
fmt.Sprintf(indexFormat, i+1),
vinfo.CveID,
cvssScore + " |",
fmt.Sprintf("%4s |", vinfo.AttackVector()),
fmt.Sprintf("%-6s |", av),
fmt.Sprintf("%3s |", exploits),
fmt.Sprintf("%6s |", vinfo.AlertDict.FormatSource()),
fmt.Sprintf("%7s |", vinfo.PatchStatus(r.Packages)),
@@ -644,6 +649,7 @@ func summaryLines(r models.ScanResult) string {
}
stable.AddRow(icols...)
}
return fmt.Sprintf("%s", stable)
}
@@ -715,11 +721,24 @@ func setChangelogLayout(g *gocui.Gui) error {
if len(pack.AffectedProcs) != 0 {
for _, p := range pack.AffectedProcs {
if len(p.ListenPortStats) == 0 {
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: []",
p.PID, p.Name))
continue
}
var ports []string
for _, pp := range p.ListenPortStats {
if len(pp.PortReachableTo) == 0 {
ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
} else {
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
}
}
lines = append(lines, fmt.Sprintf(" * PID: %s %s Port: %s",
p.PID, p.Name, p.ListenPorts))
p.PID, p.Name, ports))
}
} else {
// lines = append(lines, fmt.Sprintf(" * No affected process"))
}
}
}
@@ -751,17 +770,11 @@ func setChangelogLayout(g *gocui.Gui) error {
}
}
// check library fixedin
for _, scanner := range r.LibraryScanners {
key := scanner.GetLibraryKey()
for _, fixedin := range vinfo.LibraryFixedIns {
for _, lib := range scanner.Libs {
if fixedin.Key == key && lib.Name == fixedin.Name {
lines = append(lines, fmt.Sprintf("* %s-%s, FixedIn: %s",
lib.Name, lib.Version, fixedin.FixedIn))
continue
}
}
for _, l := range vinfo.LibraryFixedIns {
libs := r.LibraryScanners.Find(l.Path, l.Name)
for path, lib := range libs {
lines = append(lines, fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
lib.Name, lib.Version, l.FixedIn, path))
}
}
@@ -783,6 +796,21 @@ func setChangelogLayout(g *gocui.Gui) error {
}
}
if len(vinfo.Metasploits) != 0 {
lines = append(lines, "\n",
"Metasploit Modules",
"==================",
)
for _, module := range vinfo.Metasploits {
lines = append(lines, fmt.Sprintf("* %s: %s", module.Name, module.Description))
if 0 < len(module.URLs) {
for _, u := range module.URLs {
lines = append(lines, fmt.Sprintf(" - %s", u))
}
}
}
}
if len(vinfo.AlertDict.En) > 0 {
lines = append(lines, "\n",
"USCERT Alert",
@@ -835,8 +863,10 @@ type dataForTmpl struct {
CveID string
Cvsses string
Exploits []models.Exploit
Metasploits []models.Metasploit
Summary string
Mitigation string
PatchURLs []string
Confidences models.Confidences
Cwes []models.CweDictEntry
Alerts []models.Alert
@@ -865,28 +895,44 @@ func detailLines() (string, error) {
vinfo := vinfos[currentVinfo]
links := []string{}
if strings.HasPrefix(vinfo.CveID, "CVE-") {
links = append(links, vinfo.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vinfo.CveID)[0].Value,
vinfo.Cvss2CalcURL(),
vinfo.Cvss3CalcURL())
}
for _, url := range vinfo.VendorLinks(r.Family) {
links = append(links, url)
for _, r := range vinfo.CveContents.PrimarySrcURLs(config.Conf.Lang, r.Family, vinfo.CveID) {
links = append(links, r.Value)
}
refs := []models.Reference{}
refsMap := map[string]models.Reference{}
for _, rr := range vinfo.CveContents.References(r.Family) {
for _, ref := range rr.Value {
if ref.Source == "" {
ref.Source = "-"
}
refs = append(refs, ref)
refsMap[ref.Link] = ref
}
}
if cont, found := vinfo.CveContents[models.Trivy]; found {
for _, ref := range cont.References {
refsMap[ref.Link] = ref
}
}
refs := []models.Reference{}
for _, v := range refsMap {
refs = append(refs, v)
}
summary := vinfo.Summaries(r.Lang, r.Family)[0]
mitigation := vinfo.Mitigations(r.Family)[0]
mitigations := []string{}
for _, m := range vinfo.Mitigations {
switch m.CveContentType {
case models.RedHatAPI, models.Microsoft:
mitigations = append(mitigations,
fmt.Sprintf("%s (%s)", m.Mitigation, m.CveContentType))
case models.Nvd:
mitigations = append(mitigations,
fmt.Sprintf("* %s (%s)", m.URL, m.CveContentType))
default:
util.Log.Errorf("Unknown CveContentType: %s", m)
}
}
table := uitable.New()
table.MaxColWidth = maxColWidth
@@ -924,7 +970,8 @@ func detailLines() (string, error) {
CveID: vinfo.CveID,
Cvsses: fmt.Sprintf("%s\n", table),
Summary: fmt.Sprintf("%s (%s)", summary.Value, summary.Type),
Mitigation: fmt.Sprintf("%s (%s)", mitigation.Value, mitigation.Type),
Mitigation: strings.Join(mitigations, "\n"),
PatchURLs: vinfo.CveContents.PatchURLs(),
Confidences: vinfo.Confidences,
Cwes: cwes,
Links: util.Distinct(links),
@@ -953,13 +1000,18 @@ Summary
Mitigation
-----------
{{.Mitigation }}
{{.Mitigation }}
Links
Primary Src
-----------
{{range $link := .Links -}}
* {{$link}}
{{end}}
Patch
-----------
{{range $url := .PatchURLs -}}
* {{$url}}
{{end}}
CWE
-----------
{{range .Cwes -}}

View File

@@ -2,6 +2,7 @@ package report
import (
"bytes"
"encoding/csv"
"encoding/json"
"fmt"
"io/ioutil"
@@ -21,7 +22,11 @@ import (
"golang.org/x/xerrors"
)
const maxColWidth = 100
const (
vulsOpenTag = "<vulsreport>"
vulsCloseTag = "</vulsreport>"
maxColWidth = 100
)
func formatScanSummary(rs ...models.ScanResult) string {
table := uitable.New()
@@ -71,6 +76,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
r.ScannedCves.FormatFixedStatus(r.Packages),
r.FormatUpdatablePacksSummary(),
r.FormatExploitCveSummary(),
r.FormatMetasploitCveSummary(),
r.FormatAlertSummary(),
}
} else {
@@ -96,7 +102,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
}
func formatList(r models.ScanResult) string {
header := r.FormatTextReportHeadedr()
header := r.FormatTextReportHeader()
if len(r.Errors) != 0 {
return fmt.Sprintf(
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
@@ -126,7 +132,7 @@ No CVE-IDs are found in updatable packages.
// packname += strings.Join(vinfo.CpeURIs, ", ")
exploits := ""
if 0 < len(vinfo.Exploits) {
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
exploits = "POC"
}
@@ -134,7 +140,7 @@ No CVE-IDs are found in updatable packages.
if strings.HasPrefix(vinfo.CveID, "CVE-") {
link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
link = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
}
data = append(data, []string{
@@ -170,7 +176,7 @@ No CVE-IDs are found in updatable packages.
}
func formatFullPlainText(r models.ScanResult) (lines string) {
header := r.FormatTextReportHeadedr()
header := r.FormatTextReportHeader()
if len(r.Errors) != 0 {
return fmt.Sprintf(
"%s\nError: Use configtest subcommand or scan with --debug to view the details\n%s\n\n",
@@ -211,9 +217,91 @@ No CVE-IDs are found in updatable packages.
data = append(data, []string{"Summary", vuln.Summaries(
config.Conf.Lang, r.Family)[0].Value})
mitigation := vuln.Mitigations(r.Family)[0]
if mitigation.Type != models.Unknown {
data = append(data, []string{"Mitigation", mitigation.Value})
for _, m := range vuln.Mitigations {
data = append(data, []string{"Mitigation", m.URL})
}
links := vuln.CveContents.PrimarySrcURLs(
config.Conf.Lang, r.Family, vuln.CveID)
for _, link := range links {
data = append(data, []string{"Primary Src", link.Value})
}
for _, url := range vuln.CveContents.PatchURLs() {
data = append(data, []string{"Patch", url})
}
vuln.AffectedPackages.Sort()
for _, affected := range vuln.AffectedPackages {
if pack, ok := r.Packages[affected.Name]; ok {
var line string
if pack.Repository != "" {
line = fmt.Sprintf("%s (%s)",
pack.FormatVersionFromTo(affected),
pack.Repository)
} else {
line = pack.FormatVersionFromTo(affected)
}
data = append(data, []string{"Affected Pkg", line})
if len(pack.AffectedProcs) != 0 {
for _, p := range pack.AffectedProcs {
if len(p.ListenPortStats) == 0 {
data = append(data, []string{"",
fmt.Sprintf(" - PID: %s %s, Port: []", p.PID, p.Name)})
}
var ports []string
for _, pp := range p.ListenPortStats {
if len(pp.PortReachableTo) == 0 {
ports = append(ports, fmt.Sprintf("%s:%s", pp.BindAddress, pp.Port))
} else {
ports = append(ports, fmt.Sprintf("%s:%s(◉ Scannable: %s)", pp.BindAddress, pp.Port, pp.PortReachableTo))
}
}
data = append(data, []string{"",
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, ports)})
}
}
}
}
sort.Strings(vuln.CpeURIs)
for _, name := range vuln.CpeURIs {
data = append(data, []string{"CPE", name})
}
for _, alert := range vuln.GitHubSecurityAlerts {
data = append(data, []string{"GitHub", alert.PackageName})
}
for _, wp := range vuln.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
if p.Type == models.WPCore {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, FixedIn: %s", wp.Name, p.Version, wp.FixedIn)})
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, Update: %s, FixedIn: %s, %s",
wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
}
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s", wp.Name)})
}
}
for _, l := range vuln.LibraryFixedIns {
libs := r.LibraryScanners.Find(l.Path, l.Name)
for path, lib := range libs {
data = append(data, []string{l.Key,
fmt.Sprintf("%s-%s, FixedIn: %s (%s)",
lib.Name, lib.Version, l.FixedIn, path)})
}
}
for _, confidence := range vuln.Confidences {
data = append(data, []string{"Confidence", confidence.String()})
}
cweURLs, top10URLs := []string{}, []string{}
@@ -245,73 +333,6 @@ No CVE-IDs are found in updatable packages.
cweURLs = append(cweURLs, url)
}
vuln.AffectedPackages.Sort()
for _, affected := range vuln.AffectedPackages {
if pack, ok := r.Packages[affected.Name]; ok {
var line string
if pack.Repository != "" {
line = fmt.Sprintf("%s (%s)",
pack.FormatVersionFromTo(affected),
pack.Repository)
} else {
line = pack.FormatVersionFromTo(affected)
}
data = append(data, []string{"Affected Pkg", line})
if len(pack.AffectedProcs) != 0 {
for _, p := range pack.AffectedProcs {
data = append(data, []string{"",
fmt.Sprintf(" - PID: %s %s, Port: %s", p.PID, p.Name, p.ListenPorts)})
}
}
}
}
sort.Strings(vuln.CpeURIs)
for _, name := range vuln.CpeURIs {
data = append(data, []string{"CPE", name})
}
for _, alert := range vuln.GitHubSecurityAlerts {
data = append(data, []string{"GitHub", alert.PackageName})
}
for _, wp := range vuln.WpPackageFixStats {
if p, ok := r.WordPressPackages.Find(wp.Name); ok {
if p.Type == models.WPCore {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, FixedIn: %s", wp.Name, p.Version, wp.FixedIn)})
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s-%s, Update: %s, FixedIn: %s, %s",
wp.Name, p.Version, p.Update, wp.FixedIn, p.Status)})
}
} else {
data = append(data, []string{"WordPress",
fmt.Sprintf("%s", wp.Name)})
}
}
for _, confidence := range vuln.Confidences {
data = append(data, []string{"Confidence", confidence.String()})
}
if strings.HasPrefix(vuln.CveID, "CVE-") {
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
data = append(data, []string{"Source", links[0].Value})
if 0 < len(vuln.Cvss2Scores(r.Family)) {
data = append(data, []string{"CVSSv2 Calc", vuln.Cvss2CalcURL()})
}
if 0 < len(vuln.Cvss3Scores()) {
data = append(data, []string{"CVSSv3 Calc", vuln.Cvss3CalcURL()})
}
}
vlinks := vuln.VendorLinks(r.Family)
for name, url := range vlinks {
data = append(data, []string{name, url})
}
for _, url := range cweURLs {
data = append(data, []string{"CWE", url})
}
@@ -358,6 +379,45 @@ No CVE-IDs are found in updatable packages.
return
}
func formatCsvList(r models.ScanResult, path string) error {
data := [][]string{{"CVE-ID", "CVSS", "Attack", "PoC", "CERT", "Fixed", "NVD"}}
for _, vinfo := range r.ScannedCves.ToSortedSlice() {
max := vinfo.MaxCvssScore().Value.Score
exploits := ""
if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) {
exploits = "POC"
}
link := ""
if strings.HasPrefix(vinfo.CveID, "CVE-") {
link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID)
} else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") {
link = fmt.Sprintf("https://wpscan.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-"))
}
data = append(data, []string{
vinfo.CveID,
fmt.Sprintf("%4.1f", max),
vinfo.AttackVector(),
exploits,
vinfo.AlertDict.FormatSource(),
vinfo.PatchStatus(r.Packages),
link,
})
}
file, err := os.Create(path)
if err != nil {
return xerrors.Errorf("Failed to create a file: %s, err: %w", path, err)
}
defer file.Close()
if err := csv.NewWriter(file).WriteAll(data); err != nil {
return xerrors.Errorf("Failed to write to file: %s, err: %w", path, err)
}
return nil
}
func cweURL(cweID string) string {
return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
strings.TrimPrefix(cweID, "CWE-"))
@@ -378,15 +438,23 @@ func formatChangelogs(r models.ScanResult) string {
}
return strings.Join(buf, "\n")
}
func ovalSupported(r *models.ScanResult) bool {
func reuseScannedCves(r *models.ScanResult) bool {
switch r.Family {
case
config.Amazon,
config.FreeBSD,
config.Raspbian:
return false
return true
}
return true
if isTrivyResult(r) {
return true
}
return false
}
func isTrivyResult(r *models.ScanResult) bool {
_, ok := r.Optional["trivy-target"]
return ok
}
func needToRefreshCve(r models.ScanResult) bool {
@@ -490,9 +558,9 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
updated[v.CveID] = v
util.Log.Debugf("updated: %s", v.CveID)
// TODO commented out beause a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
// TODO commented out because a bug of diff logic when multiple oval defs found for a certain CVE-ID and same updated_at
// if these OVAL defs have different affected packages, this logic detects as updated.
// This logic will be uncommented after integration with ghost https://github.com/knqyf263/gost
// This logic will be uncomented after integration with gost https://github.com/knqyf263/gost
// } else if isCveFixed(v, previous) {
// updated[v.CveID] = v
// util.Log.Debugf("fixed: %s", v.CveID)
@@ -506,6 +574,10 @@ func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
}
}
if len(updated) == 0 {
util.Log.Infof("%s: There are %d vulnerabilities, but no difference between current result and previous one.", current.FormatServerName(), len(current.ScannedCves))
}
for cveID, vuln := range new {
updated[cveID] = vuln
}
@@ -529,7 +601,7 @@ func isCveFixed(current models.VulnInfo, previous models.ScanResult) bool {
func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
cTypes := []models.CveContentType{
models.NvdXML,
models.Nvd,
models.Jvn,
models.NewCveContentType(current.Family),
}
@@ -599,7 +671,7 @@ func ListValidJSONDirs() (dirs []string, err error) {
// Otherwise, returns the path of the latest directory
func JSONDir(args []string) (string, error) {
var err error
dirs := []string{}
var dirs []string
if 0 < len(args) {
if dirs, err = ListValidJSONDirs(); err != nil {

View File

@@ -42,7 +42,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
@@ -56,7 +56,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0001",
LastModified: time.Time{},
},
@@ -113,7 +113,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: new,
},
@@ -128,7 +128,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: old,
},
@@ -150,7 +150,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
CveID: "CVE-2017-0004",
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2017-0002",
LastModified: old,
},
@@ -356,7 +356,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -374,7 +374,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -397,7 +397,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},
@@ -415,7 +415,7 @@ func TestIsCveFixed(t *testing.T) {
},
CveContents: models.NewCveContents(
models.CveContent{
Type: models.NvdXML,
Type: models.Nvd,
CveID: "CVE-2016-6662",
LastModified: time.Time{},
},

View File

@@ -1,16 +1,14 @@
package report
package saas
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
@@ -23,8 +21,8 @@ import (
"golang.org/x/xerrors"
)
// SaasWriter writes results to SaaS
type SaasWriter struct{}
// Writer writes results to SaaS
type Writer struct{}
// TempCredential : TempCredential
type TempCredential struct {
@@ -34,7 +32,7 @@ type TempCredential struct {
}
type payload struct {
GroupID int `json:"GroupID"`
GroupID int64 `json:"GroupID"`
Token string `json:"Token"`
ScannedBy string `json:"ScannedBy"`
ScannedIPv4s string `json:"ScannedIPv4s"`
@@ -42,7 +40,7 @@ type payload struct {
}
// UploadSaas : UploadSaas
func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
func (w Writer) Write(rs ...models.ScanResult) (err error) {
// dir string, configPath string, config *c.Config
if len(rs) == 0 {
return nil
@@ -142,11 +140,3 @@ func (w SaasWriter) Write(rs ...models.ScanResult) (err error) {
util.Log.Infof("done")
return nil
}
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
timestr := scannedAt.UTC().Format(time.RFC3339)
if len(container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s.json", timestr, uuid)
}
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
}

280
saas/uuid.go Normal file
View File

@@ -0,0 +1,280 @@
package saas
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"reflect"
"regexp"
"sort"
"strings"
"time"
"github.com/BurntSushi/toml"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/hashicorp/go-uuid"
"golang.org/x/xerrors"
)
func renameKeyNameUTC(scannedAt time.Time, uuid string, container models.Container) string {
timestr := scannedAt.UTC().Format(time.RFC3339)
if len(container.ContainerID) == 0 {
return fmt.Sprintf("%s/%s.json", timestr, uuid)
}
return fmt.Sprintf("%s/%s@%s.json", timestr, container.UUID, uuid)
}
const reUUID = "[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"
// Scanning with the -containers-only flag at scan time, the UUID of Container Host may not be generated,
// so check it. Otherwise create a UUID of the Container Host and set it.
func getOrCreateServerUUID(r models.ScanResult, server c.ServerInfo) (serverUUID string, err error) {
if id, ok := server.UUIDs[r.ServerName]; !ok {
if serverUUID, err = uuid.GenerateUUID(); err != nil {
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
}
} else {
matched, err := regexp.MatchString(reUUID, id)
if !matched || err != nil {
if serverUUID, err = uuid.GenerateUUID(); err != nil {
return "", xerrors.Errorf("Failed to generate UUID: %w", err)
}
}
}
return serverUUID, nil
}
// EnsureUUIDs generate a new UUID of the scan target server if UUID is not assigned yet.
// And then set the generated UUID to config.toml and scan results.
func EnsureUUIDs(configPath string, results models.ScanResults) (err error) {
// Sort Host->Container
sort.Slice(results, func(i, j int) bool {
if results[i].ServerName == results[j].ServerName {
return results[i].Container.ContainerID < results[j].Container.ContainerID
}
return results[i].ServerName < results[j].ServerName
})
re := regexp.MustCompile(reUUID)
for i, r := range results {
server := c.Conf.Servers[r.ServerName]
if server.UUIDs == nil {
server.UUIDs = map[string]string{}
}
name := ""
if r.IsContainer() {
name = fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
serverUUID, err := getOrCreateServerUUID(r, server)
if err != nil {
return err
}
if serverUUID != "" {
server.UUIDs[r.ServerName] = serverUUID
}
} else {
name = r.ServerName
}
if id, ok := server.UUIDs[name]; ok {
ok := re.MatchString(id)
if !ok || err != nil {
util.Log.Warnf("UUID is invalid. Re-generate UUID %s: %s", id, err)
} else {
if r.IsContainer() {
results[i].Container.UUID = id
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = id
}
// continue if the UUID has already assigned and valid
continue
}
}
// Generate a new UUID and set to config and scan result
serverUUID, err := uuid.GenerateUUID()
if err != nil {
return err
}
server.UUIDs[name] = serverUUID
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[r.ServerName] = server
if r.IsContainer() {
results[i].Container.UUID = serverUUID
results[i].ServerUUID = server.UUIDs[r.ServerName]
} else {
results[i].ServerUUID = serverUUID
}
}
for name, server := range c.Conf.Servers {
server = cleanForTOMLEncoding(server, c.Conf.Default)
c.Conf.Servers[name] = server
}
email := &c.Conf.EMail
if email.SMTPAddr == "" {
email = nil
}
slack := &c.Conf.Slack
if slack.HookURL == "" {
slack = nil
}
cveDict := &c.Conf.CveDict
ovalDict := &c.Conf.OvalDict
gost := &c.Conf.Gost
exploit := &c.Conf.Exploit
metasploit := &c.Conf.Metasploit
http := &c.Conf.HTTP
if http.URL == "" {
http = nil
}
syslog := &c.Conf.Syslog
if syslog.Host == "" {
syslog = nil
}
aws := &c.Conf.AWS
if aws.S3Bucket == "" {
aws = nil
}
azure := &c.Conf.Azure
if azure.AccountName == "" {
azure = nil
}
chatWork := &c.Conf.ChatWork
if chatWork.APIToken == "" {
chatWork = nil
}
saas := &c.Conf.Saas
if saas.GroupID == 0 {
saas = nil
}
c := struct {
CveDict *c.GoCveDictConf `toml:"cveDict"`
OvalDict *c.GovalDictConf `toml:"ovalDict"`
Gost *c.GostConf `toml:"gost"`
Exploit *c.ExploitConf `toml:"exploit"`
Metasploit *c.MetasploitConf `toml:"metasploit"`
Slack *c.SlackConf `toml:"slack"`
Email *c.SMTPConf `toml:"email"`
HTTP *c.HTTPConf `toml:"http"`
Syslog *c.SyslogConf `toml:"syslog"`
AWS *c.AWS `toml:"aws"`
Azure *c.Azure `toml:"azure"`
ChatWork *c.ChatWorkConf `toml:"chatWork"`
Saas *c.SaasConf `toml:"saas"`
Default c.ServerInfo `toml:"default"`
Servers map[string]c.ServerInfo `toml:"servers"`
}{
CveDict: cveDict,
OvalDict: ovalDict,
Gost: gost,
Exploit: exploit,
Metasploit: metasploit,
Slack: slack,
Email: email,
HTTP: http,
Syslog: syslog,
AWS: aws,
Azure: azure,
ChatWork: chatWork,
Saas: saas,
Default: c.Conf.Default,
Servers: c.Conf.Servers,
}
// rename the current config.toml to config.toml.bak
info, err := os.Lstat(configPath)
if err != nil {
return xerrors.Errorf("Failed to lstat %s: %w", configPath, err)
}
realPath := configPath
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
if realPath, err = os.Readlink(configPath); err != nil {
return xerrors.Errorf("Failed to Read link %s: %w", configPath, err)
}
}
if err := os.Rename(realPath, realPath+".bak"); err != nil {
return xerrors.Errorf("Failed to rename %s: %w", configPath, err)
}
var buf bytes.Buffer
if err := toml.NewEncoder(&buf).Encode(c); err != nil {
return xerrors.Errorf("Failed to encode to toml: %w", err)
}
str := strings.Replace(buf.String(), "\n [", "\n\n [", -1)
str = fmt.Sprintf("%s\n\n%s",
"# See README for details: https://vuls.io/docs/en/usage-settings.html",
str)
return ioutil.WriteFile(realPath, []byte(str), 0600)
}
func cleanForTOMLEncoding(server c.ServerInfo, def c.ServerInfo) c.ServerInfo {
if reflect.DeepEqual(server.Optional, def.Optional) {
server.Optional = nil
}
if def.User == server.User {
server.User = ""
}
if def.Host == server.Host {
server.Host = ""
}
if def.Port == server.Port {
server.Port = ""
}
if def.KeyPath == server.KeyPath {
server.KeyPath = ""
}
if reflect.DeepEqual(server.ScanMode, def.ScanMode) {
server.ScanMode = nil
}
if def.Type == server.Type {
server.Type = ""
}
if reflect.DeepEqual(server.CpeNames, def.CpeNames) {
server.CpeNames = nil
}
if def.OwaspDCXMLPath == server.OwaspDCXMLPath {
server.OwaspDCXMLPath = ""
}
if reflect.DeepEqual(server.IgnoreCves, def.IgnoreCves) {
server.IgnoreCves = nil
}
if reflect.DeepEqual(server.Enablerepo, def.Enablerepo) {
server.Enablerepo = nil
}
for k, v := range def.Optional {
if vv, ok := server.Optional[k]; ok && v == vv {
delete(server.Optional, k)
}
}
return server
}

View File

@@ -1,10 +1,9 @@
package report
package saas
import (
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
@@ -42,7 +41,10 @@ func TestGetOrCreateServerUUID(t *testing.T) {
}
for testcase, v := range cases {
uuid := getOrCreateServerUUID(v.scanResult, v.server)
uuid, err := getOrCreateServerUUID(v.scanResult, v.server)
if err != nil {
t.Errorf("%s", err)
}
if (uuid == defaultUUID) != v.isDefault {
t.Errorf("%s : expected isDefault %t got %s", testcase, v.isDefault, uuid)
}

View File

@@ -147,6 +147,9 @@ func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
line := scanner.Text()
ss := strings.Split(line, "-")
if len(ss) < 3 {
if strings.Contains(ss[0], "WARNING") {
continue
}
return nil, xerrors.Errorf("Failed to parse apk info -v: %s", line)
}
name := strings.Join(ss[:len(ss)-2], "-")

View File

@@ -4,13 +4,14 @@ import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"regexp"
"strings"
"time"
"github.com/aquasecurity/fanal/analyzer"
"github.com/aquasecurity/fanal/extractor"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
@@ -416,12 +417,6 @@ func (l *base) convertToModel() models.ScanResult {
Type: ctype,
}
image := models.Image{
Name: l.ServerInfo.Image.Name,
Tag: l.ServerInfo.Image.Tag,
Digest: l.ServerInfo.Image.Digest,
}
errs, warns := []string{}, []string{}
for _, e := range l.errs {
errs = append(errs, fmt.Sprintf("%+v", e))
@@ -445,7 +440,6 @@ func (l *base) convertToModel() models.ScanResult {
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Image: image,
Platform: l.Platform,
IPv4Addrs: l.ServerInfo.IPv4Addrs,
IPv6Addrs: l.ServerInfo.IPv6Addrs,
@@ -541,8 +535,7 @@ func (l *base) scanLibraries() (err error) {
return nil
}
libFilemap := extractor.FileMap{}
libFilemap := map[string][]byte{}
detectFiles := l.ServerInfo.Lockfiles
// auto detect lockfile
@@ -553,8 +546,8 @@ func (l *base) scanLibraries() (err error) {
}
// delete last "-o "
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "Permission denied"
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "Permission denied"`)
// find / -name "*package-lock.json" -o -name "*yarn.lock" ... 2>&1 | grep -v "find: "
cmd := fmt.Sprintf(`find / ` + findopt[:len(findopt)-3] + ` 2>&1 | grep -v "find: "`)
r := exec(l.ServerInfo, cmd, noSudo)
if r.ExitStatus != 0 && r.ExitStatus != 1 {
return xerrors.Errorf("Failed to find lock files")
@@ -570,25 +563,62 @@ func (l *base) scanLibraries() (err error) {
if _, ok := libFilemap[path]; ok {
continue
}
cmd := fmt.Sprintf("cat %s", path)
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
var bytes []byte
switch l.Distro.Family {
case config.ServerTypePseudo:
bytes, err = ioutil.ReadFile(path)
if err != nil {
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", err, path)
}
default:
cmd := fmt.Sprintf("cat %s", path)
r := exec(l.ServerInfo, cmd, noSudo)
if !r.isSuccess() {
return xerrors.Errorf("Failed to get target file: %s, filepath: %s", r, path)
}
bytes = []byte(r.Stdout)
}
libFilemap[path] = []byte(r.Stdout)
libFilemap[path] = bytes
}
results, err := analyzer.GetLibraries(libFilemap)
if err != nil {
return xerrors.Errorf("Failed to get libs: %w", err)
}
l.LibraryScanners, err = convertLibWithScanner(results)
if err != nil {
return xerrors.Errorf("Failed to scan libraries: %w", err)
for path, b := range libFilemap {
res, err := analyzer.AnalyzeFile(path, &DummyFileInfo{}, func() ([]byte, error) {
return b, nil
})
if err != nil {
return xerrors.Errorf("Failed to get libs: %w", err)
}
libscan, err := convertLibWithScanner(res.Applications)
if err != nil {
return xerrors.Errorf("Failed to scan libraries: %w", err)
}
l.LibraryScanners = append(l.LibraryScanners, libscan...)
}
return nil
}
// DummyFileInfo is a dummy struct for libscan
type DummyFileInfo struct{}
// Name is
func (d *DummyFileInfo) Name() string { return "dummy" }
// Size is
func (d *DummyFileInfo) Size() int64 { return 0 }
// Mode is
func (d *DummyFileInfo) Mode() os.FileMode { return 0 }
//ModTime is
func (d *DummyFileInfo) ModTime() time.Time { return time.Now() }
// IsDir is
func (d *DummyFileInfo) IsDir() bool { return false }
//Sys is
func (d *DummyFileInfo) Sys() interface{} { return nil }
func (l *base) scanWordPress() (err error) {
wpOpts := []string{l.ServerInfo.WordPress.OSUser,
l.ServerInfo.WordPress.DocRoot,
@@ -683,7 +713,7 @@ func (l *base) detectWpThemes() ([]models.WpPackage, error) {
}
err := json.Unmarshal([]byte(r.Stdout), &themes)
if err != nil {
return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", cmd, err)
return nil, xerrors.Errorf("Failed to unmarshal wp theme list: %w", err)
}
for i := range themes {
themes[i].Type = models.WPTheme
@@ -711,6 +741,117 @@ func (l *base) detectWpPlugins() ([]models.WpPackage, error) {
return plugins, nil
}
func (l *base) scanPorts() (err error) {
dest := l.detectScanDest()
open, err := l.execPortsScan(dest)
if err != nil {
return err
}
l.updatePortStatus(open)
return nil
}
func (l *base) detectScanDest() map[string][]string {
scanIPPortsMap := map[string][]string{}
for _, p := range l.osPackages.Packages {
if p.AffectedProcs == nil {
continue
}
for _, proc := range p.AffectedProcs {
if proc.ListenPortStats == nil {
continue
}
for _, port := range proc.ListenPortStats {
scanIPPortsMap[port.BindAddress] = append(scanIPPortsMap[port.BindAddress], port.Port)
}
}
}
scanDestIPPorts := map[string][]string{}
for addr, ports := range scanIPPortsMap {
if addr == "*" {
for _, addr := range l.ServerInfo.IPv4Addrs {
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
}
} else {
scanDestIPPorts[addr] = append(scanDestIPPorts[addr], ports...)
}
}
uniqScanDestIPPorts := map[string][]string{}
for i, scanDest := range scanDestIPPorts {
m := map[string]bool{}
for _, e := range scanDest {
if !m[e] {
m[e] = true
uniqScanDestIPPorts[i] = append(uniqScanDestIPPorts[i], e)
}
}
}
return uniqScanDestIPPorts
}
func (l *base) execPortsScan(scanDestIPPorts map[string][]string) ([]string, error) {
listenIPPorts := []string{}
for ip, ports := range scanDestIPPorts {
if !isLocalExec(l.ServerInfo.Port, l.ServerInfo.Host) && net.ParseIP(ip).IsLoopback() {
continue
}
for _, port := range ports {
scanDest := ip + ":" + port
conn, err := net.DialTimeout("tcp", scanDest, time.Duration(1)*time.Second)
if err != nil {
continue
}
conn.Close()
listenIPPorts = append(listenIPPorts, scanDest)
}
}
return listenIPPorts, nil
}
func (l *base) updatePortStatus(listenIPPorts []string) {
for name, p := range l.osPackages.Packages {
if p.AffectedProcs == nil {
continue
}
for i, proc := range p.AffectedProcs {
if proc.ListenPortStats == nil {
continue
}
for j, port := range proc.ListenPortStats {
l.osPackages.Packages[name].AffectedProcs[i].ListenPortStats[j].PortReachableTo = l.findPortTestSuccessOn(listenIPPorts, port)
}
}
}
}
func (l *base) findPortTestSuccessOn(listenIPPorts []string, searchListenPort models.PortStat) []string {
addrs := []string{}
for _, ipPort := range listenIPPorts {
ipPort, err := models.NewPortStat(ipPort)
if err != nil {
util.Log.Warnf("Failed to find: %+v", err)
continue
}
if searchListenPort.BindAddress == "*" {
if searchListenPort.Port == ipPort.Port {
addrs = append(addrs, ipPort.BindAddress)
}
} else if searchListenPort.BindAddress == ipPort.BindAddress && searchListenPort.Port == ipPort.Port {
addrs = append(addrs, ipPort.BindAddress)
}
}
return addrs
}
func (l *base) ps() (stdout string, err error) {
cmd := `LANGUAGE=en_US.UTF-8 ps --no-headers --ppid 2 -p 2 --deselect -o pid,comm`
r := l.exec(util.PrependProxyEnv(cmd), noSudo)
@@ -773,13 +914,13 @@ func (l *base) lsOfListen() (stdout string, err error) {
cmd := `lsof -i -P -n | grep LISTEN`
r := l.exec(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 1) {
return "", xerrors.Errorf("Failed to SSH: %s", r)
return "", xerrors.Errorf("Failed to lsof: %s", r)
}
return r.Stdout, nil
}
func (l *base) parseLsOf(stdout string) map[string]string {
portPid := map[string]string{}
func (l *base) parseLsOf(stdout string) map[string][]string {
portPids := map[string][]string{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
ss := strings.Fields(scanner.Text())
@@ -787,7 +928,7 @@ func (l *base) parseLsOf(stdout string) map[string]string {
continue
}
pid, ipPort := ss[1], ss[8]
portPid[ipPort] = pid
portPids[ipPort] = util.AppendIfMissing(portPids[ipPort], pid)
}
return portPid
return portPids
}

Some files were not shown because too many files have changed in this diff Show More