Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a522218c4e | ||
|
|
820455399c | ||
|
|
959d612534 | ||
|
|
cd81e6eab2 | ||
|
|
e6ec6920ad | ||
|
|
18a92fa1ca | ||
|
|
f95af9897b | ||
|
|
b61adcb1fd | ||
|
|
1bbf320755 | ||
|
|
159f26171c | ||
|
|
8ac00f6c0d | ||
|
|
ce2daf2493 | ||
|
|
f014f8fd59 | ||
|
|
f50a39a9e2 | ||
|
|
e0d8147104 | ||
|
|
c5cfac62da | ||
|
|
83469ce5cc | ||
|
|
7cd7b4a9a2 | ||
|
|
7681b277cf | ||
|
|
406efa96c0 | ||
|
|
9a7a30c0bc | ||
|
|
64bdfa0e80 | ||
|
|
067089973c | ||
|
|
85e6d753c7 | ||
|
|
4094984642 | ||
|
|
85c0009a43 | ||
|
|
234e312ee2 | ||
|
|
ce3ca64678 | ||
|
|
b042a600c3 | ||
|
|
686e9f07a9 | ||
|
|
bb6725372b | ||
|
|
6f012fc9c5 | ||
|
|
4c82458481 | ||
|
|
a0ac863998 | ||
|
|
d23ef838f8 | ||
|
|
f81ac197f5 | ||
|
|
652b37e630 | ||
|
|
c57e430393 | ||
|
|
fff6047df9 | ||
|
|
1e2b93d55b | ||
|
|
66b27a7795 | ||
|
|
63f0a272c4 | ||
|
|
8d2180cf5a | ||
|
|
1986f7e4dd | ||
|
|
21beb396b4 | ||
|
|
cb5a6f38d6 | ||
|
|
67e4aaede0 | ||
|
|
b42805d00c | ||
|
|
95d6888c87 | ||
|
|
549b315a65 | ||
|
|
5b80b16684 | ||
|
|
0cd0a4bf2b | ||
|
|
b5cf06cad8 | ||
|
|
b964d19d82 | ||
|
|
cf7990d444 | ||
|
|
738ccf7dbb | ||
|
|
fc2ea48c1d | ||
|
|
3af93b93d7 | ||
|
|
f386c3be92 | ||
|
|
239d910dbe | ||
|
|
48929deabd | ||
|
|
79523de1db | ||
|
|
fbfc14dfeb | ||
|
|
a8dc886f89 | ||
|
|
cfc9e064b9 | ||
|
|
e72fa3362a | ||
|
|
26364421e8 | ||
|
|
4a07974b54 | ||
|
|
eaddc7f2ba | ||
|
|
85056aaa00 | ||
|
|
c077c740fa | ||
|
|
c2eab87a3f | ||
|
|
ea582d2d2e | ||
|
|
2f89a24100 | ||
|
|
73ebb94f67 | ||
|
|
95bf387ecc | ||
|
|
f17a8452f9 | ||
|
|
920ffe1f33 | ||
|
|
093bcb7477 | ||
|
|
c06b3ec9eb | ||
|
|
ac6fe6f9fc | ||
|
|
2dffdaac42 | ||
|
|
cb445c9504 | ||
|
|
e3fc3aa9d1 | ||
|
|
97c3f5d642 | ||
|
|
0a52fc9a56 | ||
|
|
c831339b0d | ||
|
|
058ccf575f | ||
|
|
92be12bc2f | ||
|
|
1aa2f4b5b1 | ||
|
|
bba9431985 | ||
|
|
3c39f1e737 | ||
|
|
e6f4d07a87 | ||
|
|
e43358a0d2 | ||
|
|
f0644e8a9d | ||
|
|
11b010b281 | ||
|
|
c751029127 | ||
|
|
fb70d1b2f0 | ||
|
|
3d68783b7f | ||
|
|
0d77853912 | ||
|
|
ea1b5dd8f7 | ||
|
|
2dcb7d5ce1 | ||
|
|
99cab34527 | ||
|
|
f5eeed0bc2 | ||
|
|
1b85e56961 | ||
|
|
8a8ac5fd22 | ||
|
|
00c0354a8e | ||
|
|
a2a6973ba1 | ||
|
|
dd1d3a05fa | ||
|
|
2afe2d2640 | ||
|
|
29678f9b59 | ||
|
|
77edb251bb | ||
|
|
29151fa267 | ||
|
|
b3f13790bd | ||
|
|
38857c3356 | ||
|
|
d75990d9fd | ||
|
|
ed063f6534 | ||
|
|
c8a9bdc517 | ||
|
|
595729cdf8 | ||
|
|
6119f79748 | ||
|
|
d4fb46c9ba | ||
|
|
c41301afca | ||
|
|
50fd80830e | ||
|
|
1c203b4272 | ||
|
|
c545e9045d | ||
|
|
2721dc0647 | ||
|
|
51d13f4234 | ||
|
|
a60a5d6eab | ||
|
|
5959235425 | ||
|
|
d8e6d4e5fc | ||
|
|
7dfc9815b3 | ||
|
|
0c53b187a4 | ||
|
|
42dadfed8f | ||
|
|
a46c603c77 | ||
|
|
ad0020d9a6 | ||
|
|
a224f0bfd4 | ||
|
|
d8dc3650d3 | ||
|
|
30f7527f10 | ||
|
|
b1f5bdd8b2 | ||
|
|
c8e7c8b9fa | ||
|
|
30bf3223f8 | ||
|
|
886710ec30 | ||
|
|
510dc8d828 | ||
|
|
5ff7b2aab4 | ||
|
|
1e33536205 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,7 +2,9 @@ vuls
|
||||
.vscode
|
||||
*.txt
|
||||
*.json
|
||||
*.sqlite3
|
||||
*.sqlite3*
|
||||
*.db
|
||||
tags
|
||||
.gitmodules
|
||||
coverage.out
|
||||
issues/
|
||||
@@ -10,3 +12,4 @@ vendor/
|
||||
log/
|
||||
results/
|
||||
*config.toml
|
||||
!setup/docker/*
|
||||
|
||||
117
CHANGELOG.md
117
CHANGELOG.md
@@ -1,5 +1,122 @@
|
||||
# Change Log
|
||||
|
||||
## [v0.1.7](https://github.com/future-architect/vuls/tree/v0.1.7) (2016-11-08)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.6...v0.1.7)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Enable to scan only docker container, without docker host [\#122](https://github.com/future-architect/vuls/issues/122)
|
||||
- Add -skip-broken option \[CentOS only\] \#245 [\#248](https://github.com/future-architect/vuls/pull/248) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Display unknown CVEs to TUI [\#244](https://github.com/future-architect/vuls/pull/244) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add the XML output [\#240](https://github.com/future-architect/vuls/pull/240) ([gleentea](https://github.com/gleentea))
|
||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
|
||||
- Integrate OWASP Dependency Check [\#232](https://github.com/future-architect/vuls/pull/232) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add support for reading CVE data from MySQL. [\#225](https://github.com/future-architect/vuls/pull/225) ([oswell](https://github.com/oswell))
|
||||
- Remove base docker image, -v shows commit hash [\#223](https://github.com/future-architect/vuls/pull/223) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Support ignore CveIDs in config [\#222](https://github.com/future-architect/vuls/pull/222) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Confirm before installing dependencies on prepare [\#219](https://github.com/future-architect/vuls/pull/219) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove all.json [\#218](https://github.com/future-architect/vuls/pull/218) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add GitHub issue template [\#217](https://github.com/future-architect/vuls/pull/217) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Improve makefile, -version shows git hash, fix README [\#216](https://github.com/future-architect/vuls/pull/216) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- change e-mail package from gomail to net/smtp [\#211](https://github.com/future-architect/vuls/pull/211) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Add only-containers option to scan subcommand \#122 [\#190](https://github.com/future-architect/vuls/pull/190) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix -results-dir option of scan subcommand [\#185](https://github.com/future-architect/vuls/pull/185) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Show error when no scannable servers are detected. [\#177](https://github.com/future-architect/vuls/pull/177) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add sudo check to prepare subcommand [\#176](https://github.com/future-architect/vuls/pull/176) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Supports yum --enablerepo option \(supports only base,updates for now\) [\#147](https://github.com/future-architect/vuls/pull/147) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Debian 8.6 \(jessie\) scan does not show vulnerable packages [\#235](https://github.com/future-architect/vuls/issues/235)
|
||||
- panic: runtime error: index out of range - ubuntu 16.04 + vuls history [\#180](https://github.com/future-architect/vuls/issues/180)
|
||||
- Moved golang.org/x/net/context to context [\#243](https://github.com/future-architect/vuls/pull/243) ([yoheimuta](https://github.com/yoheimuta))
|
||||
- Fix changelog cache bug on Ubuntu and Debian \#235 [\#238](https://github.com/future-architect/vuls/pull/238) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
|
||||
- Fixed error for the latest version of gocui [\#231](https://github.com/future-architect/vuls/pull/231) ([ymd38](https://github.com/ymd38))
|
||||
- Handle the refactored gocui SetCurrentView method. [\#229](https://github.com/future-architect/vuls/pull/229) ([oswell](https://github.com/oswell))
|
||||
- Fix locale env var LANG to LANGUAGE [\#215](https://github.com/future-architect/vuls/pull/215) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fixed bug with parsing update line on CentOS/RHEL [\#206](https://github.com/future-architect/vuls/pull/206) ([andyone](https://github.com/andyone))
|
||||
- Fix defer cache.DB.close [\#201](https://github.com/future-architect/vuls/pull/201) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a help message of -report-azure-blob option [\#195](https://github.com/future-architect/vuls/pull/195) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling in tui [\#193](https://github.com/future-architect/vuls/pull/193) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix not working changelog cache on Container [\#189](https://github.com/future-architect/vuls/pull/189) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix release version detection on FreeBSD [\#184](https://github.com/future-architect/vuls/pull/184) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix defer cahce.DB.close\(\) [\#183](https://github.com/future-architect/vuls/pull/183) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a mode of files/dir \(report, log\) [\#182](https://github.com/future-architect/vuls/pull/182) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a error when no json dirs are found under results \#180 [\#181](https://github.com/future-architect/vuls/pull/181) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- ssh-external option of configtest is not working \#178 [\#179](https://github.com/future-architect/vuls/pull/179) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Recent changes to gobui cause build failures [\#228](https://github.com/future-architect/vuls/issues/228)
|
||||
- https://hub.docker.com/r/vuls/go-cve-dictionary/ is empty [\#208](https://github.com/future-architect/vuls/issues/208)
|
||||
- Not able to install gomail fails [\#202](https://github.com/future-architect/vuls/issues/202)
|
||||
- No results file created - vuls tui failed [\#199](https://github.com/future-architect/vuls/issues/199)
|
||||
- Wrong file permissions for results/\*.json in official Docker container [\#197](https://github.com/future-architect/vuls/issues/197)
|
||||
- Failed: Unknown OS Type [\#196](https://github.com/future-architect/vuls/issues/196)
|
||||
- Segmentation fault with configtest [\#192](https://github.com/future-architect/vuls/issues/192)
|
||||
- Failed to scan. err: No server defined. Check the configuration [\#187](https://github.com/future-architect/vuls/issues/187)
|
||||
- vuls configtest -ssh-external doesnt work [\#178](https://github.com/future-architect/vuls/issues/178)
|
||||
- apt-get update: time out [\#175](https://github.com/future-architect/vuls/issues/175)
|
||||
- scanning on Centos6, but vuls recognizes debian. [\#174](https://github.com/future-architect/vuls/issues/174)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Update README \#225 [\#242](https://github.com/future-architect/vuls/pull/242) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix readme [\#241](https://github.com/future-architect/vuls/pull/241) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix README \#234 [\#237](https://github.com/future-architect/vuls/pull/237) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update glide files [\#236](https://github.com/future-architect/vuls/pull/236) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix README [\#226](https://github.com/future-architect/vuls/pull/226) ([usiusi360](https://github.com/usiusi360))
|
||||
- fix some misspelling. [\#221](https://github.com/future-architect/vuls/pull/221) ([ymomoi](https://github.com/ymomoi))
|
||||
- fix docker readme [\#214](https://github.com/future-architect/vuls/pull/214) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix ja document about typo [\#213](https://github.com/future-architect/vuls/pull/213) ([shokohara](https://github.com/shokohara))
|
||||
- fix readme [\#212](https://github.com/future-architect/vuls/pull/212) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix README [\#207](https://github.com/future-architect/vuls/pull/207) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- fix typo [\#204](https://github.com/future-architect/vuls/pull/204) ([usiusi360](https://github.com/usiusi360))
|
||||
- fix gitignore [\#191](https://github.com/future-architect/vuls/pull/191) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Update glide.lock [\#188](https://github.com/future-architect/vuls/pull/188) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix path in setup/docker/README [\#186](https://github.com/future-architect/vuls/pull/186) ([dladuke](https://github.com/dladuke))
|
||||
- Vuls and vulsrepo are now separated [\#163](https://github.com/future-architect/vuls/pull/163) ([hikachan](https://github.com/hikachan))
|
||||
|
||||
## [v0.1.6](https://github.com/future-architect/vuls/tree/v0.1.6) (2016-09-12)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.5...v0.1.6)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- High speed scan on Ubuntu/Debian [\#172](https://github.com/future-architect/vuls/pull/172) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support CWE\(Common Weakness Enumeration\) [\#169](https://github.com/future-architect/vuls/pull/169) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Enable to scan without sudo on amazon linux [\#167](https://github.com/future-architect/vuls/pull/167) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security [\#161](https://github.com/future-architect/vuls/pull/161) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- delete sqlite3 [\#152](https://github.com/future-architect/vuls/pull/152) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Failed to setup vuls docker [\#170](https://github.com/future-architect/vuls/issues/170)
|
||||
- yum check-update error occurred when no reboot after kernel updating [\#165](https://github.com/future-architect/vuls/issues/165)
|
||||
- error thrown from 'docker build .' [\#157](https://github.com/future-architect/vuls/issues/157)
|
||||
- CVE-ID is truncated to 4 digits [\#153](https://github.com/future-architect/vuls/issues/153)
|
||||
- 'yum update --changelog' stalled in 'vuls scan'. if ssh user is not 'root'. [\#150](https://github.com/future-architect/vuls/issues/150)
|
||||
- Panic on packet scan [\#131](https://github.com/future-architect/vuls/issues/131)
|
||||
- Update glide.lock \#170 [\#171](https://github.com/future-architect/vuls/pull/171) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix detecting a platform on Azure [\#168](https://github.com/future-architect/vuls/pull/168) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix parse error for yum check-update \#165 [\#166](https://github.com/future-architect/vuls/pull/166) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix bug: Vuls on Docker [\#159](https://github.com/future-architect/vuls/pull/159) ([tjinjin](https://github.com/tjinjin))
|
||||
- Fix CVE-ID is truncated to 4 digits [\#155](https://github.com/future-architect/vuls/pull/155) ([usiusi360](https://github.com/usiusi360))
|
||||
- Fix yum update --changelog stalled when non-root ssh user on CentOS \#150 [\#151](https://github.com/future-architect/vuls/pull/151) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Support su for root privilege escalation [\#44](https://github.com/future-architect/vuls/issues/44)
|
||||
- Support FreeBSD [\#34](https://github.com/future-architect/vuls/issues/34)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Change scripts for data fetching from jvn [\#164](https://github.com/future-architect/vuls/pull/164) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix: setup vulsrepo [\#162](https://github.com/future-architect/vuls/pull/162) ([tjinjin](https://github.com/tjinjin))
|
||||
- Fix-docker-vulsrepo-install [\#160](https://github.com/future-architect/vuls/pull/160) ([usiusi360](https://github.com/usiusi360))
|
||||
- Reduce regular expression compilation [\#158](https://github.com/future-architect/vuls/pull/158) ([itchyny](https://github.com/itchyny))
|
||||
- Add testcases for \#153 [\#156](https://github.com/future-architect/vuls/pull/156) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.5](https://github.com/future-architect/vuls/tree/v0.1.5) (2016-08-16)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.4...v0.1.5)
|
||||
|
||||
|
||||
36
ISSUE_TEMPLATE
Normal file
36
ISSUE_TEMPLATE
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
# Environment
|
||||
|
||||
## Vuls
|
||||
|
||||
Hash : ____
|
||||
|
||||
To check the commit hash of HEAD
|
||||
$ vuls -v
|
||||
|
||||
or
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git rev-parse --short HEAD
|
||||
|
||||
## OS
|
||||
- Target Server: Write here
|
||||
- Vuls Server: Write here
|
||||
|
||||
## Go
|
||||
- Go version: here
|
||||
|
||||
# Current Output
|
||||
|
||||
Please re-run the command using ```-debug``` and provide the output below.
|
||||
|
||||
# Addition Details
|
||||
|
||||
Can you also please fill in each of the remaining sections.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
## Steps to reproduce the behaviour
|
||||
|
||||
|
||||
30
Makefile
30
Makefile
@@ -1,4 +1,9 @@
|
||||
.PHONY: \
|
||||
glide \
|
||||
deps \
|
||||
update \
|
||||
build \
|
||||
install \
|
||||
all \
|
||||
vendor \
|
||||
lint \
|
||||
@@ -11,14 +16,29 @@
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go')
|
||||
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
|
||||
PKGS = ./. ./config ./models ./report ./cveapi ./scan ./util ./commands ./cache
|
||||
VERSION := $(shell git describe --tags --abbrev=0)
|
||||
REVISION := $(shell git rev-parse --short HEAD)
|
||||
LDFLAGS := -X 'main.version=$(VERSION)' \
|
||||
-X 'main.revision=$(REVISION)'
|
||||
|
||||
glide:
|
||||
go get github.com/Masterminds/glide
|
||||
|
||||
deps: glide
|
||||
glide install
|
||||
|
||||
update: glide
|
||||
glide update
|
||||
|
||||
build: main.go deps
|
||||
go build -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
|
||||
install: main.go deps
|
||||
go install -ldflags "$(LDFLAGS)"
|
||||
|
||||
all: test
|
||||
|
||||
# vendor:
|
||||
# @ go get -v github.com/mjibson/party
|
||||
# party -d external -c -u
|
||||
|
||||
lint:
|
||||
@ go get -v github.com/golang/lint/golint
|
||||
$(foreach file,$(SRCS),golint $(file) || exit;)
|
||||
|
||||
@@ -107,14 +107,14 @@ Vuls requiert l'installation des paquets suivants :
|
||||
- sqlite
|
||||
- git
|
||||
- gcc
|
||||
- go v1.6
|
||||
- go v1.7.1 or later
|
||||
- https://golang.org/doc/install
|
||||
|
||||
```bash
|
||||
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
|
||||
$ sudo yum -y install sqlite git gcc
|
||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
|
||||
$ mkdir $HOME/go
|
||||
```
|
||||
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
|
||||
@@ -201,7 +201,7 @@ Summary Unspecified vulnerability in the Java SE and Java SE Embedded co
|
||||
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
|
||||
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
|
||||
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
|
||||
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
|
||||
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
|
||||
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
|
||||
|
||||
396
README.ja.md
396
README.ja.md
@@ -46,10 +46,13 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり
|
||||
- CPEに登録されているソフトウェアが対象
|
||||
- エージェントレスアーキテクチャ
|
||||
- スキャン対象サーバにSSH接続可能なマシン1台にセットアップするだけで動作
|
||||
- 非破壊スキャン(SSHでコマンド発行するだけ)
|
||||
- AWSでの脆弱性/侵入テスト事前申請は必要なし
|
||||
- 設定ファイルのテンプレート自動生成
|
||||
- CIDRを指定してサーバを自動検出、設定ファイルのテンプレートを生成
|
||||
- EmailやSlackで通知可能(日本語でのレポートも可能)
|
||||
- 付属するTerminal-Based User Interfaceビューアでは、Vim風キーバインドでスキャン結果を参照可能
|
||||
- Web UI([VulsRepo](https://github.com/usiusi360/vulsrepo))を使えばピボットテーブルのように分析可能
|
||||
|
||||
----
|
||||
|
||||
@@ -64,7 +67,6 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり
|
||||
Vulsのセットアップは以下の3パターンがある
|
||||
|
||||
- Dockerコンテナ上にセットアップ
|
||||
Docker Composeを用いて少ないコマンドでセットアップ可能
|
||||
see https://github.com/future-architect/vuls/tree/master/setup/docker
|
||||
[日本語README](https://github.com/future-architect/vuls/blob/master/setup/docker/README.ja.md)
|
||||
- Chefでセットアップ
|
||||
@@ -88,6 +90,7 @@ Hello Vulsチュートリアルでは手動でのセットアップ方法で説
|
||||
1. Prepare
|
||||
1. Scan
|
||||
1. TUI(Terminal-Based User Interface)で結果を参照する
|
||||
1. Web UI([VulsRepo](https://github.com/usiusi360/vulsrepo))で結果を参照する
|
||||
|
||||
## Step1. Launch Amazon Linux
|
||||
|
||||
@@ -119,17 +122,17 @@ VulsはSSHパスワード認証をサポートしていない。SSH公開鍵鍵
|
||||
|
||||
Vulsセットアップに必要な以下のソフトウェアをインストールする。
|
||||
|
||||
- SQLite3
|
||||
- git v2
|
||||
- SQLite3 or MySQL
|
||||
- git
|
||||
- gcc
|
||||
- go v1.6
|
||||
- go v1.7.1 or later
|
||||
- https://golang.org/doc/install
|
||||
|
||||
```bash
|
||||
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
|
||||
$ sudo yum -y install sqlite git gcc
|
||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
|
||||
$ mkdir $HOME/go
|
||||
```
|
||||
/etc/profile.d/goenv.sh を作成し、下記を追加する。
|
||||
@@ -147,26 +150,19 @@ $ source /etc/profile.d/goenv.sh
|
||||
|
||||
## Step4. Deploy [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
|
||||
go get
|
||||
|
||||
```bash
|
||||
$ sudo mkdir /var/log/vuls
|
||||
$ sudo chown ec2-user /var/log/vuls
|
||||
$ sudo chmod 700 /var/log/vuls
|
||||
$ go get github.com/kotakanbe/go-cve-dictionary
|
||||
$
|
||||
$ mkdir -p $GOPATH/src/github.com/kotakanbe
|
||||
$ cd $GOPATH/src/github.com/kotakanbe
|
||||
$ git clone https://github.com/kotakanbe/go-cve-dictionary.git
|
||||
$ cd go-cve-dictionary
|
||||
$ make install
|
||||
```
|
||||
バイナリは、`$GOPATH/bin`いかに生成される
|
||||
|
||||
go-cve-dictionaryを既にインストール済みでupdateしたい場合は
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/kotakanbe/go-cve-dictionary
|
||||
```
|
||||
|
||||
で可能である。
|
||||
|
||||
go getでエラーが発生した場合は、以下の点を確認する。
|
||||
- Gitのバージョンがv2以降か?
|
||||
- Go依存パッケージの問題でgo getに失敗する場合は [deploying with glide](https://github.com/future-architect/vuls/blob/master/README.md#deploy-with-glide) を試す。
|
||||
|
||||
NVDから脆弱性データベースを取得する。
|
||||
環境によって異なるが、AWS上では10分程度かかる。
|
||||
@@ -181,10 +177,12 @@ $ ls -alh cve.sqlite3
|
||||
## Step5. Deploy Vuls
|
||||
|
||||
新規にターミナルを起動し、先ほど作成したEC2にSSH接続する。
|
||||
|
||||
go get
|
||||
```
|
||||
$ go get github.com/future-architect/vuls
|
||||
$ mkdir -p $GOPATH/src/github.com/future-architect
|
||||
$ cd $GOPATH/src/github.com/future-architect
|
||||
$ git clone https://github.com/future-architect/vuls.git
|
||||
$ cd vuls
|
||||
$ make install
|
||||
```
|
||||
|
||||
vulsを既にインストール済みでupdateしたい場合は
|
||||
@@ -227,7 +225,7 @@ $ vuls prepare
|
||||
## Step8. Start Scanning
|
||||
|
||||
```
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3 -report-json
|
||||
INFO[0000] Start scanning (config: /home/ec2-user/config.toml)
|
||||
INFO[0000] Start scanning
|
||||
INFO[0000] config: /home/ec2-user/config.toml
|
||||
@@ -254,7 +252,7 @@ Summary Unspecified vulnerability in the Java SE and Java SE Embedded co
|
||||
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
|
||||
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
|
||||
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
|
||||
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
|
||||
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
|
||||
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
|
||||
@@ -271,6 +269,11 @@ $ vuls tui
|
||||
|
||||

|
||||
|
||||
## Step10. Web UI
|
||||
|
||||
[VulsRepo](https://github.com/usiusi360/vulsrepo)はスキャン結果をビボットテーブルのように分析可能にするWeb UIである。
|
||||
[Online Demo](http://usiusi360.github.io/vulsrepo/)があるので試してみて。
|
||||
|
||||
----
|
||||
|
||||
# Architecture
|
||||
@@ -281,35 +284,36 @@ $ vuls tui
|
||||
- NVDとJVN(日本語)から脆弱性データベースを取得し、SQLite3に格納する。
|
||||
|
||||
## Vuls
|
||||

|
||||
- SSHでサーバに存在する脆弱性をスキャンし、CVE IDのリストを作成する
|
||||
- Dockerコンテナのスキャンする場合、VulsはまずDockerホストにSSHで接続する。その後、Dockerホスト上で `docker exec` 経由でコマンドを実効する。Dockerコンテナ内にSSHデーモンを起動する必要はない
|
||||
- 検出されたCVEの詳細情報をgo-cve-dictionaryから取得する
|
||||
- スキャン結果をSQLite3に格納する
|
||||
- スキャン結果レポートを生成し、SlackやEmailなどで送信する
|
||||
- スキャン結果の詳細情報はターミナル上で参照可能
|
||||
- スキャン結果をJSONファイルに出力すると詳細情報をターミナル上で参照可能
|
||||
|
||||

|
||||
|
||||
----
|
||||
# Performance Considerations
|
||||
|
||||
- Ubuntu, Debian
|
||||
アップデート対象のパッケージが沢山ある場合は、毎回apt-get changelogするので遅いし、スキャン対象サーバのリソースを消費する。
|
||||
`apt-get changelog`でアップデート対象のパッケージのチェンジログを取得し、含まれるCVE IDをパースする。
|
||||
アップデート対象のパッケージが沢山ある場合、チェンジログの取得に時間がかかるので、初回のスキャンは遅い。
|
||||
ただ、2回目以降はキャッシュしたchangelogを使うので速くなる。
|
||||
|
||||
- CentOS
|
||||
アップデート対象すべてのchangelogを一度で取得しパースする。スキャンスピードは高速、サーバリソース消費量は小さい。
|
||||
アップデート対象すべてのchangelogを一度で取得しパースする。スキャンスピードは速い、サーバリソース消費量は小さい。
|
||||
|
||||
- Amazon, RHEL and FreeBSD
|
||||
高速にスキャンし、スキャン対象サーバのリソース消費量は小さい。
|
||||
|
||||
| Distribution| Scan Speed | Resource Usage On Target Server |
|
||||
|:------------|:-------------------|:-------------|
|
||||
| Ubuntu | Slow | Heavy |
|
||||
| Debian | Slow | Heavy |
|
||||
| CentOS | Fast | Light |
|
||||
| Amazon | Fast | Light |
|
||||
| RHEL | Fast | Light |
|
||||
| FreeBSD | Fast | Light |
|
||||
| Distribution| Scan Speed |
|
||||
|:------------|:-------------------|
|
||||
| Ubuntu | 初回は遅い / 2回目以降速い |
|
||||
| Debian | 初回は遅い / 2回目以降速い |
|
||||
| CentOS | 速い |
|
||||
| Amazon | 速い |
|
||||
| RHEL | 速い |
|
||||
| FreeBSD | 速い |
|
||||
|
||||
----
|
||||
|
||||
@@ -333,7 +337,7 @@ web/app server in the same configuration under the load balancer
|
||||
|:------------|-------------------:|
|
||||
| Ubuntu | 12, 14, 16|
|
||||
| Debian | 7, 8|
|
||||
| RHEL | 4, 5, 6, 7|
|
||||
| RHEL | 6, 7|
|
||||
| CentOS | 5, 6, 7|
|
||||
| Amazon Linux| All|
|
||||
| FreeBSD | 10|
|
||||
@@ -367,7 +371,7 @@ notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = "465"
|
||||
smtpPort = "587"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -420,7 +424,7 @@ host = "172.31.4.82"
|
||||
notifyUsers = ["@username"]
|
||||
```
|
||||
|
||||
- hookURL : Incomming webhook's URL
|
||||
- hookURL : Incoming webhook's URL
|
||||
- channel : channel name.
|
||||
channelに`${servername}`を指定すると、結果レポートをサーバごとに別チャネルにすることが出来る。
|
||||
以下のサンプルでは、`#server1`チャネルと`#server2`チャネルに送信される。スキャン前にチャネルを作成する必要がある。
|
||||
@@ -448,7 +452,7 @@ host = "172.31.4.82"
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = "465"
|
||||
smtpPort = "587"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -467,6 +471,7 @@ host = "172.31.4.82"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6313"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -486,6 +491,7 @@ host = "172.31.4.82"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6314"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -500,6 +506,7 @@ host = "172.31.4.82"
|
||||
- keyPath: SSH private key path
|
||||
- cpeNames: see [Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls/blob/master/README.ja.md#usage-scan-vulnerability-of-non-os-package)
|
||||
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls/blob/master/README.ja.md#usage-scan-docker-containers)
|
||||
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
|
||||
- optional: JSONレポートに含めたい追加情報
|
||||
|
||||
|
||||
@@ -538,29 +545,31 @@ configtest:
|
||||
```
|
||||
|
||||
また、スキャン対象サーバに対してパスワードなしでSUDO可能な状態かもチェックする。
|
||||
|
||||
スキャン対象サーバ上の`/etc/sudoers`のサンプル
|
||||
- CentOS, Amazon Linux, RedHat Enterprise Linux
|
||||
|
||||
- CentOS, RHEL, Amazon Linux
|
||||
```
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/yum
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/yum, /bin/echo
|
||||
```
|
||||
- Ubuntu, Debian
|
||||
```
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache
|
||||
```
|
||||
|
||||
- Amazon Linux, FreeBSDはRoot権限なしでスキャン可能
|
||||
|
||||
----
|
||||
|
||||
# Usage: Prepare
|
||||
|
||||
Prepareサブコマンドは、Vuls内部で利用する以下のパッケージをスキャン対象サーバにインストーする。
|
||||
Prepareサブコマンドは、Vuls内部で利用する以下のパッケージをスキャン対象サーバにインストールする。
|
||||
|
||||
| Distribution| Release | Requirements |
|
||||
|:------------|-------------------:|:-------------|
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8| aptitude |
|
||||
| CentOS | 5| yum-plugin-security, yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
|
||||
| CentOS | 5| yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-changelog |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 4, 5, 6, 7 | - |
|
||||
| FreeBSD | 10 | - |
|
||||
@@ -568,19 +577,22 @@ Prepareサブコマンドは、Vuls内部で利用する以下のパッケージ
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare
|
||||
[-config=/path/to/config.toml] [-debug]
|
||||
prepare:
|
||||
prepare
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-key-password]
|
||||
[SERVER]...
|
||||
[-debug]
|
||||
[-ssh-external]
|
||||
|
||||
[SERVER]...
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
```
|
||||
|
||||
----
|
||||
@@ -588,24 +600,28 @@ prepare
|
||||
# Usage: Scan
|
||||
|
||||
```
|
||||
|
||||
$ vuls scan -help
|
||||
scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[--cve-dictionary-dbpath=/path/to/cve.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
[-cve-dictionary-dbtype=sqlite3|mysql]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3 or mysql connection string]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cache-dbpath=/path/to/cache.db]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ssh-external]
|
||||
[-containers-only]
|
||||
[-skip-broken]
|
||||
[-report-azure-blob]
|
||||
[-report-json]
|
||||
[-report-mail]
|
||||
[-report-s3]
|
||||
[-report-slack]
|
||||
[-report-text]
|
||||
[-report-xml]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
@@ -619,7 +635,6 @@ scan:
|
||||
[SERVER]...
|
||||
|
||||
|
||||
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-aws-profile string
|
||||
@@ -634,16 +649,20 @@ scan:
|
||||
Azure storage container name
|
||||
-azure-key string
|
||||
Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified
|
||||
-cache-dbpath string
|
||||
/path/to/cache.db (local cache of changelog for Ubuntu/Debian) (default "$PWD/cache.db")
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-containers-only
|
||||
Scan concontainers Only. Default: Scan both of hosts and containers
|
||||
-cve-dictionary-dbpath string
|
||||
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
|
||||
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
|
||||
-cve-dictionary-dbtype string
|
||||
DB type for fetching CVE dictionary (sqlite3 or mysql) (default "sqlite3")
|
||||
-cve-dictionary-url string
|
||||
http://CVE.Dictionary (default "http://127.0.0.1:1323")
|
||||
-cvss-over float
|
||||
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
@@ -664,13 +683,14 @@ scan:
|
||||
Send report via Slack
|
||||
-report-text
|
||||
Write report to text files ($PWD/results/current)
|
||||
-report-xml
|
||||
Write report to XML files ($PWDresults/current)
|
||||
-results-dir string
|
||||
/path/to/results (default "$PWD/results")
|
||||
-skip-broken
|
||||
[For CentOS] yum update changelog with --skip-broken option
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
|
||||
-use-yum-plugin-security
|
||||
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
|
||||
|
||||
```
|
||||
|
||||
## -ssh-external option
|
||||
@@ -694,11 +714,10 @@ Defaults:vuls !requiretty
|
||||
| empty password | - | |
|
||||
| with password | required | or use ssh-agent |
|
||||
|
||||
## -report-json , -report-text option
|
||||
## -report-json , -report-text , -report-xml option
|
||||
|
||||
結果をファイルに出力したい場合に指定する。出力先は、`$PWD/result/current/`
|
||||
`all.(json|txt)`には、全サーバのスキャン結果が出力される。
|
||||
`servername.(json|txt)`には、サーバごとのスキャン結果が出力される。
|
||||
`servername.(json|txt|xml)`には、サーバごとのスキャン結果が出力される。
|
||||
|
||||
## Example: Scan all servers defined in config file
|
||||
```
|
||||
@@ -781,6 +800,43 @@ $ vuls scan \
|
||||
-azure-container=vuls
|
||||
```
|
||||
|
||||
## Example: IgnoreCves
|
||||
|
||||
Slack, Mail, テキスト出力しないくないCVE IDがある場合は、設定ファイルに定義することでレポートされなくなる。
|
||||
ただ、JSONファイルには以下のように出力される。
|
||||
|
||||
- config.toml
|
||||
```toml
|
||||
[default]
|
||||
ignoreCves = ["CVE-2016-6313"]
|
||||
|
||||
[servers.bsd]
|
||||
host = "192.168.11.11"
|
||||
user = "kanbe"
|
||||
ignoreCves = ["CVE-2016-6314"]
|
||||
```
|
||||
|
||||
- bsd.json
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ServerName": "bsd",
|
||||
"Family": "FreeBSD",
|
||||
"Release": "10.3-RELEASE",
|
||||
"IgnoredCves" : [
|
||||
"CveDetail" : {
|
||||
"CVE-2016-6313",
|
||||
...
|
||||
},
|
||||
"CveDetail" : {
|
||||
"CVE-2016-6314",
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Example: Add optional key-value pairs to JSON
|
||||
|
||||
追加情報をJSONに含めることができる。
|
||||
@@ -821,11 +877,19 @@ optional = [
|
||||
]
|
||||
```
|
||||
|
||||
## Example: Use MySQL as a DB storage back-end
|
||||
|
||||
```
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbtype=mysql \
|
||||
-cve-dictionary-dbpath="user:pass@tcp(localhost:3306)/dbname?parseTime=true"
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan vulnerability of non-OS package
|
||||
|
||||
Vulsは、[CPE](https://nvd.nist.gov/cpe.cfm)に登録されているソフトウェアであれば、OSパッケージ以外のソフトウェアの脆弱性もスキャン可能。
|
||||
Vulsは、[CPE](https://nvd.nist.gov/cpe.cfm)に登録されているソフトウェアであれば、OSパッケージ以外のソフトウェアの脆弱性もスキャン可能。
|
||||
たとえば、自分でコンパイルしたものや、言語のライブラリ、フレームワークなど。
|
||||
|
||||
- CPEの検索方法
|
||||
@@ -848,13 +912,38 @@ Vulsは、[CPE](https://nvd.nist.gov/cpe.cfm)に登録されているソフト
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
|
||||
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) は、プログラミング言語のライブラリを特定し(CPEを推測)、公開済みの脆弱性を検知するツール。
|
||||
|
||||
VulsとDependency Checkを連携させる方法は以下
|
||||
- Dependency Checkを、--format=XMLをつけて実行する
|
||||
- そのXMLをconfig.toml内で以下のように定義する
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
```
|
||||
|
||||
VulsとDependency Checkの連携すると以下の利点がある
|
||||
- ライブラリを更新した場合に、config.tomlのCPEの定義を変更しなくても良い
|
||||
- Vulsの機能でSlack, Emailで通知可能
|
||||
- 日本語のレポートが可能
|
||||
- Dependency Checkは日本語レポートに対応していない
|
||||
|
||||
|
||||
# Usage: Scan Docker containers
|
||||
|
||||
DockerコンテナはSSHデーモンを起動しないで運用するケースが一般的。
|
||||
[Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
|
||||
|
||||
Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナにコマンドを発行して脆弱性をスキャンする。
|
||||
Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナにコマンドを発行して脆弱性をスキャンする。
|
||||
詳細は、[Architecture section](https://github.com/future-architect/vuls#architecture)を参照
|
||||
|
||||
- 全ての起動中のDockerコンテナをスキャン
|
||||
@@ -869,10 +958,10 @@ Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナ
|
||||
containers = ["${running}"]
|
||||
```
|
||||
|
||||
- あるコンテナのみスキャン
|
||||
コンテナID、または、コンテナ名を、containersに指定する。
|
||||
以下の例では、`container_name_a`と、`4aa37a8b63b9`のコンテナのみスキャンする
|
||||
スキャン実行前に、コンテナが起動中か確認すること。もし起動してない場合はエラーメッセージを出力してスキャンを中断する。
|
||||
- あるコンテナのみスキャン
|
||||
コンテナID、または、コンテナ名を、containersに指定する。
|
||||
以下の例では、`container_name_a`と、`4aa37a8b63b9`のコンテナのみスキャンする
|
||||
スキャン実行前に、コンテナが起動中か確認すること。もし起動してない場合はエラーメッセージを出力してスキャンを中断する。
|
||||
```
|
||||
[servers]
|
||||
|
||||
@@ -882,6 +971,9 @@ Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナ
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
containers = ["container_name_a", "4aa37a8b63b9"]
|
||||
```
|
||||
- コンテナのみをスキャンする場合(ホストはスキャンしない)
|
||||
--containers-onlyオプションを指定する
|
||||
|
||||
|
||||
# Usage: TUI
|
||||
|
||||
@@ -890,23 +982,23 @@ Vulsは、DockerホストにSSHで接続し、`docker exec`でDockerコンテナ
|
||||
```
|
||||
$ vuls tui -h
|
||||
tui:
|
||||
tui [-dbpath=/path/to/vuls.sqlite3]
|
||||
tui [-results-dir=/path/to/results]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-results-dir string
|
||||
/path/to/results (default "$PWD/results")
|
||||
-debug-sql
|
||||
debug SQL
|
||||
|
||||
```
|
||||
|
||||
Key binding is bellow.
|
||||
Key binding is below.
|
||||
|
||||
| key | |
|
||||
|:-----------------|:-------|:------|
|
||||
| TAB | move cursor among the panes |
|
||||
| Arrow up/down | move cursor to up/down |
|
||||
| Ctrl+j, Ctrl+k | move cursor to up/donw |
|
||||
| Ctrl+u, Ctrl+d | page up/donw |
|
||||
| Ctrl+j, Ctrl+k | move cursor to up/down |
|
||||
| Ctrl+u, Ctrl+d | page up/down |
|
||||
|
||||
For details, see https://github.com/future-architect/vuls/blob/master/report/tui.go
|
||||
|
||||
@@ -952,103 +1044,33 @@ $ vuls scan -cve-dictionary-url=http://192.168.0.1:1323
|
||||
|
||||
# Usage: Update NVD Data
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -h
|
||||
fetchnvd:
|
||||
fetchnvd
|
||||
[-last2y]
|
||||
[-dbpath=/path/to/cve.sqlite3]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-last2y
|
||||
Refresh NVD data in the last two years.
|
||||
```
|
||||
|
||||
- Fetch data of the entire period
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -entire
|
||||
```
|
||||
|
||||
- Fetch data in the last 2 years
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -last2y
|
||||
```
|
||||
see [go-cve-dictionary#usage-fetch-nvd-data](https://github.com/kotakanbe/go-cve-dictionary#usage-fetch-nvd-data)
|
||||
|
||||
----
|
||||
|
||||
# レポートの日本語化
|
||||
|
||||
- JVNから日本語の脆弱性情報を取得
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -help
|
||||
fetchjvn:
|
||||
fetchjvn [-dump-path=$PWD/cve] [-dpath=$PWD/vuls.sqlite3] [-week] [-month] [-entire]
|
||||
see [go-cve-dictionary#usage-fetch-jvn-data](https://github.com/kotakanbe/go-cve-dictionary#usage-fetch-jvn-data)
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3/DBfile (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-dump-path string
|
||||
/path/to/dump.json (default "$PWD/cve.json")
|
||||
-entire
|
||||
Fetch data for entire period.(This operation is time-consuming) (default: false)
|
||||
-month
|
||||
Fetch data in the last month (default: false)
|
||||
-week
|
||||
Fetch data in the last week. (default: false)
|
||||
|
||||
```
|
||||
## fetchnvd, fetchjvnの実行順序の注意
|
||||
|
||||
- すべての期間の脆弱性情報を取得(1時間以上かかる)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -entire
|
||||
```
|
||||
**fetchjvn -> fetchnvdの順番だとすごく時間がかかる** (2016年9月現在)
|
||||
**fetchnvd -> fetchjvnの順番で実行すること**
|
||||
|
||||
- 直近1ヶ月間に更新された脆弱性情報を取得(1分未満)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -month
|
||||
```
|
||||
```
|
||||
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
|
||||
$ for i in {1998..2016}; do go-cve-dictionary fetchjvn -years $i; done
|
||||
```
|
||||
の順でやった場合、最初のコマンドが15分程度、二つ目のコマンドが10分程度(環境依存)
|
||||
|
||||
- 直近1週間に更新された脆弱性情報を取得(1分未満)
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -week
|
||||
```
|
||||
|
||||
- 脆弱性情報の自動アップデート
|
||||
Cronなどのジョブスケジューラを用いて実現可能。
|
||||
-week オプションを指定して夜間の日次実行を推奨。
|
||||
|
||||
- 注意
|
||||
NVDとJVNの両方の情報を取得する場合、NVDからの情報を取得した後でJVNの情報を取得することを勧める
|
||||
2016年7月現在で、
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -years 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
|
||||
|
||||
$ go-cve-dictionary fetchjvn -entire
|
||||
```
|
||||
|
||||
の順でやった場合、最初のコマンドが15分程度、二つ目のコマンドが70分程度で、トータルでも1時間半程度で終わる(環境依存)が、
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchjvn -entire
|
||||
|
||||
$ nohup go-cve-dictionary fetchnvd -years 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 &
|
||||
```
|
||||
|
||||
```
|
||||
$ for i in {1998..2016}; do go-cve-dictionary fetchjvn -years $i; done
|
||||
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
|
||||
```
|
||||
の順で行うと、最初のコマンドは1時間くらいで終わるが二つ目のコマンドが21時間かかることもある(環境依存)。
|
||||
時間がかかりそうな時は上記のようにnohupして進捗を確認するようにした方が精神衛生的にも良い。
|
||||
|
||||
|
||||
## スキャン実行
|
||||
|
||||
@@ -1060,47 +1082,25 @@ slack, emailは日本語対応済み TUIは日本語表示未対応
|
||||
|
||||
----
|
||||
|
||||
# Deploy With Glide
|
||||
|
||||
If an error occurred while go get, try deploying with glide.
|
||||
- Install [Glide](https://github.com/Masterminds/glide)
|
||||
- Deploy go-cve-dictionary
|
||||
```
|
||||
$ go get -d github.com/kotakanbe/go-cve-dictionary
|
||||
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
- Deploy vuls
|
||||
```
|
||||
$ go get -d github.com/future-architect/vuls
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
- The binaries are created under $GOPARH/bin
|
||||
|
||||
----
|
||||
|
||||
# Update Vuls With Glide
|
||||
|
||||
- Update go-cve-dictionary
|
||||
- Update go-cve-dictionary
|
||||
If the DB schema was changed, please specify new SQLite3 or MySQL DB file.
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
$ mv vendor /tmp/foo
|
||||
$ make install
|
||||
```
|
||||
|
||||
- Update vuls
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
$ mv vendor /tmp/bar
|
||||
$ make install
|
||||
```
|
||||
- The binaries are created under $GOPARH/bin
|
||||
- If the DB schema was changed, please specify new SQLite3 DB file.
|
||||
- バイナリファイルは`$GOPARH/bin`以下に作成される
|
||||
|
||||
---
|
||||
|
||||
@@ -1120,12 +1120,12 @@ Use Systemd, Upstart or supervisord, daemontools...
|
||||
CRONなどを使えば可能
|
||||
|
||||
- 自動定期スキャン
|
||||
CRONなどを使い、自動化のためにsudoと、秘密鍵のパスワードなしでも実行可能なようにする
|
||||
CRONなどを使い、自動化のためにsudoと、秘密鍵のパスワードなしでも実行可能なようにする
|
||||
- スキャン対象サーバの /etc/sudoers に NOPASSWORD を設定する
|
||||
- 秘密鍵パスフレーズなしの公開鍵認証か、ssh-agentを使う
|
||||
|
||||
- スキャンが重く感じる
|
||||
vulsのスキャン対象に脆弱性が溜まりすぎると実行時間が長くなります
|
||||
- スキャンが重く感じる
|
||||
vulsのスキャン対象に脆弱性が溜まりすぎると実行時間が長くなります
|
||||
脆弱性のある状態は溜めすぎないようにしましょう
|
||||
|
||||
- クロスコンパイル
|
||||
@@ -1195,7 +1195,7 @@ Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHAN
|
||||
|
||||
----
|
||||
|
||||
# Licence
|
||||
# License
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
322
README.md
322
README.md
@@ -52,10 +52,12 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
- Support software registered in CPE
|
||||
- Agentless architecture
|
||||
- User is required to only setup one machine that is connected to other target servers via SSH
|
||||
- Nondestructive testing
|
||||
- Pre-authorization is not necessary before scanning on AWS
|
||||
- Auto generation of configuration file template
|
||||
- Auto detection of servers set using CIDR, generate configuration file template
|
||||
- Email and Slack notification is possible (supports Japanese language)
|
||||
- Scan result is viewable on accessory software, TUI Viewer terminal.
|
||||
- Scan result is viewable on accessory software, TUI Viewer terminal or Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo)).
|
||||
|
||||
----
|
||||
|
||||
@@ -96,6 +98,7 @@ This can be done in the following steps.
|
||||
1. Prepare
|
||||
1. Scan
|
||||
1. TUI(Terminal-Based User Interface)
|
||||
1. Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo))
|
||||
|
||||
## Step1. Launch Amazon Linux
|
||||
|
||||
@@ -127,17 +130,17 @@ And also, SUDO with password is not supported for security reasons. So you have
|
||||
|
||||
Vuls requires the following packages.
|
||||
|
||||
- SQLite3
|
||||
- git v2
|
||||
- SQLite3 or MySQL
|
||||
- git
|
||||
- gcc
|
||||
- go v1.6
|
||||
- go v1.7.1 or later
|
||||
- https://golang.org/doc/install
|
||||
|
||||
```bash
|
||||
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
|
||||
$ sudo yum -y install sqlite git gcc
|
||||
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
|
||||
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
|
||||
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
|
||||
$ mkdir $HOME/go
|
||||
```
|
||||
Add these lines into /etc/profile.d/goenv.sh
|
||||
@@ -155,18 +158,18 @@ $ source /etc/profile.d/goenv.sh
|
||||
|
||||
## Step4. Deploy [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
|
||||
go get
|
||||
|
||||
```bash
|
||||
$ sudo mkdir /var/log/vuls
|
||||
$ sudo chown ec2-user /var/log/vuls
|
||||
$ sudo chmod 700 /var/log/vuls
|
||||
$ go get github.com/kotakanbe/go-cve-dictionary
|
||||
$
|
||||
$ mkdir -p $GOPATH/src/github.com/kotakanbe
|
||||
$ cd $GOPATH/src/github.com/kotakanbe
|
||||
$ git clone https://github.com/kotakanbe/go-cve-dictionary.git
|
||||
$ cd go-cve-dictionary
|
||||
$ make install
|
||||
```
|
||||
|
||||
If an error occurred while go get, check the following points.
|
||||
- Update Git
|
||||
- try [deploying with glide](https://github.com/future-architect/vuls/blob/master/README.md#deploy-with-glide).
|
||||
The binary was built under `$GOPARH/bin`
|
||||
|
||||
Fetch vulnerability data from NVD.
|
||||
It takes about 10 minutes (on AWS).
|
||||
@@ -182,14 +185,14 @@ $ ls -alh cve.sqlite3
|
||||
|
||||
Launch a new terminal and SSH to the ec2 instance.
|
||||
|
||||
go get
|
||||
```
|
||||
$ go get github.com/future-architect/vuls
|
||||
$ mkdir -p $GOPATH/src/github.com/future-architect
|
||||
$ cd $GOPATH/src/github.com/future-architect
|
||||
$ git clone https://github.com/future-architect/vuls.git
|
||||
$ cd vuls
|
||||
$ make install
|
||||
```
|
||||
|
||||
If an error occurred while go get, check the following points.
|
||||
- Update Git
|
||||
- try [deploying with glide](https://github.com/future-architect/vuls/blob/master/README.md#deploy-with-glide).
|
||||
The binary was built under `$GOPARH/bin`
|
||||
|
||||
## Step6. Config
|
||||
|
||||
@@ -219,7 +222,7 @@ see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
|
||||
## Step8. Start Scanning
|
||||
|
||||
```
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3 -report-json
|
||||
INFO[0000] Start scanning (config: /home/ec2-user/config.toml)
|
||||
INFO[0000] Start scanning
|
||||
INFO[0000] config: /home/ec2-user/config.toml
|
||||
@@ -246,7 +249,7 @@ Summary Unspecified vulnerability in the Java SE and Java SE Embedded co
|
||||
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
|
||||
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
|
||||
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
|
||||
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
|
||||
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
|
||||
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
|
||||
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
|
||||
@@ -263,6 +266,10 @@ $ vuls tui
|
||||
|
||||

|
||||
|
||||
## Step10. Web UI
|
||||
|
||||
[VulsRepo](https://github.com/usiusi360/vulsrepo) is a awesome Web UI for Vuls.
|
||||
Check it out the [Online Demo](http://usiusi360.github.io/vulsrepo/).
|
||||
|
||||
----
|
||||
|
||||
@@ -277,40 +284,40 @@ see https://github.com/future-architect/vuls/tree/master/setup/docker
|
||||

|
||||
|
||||
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3.
|
||||
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3 or MySQL.
|
||||
|
||||
## Vuls
|
||||
## Scanning Flow
|
||||

|
||||
- Scan vulnerabilities on the servers via SSH and create a list of the CVE ID
|
||||
- To scan Docker containers, Vuls connect via ssh to the Docker host and then `docker exec` to the containers. So, no need to run sshd daemon on the containers.
|
||||
- Fetch more detailed information of the detected CVE from go-cve-dictionary
|
||||
- Insert scan result into SQLite3
|
||||
- Send a report by Slack and Email
|
||||
- Show the latest report on your terminal
|
||||
|
||||

|
||||
- Write scan results to JSON file to show the latest report on your terminal
|
||||
|
||||
----
|
||||
# Performance Considerations
|
||||
|
||||
- on Ubuntu and Debian
|
||||
- On Ubuntu and Debian
|
||||
Vuls issues `apt-get changelog` for each upgradable packages and parse the changelog.
|
||||
`apt-get changelog` is slow and resource usage is heavy when there are many updatable packages on target server.
|
||||
`apt-get changelog` is slow and resource usage is heavy when there are many updatable packages on target server.
|
||||
Vuls stores these changelogs to KVS([boltdb](https://github.com/boltdb/bolt)).
|
||||
From the second time on, the scan speed is fast by using the local cache.
|
||||
|
||||
- on CentOS
|
||||
- On CentOS
|
||||
Vuls issues `yum update --changelog` to get changelogs of upgradable packages at once and parse the changelog.
|
||||
Scan speed is fast and resource usage is light.
|
||||
|
||||
- On Amazon, RHEL and FreeBSD
|
||||
High speed scan and resource usage is light because Vuls can get CVE IDs by using package manager(no need to parse a changelog).
|
||||
|
||||
| Distribution| Scan Speed | Resource Usage On Target Server |
|
||||
| Distribution| Scan Speed |
|
||||
|:------------|:-------------------|:-------------|
|
||||
| Ubuntu | Slow | Heavy |
|
||||
| Debian | Slow | Heavy |
|
||||
| CentOS | Fast | Light |
|
||||
| Amazon | Fast | Light |
|
||||
| RHEL | Fast | Light |
|
||||
| FreeBSD | Fast | Light |
|
||||
| Ubuntu | First time: Slow / From the second time: Fast |
|
||||
| Debian | First time: Slow / From the second time: Fast |
|
||||
| CentOS | Fast |
|
||||
| Amazon | Fast |
|
||||
| RHEL | Fast |
|
||||
| FreeBSD | Fast |
|
||||
|
||||
----
|
||||
|
||||
@@ -334,7 +341,7 @@ web/app server in the same configuration under the load balancer
|
||||
|:------------|-------------------:|
|
||||
| Ubuntu | 12, 14, 16|
|
||||
| Debian | 7, 8|
|
||||
| RHEL | 4, 5, 6, 7|
|
||||
| RHEL | 6, 7|
|
||||
| CentOS | 5, 6, 7|
|
||||
| Amazon Linux| All|
|
||||
| FreeBSD | 10|
|
||||
@@ -368,7 +375,7 @@ notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = "465"
|
||||
smtpPort = "587"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -384,6 +391,7 @@ subjectPrefix = "[vuls]"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6313"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -399,6 +407,7 @@ host = "172.31.4.82"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6313"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -421,7 +430,7 @@ You can customize your configuration using this template.
|
||||
notifyUsers = ["@username"]
|
||||
```
|
||||
|
||||
- hookURL : Incomming webhook's URL
|
||||
- hookURL : Incoming webhook's URL
|
||||
- channel : channel name.
|
||||
If you set `${servername}` to channel, the report will be sent to each channel.
|
||||
In the following example, the report will be sent to the `#server1` and `#server2`.
|
||||
@@ -452,7 +461,7 @@ You can customize your configuration using this template.
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = "465"
|
||||
smtpPort = "587"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -471,6 +480,7 @@ You can customize your configuration using this template.
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6313"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -490,6 +500,7 @@ You can customize your configuration using this template.
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2016-6314"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -503,6 +514,7 @@ You can customize your configuration using this template.
|
||||
- keyPath: SSH private key path
|
||||
- cpeNames: see [Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls#usage-scan-vulnerability-of-non-os-package)
|
||||
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls#usage-scan-docker-containers)
|
||||
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
|
||||
- optional: Add additional information to JSON report.
|
||||
|
||||
Vuls supports two types of SSH. One is native go implementation. The other is external SSH command. For details, see [-ssh-external option](https://github.com/future-architect/vuls#-ssh-external-option)
|
||||
@@ -538,15 +550,19 @@ configtest:
|
||||
```
|
||||
|
||||
And also, configtest subcommand checks sudo settings on target servers whether Vuls is able to SUDO with nopassword via SSH.
|
||||
|
||||
Example of /etc/sudoers on target servers
|
||||
- CentOS, Amazon Linux, RedHat Enterprise Linux
|
||||
- CentOS, RHEL
|
||||
```
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/yum
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/yum, /bin/echo
|
||||
```
|
||||
- Ubuntu, Debian
|
||||
```
|
||||
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache
|
||||
```
|
||||
- It is possible to scan without root privilege for Amazon Linux, FreeBSD.
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
@@ -558,8 +574,8 @@ Prepare subcommand installs required packages on each server.
|
||||
|:------------|-------------------:|:-------------|
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8| aptitude |
|
||||
| CentOS | 5| yum-plugin-security, yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
|
||||
| CentOS | 5| yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-changelog |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 4, 5, 6, 7 | - |
|
||||
| FreeBSD | 10 | - |
|
||||
@@ -567,19 +583,22 @@ Prepare subcommand installs required packages on each server.
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare
|
||||
[-config=/path/to/config.toml] [-debug]
|
||||
prepare:
|
||||
prepare
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-key-password]
|
||||
[SERVER]...
|
||||
[-debug]
|
||||
[-ssh-external]
|
||||
|
||||
[SERVER]...
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
```
|
||||
|
||||
----
|
||||
@@ -593,18 +612,23 @@ scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[--cve-dictionary-dbpath=/path/to/cve.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
[-cve-dictionary-dbtype=sqlite3|mysql]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3 or mysql connection string]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cache-dbpath=/path/to/cache.db]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ssh-external]
|
||||
[-containers-only]
|
||||
[-skip-broken]
|
||||
[-report-azure-blob]
|
||||
[-report-json]
|
||||
[-report-mail]
|
||||
[-report-s3]
|
||||
[-report-slack]
|
||||
[-report-text]
|
||||
[-report-xml]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
@@ -632,16 +656,20 @@ scan:
|
||||
Azure storage container name
|
||||
-azure-key string
|
||||
Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified
|
||||
-cache-dbpath string
|
||||
/path/to/cache.db (local cache of changelog for Ubuntu/Debian) (default "$PWD/cache.db")
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-containers-only
|
||||
Scan concontainers Only. Default: Scan both of hosts and containers
|
||||
-cve-dictionary-dbpath string
|
||||
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
|
||||
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
|
||||
-cve-dictionary-dbtype string
|
||||
DB type for fetching CVE dictionary (sqlite3 or mysql) (default "sqlite3")
|
||||
-cve-dictionary-url string
|
||||
http://CVE.Dictionary (default "http://127.0.0.1:1323")
|
||||
-cvss-over float
|
||||
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
@@ -662,24 +690,25 @@ scan:
|
||||
Send report via Slack
|
||||
-report-text
|
||||
Write report to text files ($PWD/results/current)
|
||||
-report-xml
|
||||
Write report to XML files ($PWDresults/current)
|
||||
-results-dir string
|
||||
/path/to/results (default "$PWD/results")
|
||||
-skip-broken
|
||||
[For CentOS] yum update changelog with --skip-broken option
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
|
||||
-use-yum-plugin-security
|
||||
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
|
||||
|
||||
```
|
||||
|
||||
## -ssh-external option
|
||||
|
||||
Vuls supports different types of SSH.
|
||||
|
||||
By Defaut, using a native Go implementation from crypto/ssh.
|
||||
By Default, using a native Go implementation from crypto/ssh.
|
||||
This is useful in situations where you may not have access to traditional UNIX tools.
|
||||
|
||||
To use external ssh command, specify this option.
|
||||
This is useful If you want to use ProxyCommand or chiper algorithm of SSH that is not supported by native go implementation.
|
||||
This is useful If you want to use ProxyCommand or cipher algorithm of SSH that is not supported by native go implementation.
|
||||
Don't forget to add below line to /etc/sudoers on the target servers. (username: vuls)
|
||||
```
|
||||
Defaults:vuls !requiretty
|
||||
@@ -693,10 +722,10 @@ Defaults:vuls !requiretty
|
||||
| empty password | - | |
|
||||
| with password | required | or use ssh-agent |
|
||||
|
||||
## -report-json , -report-text option
|
||||
## -report-json , -report-text , -report-xml option
|
||||
|
||||
At the end of the scan, scan results will be available in the `$PWD/result/current/` directory.
|
||||
`all.(json|txt)` includes the scan results of all servres and `servername.(json|txt)` includes the scan result of the server.
|
||||
`servername.(json|txt|xml)` includes the scan result of the server.
|
||||
|
||||
## Example: Scan all servers defined in config file
|
||||
```
|
||||
@@ -708,7 +737,7 @@ $ vuls scan \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Ask SSH key passsword before scanning
|
||||
- Ask SSH key password before scanning
|
||||
- Scan all servers defined in config file
|
||||
- Send scan results to slack and email
|
||||
- Only Report CVEs that CVSS score is over 7
|
||||
@@ -772,6 +801,43 @@ $ vuls scan \
|
||||
-azure-container=vuls
|
||||
```
|
||||
|
||||
## Example: IgnoreCves
|
||||
|
||||
Define ignoreCves in config if you don't want to report(slack, mail, text...) specific CVE IDs. But these ignoreCves will be output to JSON file like below.
|
||||
|
||||
- config.toml
|
||||
```toml
|
||||
[default]
|
||||
ignoreCves = ["CVE-2016-6313"]
|
||||
|
||||
[servers.bsd]
|
||||
host = "192.168.11.11"
|
||||
user = "kanbe"
|
||||
ignoreCves = ["CVE-2016-6314"]
|
||||
```
|
||||
|
||||
- bsd.json
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ServerName": "bsd",
|
||||
"Family": "FreeBSD",
|
||||
"Release": "10.3-RELEASE",
|
||||
"IgnoredCves" : [
|
||||
"CveDetail" : {
|
||||
"CVE-2016-6313",
|
||||
...
|
||||
},
|
||||
"CveDetail" : {
|
||||
"CVE-2016-6314",
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## Example: Add optional key-value pairs to JSON
|
||||
|
||||
Optional key-value can be outputted to JSON.
|
||||
@@ -812,9 +878,17 @@ optional = [
|
||||
]
|
||||
```
|
||||
|
||||
## Example: Use MySQL as a DB storage back-end
|
||||
|
||||
```
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbtype=mysql \
|
||||
-cve-dictionary-dbpath="user:pass@tcp(localhost:3306)/dbname?parseTime=true"
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan vulnerability of non-OS package
|
||||
# Usage: Scan vulnerabilites of non-OS packages
|
||||
|
||||
It is possible to detect vulnerabilities in non-OS packages, such as something you compiled by yourself, language libraries and frameworks, that have been registered in the [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
|
||||
@@ -823,10 +897,10 @@ It is possible to detect vulnerabilities in non-OS packages, such as something y
|
||||
**Check CPE Naming Format: 2.2**
|
||||
|
||||
- [go-cpe-dictionary](https://github.com/kotakanbe/go-cpe-dictionary) is a good choice for geeks.
|
||||
You can search a CPE name by the application name incremenally.
|
||||
You can search a CPE name by the application name incrementally.
|
||||
|
||||
- Configuration
|
||||
To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
|
||||
To detect the vulnerability of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
|
||||
```
|
||||
[servers]
|
||||
|
||||
@@ -838,10 +912,34 @@ To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
|
||||
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities.
|
||||
|
||||
Benefit of integrating Vuls And OWASP Dependency Check is below.
|
||||
- Automatic Update of Vuls config when the libraries are updated.
|
||||
- Reporting by Email or Slack by using Vuls.
|
||||
- Reporting in Japanese
|
||||
- OWASP Dependency Check supports only English
|
||||
|
||||
How to integrate Vuls with OWASP Dependency Check
|
||||
- Execute OWASP Dependency Check with --format=XML option.
|
||||
- Define the xml file path of dependency check in config.toml.
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
```
|
||||
|
||||
|
||||
# Usage: Scan Docker containers
|
||||
|
||||
It is common that keep Docker containers runnning without SSHd daemon.
|
||||
It is common that keep Docker containers running without SSHd daemon.
|
||||
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
|
||||
|
||||
Vuls scans Docker containers via `docker exec` instead of SSH.
|
||||
@@ -873,6 +971,9 @@ For more details, see [Architecture section](https://github.com/future-architect
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
containers = ["container_name_a", "4aa37a8b63b9"]
|
||||
```
|
||||
- To scan containers only
|
||||
- --containers-only option is available.
|
||||
|
||||
|
||||
# Usage: TUI
|
||||
|
||||
@@ -881,23 +982,23 @@ For more details, see [Architecture section](https://github.com/future-architect
|
||||
```
|
||||
$ vuls tui -h
|
||||
tui:
|
||||
tui [-dbpath=/path/to/vuls.sqlite3]
|
||||
tui [-results-dir=/path/to/results]
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
|
||||
-results-dir string
|
||||
/path/to/results (default "$PWD/results")
|
||||
-debug-sql
|
||||
debug SQL
|
||||
|
||||
```
|
||||
|
||||
Key binding is bellow.
|
||||
Key binding is below.
|
||||
|
||||
| key | |
|
||||
|:-----------------|:-------|:------|
|
||||
| TAB | move cursor among the panes |
|
||||
| Arrow up/down | move cursor to up/down |
|
||||
| Ctrl+j, Ctrl+k | move cursor to up/donw |
|
||||
| Ctrl+u, Ctrl+d | page up/donw |
|
||||
| Ctrl+j, Ctrl+k | move cursor to up/down |
|
||||
| Ctrl+u, Ctrl+d | page up/down |
|
||||
|
||||
For details, see https://github.com/future-architect/vuls/blob/master/report/tui.go
|
||||
|
||||
@@ -943,80 +1044,30 @@ $ vuls scan -cve-dictionary-url=http://192.168.0.1:1323
|
||||
|
||||
# Usage: Update NVD Data
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -h
|
||||
fetchnvd:
|
||||
fetchnvd
|
||||
[-last2y]
|
||||
[-dbpath=/path/to/cve.sqlite3]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
see [go-cve-dictionary#usage-fetch-nvd-data](https://github.com/kotakanbe/go-cve-dictionary#usage-fetch-nvd-data)
|
||||
|
||||
-dbpath string
|
||||
/path/to/sqlite3 (default "$PWD/cve.sqlite3")
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
SQL debug mode
|
||||
-last2y
|
||||
Refresh NVD data in the last two years.
|
||||
```
|
||||
|
||||
- Fetch data of the entire period
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -entire
|
||||
```
|
||||
|
||||
- Fetch data in the last 2 years
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -last2y
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Deploy With Glide
|
||||
|
||||
If an error occurred while go get, try deploying with glide.
|
||||
- Install [Glide](https://github.com/Masterminds/glide)
|
||||
- Deploy go-cve-dictionary
|
||||
```
|
||||
$ go get -d github.com/kotakanbe/go-cve-dictionary
|
||||
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
- Deploy vuls
|
||||
```
|
||||
$ go get -d github.com/future-architect/vuls
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
- The binaries are created under $GOPARH/bin
|
||||
|
||||
----
|
||||
|
||||
# Update Vuls With Glide
|
||||
|
||||
- Update go-cve-dictionary
|
||||
- Update go-cve-dictionary
|
||||
If the DB schema was changed, please specify new SQLite3 or MySQL DB file.
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
$ mv vendor /tmp/foo
|
||||
$ make install
|
||||
```
|
||||
|
||||
- Update vuls
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
$ mv vendor /tmp/bar
|
||||
$ make install
|
||||
```
|
||||
- The binaries are created under $GOPARH/bin
|
||||
- If the DB schema was changed, please specify new SQLite3 DB file.
|
||||
Binary file was built under $GOPARH/bin
|
||||
|
||||
---
|
||||
|
||||
@@ -1032,7 +1083,7 @@ If your system is behind HTTP proxy, you have to specify --http-proxy option.
|
||||
- How to Daemonize go-cve-dictionary
|
||||
Use Systemd, Upstart or supervisord, daemontools...
|
||||
|
||||
- How to Enable Automatic-Update of Vunerability Data.
|
||||
- How to Enable Automatic-Update of Vulnerability Data.
|
||||
Use job scheduler like Cron (with -last2y option).
|
||||
|
||||
- How to Enable Automatic-Scan.
|
||||
@@ -1107,7 +1158,6 @@ Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHAN
|
||||
|
||||
----
|
||||
|
||||
# Licence
|
||||
# License
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
173
cache/bolt.go
vendored
Normal file
173
cache/bolt.go
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// Bolt holds a pointer of bolt.DB
|
||||
// boltdb is used to store a cache of Changelogs of Ubuntu/Debian
|
||||
type Bolt struct {
|
||||
Path string
|
||||
Log *logrus.Entry
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
// SetupBolt opens a boltdb and creates a meta bucket if not exists.
|
||||
func SetupBolt(path string, l *logrus.Entry) error {
|
||||
l.Infof("Open boltDB: %s", path)
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := Bolt{
|
||||
Path: path,
|
||||
Log: l,
|
||||
db: db,
|
||||
}
|
||||
if err = b.createBucketIfNotExists(metabucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DB = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close a db.
|
||||
func (b Bolt) Close() error {
|
||||
if b.db == nil {
|
||||
return nil
|
||||
}
|
||||
return b.db.Close()
|
||||
}
|
||||
|
||||
// CreateBucketIfNotExists creates a buket that is specified by arg.
|
||||
func (b *Bolt) createBucketIfNotExists(name string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists([]byte(name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create bucket: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetMeta gets a Meta Information os the servername to boltdb.
|
||||
func (b Bolt) GetMeta(serverName string) (meta Meta, found bool, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(serverName))
|
||||
if len(v) == 0 {
|
||||
found = false
|
||||
return nil
|
||||
}
|
||||
if e := json.Unmarshal(v, &meta); e != nil {
|
||||
return e
|
||||
}
|
||||
found = true
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// EnsureBuckets puts a Meta information and create a buket that holds changelogs.
|
||||
func (b Bolt) EnsureBuckets(meta Meta) error {
|
||||
jsonBytes, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to marshal to JSON: %s", err)
|
||||
}
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
b.Log.Debugf("Put to meta: %s", meta.Name)
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// re-create a bucket (bucket name: servername)
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
if bkt != nil {
|
||||
b.Log.Debugf("Delete bucket: %s", meta.Name)
|
||||
if err := tx.DeleteBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket deleted: %s", meta.Name)
|
||||
}
|
||||
b.Log.Debugf("Create bucket: %s", meta.Name)
|
||||
if _, err := tx.CreateBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket created: %s", meta.Name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// PrettyPrint is for debuging
|
||||
func (b Bolt) PrettyPrint(meta Meta) error {
|
||||
return b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(meta.Name))
|
||||
b.Log.Debugf("key:%s, value:%s", meta.Name, v)
|
||||
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
c := bkt.Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
b.Log.Debugf("key:%s, len: %d, %s...",
|
||||
k, len(v), util.Truncate(string(v), 30))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetChangelog get the changelgo 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))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
}
|
||||
v := bkt.Get([]byte(packName))
|
||||
if v == nil {
|
||||
changelog = ""
|
||||
return nil
|
||||
}
|
||||
changelog = string(v)
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// PutChangelog put the changelgo of specified packName into the Bucket
|
||||
func (b Bolt) PutChangelog(servername, packName, changelog string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
}
|
||||
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
134
cache/bolt_test.go
vendored
Normal file
134
cache/bolt_test.go
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
const path = "/tmp/vuls-test-cache-11111111.db"
|
||||
const servername = "server1"
|
||||
|
||||
var meta = Meta{
|
||||
Name: servername,
|
||||
Distro: config.Distro{
|
||||
Family: "ubuntu",
|
||||
Release: "16.04",
|
||||
},
|
||||
Packs: []models.PackageInfo{
|
||||
{
|
||||
Name: "apt",
|
||||
Version: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSetupBolt(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
err := SetupBolt(path, log)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
// check if meta bucket exists
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestEnsureBuckets(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
m, found, err := DB.GetMeta(servername)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get meta: %s", err)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Not Found in meta")
|
||||
}
|
||||
if !reflect.DeepEqual(meta, m) {
|
||||
t.Errorf("expected %v, actual %v", meta, m)
|
||||
}
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestPutGetChangelog(t *testing.T) {
|
||||
clog := "changelog-text"
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
if err := DB.PutChangelog(servername, "apt", clog); err != nil {
|
||||
t.Errorf("Failed to put changelog: %s", err)
|
||||
}
|
||||
if actual, err := DB.GetChangelog(servername, "apt"); err != nil {
|
||||
t.Errorf("Failed to get changelog: %s", err)
|
||||
} else {
|
||||
if actual != clog {
|
||||
t.Errorf("changelog is not same. e: %s, a: %s", clog, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
56
cache/db.go
vendored
Normal file
56
cache/db.go
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// DB has a cache instance
|
||||
var DB Cache
|
||||
|
||||
const metabucket = "changelog-meta"
|
||||
|
||||
// Cache is a interface of cache
|
||||
type Cache interface {
|
||||
Close() error
|
||||
GetMeta(string) (Meta, bool, error)
|
||||
EnsureBuckets(Meta) error
|
||||
PrettyPrint(Meta) error
|
||||
GetChangelog(string, string) (string, error)
|
||||
PutChangelog(string, string, string) error
|
||||
}
|
||||
|
||||
// Meta holds a server name, distro information of the scanned server and
|
||||
// package information that was collected at the last scan.
|
||||
type Meta struct {
|
||||
Name string
|
||||
Distro config.Distro
|
||||
Packs []models.PackageInfo
|
||||
}
|
||||
|
||||
// FindPack search a PackageInfo
|
||||
func (m Meta) FindPack(name string) (pack models.PackageInfo, found bool) {
|
||||
for _, p := range m.Packs {
|
||||
if name == p.Name {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return pack, false
|
||||
}
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
@@ -97,6 +97,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
@@ -150,7 +151,10 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server/Contianer OS... ")
|
||||
scan.InitServers(Log)
|
||||
if err := scan.InitServers(Log); err != nil {
|
||||
Log.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -25,7 +26,6 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
ps "github.com/kotakanbe/go-pingscanner"
|
||||
@@ -100,7 +100,7 @@ notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = "465"
|
||||
smtpPort = "587"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -115,7 +115,9 @@ subjectPrefix = "[vuls]"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2014-6271"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
@@ -131,7 +133,9 @@ host = "{{$ip}}"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
|
||||
#containers = ["${running}"]
|
||||
#ignoreCves = ["CVE-2014-0160"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
|
||||
@@ -18,27 +18,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/db"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/google/subcommands"
|
||||
)
|
||||
|
||||
// HistoryCmd is Subcommand of list scanned results
|
||||
type HistoryCmd struct {
|
||||
debug bool
|
||||
debugSQL bool
|
||||
|
||||
dbpath string
|
||||
debug bool
|
||||
debugSQL bool
|
||||
resultsDir string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -53,7 +50,7 @@ func (*HistoryCmd) Synopsis() string {
|
||||
func (*HistoryCmd) Usage() string {
|
||||
return `history:
|
||||
history
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
`
|
||||
}
|
||||
|
||||
@@ -62,47 +59,41 @@ func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
|
||||
// _, err := scanHistories()
|
||||
histories, err := scanHistories()
|
||||
if err != nil {
|
||||
logrus.Error("Failed to select scan histories: ", err)
|
||||
var err error
|
||||
var jsonDirs report.JSONDirs
|
||||
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
const timeLayout = "2006-01-02 15:04"
|
||||
for _, history := range histories {
|
||||
names := []string{}
|
||||
for _, result := range history.ScanResults {
|
||||
if 0 < len(result.Container.ContainerID) {
|
||||
names = append(names, result.Container.Name)
|
||||
} else {
|
||||
names = append(names, result.ServerName)
|
||||
}
|
||||
for _, d := range jsonDirs {
|
||||
var files []os.FileInfo
|
||||
if files, err = ioutil.ReadDir(d); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fmt.Printf("%-3d %s scanned %d servers: %s\n",
|
||||
history.ID,
|
||||
history.ScannedAt.Format(timeLayout),
|
||||
len(history.ScanResults),
|
||||
strings.Join(names, ", "),
|
||||
var hosts []string
|
||||
for _, f := range files {
|
||||
if filepath.Ext(f.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
fileBase := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
hosts = append(hosts, fileBase)
|
||||
}
|
||||
splitPath := strings.Split(d, string(os.PathSeparator))
|
||||
timeStr := splitPath[len(splitPath)-1]
|
||||
fmt.Printf("%s scanned %d servers: %s\n",
|
||||
timeStr,
|
||||
len(hosts),
|
||||
strings.Join(hosts, ", "),
|
||||
)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
func scanHistories() (histories []models.ScanHistory, err error) {
|
||||
if err := db.OpenDB(); err != nil {
|
||||
return histories, fmt.Errorf(
|
||||
"Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
|
||||
}
|
||||
histories, err = db.SelectScanHistories()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -27,7 +28,6 @@ import (
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// PrepareCmd is Subcommand of host discovery mode
|
||||
@@ -38,7 +38,7 @@ type PrepareCmd struct {
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useUnattendedUpgrades bool
|
||||
sshExternal bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -46,7 +46,6 @@ func (*PrepareCmd) Name() string { return "prepare" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*PrepareCmd) Synopsis() string {
|
||||
// return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
|
||||
return `Install required packages to scan.
|
||||
CentOS: yum-plugin-security, yum-plugin-changelog
|
||||
Amazon: None
|
||||
@@ -63,8 +62,9 @@ func (*PrepareCmd) Usage() string {
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
[-ssh-external]
|
||||
|
||||
[SERVER]...
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
@@ -89,15 +89,15 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useUnattendedUpgrades,
|
||||
"use-unattended-upgrades",
|
||||
&p.sshExternal,
|
||||
"ssh-external",
|
||||
false,
|
||||
"[Deprecated] For Ubuntu, install unattended-upgrades",
|
||||
)
|
||||
"Use external ssh command. Default: Use the Go native implementation")
|
||||
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
@@ -112,7 +112,7 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
|
||||
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
@@ -143,22 +143,29 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
|
||||
// Set up custom logger
|
||||
logger := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
logger.Info("Detecting OS... ")
|
||||
scan.InitServers(logger)
|
||||
if err := scan.InitServers(logger); err != nil {
|
||||
logger.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(logger); err != nil {
|
||||
logger.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Installing...")
|
||||
if errs := scan.Prepare(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
logger.Errorf("Failed: %s", e)
|
||||
logger.Errorf("Failed to prepare: %s", e)
|
||||
}
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
logger.Info("Success")
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
129
commands/scan.go
129
commands/scan.go
@@ -18,22 +18,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/cveapi"
|
||||
"github.com/future-architect/vuls/db"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/future-architect/vuls/scan"
|
||||
"github.com/future-architect/vuls/util"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
// ScanCmd is Subcommand of host discovery mode
|
||||
@@ -44,9 +45,11 @@ type ScanCmd struct {
|
||||
|
||||
configPath string
|
||||
|
||||
dbpath string
|
||||
resultsDir string
|
||||
cvedbtype string
|
||||
cvedbpath string
|
||||
cveDictionaryURL string
|
||||
cacheDBPath string
|
||||
|
||||
cvssScoreOver float64
|
||||
ignoreUnscoredCves bool
|
||||
@@ -55,6 +58,9 @@ type ScanCmd struct {
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
containersOnly bool
|
||||
skipBroken bool
|
||||
|
||||
// reporting
|
||||
reportSlack bool
|
||||
reportMail bool
|
||||
@@ -62,6 +68,7 @@ type ScanCmd struct {
|
||||
reportText bool
|
||||
reportS3 bool
|
||||
reportAzureBlob bool
|
||||
reportXML bool
|
||||
|
||||
awsProfile string
|
||||
awsS3Bucket string
|
||||
@@ -71,9 +78,6 @@ type ScanCmd struct {
|
||||
azureKey string
|
||||
azureContainer string
|
||||
|
||||
useYumPluginSecurity bool
|
||||
useUnattendedUpgrades bool
|
||||
|
||||
sshExternal bool
|
||||
}
|
||||
|
||||
@@ -89,18 +93,23 @@ func (*ScanCmd) Usage() string {
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
[-cve-dictionary-dbtype=sqlite3|mysql]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3 or mysql connection string]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cache-dbpath=/path/to/cache.db]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ssh-external]
|
||||
[-containers-only]
|
||||
[-skip-broken]
|
||||
[-report-azure-blob]
|
||||
[-report-json]
|
||||
[-report-mail]
|
||||
[-report-s3]
|
||||
[-report-slack]
|
||||
[-report-text]
|
||||
[-report-xml]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
@@ -127,8 +136,14 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
|
||||
f.StringVar(
|
||||
&p.cvedbtype,
|
||||
"cve-dictionary-dbtype",
|
||||
"sqlite3",
|
||||
"DB type for fetching CVE dictionary (sqlite3 or mysql)")
|
||||
|
||||
f.StringVar(
|
||||
&p.cvedbpath,
|
||||
@@ -143,6 +158,13 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
defaultURL,
|
||||
"http://CVE.Dictionary")
|
||||
|
||||
defaultCacheDBPath := filepath.Join(wd, "cache.db")
|
||||
f.StringVar(
|
||||
&p.cacheDBPath,
|
||||
"cache-dbpath",
|
||||
defaultCacheDBPath,
|
||||
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
|
||||
|
||||
f.Float64Var(
|
||||
&p.cvssScoreOver,
|
||||
"cvss-over",
|
||||
@@ -161,6 +183,18 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
false,
|
||||
"Use external ssh command. Default: Use the Go native implementation")
|
||||
|
||||
f.BoolVar(
|
||||
&p.containersOnly,
|
||||
"containers-only",
|
||||
false,
|
||||
"Scan containers only. Default: Scan both of hosts and containers")
|
||||
|
||||
f.BoolVar(
|
||||
&p.skipBroken,
|
||||
"skip-broken",
|
||||
false,
|
||||
"[For CentOS] yum update changelog with --skip-broken option")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
@@ -180,6 +214,11 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
false,
|
||||
fmt.Sprintf("Write report to text files (%s/results/current)", wd),
|
||||
)
|
||||
f.BoolVar(&p.reportXML,
|
||||
"report-xml",
|
||||
false,
|
||||
fmt.Sprintf("Write report to XML files (%s/results/current)", wd),
|
||||
)
|
||||
|
||||
f.BoolVar(&p.reportS3,
|
||||
"report-s3",
|
||||
@@ -193,7 +232,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&p.reportAzureBlob,
|
||||
"report-azure-blob",
|
||||
false,
|
||||
"Write report to S3 (container/yyyyMMdd_HHmm/servername.json)",
|
||||
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json)",
|
||||
)
|
||||
f.StringVar(&p.azureAccount, "azure-account", "", "Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified")
|
||||
f.StringVar(&p.azureKey, "azure-key", "", "Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified")
|
||||
@@ -210,23 +249,8 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useYumPluginSecurity,
|
||||
"use-yum-plugin-security",
|
||||
false,
|
||||
"[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useUnattendedUpgrades,
|
||||
"use-unattended-upgrades",
|
||||
false,
|
||||
"[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
@@ -241,10 +265,11 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
|
||||
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
@@ -254,7 +279,9 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
logrus.Info("Start scanning")
|
||||
logrus.Infof("config: %s", p.configPath)
|
||||
if p.cvedbpath != "" {
|
||||
logrus.Infof("cve-dictionary: %s", p.cvedbpath)
|
||||
if p.cvedbtype == "sqlite3" {
|
||||
logrus.Infof("cve-dictionary: %s", p.cvedbpath)
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("cve-dictionary: %s", p.cveDictionaryURL)
|
||||
}
|
||||
@@ -295,13 +322,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
logrus.Debugf("%s", pp.Sprintf("%v", target))
|
||||
|
||||
c.Conf.Lang = p.lang
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
|
||||
// logger
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
scannedAt := time.Now()
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
@@ -315,10 +343,13 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
reports = append(reports, report.MailWriter{})
|
||||
}
|
||||
if p.reportJSON {
|
||||
reports = append(reports, report.JSONWriter{})
|
||||
reports = append(reports, report.JSONWriter{ScannedAt: scannedAt})
|
||||
}
|
||||
if p.reportText {
|
||||
reports = append(reports, report.TextFileWriter{})
|
||||
reports = append(reports, report.TextFileWriter{ScannedAt: scannedAt})
|
||||
}
|
||||
if p.reportXML {
|
||||
reports = append(reports, report.XMLWriter{ScannedAt: scannedAt})
|
||||
}
|
||||
if p.reportS3 {
|
||||
c.Conf.AwsRegion = p.awsRegion
|
||||
@@ -333,17 +364,17 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
if p.reportAzureBlob {
|
||||
c.Conf.AzureAccount = p.azureAccount
|
||||
if c.Conf.AzureAccount == "" {
|
||||
if len(c.Conf.AzureAccount) == 0 {
|
||||
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
|
||||
}
|
||||
|
||||
c.Conf.AzureKey = p.azureKey
|
||||
if c.Conf.AzureKey == "" {
|
||||
if len(c.Conf.AzureKey) == 0 {
|
||||
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
|
||||
}
|
||||
|
||||
c.Conf.AzureContainer = p.azureContainer
|
||||
if c.Conf.AzureContainer == "" {
|
||||
if len(c.Conf.AzureContainer) == 0 {
|
||||
Log.Error("Azure storage container name is requied with --azure-container option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
@@ -355,15 +386,17 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
}
|
||||
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
c.Conf.CveDBType = p.cvedbtype
|
||||
c.Conf.CveDBPath = p.cvedbpath
|
||||
c.Conf.CveDictionaryURL = p.cveDictionaryURL
|
||||
c.Conf.CacheDBPath = p.cacheDBPath
|
||||
c.Conf.CvssScoreOver = p.cvssScoreOver
|
||||
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
|
||||
c.Conf.SSHExternal = p.sshExternal
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
|
||||
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
|
||||
c.Conf.ContainersOnly = p.containersOnly
|
||||
c.Conf.SkipBroken = p.skipBroken
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
if !c.Conf.Validate() {
|
||||
@@ -377,7 +410,10 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server/Contianer OS... ")
|
||||
scan.InitServers(Log)
|
||||
if err := scan.InitServers(Log); err != nil {
|
||||
Log.Errorf("Failed to init servers: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
@@ -402,21 +438,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Insert to DB...")
|
||||
if err := db.OpenDB(); err != nil {
|
||||
Log.Errorf("Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
if err := db.MigrateDB(); err != nil {
|
||||
Log.Errorf("Failed to migrate. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
if err := db.Insert(scanResults); err != nil {
|
||||
Log.Fatalf("Failed to insert. dbpath: %s, err: %s", c.Conf.DBPath, err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Reporting...")
|
||||
filtered := scanResults.FilterByCvssOver()
|
||||
for _, w := range reports {
|
||||
|
||||
@@ -18,26 +18,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// TuiCmd is Subcommand of host discovery mode
|
||||
type TuiCmd struct {
|
||||
lang string
|
||||
debugSQL bool
|
||||
dbpath string
|
||||
lang string
|
||||
debugSQL bool
|
||||
resultsDir string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -49,7 +47,7 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites
|
||||
// Usage return usage
|
||||
func (*TuiCmd) Usage() string {
|
||||
return `tui:
|
||||
tui [-dbpath=/path/to/vuls.sqlite3]
|
||||
tui [-results-dir=/path/to/results]
|
||||
|
||||
`
|
||||
}
|
||||
@@ -61,24 +59,34 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
|
||||
fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
|
||||
defaultResultsDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
c.Conf.Lang = "en"
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.ResultsDir = p.resultsDir
|
||||
|
||||
historyID := ""
|
||||
var jsonDirName string
|
||||
var err error
|
||||
if 0 < len(f.Args()) {
|
||||
if _, err := strconv.Atoi(f.Args()[0]); err != nil {
|
||||
log.Errorf("First Argument have to be scan_histores record ID: %s", err)
|
||||
var jsonDirs report.JSONDirs
|
||||
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
for _, d := range jsonDirs {
|
||||
splitPath := strings.Split(d, string(os.PathSeparator))
|
||||
if splitPath[len(splitPath)-1] == f.Args()[0] {
|
||||
jsonDirName = f.Args()[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(jsonDirName) == 0 {
|
||||
log.Errorf("First Argument have to be JSON directory name : %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
historyID = f.Args()[0]
|
||||
} else {
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||
@@ -89,9 +97,9 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
historyID = fields[0]
|
||||
jsonDirName = fields[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return report.RunTui(historyID)
|
||||
return report.RunTui(jsonDirName)
|
||||
}
|
||||
|
||||
@@ -44,11 +44,15 @@ type Config struct {
|
||||
CvssScoreOver float64
|
||||
IgnoreUnscoredCves bool
|
||||
|
||||
SSHExternal bool
|
||||
SSHExternal bool
|
||||
ContainersOnly bool
|
||||
SkipBroken bool
|
||||
|
||||
HTTPProxy string `valid:"url"`
|
||||
DBPath string
|
||||
CveDBPath string
|
||||
HTTPProxy string `valid:"url"`
|
||||
ResultsDir string
|
||||
CveDBType string
|
||||
CveDBPath string
|
||||
CacheDBPath string
|
||||
|
||||
AwsProfile string
|
||||
AwsRegion string
|
||||
@@ -60,25 +64,42 @@ type Config struct {
|
||||
|
||||
// CpeNames []string
|
||||
// SummaryMode bool
|
||||
UseYumPluginSecurity bool
|
||||
UseUnattendedUpgrades bool
|
||||
}
|
||||
|
||||
// Validate configuration
|
||||
func (c Config) Validate() bool {
|
||||
errs := []error{}
|
||||
|
||||
if len(c.DBPath) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.DBPath); !ok {
|
||||
if len(c.ResultsDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"SQLite3 DB path must be a *Absolute* file path. dbpath: %s", c.DBPath))
|
||||
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.CveDBPath) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
|
||||
// If no valid DB type is set, default to sqlite3
|
||||
if c.CveDBType == "" {
|
||||
c.CveDBType = "sqlite3"
|
||||
}
|
||||
|
||||
if c.CveDBType != "sqlite3" && c.CveDBType != "mysql" {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"CVE DB type must be either 'sqlite3' or 'mysql'. -cve-dictionary-dbtype: %s", c.CveDBType))
|
||||
}
|
||||
|
||||
if c.CveDBType == "sqlite3" {
|
||||
if len(c.CveDBPath) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. -cve-dictionary-dbpath: %s", c.CveDBPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.CacheDBPath) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. dbpath: %s", c.CveDBPath))
|
||||
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", c.CacheDBPath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +202,6 @@ type SlackConf struct {
|
||||
|
||||
// Validate validates configuration
|
||||
func (c *SlackConf) Validate() (errs []error) {
|
||||
|
||||
if !c.UseThisTime {
|
||||
return
|
||||
}
|
||||
@@ -221,18 +241,43 @@ type ServerInfo struct {
|
||||
KeyPath string
|
||||
KeyPassword string
|
||||
|
||||
CpeNames []string
|
||||
CpeNames []string
|
||||
DependencyCheckXMLPath string
|
||||
|
||||
// Container Names or IDs
|
||||
Containers []string
|
||||
|
||||
IgnoreCves []string
|
||||
|
||||
// Optional key-value set that will be outputted to JSON
|
||||
Optional [][]interface{}
|
||||
|
||||
// For CentOS, RHEL, Amazon
|
||||
Enablerepo string
|
||||
|
||||
// used internal
|
||||
LogMsgAnsiColor string // DebugLog Color
|
||||
Container Container
|
||||
Family string
|
||||
Distro Distro
|
||||
}
|
||||
|
||||
// GetServerName returns ServerName if this serverInfo is about host.
|
||||
// If this serverInfo is abount a container, returns containerID@ServerName
|
||||
func (s ServerInfo) GetServerName() string {
|
||||
if len(s.Container.ContainerID) == 0 {
|
||||
return s.ServerName
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", s.Container.ContainerID, s.ServerName)
|
||||
}
|
||||
|
||||
// Distro has distribution info
|
||||
type Distro struct {
|
||||
Family string
|
||||
Release string
|
||||
}
|
||||
|
||||
func (l Distro) String() string {
|
||||
return fmt.Sprintf("%s %s", l.Family, l.Release)
|
||||
}
|
||||
|
||||
// IsContainer returns whether this ServerInfo is about container
|
||||
|
||||
@@ -20,10 +20,11 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
|
||||
)
|
||||
|
||||
// TOMLLoader loads config
|
||||
@@ -31,7 +32,11 @@ type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
|
||||
if Conf.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
var conf Config
|
||||
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
|
||||
log.Error("Load config failed", err)
|
||||
@@ -51,7 +56,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
|
||||
i := 0
|
||||
for name, v := range conf.Servers {
|
||||
|
||||
if 0 < len(v.KeyPassword) {
|
||||
log.Warn("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE.")
|
||||
}
|
||||
@@ -68,7 +72,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
}
|
||||
|
||||
s.Host = v.Host
|
||||
if s.Host == "" {
|
||||
if len(s.Host) == 0 {
|
||||
return fmt.Errorf("%s is invalid. host is empty", name)
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if s.KeyPath == "" {
|
||||
if len(s.KeyPath) == 0 {
|
||||
s.KeyPath = d.KeyPath
|
||||
}
|
||||
if s.KeyPath != "" {
|
||||
@@ -94,7 +98,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
|
||||
// s.KeyPassword = keyPass
|
||||
s.KeyPassword = v.KeyPassword
|
||||
if s.KeyPassword == "" {
|
||||
if len(s.KeyPassword) == 0 {
|
||||
s.KeyPassword = d.KeyPassword
|
||||
}
|
||||
|
||||
@@ -103,11 +107,42 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
s.CpeNames = d.CpeNames
|
||||
}
|
||||
|
||||
s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
|
||||
if len(s.DependencyCheckXMLPath) == 0 {
|
||||
s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
|
||||
}
|
||||
|
||||
// Load CPEs from OWASP Dependency Check XML
|
||||
if len(s.DependencyCheckXMLPath) != 0 {
|
||||
cpes, err := parser.Parse(s.DependencyCheckXMLPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Failed to read OWASP Dependency Check XML: %s", err)
|
||||
}
|
||||
log.Infof("Loaded from OWASP Dependency Check XML: %s",
|
||||
s.ServerName)
|
||||
s.CpeNames = append(s.CpeNames, cpes...)
|
||||
}
|
||||
|
||||
s.Containers = v.Containers
|
||||
if len(s.Containers) == 0 {
|
||||
s.Containers = d.Containers
|
||||
}
|
||||
|
||||
s.IgnoreCves = v.IgnoreCves
|
||||
for _, cve := range d.IgnoreCves {
|
||||
found := false
|
||||
for _, c := range s.IgnoreCves {
|
||||
if cve == c {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.IgnoreCves = append(s.IgnoreCves, cve)
|
||||
}
|
||||
}
|
||||
|
||||
s.Optional = v.Optional
|
||||
for _, dkv := range d.Optional {
|
||||
found := false
|
||||
@@ -122,13 +157,28 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
s.Enablerepo = v.Enablerepo
|
||||
if len(s.Enablerepo) == 0 {
|
||||
s.Enablerepo = d.Enablerepo
|
||||
}
|
||||
if len(s.Enablerepo) != 0 {
|
||||
for _, repo := range strings.Split(s.Enablerepo, ",") {
|
||||
switch repo {
|
||||
case "base", "updates":
|
||||
// nop
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"For now, enablerepo have to be base or updates: %s, servername: %s",
|
||||
s.Enablerepo, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.LogMsgAnsiColor = Colors[i%len(Colors)]
|
||||
i++
|
||||
|
||||
servers[name] = s
|
||||
}
|
||||
log.Debug("Config loaded")
|
||||
log.Debugf("%s", pp.Sprintf("%v", servers))
|
||||
Conf.Servers = servers
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
64
contrib/owasp-dependency-check/parser/parser.go
Normal file
64
contrib/owasp-dependency-check/parser/parser.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type analysis struct {
|
||||
Dependencies []dependency `xml:"dependencies>dependency"`
|
||||
}
|
||||
|
||||
type dependency struct {
|
||||
Identifiers []identifier `xml:"identifiers>identifier"`
|
||||
}
|
||||
|
||||
type identifier struct {
|
||||
Name string `xml:"name"`
|
||||
Type string `xml:"type,attr"`
|
||||
}
|
||||
|
||||
func appendIfMissing(slice []string, str string) []string {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return slice
|
||||
}
|
||||
}
|
||||
return append(slice, str)
|
||||
}
|
||||
|
||||
// Parse parses XML and collect list of cpe
|
||||
func Parse(path string) ([]string, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to open: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("Failed to read: %s", err)
|
||||
}
|
||||
|
||||
var anal analysis
|
||||
if err := xml.Unmarshal(b, &anal); err != nil {
|
||||
fmt.Errorf("Failed to unmarshal: %s", err)
|
||||
}
|
||||
|
||||
cpes := []string{}
|
||||
for _, d := range anal.Dependencies {
|
||||
for _, ident := range d.Identifiers {
|
||||
if ident.Type == "cpe" {
|
||||
name := strings.TrimPrefix(ident.Name, "(")
|
||||
name = strings.TrimSuffix(name, ")")
|
||||
cpes = appendIfMissing(cpes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(cpes)
|
||||
return cpes, nil
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func (api *cvedictClient) initialize() {
|
||||
|
||||
func (api cvedictClient) CheckHealth() (ok bool, err error) {
|
||||
if config.Conf.CveDBPath != "" {
|
||||
log.Debugf("get cve-dictionary from sqlite3")
|
||||
log.Debugf("get cve-dictionary from %s", config.Conf.CveDBType)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (api cvedictClient) CheckHealth() (ok bool, err error) {
|
||||
var resp *http.Response
|
||||
resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
|
||||
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
|
||||
if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v", url, errs)
|
||||
}
|
||||
return true, nil
|
||||
@@ -135,8 +135,10 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDet
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails cve.CveDetails, err error) {
|
||||
log.Debugf("open cve-dictionary db")
|
||||
log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
|
||||
cveconfig.Conf.DBType = config.Conf.CveDBType
|
||||
cveconfig.Conf.DBPath = config.Conf.CveDBPath
|
||||
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
|
||||
if err := cvedb.OpenDB(); err != nil {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to open DB. err: %s", err)
|
||||
@@ -186,41 +188,6 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
|
||||
}
|
||||
}
|
||||
|
||||
// func (api cvedictClient) httpGet(key, url string, query map[string]string, resChan chan<- response, errChan chan<- error) {
|
||||
|
||||
// var body string
|
||||
// var errs []error
|
||||
// var resp *http.Response
|
||||
// f := func() (err error) {
|
||||
// req := gorequest.New().SetDebug(true).Proxy(api.httpProxy).Get(url)
|
||||
// for key := range query {
|
||||
// req = req.Query(fmt.Sprintf("%s=%s", key, query[key])).Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
// }
|
||||
// pp.Println(req)
|
||||
// resp, body, errs = req.End()
|
||||
// if len(errs) > 0 || resp.StatusCode != 200 {
|
||||
// errChan <- fmt.Errorf("HTTP error. errs: %v, url: %s", errs, url)
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// notify := func(err error, t time.Duration) {
|
||||
// log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
|
||||
// }
|
||||
// err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
|
||||
// if err != nil {
|
||||
// errChan <- fmt.Errorf("HTTP Error %s", err)
|
||||
// }
|
||||
// // resChan <- body
|
||||
// cveDetail := cve.CveDetail{}
|
||||
// if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
|
||||
// errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
|
||||
// }
|
||||
// resChan <- response{
|
||||
// key,
|
||||
// cveDetail,
|
||||
// }
|
||||
// }
|
||||
|
||||
type responseGetCveDetailByCpeName struct {
|
||||
CpeName string
|
||||
CveDetails []cve.CveDetail
|
||||
@@ -252,7 +219,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
|
||||
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
|
||||
}
|
||||
resp, body, errs = req.End()
|
||||
if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
|
||||
}
|
||||
return nil
|
||||
@@ -274,8 +241,11 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.CveDetail, error) {
|
||||
log.Debugf("open cve-dictionary db")
|
||||
log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
|
||||
cveconfig.Conf.DBType = config.Conf.CveDBType
|
||||
cveconfig.Conf.DBPath = config.Conf.CveDBPath
|
||||
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
|
||||
|
||||
if err := cvedb.OpenDB(); err != nil {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to open DB. err: %s", err)
|
||||
|
||||
324
db/db.go
324
db/db.go
@@ -1,324 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
m "github.com/future-architect/vuls/models"
|
||||
"github.com/jinzhu/gorm"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
// OpenDB opens Database
|
||||
func OpenDB() (err error) {
|
||||
db, err = gorm.Open("sqlite3", config.Conf.DBPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
|
||||
return
|
||||
|
||||
}
|
||||
db.LogMode(config.Conf.DebugSQL)
|
||||
return
|
||||
}
|
||||
|
||||
// MigrateDB migrates Database
|
||||
func MigrateDB() error {
|
||||
if err := db.AutoMigrate(
|
||||
&m.ScanHistory{},
|
||||
&m.ScanResult{},
|
||||
// &m.NWLink{},
|
||||
&m.Container{},
|
||||
&m.CveInfo{},
|
||||
&m.CpeName{},
|
||||
&m.PackageInfo{},
|
||||
&m.DistroAdvisory{},
|
||||
&cve.CveDetail{},
|
||||
&cve.Jvn{},
|
||||
&cve.Nvd{},
|
||||
&cve.Reference{},
|
||||
&cve.Cpe{},
|
||||
).Error; err != nil {
|
||||
return fmt.Errorf("Failed to migrate. err: %s", err)
|
||||
}
|
||||
|
||||
errMsg := "Failed to create index. err: %s"
|
||||
// if err := db.Model(&m.NWLink{}).
|
||||
// AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
// return fmt.Errorf(errMsg, err)
|
||||
// }
|
||||
if err := db.Model(&m.Container{}).
|
||||
AddIndex("idx_containers_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.CveInfo{}).
|
||||
AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.CpeName{}).
|
||||
AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.PackageInfo{}).
|
||||
AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.DistroAdvisory{}).
|
||||
//TODO check table name
|
||||
AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_details_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_details_cveid", "cve_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Nvd{}).
|
||||
AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Jvn{}).
|
||||
AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert inserts scan results into DB
|
||||
func Insert(results []m.ScanResult) error {
|
||||
for _, r := range results {
|
||||
r.KnownCves = resetGormIDs(r.KnownCves)
|
||||
r.UnknownCves = resetGormIDs(r.UnknownCves)
|
||||
}
|
||||
|
||||
history := m.ScanHistory{
|
||||
ScanResults: results,
|
||||
ScannedAt: time.Now(),
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
if err := db.Create(&history).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, scanResult := range history.ScanResults {
|
||||
scanResult.ScanHistoryID = history.ID
|
||||
if err := db.Create(&scanResult).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
scanResult.Container.ScanResultID = scanResult.ID
|
||||
if err := db.Create(&scanResult.Container).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
|
||||
for _, cveInfo := range infos {
|
||||
cveInfo.ScanResultID = scanResultID
|
||||
if err := db.Create(&cveInfo).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pack := range cveInfo.Packages {
|
||||
pack.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&pack).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, distroAdvisory := range cveInfo.DistroAdvisories {
|
||||
distroAdvisory.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&distroAdvisory).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cpeName := range cveInfo.CpeNames {
|
||||
cpeName.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cpeName).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", true)
|
||||
cveDetail := cveInfo.CveDetail
|
||||
cveDetail.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cveDetail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
|
||||
for i := range infos {
|
||||
infos[i].CveDetail.ID = 0
|
||||
// NVD
|
||||
infos[i].CveDetail.Nvd.ID = 0
|
||||
for j := range infos[i].CveDetail.Nvd.Cpes {
|
||||
infos[i].CveDetail.Nvd.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Nvd.References {
|
||||
infos[i].CveDetail.Nvd.References[j].ID = 0
|
||||
}
|
||||
|
||||
// JVN
|
||||
infos[i].CveDetail.Jvn.ID = 0
|
||||
for j := range infos[i].CveDetail.Jvn.Cpes {
|
||||
infos[i].CveDetail.Jvn.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Jvn.References {
|
||||
infos[i].CveDetail.Jvn.References[j].ID = 0
|
||||
}
|
||||
|
||||
//Packages
|
||||
for j := range infos[i].Packages {
|
||||
infos[i].Packages[j].ID = 0
|
||||
infos[i].Packages[j].CveInfoID = 0
|
||||
}
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// SelectScanHistory select scan history from DB
|
||||
func SelectScanHistory(historyID string) (m.ScanHistory, error) {
|
||||
var err error
|
||||
|
||||
scanHistory := m.ScanHistory{}
|
||||
if historyID == "" {
|
||||
// select latest
|
||||
db.Order("scanned_at desc").First(&scanHistory)
|
||||
} else {
|
||||
var id int
|
||||
if id, err = strconv.Atoi(historyID); err != nil {
|
||||
return m.ScanHistory{},
|
||||
fmt.Errorf("historyID have to be numeric number: %s", err)
|
||||
}
|
||||
db.First(&scanHistory, id)
|
||||
}
|
||||
|
||||
if scanHistory.ID == 0 {
|
||||
return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
|
||||
}
|
||||
|
||||
// results := []m.ScanResult{}
|
||||
results := m.ScanResults{}
|
||||
db.Model(&scanHistory).Related(&results, "ScanResults")
|
||||
scanHistory.ScanResults = results
|
||||
|
||||
for i, r := range results {
|
||||
// nw := []m.NWLink{}
|
||||
// db.Model(&r).Related(&nw, "NWLinks")
|
||||
// scanHistory.ScanResults[i].NWLinks = nw
|
||||
|
||||
di := m.Container{}
|
||||
db.Model(&r).Related(&di, "Container")
|
||||
scanHistory.ScanResults[i].Container = di
|
||||
|
||||
knownCves := selectCveInfos(&r, "KnownCves")
|
||||
sort.Sort(m.CveInfos(knownCves))
|
||||
scanHistory.ScanResults[i].KnownCves = knownCves
|
||||
}
|
||||
|
||||
sort.Sort(scanHistory.ScanResults)
|
||||
return scanHistory, nil
|
||||
}
|
||||
|
||||
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
|
||||
cveInfos := []m.CveInfo{}
|
||||
db.Model(&result).Related(&cveInfos, fieldName)
|
||||
|
||||
for i, cveInfo := range cveInfos {
|
||||
cveDetail := cve.CveDetail{}
|
||||
db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
|
||||
id := cveDetail.CveID
|
||||
filledCveDetail := cvedb.Get(id, db)
|
||||
cveInfos[i].CveDetail = filledCveDetail
|
||||
|
||||
packs := []m.PackageInfo{}
|
||||
db.Model(&cveInfo).Related(&packs, "Packages")
|
||||
cveInfos[i].Packages = packs
|
||||
|
||||
advisories := []m.DistroAdvisory{}
|
||||
db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
|
||||
cveInfos[i].DistroAdvisories = advisories
|
||||
|
||||
names := []m.CpeName{}
|
||||
db.Model(&cveInfo).Related(&names, "CpeNames")
|
||||
cveInfos[i].CpeNames = names
|
||||
}
|
||||
return cveInfos
|
||||
}
|
||||
|
||||
// SelectScanHistories select latest scan history from DB
|
||||
func SelectScanHistories() ([]m.ScanHistory, error) {
|
||||
scanHistories := []m.ScanHistory{}
|
||||
db.Order("scanned_at desc").Find(&scanHistories)
|
||||
|
||||
if len(scanHistories) == 0 {
|
||||
return []m.ScanHistory{}, fmt.Errorf("No scanHistory records")
|
||||
}
|
||||
|
||||
for i, history := range scanHistories {
|
||||
results := m.ScanResults{}
|
||||
db.Model(&history).Related(&results, "ScanResults")
|
||||
scanHistories[i].ScanResults = results
|
||||
|
||||
for j, r := range results {
|
||||
di := m.Container{}
|
||||
db.Model(&r).Related(&di, "Container")
|
||||
scanHistories[i].ScanResults[j].Container = di
|
||||
}
|
||||
}
|
||||
return scanHistories, nil
|
||||
}
|
||||
110
glide.lock
generated
110
glide.lock
generated
@@ -1,117 +1,123 @@
|
||||
hash: 9683c87b3cf998e7fac1b12c4a94bf2bd18cb5422e9108539811546e703a439a
|
||||
updated: 2016-07-12T16:20:45.462913061+09:00
|
||||
hash: ca64aef6e9e94c7be91f79b88edb847363c8a5bd48da4ad27784e9342c8db6e2
|
||||
updated: 2016-11-01T15:05:15.23083077+09:00
|
||||
imports:
|
||||
- name: github.com/asaskevich/govalidator
|
||||
version: df81827fdd59d8b4fb93d8910b286ab7a3919520
|
||||
version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877
|
||||
- name: github.com/aws/aws-sdk-go
|
||||
version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
|
||||
version: 9e5bedb97b1cd85e53fd99209f93fd1a8a9f1df7
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/credentials
|
||||
- aws/session
|
||||
- service/s3
|
||||
- aws/awserr
|
||||
- aws/client
|
||||
- aws/corehandlers
|
||||
- aws/defaults
|
||||
- aws/request
|
||||
- private/endpoints
|
||||
- aws/awsutil
|
||||
- aws/client
|
||||
- aws/client/metadata
|
||||
- aws/signer/v4
|
||||
- private/protocol
|
||||
- private/protocol/restxml
|
||||
- private/waiter
|
||||
- aws/corehandlers
|
||||
- aws/credentials
|
||||
- aws/credentials/ec2rolecreds
|
||||
- aws/credentials/endpointcreds
|
||||
- aws/credentials/stscreds
|
||||
- aws/defaults
|
||||
- aws/ec2metadata
|
||||
- private/protocol/rest
|
||||
- aws/request
|
||||
- aws/session
|
||||
- aws/signer/v4
|
||||
- private/endpoints
|
||||
- private/protocol
|
||||
- private/protocol/query
|
||||
- private/protocol/xml/xmlutil
|
||||
- private/protocol/query/queryutil
|
||||
- private/protocol/rest
|
||||
- private/protocol/restxml
|
||||
- private/protocol/xml/xmlutil
|
||||
- private/waiter
|
||||
- service/s3
|
||||
- service/sts
|
||||
- name: github.com/Azure/azure-sdk-for-go
|
||||
version: 58a13e378daf3b06e65925397185684b16321111
|
||||
version: 9016164015faa51e549605e7b4b117f7de2aa6f9
|
||||
subpackages:
|
||||
- storage
|
||||
- name: github.com/boltdb/bolt
|
||||
version: 074dffcc83e9f421e261526d297cd93f22a34080
|
||||
- name: github.com/BurntSushi/toml
|
||||
version: ffaa107fbd880f6d18cd6fec9b511668dcad8639
|
||||
version: 99064174e013895bbd9b025c31100bd1d9b590ca
|
||||
- name: github.com/cenkalti/backoff
|
||||
version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
|
||||
version: b02f2bbce11d7ea6b97f282ef1771b0fe2f65ef3
|
||||
- name: github.com/cheggaaa/pb
|
||||
version: 04b234c80d661c663dbcebd52fc7218fdacc6d0c
|
||||
version: ad4efe000aa550bb54918c06ebbadc0ff17687b9
|
||||
- name: github.com/go-ini/ini
|
||||
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
|
||||
version: 6e4869b434bd001f6983749881c7ead3545887d8
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: 2a6c6079c7eff49a7e9d641e109d922f124a3e4c
|
||||
- name: github.com/google/subcommands
|
||||
version: 1c7173745a6001f67d8d96ab4e178284c77f7759
|
||||
version: a71b91e238406bd68766ee52db63bebedce0e9f6
|
||||
- name: github.com/gosuri/uitable
|
||||
version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
|
||||
subpackages:
|
||||
- util/strutil
|
||||
- util/wordwrap
|
||||
- name: github.com/howeyc/gopass
|
||||
version: 66487b23f2880ba32e185121d2cd51a338ea069a
|
||||
version: f5387c492211eb133053880d23dfae62aa14123d
|
||||
- name: github.com/jinzhu/gorm
|
||||
version: 613c0655691abb7691b70c5fda80a716d9e20b1b
|
||||
version: c1b9cf186e4bcd8e5d566ef43f2ae2dfe22dc34e
|
||||
subpackages:
|
||||
- dialects/mysql
|
||||
- name: github.com/jinzhu/inflection
|
||||
version: 8f4d3a0d04ce0b7c0cf3126fb98524246d00d102
|
||||
version: 74387dc39a75e970e7a3ae6a3386b5bd2e5c5cff
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jroimartin/gocui
|
||||
version: 2dcda558bf18ec07c7065bf1eaf071b5305f7c0c
|
||||
version: 357a541add9e311f7b67dfbaf92e28c71680a6b7
|
||||
- name: github.com/k0kubun/pp
|
||||
version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
|
||||
- name: github.com/kotakanbe/go-cve-dictionary
|
||||
version: 1a336b8ac785badfe89a175ee926d39574901232
|
||||
version: 70989b6709c3102924ad8c8483e9bdc99bcb598b
|
||||
subpackages:
|
||||
- config
|
||||
- db
|
||||
- models
|
||||
- log
|
||||
- jvn
|
||||
- log
|
||||
- models
|
||||
- nvd
|
||||
- util
|
||||
- name: github.com/kotakanbe/go-pingscanner
|
||||
version: 58e188a3e4f6ab1a6371e33421e4502e26fa1e80
|
||||
- name: github.com/kotakanbe/logrus-prefixed-formatter
|
||||
version: f4f7d41649cf1e75e736884da8d05324aa76ea25
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
|
||||
version: 6c903ff4aa50920ca86087a280590b36b3152b9c
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
|
||||
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
|
||||
- name: github.com/mattn/go-runewidth
|
||||
version: d6bea18f789704b5f83375793155289da36a3c7f
|
||||
version: 737072b4e32b7a5018b4a7125da8d12de90e8045
|
||||
- name: github.com/mattn/go-sqlite3
|
||||
version: 38ee283dabf11c9cbdb968eebd79b1fa7acbabe6
|
||||
version: e5a3c16c5c1d80b24f633e68aecd6b0702786d3d
|
||||
- name: github.com/mgutz/ansi
|
||||
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
|
||||
- name: github.com/moul/http2curl
|
||||
version: b1479103caacaa39319f75e7f57fc545287fca0d
|
||||
version: c984a4ec331f8ef0e5cd782975a97c92bd8ab40c
|
||||
- name: github.com/nsf/termbox-go
|
||||
version: c45773466a30b680355d6494cc8826113c93cd0f
|
||||
version: b6acae516ace002cb8105a89024544a1480655a5
|
||||
- name: github.com/parnurzeal/gorequest
|
||||
version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
|
||||
version: e37b9d1efacf7c94820b29b75dd7d0c2996b3fb1
|
||||
- name: github.com/rifflock/lfshook
|
||||
version: 05a24e24fa8d3a2eca8c2baf23aa2d5a2c51490c
|
||||
version: 3f9d976bd7402de39b46357069fb6325a974572e
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: f3cfb454f4c209e6668c95216c4744b8fddb2356
|
||||
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
|
||||
- name: golang.org/x/crypto
|
||||
version: c2f4947f41766b144bb09066e919466da5eddeae
|
||||
version: 1150b8bd09e53aea1d415621adae9bad665061a1
|
||||
subpackages:
|
||||
- ssh
|
||||
- ssh/agent
|
||||
- ssh/terminal
|
||||
- curve25519
|
||||
- ed25519
|
||||
- ed25519/internal/edwards25519
|
||||
- ssh
|
||||
- ssh/agent
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/net
|
||||
version: f841c39de738b1d0df95b5a7187744f0e03d8112
|
||||
version: 65dfc08770ce66f74becfdff5f8ab01caef4e946
|
||||
subpackages:
|
||||
- context
|
||||
- publicsuffix
|
||||
- name: golang.org/x/sys
|
||||
version: a408501be4d17ee978c04a618e7a1b22af058c0e
|
||||
version: c200b10b5d5e122be351b67af224adc6128af5bf
|
||||
subpackages:
|
||||
- unix
|
||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
|
||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
|
||||
devImports: []
|
||||
testImports: []
|
||||
|
||||
@@ -12,6 +12,7 @@ import:
|
||||
- aws/credentials
|
||||
- aws/session
|
||||
- service/s3
|
||||
- package: github.com/boltdb/bolt
|
||||
- package: github.com/cenkalti/backoff
|
||||
- package: github.com/google/subcommands
|
||||
- package: github.com/gosuri/uitable
|
||||
@@ -36,4 +37,3 @@ import:
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- context
|
||||
- package: gopkg.in/gomail.v2
|
||||
|
||||
@@ -222,14 +222,14 @@ ALAS (Amazon)<y:LabelModel>
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="365.54366048177087" width="251.74999999999977" x="641.4999999999995" y="285.984130859375"/>
|
||||
<y:Geometry height="382.650146484375" width="320.0" x="575.0" y="285.984130859375"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">Vuls</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="320.0" x="0.0" y="0.0">Vuls</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="14" bottomF="13.710652669270871" left="25" leftF="25.499999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
|
||||
<y:BorderInsets bottom="29" bottomF="28.63427734375" left="0" leftF="0.0" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -248,7 +248,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
<node id="n4::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="798.2499999999993" y="566.4311319986981"/>
|
||||
<y:Geometry height="50.0" width="80.0" x="800.0" y="575.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.595703125" x="18.7021484375" y="15.93359375">Report<y:LabelModel>
|
||||
@@ -296,6 +296,24 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4::n3">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="590.0" y="575.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.203125" x="8.3984375" y="8.8671875">Web View
|
||||
(Vulsrepo)<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="n5">
|
||||
@@ -319,30 +337,6 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="699.9999999999993" y="453.02174886067706"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<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:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
|
||||
<y: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:SVGNode>
|
||||
<y:Geometry height="37.0" width="109.57881927490234" x="889.737640380859" y="748.5119222005209"/>
|
||||
@@ -356,7 +350,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:SVGNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
|
||||
<y:Geometry height="24.0" width="35.0" x="811.8264548778523" y="681.5277913411459"/>
|
||||
@@ -379,20 +373,20 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9" yfiles.foldertype="group">
|
||||
<node id="n8" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="293.63460286458337" width="251.74999999999977" x="645.7499999999998" y="-51.181884765625114"/>
|
||||
<y:Geometry height="293.63460286458337" width="322.9999999999998" x="574.4999999999998" y="-51.181884765625114"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="322.9999999999998" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="25" bottomF="24.639889388761645" left="12" leftF="12.249999999999773" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
|
||||
<y:BorderInsets bottom="27" bottomF="27.139889388761617" left="37" leftF="37.249999999999886" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -407,12 +401,12 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n9:">
|
||||
<node id="n9::n0">
|
||||
<graph edgedefault="directed" id="n8:">
|
||||
<node id="n8::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="809.75" y="132.8128287101966"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:Geometry height="70.0" width="60.5" x="779.75" y="130.31282871019664"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<y:LabelModel>
|
||||
@@ -432,10 +426,10 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9::n1">
|
||||
<node id="n8::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="101.0" x="672.9999999999995" y="148.4841308593749"/>
|
||||
<y:Geometry height="50.0" width="101.0" x="626.7499999999997" y="142.23413085937491"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
|
||||
@@ -449,7 +443,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9::n2">
|
||||
<node id="n8::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="800.0" y="35.0"/>
|
||||
@@ -468,7 +462,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<node id="n9" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
@@ -496,8 +490,8 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n10:">
|
||||
<node id="n10::n0">
|
||||
<graph edgedefault="directed" id="n9:">
|
||||
<node id="n9::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="433.22635904947913"/>
|
||||
@@ -514,7 +508,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10::n1">
|
||||
<node id="n9::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="333.3980916341144"/>
|
||||
@@ -534,7 +528,7 @@ Container<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n11" yfiles.foldertype="group">
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
@@ -562,8 +556,8 @@ Container<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n11:">
|
||||
<node id="n11::n0">
|
||||
<graph edgedefault="directed" id="n10:">
|
||||
<node id="n10::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="553.0387573242189"/>
|
||||
@@ -580,7 +574,7 @@ Container<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n1">
|
||||
<node id="n10::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1032.6223485469814" y="566.4311319986981"/>
|
||||
@@ -599,7 +593,89 @@ Container<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n9::n2" target="n0">
|
||||
<node id="n11" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="108.94242662355293" width="124.0" x="649.0" y="434.05757337644707"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="124.0" x="0.0" y="0.0">results dir</y:NodeLabel>
|
||||
<y: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" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n11:">
|
||||
<node id="n11::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="664.0" y="470.72358900144707"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="671.0" y="480.2981186685961"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="682.0" y="492.0"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y: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="n8::n2" target="n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -636,17 +712,17 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n4::n2" target="n9::n1">
|
||||
<edge id="e2" source="n4::n2" target="n8::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-70.78547462732604" y="-104.38485166353485">HTTP<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="241.78515625" x="-130.8343747008911" y="-104.32744691064147">HTTP or --cve-dictoianry-dbpath option<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.6418953379495804" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.6111993328569665" 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>
|
||||
@@ -678,7 +754,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-23.436084747315135" y="96.06095129235564">send<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-24.439868927002635" y="91.6240450195761">send<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -696,7 +772,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="39.557346848955035" y="107.80743708610783">Generate<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="41.04640068840672" y="114.35365985463022">Generate<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -714,11 +790,11 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="109.7265625" x="-125.3179906251471" y="-50.63345557215132">Detail Information<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="140.76953125" x="-148.83947500014722" y="-53.63345557215143">View Detail Information<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="58.40488430810431" distanceToCenter="true" position="left" ratio="0.3174763616620919" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="65.44983715439666" distanceToCenter="true" position="left" ratio="0.3605218238342404" 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>
|
||||
@@ -726,7 +802,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n4::n2" target="n11::n0">
|
||||
<edge id="e6" source="n4::n2" target="n10::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="87.9098606499872" sy="24.54915453361525" tx="0.0" ty="0.0"/>
|
||||
@@ -744,7 +820,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n4::n2" target="n10::n0">
|
||||
<edge id="e7" source="n4::n2" target="n9::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -762,7 +838,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n10::e0" source="n10::n0" target="n10::n1">
|
||||
<edge id="n9::e0" source="n9::n0" target="n9::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -780,7 +856,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n10::n1" target="n3">
|
||||
<edge id="e8" source="n9::n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -790,7 +866,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n11::n0" target="n3">
|
||||
<edge id="e9" source="n10::n0" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -800,13 +876,13 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e0" source="n9::n2" target="n9::n0">
|
||||
<edge id="n8::e0" source="n8::n2" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-48.552734375" y="14.840008105098306">Insert<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-56.200249246840485" y="13.59000810509832">Insert<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -818,44 +894,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n4::n2" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="70.275390625" x="-75.4169220517914" y="5.2924810128525905">Insert
|
||||
Scan Result<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n6" target="n4::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-48.96875000000057" y="19.334788004557254">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e12" source="n4::n0" target="n7">
|
||||
<edge id="e10" source="n4::n0" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -865,7 +904,7 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n7" target="n5">
|
||||
<edge id="e11" source="n6" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -883,18 +922,18 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n4::n2" target="n9::n0">
|
||||
<data key="d9"/>
|
||||
<edge id="e12" source="n4::n2" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="191.283203125" x="39.258457373248575" y="-102.36487019599565">--cve-dictoianry-dbpath option<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="4.0" x="116.345248769779" y="-97.77053302962645">
|
||||
<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.749331808189429" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.7403938917830506" 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>
|
||||
@@ -902,14 +941,13 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e1" source="n9::n1" target="n9::n0">
|
||||
<data key="d9"/>
|
||||
<edge id="n8::e1" source="n8::n1" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="0.8643523609480326" y="20.003628886299452">Select<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -921,6 +959,48 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n4::n2" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<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="n11" target="n4::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e15" source="n5" target="n4::n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e16" source="n11" target="n4::n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
@@ -72,7 +72,7 @@ FreeBSD: pkg<y:LabelModel>
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Get upgradable packages
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -168,22 +168,6 @@ FreeBSD: pkg audit<y:LabelModel>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="64.1719342604298" width="111.96965865992411" x="687.3850119398792" y="807.0697396491782"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.56640625" x="31.701626204962054" y="23.019560880214726">Vuls DB<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="-8.881784197001252E-16" 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.dataBase">
|
||||
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="687.385587863464"/>
|
||||
@@ -199,13 +183,13 @@ FreeBSD: pkg audit<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11">
|
||||
<node id="n10">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="126.396484375" x="70.8017578125" y="11.8671875">Insert results into DB
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="152.634765625" x="57.6826171875" y="11.8671875">Write results to JSON files
|
||||
Reporting<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -216,13 +200,13 @@ Reporting<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n12">
|
||||
<node id="n11">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="271.369140625" x="-1.6845703124999432" y="11.8671875">Get all changelogs by using package manager
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
|
||||
CentOS: yum update --changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -233,7 +217,7 @@ CentOS: yum update --changelog<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n13">
|
||||
<node id="n12">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="373.8409153761062"/>
|
||||
@@ -364,7 +348,7 @@ FreeBSD<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n7" target="n11">
|
||||
<edge id="e9" source="n7" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
@@ -374,7 +358,7 @@ FreeBSD<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n7" target="n10">
|
||||
<edge id="e10" source="n7" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-134.01566143419018" sy="6.159084623893818" tx="0.0" ty="-29.333162136535975">
|
||||
@@ -386,18 +370,7 @@ FreeBSD<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n11" target="n9">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.86721713021484"/>
|
||||
<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="e12" source="n1" target="n12">
|
||||
<data key="d9"/>
|
||||
<edge id="e11" source="n1" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
|
||||
@@ -415,8 +388,7 @@ FreeBSD<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n12" target="n13">
|
||||
<data key="d9"/>
|
||||
<edge id="e12" source="n11" target="n12">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
@@ -426,8 +398,7 @@ FreeBSD<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n13" target="n7">
|
||||
<data key="d9"/>
|
||||
<edge id="e13" source="n12" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 81 KiB |
11
main.go
11
main.go
@@ -22,15 +22,20 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"context"
|
||||
|
||||
"github.com/future-architect/vuls/commands"
|
||||
"github.com/future-architect/vuls/version"
|
||||
"github.com/google/subcommands"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// Version of Vuls
|
||||
var version = "0.1.7"
|
||||
|
||||
// Revision of Git
|
||||
var revision string
|
||||
|
||||
func main() {
|
||||
subcommands.Register(subcommands.HelpCommand(), "")
|
||||
subcommands.Register(subcommands.FlagsCommand(), "")
|
||||
@@ -47,7 +52,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *v {
|
||||
fmt.Printf("%s %s\n", version.Name, version.Version)
|
||||
fmt.Printf("vuls %s %s\n", version, revision)
|
||||
os.Exit(int(subcommands.ExitSuccess))
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,9 @@ func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
|
||||
|
||||
// ScanResult has the result of scanned CVE information.
|
||||
type ScanResult struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanHistoryID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
ScanHistoryID uint `json:"-" xml:"-"`
|
||||
ScannedAt time.Time
|
||||
|
||||
ServerName string // TOML Section key
|
||||
// Hostname string
|
||||
@@ -88,6 +89,7 @@ type ScanResult struct {
|
||||
// NWLinks []NWLink
|
||||
KnownCves []CveInfo
|
||||
UnknownCves []CveInfo
|
||||
IgnoredCves []CveInfo
|
||||
|
||||
Optional [][]interface{} `gorm:"-"`
|
||||
}
|
||||
@@ -95,7 +97,7 @@ type ScanResult struct {
|
||||
// ServerInfo returns server name one line
|
||||
func (r ScanResult) ServerInfo() string {
|
||||
hostinfo := ""
|
||||
if r.Container.ContainerID == "" {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
hostinfo = fmt.Sprintf(
|
||||
"%s (%s%s)",
|
||||
r.ServerName,
|
||||
@@ -118,7 +120,7 @@ func (r ScanResult) ServerInfo() string {
|
||||
// ServerInfoTui returns server infromation for TUI sidebar
|
||||
func (r ScanResult) ServerInfoTui() string {
|
||||
hostinfo := ""
|
||||
if r.Container.ContainerID == "" {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
hostinfo = fmt.Sprintf(
|
||||
"%s (%s%s)",
|
||||
r.ServerName,
|
||||
@@ -163,10 +165,15 @@ func (r ScanResult) CveSummary() string {
|
||||
high+middle+low+unknown, high, middle, low, unknown)
|
||||
}
|
||||
|
||||
// AllCves returns Known and Unknown CVEs
|
||||
func (r ScanResult) AllCves() []CveInfo {
|
||||
return append(r.KnownCves, r.UnknownCves...)
|
||||
}
|
||||
|
||||
// NWLink has network link information.
|
||||
type NWLink struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
ScanResultID uint `json:"-" xml:"-"`
|
||||
|
||||
IPAddress string
|
||||
Netmask string
|
||||
@@ -190,13 +197,13 @@ func (c CveInfos) Less(i, j int) bool {
|
||||
if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
|
||||
return c[i].CveDetail.CveID < c[j].CveDetail.CveID
|
||||
}
|
||||
return c[i].CveDetail.CvssScore(lang) > c[j].CveDetail.CvssScore(lang)
|
||||
return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
|
||||
}
|
||||
|
||||
// CveInfo has Cve Information.
|
||||
type CveInfo struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
ScanResultID uint `json:"-" xml:"-"`
|
||||
|
||||
CveDetail cve.CveDetail
|
||||
Packages []PackageInfo
|
||||
@@ -206,8 +213,8 @@ type CveInfo struct {
|
||||
|
||||
// CpeName has CPE name
|
||||
type CpeName struct {
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
CveInfoID uint `json:"-" xml:"-"`
|
||||
|
||||
Name string
|
||||
}
|
||||
@@ -272,15 +279,15 @@ func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found boo
|
||||
|
||||
// PackageInfo has installed packages.
|
||||
type PackageInfo struct {
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
|
||||
Name string
|
||||
Version string
|
||||
Release string
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
CveInfoID uint `json:"-" xml:"-"`
|
||||
|
||||
Name string
|
||||
Version string
|
||||
Release string
|
||||
NewVersion string
|
||||
NewRelease string
|
||||
Repository string
|
||||
}
|
||||
|
||||
// ToStringCurrentVersion returns package name-version-release
|
||||
@@ -309,8 +316,8 @@ func (p PackageInfo) ToStringNewVersion() string {
|
||||
|
||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
|
||||
type DistroAdvisory struct {
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
CveInfoID uint `json:"-" xml:"-"`
|
||||
|
||||
AdvisoryID string
|
||||
Severity string
|
||||
@@ -320,8 +327,8 @@ type DistroAdvisory struct {
|
||||
|
||||
// Container has Container information
|
||||
type Container struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
ScanResultID uint `json:"-" xml:"-"`
|
||||
|
||||
ContainerID string
|
||||
Name string
|
||||
@@ -329,8 +336,8 @@ type Container struct {
|
||||
|
||||
// Platform has platform information
|
||||
type Platform struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
gorm.Model `json:"-" xml:"-"`
|
||||
ScanResultID uint `json:"-" xml:"-"`
|
||||
|
||||
Name string // aws or azure or gcp or other...
|
||||
InstanceID string
|
||||
|
||||
@@ -115,7 +115,7 @@ func (w AzureBlobWriter) upload(res models.ScanResult) (err error) {
|
||||
}
|
||||
timestr := time.Now().Format("20060102_1504")
|
||||
name := ""
|
||||
if res.Container.ContainerID == "" {
|
||||
if len(res.Container.ContainerID) == 0 {
|
||||
name = fmt.Sprintf("%s/%s.json", timestr, res.ServerName)
|
||||
} else {
|
||||
name = fmt.Sprintf("%s/%s_%s.json", timestr, res.ServerName, res.Container.Name)
|
||||
|
||||
113
report/json.go
113
report/json.go
@@ -21,30 +21,49 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// JSONDirs array of json files path.
|
||||
type JSONDirs []string
|
||||
|
||||
func (d JSONDirs) Len() int {
|
||||
return len(d)
|
||||
}
|
||||
func (d JSONDirs) Swap(i, j int) {
|
||||
d[i], d[j] = d[j], d[i]
|
||||
}
|
||||
func (d JSONDirs) Less(i, j int) bool {
|
||||
return d[j] < d[i]
|
||||
}
|
||||
|
||||
// JSONWriter writes results to file.
|
||||
type JSONWriter struct{}
|
||||
type JSONWriter struct {
|
||||
ScannedAt time.Time
|
||||
}
|
||||
|
||||
func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
var path string
|
||||
if path, err = ensureResultDir(w.ScannedAt); err != nil {
|
||||
return fmt.Errorf("Failed to make direcotory/symlink : %s", err)
|
||||
}
|
||||
|
||||
path, err := ensureResultDir()
|
||||
for _, scanResult := range scanResults {
|
||||
scanResult.ScannedAt = w.ScannedAt
|
||||
}
|
||||
|
||||
var jsonBytes []byte
|
||||
if jsonBytes, err = json.Marshal(scanResults); err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
||||
}
|
||||
all := filepath.Join(path, "all.json")
|
||||
if err := ioutil.WriteFile(all, jsonBytes, 0644); err != nil {
|
||||
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", all, err)
|
||||
}
|
||||
|
||||
for _, r := range scanResults {
|
||||
jsonPath := ""
|
||||
if r.Container.ContainerID == "" {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
jsonPath = filepath.Join(path, fmt.Sprintf("%s.json", r.ServerName))
|
||||
} else {
|
||||
jsonPath = filepath.Join(path,
|
||||
@@ -54,9 +73,79 @@ func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
if jsonBytes, err = json.Marshal(r); err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(jsonPath, jsonBytes, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(jsonPath, jsonBytes, 0600); err != nil {
|
||||
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", jsonPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// JSONDirPattern is file name pattern of JSON directory
|
||||
var JSONDirPattern = regexp.MustCompile(`^\d{8}_\d{4}$`)
|
||||
|
||||
// GetValidJSONDirs return valid json directory as array
|
||||
func GetValidJSONDirs() (jsonDirs JSONDirs, err error) {
|
||||
var dirInfo []os.FileInfo
|
||||
if dirInfo, err = ioutil.ReadDir(c.Conf.ResultsDir); err != nil {
|
||||
err = fmt.Errorf("Failed to read %s: %s", c.Conf.ResultsDir, err)
|
||||
return
|
||||
}
|
||||
for _, d := range dirInfo {
|
||||
if d.IsDir() && JSONDirPattern.MatchString(d.Name()) {
|
||||
jsonDir := filepath.Join(c.Conf.ResultsDir, d.Name())
|
||||
jsonDirs = append(jsonDirs, jsonDir)
|
||||
}
|
||||
}
|
||||
sort.Sort(jsonDirs)
|
||||
return
|
||||
}
|
||||
|
||||
// LoadOneScanHistory read JSON data
|
||||
func LoadOneScanHistory(jsonDir string) (scanHistory models.ScanHistory, err error) {
|
||||
var scanResults []models.ScanResult
|
||||
var files []os.FileInfo
|
||||
if files, err = ioutil.ReadDir(jsonDir); err != nil {
|
||||
err = fmt.Errorf("Failed to read %s: %s", jsonDir, err)
|
||||
return
|
||||
}
|
||||
for _, file := range files {
|
||||
if filepath.Ext(file.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
var scanResult models.ScanResult
|
||||
var data []byte
|
||||
jsonPath := filepath.Join(jsonDir, file.Name())
|
||||
if data, err = ioutil.ReadFile(jsonPath); err != nil {
|
||||
err = fmt.Errorf("Failed to read %s: %s", jsonPath, err)
|
||||
return
|
||||
}
|
||||
if json.Unmarshal(data, &scanResult) != nil {
|
||||
err = fmt.Errorf("Failed to parse %s: %s", jsonPath, err)
|
||||
return
|
||||
}
|
||||
scanResults = append(scanResults, scanResult)
|
||||
}
|
||||
if len(scanResults) == 0 {
|
||||
err = fmt.Errorf("There is no json file under %s", jsonDir)
|
||||
return
|
||||
}
|
||||
|
||||
var scannedAt time.Time
|
||||
if scanResults[0].ScannedAt.IsZero() {
|
||||
splitPath := strings.Split(jsonDir, string(os.PathSeparator))
|
||||
timeStr := splitPath[len(splitPath)-1]
|
||||
timeformat := "20060102_1504"
|
||||
if scannedAt, err = time.Parse(timeformat, timeStr); err != nil {
|
||||
err = fmt.Errorf("Failed to parse %s: %s", timeStr, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
scannedAt = scanResults[0].ScannedAt
|
||||
}
|
||||
|
||||
scanHistory = models.ScanHistory{
|
||||
ScanResults: scanResults,
|
||||
ScannedAt: scannedAt,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func (w LogrusWriter) Write(scanResults []models.ScanResult) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
path = filepath.Join(os.Getenv("APPDATA"), "vuls", "report.log")
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package report
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
// MailWriter send mail
|
||||
@@ -33,37 +34,53 @@ type MailWriter struct{}
|
||||
func (w MailWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
conf := config.Conf
|
||||
for _, s := range scanResults {
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", conf.Mail.From)
|
||||
m.SetHeader("To", conf.Mail.To...)
|
||||
m.SetHeader("Cc", conf.Mail.Cc...)
|
||||
to := strings.Join(conf.Mail.To[:], ", ")
|
||||
cc := strings.Join(conf.Mail.Cc[:], ", ")
|
||||
mailAddresses := append(conf.Mail.To, conf.Mail.Cc...)
|
||||
if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
|
||||
return fmt.Errorf("Failed to parse email addresses: %s", err)
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s%s %s",
|
||||
conf.Mail.SubjectPrefix,
|
||||
s.ServerInfo(),
|
||||
s.CveSummary(),
|
||||
)
|
||||
m.SetHeader("Subject", subject)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["From"] = conf.Mail.From
|
||||
headers["To"] = to
|
||||
headers["Cc"] = cc
|
||||
headers["Subject"] = subject
|
||||
|
||||
var message string
|
||||
for k, v := range headers {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
|
||||
var body string
|
||||
if body, err = toPlainText(s); err != nil {
|
||||
return err
|
||||
}
|
||||
m.SetBody("text/plain", body)
|
||||
port, _ := strconv.Atoi(conf.Mail.SMTPPort)
|
||||
d := gomail.NewPlainDialer(
|
||||
conf.Mail.SMTPAddr,
|
||||
port,
|
||||
conf.Mail.User,
|
||||
conf.Mail.Password,
|
||||
message += "\r\n" + body
|
||||
|
||||
smtpServer := net.JoinHostPort(conf.Mail.SMTPAddr, conf.Mail.SMTPPort)
|
||||
|
||||
err := smtp.SendMail(
|
||||
smtpServer,
|
||||
smtp.PlainAuth(
|
||||
"",
|
||||
conf.Mail.User,
|
||||
conf.Mail.Password,
|
||||
conf.Mail.SMTPAddr,
|
||||
),
|
||||
conf.Mail.From,
|
||||
conf.Mail.To,
|
||||
[]byte(message),
|
||||
)
|
||||
|
||||
d.TLSConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
if err := d.DialAndSend(m); err != nil {
|
||||
panic(err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to send emails: %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
12
report/s3.go
12
report/s3.go
@@ -78,19 +78,9 @@ func (w S3Writer) Write(scanResults []models.ScanResult) (err error) {
|
||||
// http://docs.aws.amazon.com/sdk-for-go/latest/v1/developerguide/common-examples.title.html
|
||||
svc := getS3()
|
||||
timestr := time.Now().Format("20060102_1504")
|
||||
key := fmt.Sprintf("%s/%s", timestr, "all.json")
|
||||
_, err = svc.PutObject(&s3.PutObjectInput{
|
||||
Bucket: &c.Conf.S3Bucket,
|
||||
Key: &key,
|
||||
Body: bytes.NewReader(jsonBytes),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to upload data to %s/%s, %s", c.Conf.S3Bucket, key, err)
|
||||
}
|
||||
|
||||
for _, r := range scanResults {
|
||||
key := ""
|
||||
if r.Container.ContainerID == "" {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
key = fmt.Sprintf("%s/%s.json", timestr, r.ServerName)
|
||||
} else {
|
||||
key = fmt.Sprintf("%s/%s_%s.json", timestr, r.ServerName, r.Container.Name)
|
||||
|
||||
@@ -59,7 +59,6 @@ type SlackWriter struct{}
|
||||
func (w SlackWriter) Write(scanResults []models.ScanResult) error {
|
||||
conf := config.Conf.Slack
|
||||
for _, s := range scanResults {
|
||||
|
||||
channel := conf.Channel
|
||||
if channel == "${servername}" {
|
||||
channel = fmt.Sprintf("#%s", s.ServerName)
|
||||
@@ -80,7 +79,7 @@ func (w SlackWriter) Write(scanResults []models.ScanResult) error {
|
||||
Send(string(jsonBody)).End()
|
||||
if resp.StatusCode != 200 {
|
||||
log.Errorf("Resonse body: %s", body)
|
||||
if len(errs) > 0 {
|
||||
if 0 < len(errs) {
|
||||
return errs[0]
|
||||
}
|
||||
}
|
||||
@@ -97,7 +96,6 @@ func (w SlackWriter) Write(scanResults []models.ScanResult) error {
|
||||
}
|
||||
|
||||
func msgText(r models.ScanResult) string {
|
||||
|
||||
notifyUsers := ""
|
||||
if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) {
|
||||
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
@@ -108,7 +106,6 @@ func msgText(r models.ScanResult) string {
|
||||
}
|
||||
|
||||
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
|
||||
|
||||
cves := scanResult.KnownCves
|
||||
if !config.Conf.IgnoreUnscoredCves {
|
||||
cves = append(cves, scanResult.UnknownCves...)
|
||||
@@ -205,6 +202,17 @@ func attachmentText(cveInfo models.CveInfo, osFamily string) string {
|
||||
|
||||
func links(cveInfo models.CveInfo, osFamily string) string {
|
||||
links := []string{}
|
||||
|
||||
cweID := cveInfo.CveDetail.CweID()
|
||||
if 0 < len(cweID) {
|
||||
links = append(links, fmt.Sprintf("<%s|%s>",
|
||||
cweURL(cweID), cweID))
|
||||
if config.Conf.Lang == "ja" {
|
||||
links = append(links, fmt.Sprintf("<%s|%s(JVN)>",
|
||||
cweJvnURL(cweID), cweID))
|
||||
}
|
||||
}
|
||||
|
||||
cveID := cveInfo.CveDetail.CveID
|
||||
if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) {
|
||||
jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link())
|
||||
|
||||
@@ -22,21 +22,22 @@ import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// TextFileWriter writes results to file.
|
||||
type TextFileWriter struct{}
|
||||
type TextFileWriter struct {
|
||||
ScannedAt time.Time
|
||||
}
|
||||
|
||||
func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
|
||||
path, err := ensureResultDir()
|
||||
|
||||
path, err := ensureResultDir(w.ScannedAt)
|
||||
all := []string{}
|
||||
for _, r := range scanResults {
|
||||
textFilePath := ""
|
||||
if r.Container.ContainerID == "" {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
textFilePath = filepath.Join(path, fmt.Sprintf("%s.txt", r.ServerName))
|
||||
} else {
|
||||
textFilePath = filepath.Join(path,
|
||||
@@ -48,7 +49,7 @@ func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
}
|
||||
all = append(all, text)
|
||||
b := []byte(text)
|
||||
if err := ioutil.WriteFile(textFilePath, b, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(textFilePath, b, 0600); err != nil {
|
||||
return fmt.Errorf("Failed to write text files. path: %s, err: %s", textFilePath, err)
|
||||
}
|
||||
}
|
||||
@@ -56,7 +57,7 @@ func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
text := strings.Join(all, "\n\n")
|
||||
b := []byte(text)
|
||||
allPath := filepath.Join(path, "all.txt")
|
||||
if err := ioutil.WriteFile(allPath, b, 0644); err != nil {
|
||||
if err := ioutil.WriteFile(allPath, b, 0600); err != nil {
|
||||
return fmt.Errorf("Failed to write text files. path: %s, err: %s", allPath, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
112
report/tui.go
112
report/tui.go
@@ -20,13 +20,13 @@ package report
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/db"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/gosuri/uitable"
|
||||
@@ -40,42 +40,55 @@ var currentCveInfo int
|
||||
var currentDetailLimitY int
|
||||
|
||||
// RunTui execute main logic
|
||||
func RunTui(historyID string) subcommands.ExitStatus {
|
||||
func RunTui(jsonDirName string) subcommands.ExitStatus {
|
||||
var err error
|
||||
scanHistory, err = selectScanHistory(historyID)
|
||||
scanHistory, err = selectScanHistory(jsonDirName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Errorf("%s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
g := gocui.NewGui()
|
||||
if err := g.Init(); err != nil {
|
||||
log.Panicln(err)
|
||||
g, err := gocui.NewGui()
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
defer g.Close()
|
||||
|
||||
g.SetLayout(layout)
|
||||
g.SetManagerFunc(layout)
|
||||
if err := keybindings(g); err != nil {
|
||||
log.Panicln(err)
|
||||
log.Errorf("%s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
g.SelBgColor = gocui.ColorGreen
|
||||
g.SelFgColor = gocui.ColorBlack
|
||||
g.Cursor = true
|
||||
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
log.Panicln(err)
|
||||
log.Errorf("%s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
func selectScanHistory(historyID string) (latest models.ScanHistory, err error) {
|
||||
if err := db.OpenDB(); err != nil {
|
||||
return latest, fmt.Errorf(
|
||||
"Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
|
||||
func selectScanHistory(jsonDirName string) (latest models.ScanHistory, err error) {
|
||||
var jsonDir string
|
||||
if 0 < len(jsonDirName) {
|
||||
jsonDir = filepath.Join(config.Conf.ResultsDir, jsonDirName)
|
||||
} else {
|
||||
var jsonDirs JSONDirs
|
||||
if jsonDirs, err = GetValidJSONDirs(); err != nil {
|
||||
return
|
||||
}
|
||||
if len(jsonDirs) == 0 {
|
||||
return latest, fmt.Errorf("No scan results are found in %s", config.Conf.ResultsDir)
|
||||
}
|
||||
jsonDir = jsonDirs[0]
|
||||
}
|
||||
if latest, err = LoadOneScanHistory(jsonDir); err != nil {
|
||||
return
|
||||
}
|
||||
latest, err = db.SelectScanHistory(historyID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -163,35 +176,41 @@ func keybindings(g *gocui.Gui) (err error) {
|
||||
}
|
||||
|
||||
func nextView(g *gocui.Gui, v *gocui.View) error {
|
||||
var err error
|
||||
|
||||
if v == nil {
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
}
|
||||
switch v.Name() {
|
||||
case "side":
|
||||
return g.SetCurrentView("summary")
|
||||
_, err = g.SetCurrentView("summary")
|
||||
case "summary":
|
||||
return g.SetCurrentView("detail")
|
||||
_, err = g.SetCurrentView("detail")
|
||||
case "detail":
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
default:
|
||||
return g.SetCurrentView("summary")
|
||||
_, err = g.SetCurrentView("summary")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func previousView(g *gocui.Gui, v *gocui.View) error {
|
||||
var err error
|
||||
|
||||
if v == nil {
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
}
|
||||
switch v.Name() {
|
||||
case "side":
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
case "summary":
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
case "detail":
|
||||
return g.SetCurrentView("summary")
|
||||
_, err = g.SetCurrentView("summary")
|
||||
default:
|
||||
return g.SetCurrentView("side")
|
||||
_, err = g.SetCurrentView("side")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
|
||||
@@ -203,7 +222,7 @@ func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
|
||||
}
|
||||
return true, yLimit
|
||||
case "summary":
|
||||
yLimit = len(currentScanResult.KnownCves) - 1
|
||||
yLimit = len(currentScanResult.AllCves()) - 1
|
||||
if yLimit < nextY {
|
||||
return false, yLimit
|
||||
}
|
||||
@@ -332,7 +351,7 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
ox, oy := v.Origin()
|
||||
cx, cy := v.Cursor()
|
||||
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
|
||||
if err := v.SetCursor(cx, cy-1); err != nil && 0 < oy {
|
||||
if err := v.SetOrigin(ox, oy-1); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -360,7 +379,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
|
||||
@@ -368,7 +387,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
|
||||
}
|
||||
}
|
||||
@@ -378,7 +397,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
|
||||
@@ -386,7 +405,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
|
||||
}
|
||||
}
|
||||
@@ -451,7 +470,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
|
||||
}
|
||||
}
|
||||
@@ -473,7 +492,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
|
||||
}
|
||||
}
|
||||
@@ -484,7 +503,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := g.DeleteView("msg"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetCurrentView("summary"); err != nil {
|
||||
if _, err := g.SetCurrentView("summary"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -519,7 +538,7 @@ func setSideLayout(g *gocui.Gui) error {
|
||||
fmt.Fprintln(v, result.ServerInfoTui())
|
||||
}
|
||||
currentScanResult = scanHistory.ScanResults[0]
|
||||
if err := g.SetCurrentView("side"); err != nil {
|
||||
if _, err := g.SetCurrentView("side"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -533,7 +552,7 @@ func setSummaryLayout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := summaryLines(currentScanResult)
|
||||
lines := summaryLines()
|
||||
fmt.Fprintf(v, lines)
|
||||
|
||||
v.Highlight = true
|
||||
@@ -543,21 +562,21 @@ func setSummaryLayout(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func summaryLines(data models.ScanResult) string {
|
||||
func summaryLines() string {
|
||||
stable := uitable.New()
|
||||
stable.MaxColWidth = 1000
|
||||
stable.Wrap = false
|
||||
|
||||
indexFormat := ""
|
||||
if len(data.KnownCves) < 10 {
|
||||
if len(currentScanResult.AllCves()) < 10 {
|
||||
indexFormat = "[%1d]"
|
||||
} else if len(data.KnownCves) < 100 {
|
||||
} else if len(currentScanResult.AllCves()) < 100 {
|
||||
indexFormat = "[%2d]"
|
||||
} else {
|
||||
indexFormat = "[%3d]"
|
||||
}
|
||||
|
||||
for i, d := range data.KnownCves {
|
||||
for i, d := range currentScanResult.AllCves() {
|
||||
var cols []string
|
||||
// packs := []string{}
|
||||
// for _, pack := range d.Packages {
|
||||
@@ -643,6 +662,7 @@ type dataForTmpl struct {
|
||||
CvssVector string
|
||||
CvssSeverity string
|
||||
Summary string
|
||||
CweURL string
|
||||
VulnSiteLinks []string
|
||||
References []cve.Reference
|
||||
Packages []string
|
||||
@@ -652,11 +672,11 @@ type dataForTmpl struct {
|
||||
}
|
||||
|
||||
func detailLines() (string, error) {
|
||||
if len(currentScanResult.KnownCves) == 0 {
|
||||
if len(currentScanResult.AllCves()) == 0 {
|
||||
return "No vulnerable packages", nil
|
||||
}
|
||||
|
||||
cveInfo := currentScanResult.KnownCves[currentCveInfo]
|
||||
cveInfo := currentScanResult.AllCves()[currentCveInfo]
|
||||
cveID := cveInfo.CveDetail.CveID
|
||||
|
||||
tmpl, err := template.New("detail").Parse(detailTemplate())
|
||||
@@ -682,6 +702,8 @@ func detailLines() (string, error) {
|
||||
refs = nvd.VulnSiteReferences()
|
||||
}
|
||||
|
||||
cweURL := cweURL(cveInfo.CveDetail.CweID())
|
||||
|
||||
links := []string{
|
||||
fmt.Sprintf("[NVD]( %s )", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID)),
|
||||
fmt.Sprintf("[MITRE]( %s )", fmt.Sprintf("%s%s", mitreBaseURL, cveID)),
|
||||
@@ -715,6 +737,7 @@ func detailLines() (string, error) {
|
||||
CvssSeverity: cvssSeverity,
|
||||
CvssVector: cvssVector,
|
||||
Summary: summary,
|
||||
CweURL: cweURL,
|
||||
VulnSiteLinks: links,
|
||||
References: refs,
|
||||
Packages: packages,
|
||||
@@ -746,6 +769,11 @@ Summary
|
||||
|
||||
{{.Summary }}
|
||||
|
||||
CWE
|
||||
--------------
|
||||
|
||||
{{.CweURL }}
|
||||
|
||||
Package/CPE
|
||||
--------------
|
||||
|
||||
|
||||
@@ -30,32 +30,34 @@ import (
|
||||
"github.com/gosuri/uitable"
|
||||
)
|
||||
|
||||
func ensureResultDir() (path string, err error) {
|
||||
if resultDirPath != "" {
|
||||
return resultDirPath, nil
|
||||
}
|
||||
|
||||
func ensureResultDir(scannedAt time.Time) (path string, err error) {
|
||||
const timeLayout = "20060102_1504"
|
||||
timedir := time.Now().Format(timeLayout)
|
||||
wd, _ := os.Getwd()
|
||||
dir := filepath.Join(wd, "results", timedir)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
jsonDirName := scannedAt.Format(timeLayout)
|
||||
|
||||
resultsDir := config.Conf.ResultsDir
|
||||
if len(resultsDir) == 0 {
|
||||
wd, _ := os.Getwd()
|
||||
resultsDir = filepath.Join(wd, "results")
|
||||
}
|
||||
jsonDir := filepath.Join(resultsDir, jsonDirName)
|
||||
|
||||
if err := os.MkdirAll(jsonDir, 0700); err != nil {
|
||||
return "", fmt.Errorf("Failed to create dir: %s", err)
|
||||
}
|
||||
|
||||
symlinkPath := filepath.Join(wd, "results", "current")
|
||||
if _, err := os.Stat(symlinkPath); err == nil {
|
||||
symlinkPath := filepath.Join(resultsDir, "current")
|
||||
if _, err := os.Lstat(symlinkPath); err == nil {
|
||||
if err := os.Remove(symlinkPath); err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Symlink(dir, symlinkPath); err != nil {
|
||||
if err := os.Symlink(jsonDir, symlinkPath); err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
|
||||
}
|
||||
return dir, nil
|
||||
return jsonDir, nil
|
||||
}
|
||||
|
||||
func toPlainText(scanResult models.ScanResult) (string, error) {
|
||||
@@ -205,13 +207,11 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
|
||||
}
|
||||
|
||||
func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
|
||||
|
||||
cveDetail := cveInfo.CveDetail
|
||||
cveID := cveDetail.CveID
|
||||
jvn := cveDetail.Jvn
|
||||
|
||||
dtable := uitable.New()
|
||||
//TODO resize
|
||||
dtable.MaxColWidth = 100
|
||||
dtable.Wrap = true
|
||||
dtable.AddRow(cveID)
|
||||
@@ -228,6 +228,8 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
|
||||
dtable.AddRow("Vector", jvn.CvssVector())
|
||||
dtable.AddRow("Title", jvn.CveTitle())
|
||||
dtable.AddRow("Description", jvn.CveSummary())
|
||||
dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID()))
|
||||
dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID()))
|
||||
|
||||
dtable.AddRow("JVN", jvn.Link())
|
||||
dtable.AddRow("NVD", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID))
|
||||
@@ -252,7 +254,6 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
|
||||
nvd := cveDetail.Nvd
|
||||
|
||||
dtable := uitable.New()
|
||||
//TODO resize
|
||||
dtable.MaxColWidth = 100
|
||||
dtable.Wrap = true
|
||||
dtable.AddRow(cveID)
|
||||
@@ -270,6 +271,8 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
|
||||
|
||||
dtable.AddRow("Vector", nvd.CvssVector())
|
||||
dtable.AddRow("Summary", nvd.CveSummary())
|
||||
dtable.AddRow("CWE", cweURL(cveDetail.CweID()))
|
||||
|
||||
dtable.AddRow("NVD", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID))
|
||||
dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID))
|
||||
dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID))
|
||||
@@ -376,3 +379,12 @@ func addCpeNames(table *uitable.Table, names []models.CpeName) *uitable.Table {
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func cweURL(cweID string) string {
|
||||
return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
|
||||
strings.TrimPrefix(cweID, "CWE-"))
|
||||
}
|
||||
|
||||
func cweJvnURL(cweID string) string {
|
||||
return fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
|
||||
}
|
||||
|
||||
@@ -39,5 +39,3 @@ const (
|
||||
type ResultWriter interface {
|
||||
Write([]models.ScanResult) error
|
||||
}
|
||||
|
||||
var resultDirPath string
|
||||
|
||||
54
report/xml.go
Normal file
54
report/xml.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
const (
|
||||
vulsOpenTag = "<vulsreport>"
|
||||
vulsCloseTag = "</vulsreport>"
|
||||
)
|
||||
|
||||
// XMLWriter writes results to file.
|
||||
type XMLWriter struct {
|
||||
ScannedAt time.Time
|
||||
}
|
||||
|
||||
func (w XMLWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
var path string
|
||||
if path, err = ensureResultDir(w.ScannedAt); err != nil {
|
||||
return fmt.Errorf("Failed to make direcotory/symlink : %s", err)
|
||||
}
|
||||
|
||||
for _, scanResult := range scanResults {
|
||||
scanResult.ScannedAt = w.ScannedAt
|
||||
}
|
||||
|
||||
var xmlBytes []byte
|
||||
for _, r := range scanResults {
|
||||
xmlPath := ""
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
xmlPath = filepath.Join(path, fmt.Sprintf("%s.xml", r.ServerName))
|
||||
} else {
|
||||
xmlPath = filepath.Join(path,
|
||||
fmt.Sprintf("%s_%s.xml", r.ServerName, r.Container.Name))
|
||||
}
|
||||
|
||||
if xmlBytes, err = xml.Marshal(r); err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to XML: %s", err)
|
||||
}
|
||||
|
||||
allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), xmlBytes, []byte(vulsCloseTag)}, []byte{})
|
||||
if err := ioutil.WriteFile(xmlPath, allBytes, 0600); err != nil {
|
||||
return fmt.Errorf("Failed to write XML. path: %s, err: %s", xmlPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
75
scan/base.go
75
scan/base.go
@@ -19,8 +19,10 @@ package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
@@ -30,10 +32,10 @@ import (
|
||||
|
||||
type base struct {
|
||||
ServerInfo config.ServerInfo
|
||||
Distro config.Distro
|
||||
Platform models.Platform
|
||||
|
||||
Family string
|
||||
Release string
|
||||
Platform models.Platform
|
||||
lackDependencies []string
|
||||
osPackages
|
||||
|
||||
log *logrus.Entry
|
||||
@@ -52,13 +54,20 @@ func (l base) getServerInfo() config.ServerInfo {
|
||||
return l.ServerInfo
|
||||
}
|
||||
|
||||
func (l *base) setDistributionInfo(fam, rel string) {
|
||||
l.Family = fam
|
||||
l.Release = rel
|
||||
func (l *base) setDistro(fam, rel string) {
|
||||
d := config.Distro{
|
||||
Family: fam,
|
||||
Release: rel,
|
||||
}
|
||||
l.Distro = d
|
||||
|
||||
s := l.getServerInfo()
|
||||
s.Distro = d
|
||||
l.setServerInfo(s)
|
||||
}
|
||||
|
||||
func (l base) getDistributionInfo() string {
|
||||
return fmt.Sprintf("%s %s", l.Family, l.Release)
|
||||
func (l base) getDistro() config.Distro {
|
||||
return l.Distro
|
||||
}
|
||||
|
||||
func (l *base) setPlatform(p models.Platform) {
|
||||
@@ -69,6 +78,10 @@ func (l base) getPlatform() models.Platform {
|
||||
return l.Platform
|
||||
}
|
||||
|
||||
func (l base) getLackDependencies() []string {
|
||||
return l.lackDependencies
|
||||
}
|
||||
|
||||
func (l base) allContainers() (containers []config.Container, err error) {
|
||||
switch l.ServerInfo.Container.Type {
|
||||
case "", "docker":
|
||||
@@ -164,9 +177,7 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
id := strings.TrimSpace(r.Stdout)
|
||||
|
||||
if id == "not found" {
|
||||
// status: 0, stdout: "not found" on degitalocean or Azure
|
||||
if !l.isAwsInstanceID(id) {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, id, nil
|
||||
@@ -186,6 +197,9 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
id := strings.TrimSpace(r.Stdout)
|
||||
if !l.isAwsInstanceID(id) {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, id, nil
|
||||
}
|
||||
|
||||
@@ -202,14 +216,39 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
|
||||
l.ServerInfo.ServerName, l.ServerInfo.Container.Name)
|
||||
}
|
||||
|
||||
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/resource-ids.html
|
||||
var awsInstanceIDPattern = regexp.MustCompile(`^i-[0-9a-f]+$`)
|
||||
|
||||
func (l base) isAwsInstanceID(str string) bool {
|
||||
return awsInstanceIDPattern.MatchString(str)
|
||||
}
|
||||
|
||||
func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
var scoredCves, unscoredCves models.CveInfos
|
||||
var scoredCves, unscoredCves, ignoredCves models.CveInfos
|
||||
for _, p := range l.UnsecurePackages {
|
||||
// ignoreCves
|
||||
found := false
|
||||
for _, icve := range l.getServerInfo().IgnoreCves {
|
||||
if icve == p.CveDetail.CveID {
|
||||
ignoredCves = append(ignoredCves, models.CveInfo{
|
||||
CveDetail: p.CveDetail,
|
||||
Packages: p.Packs,
|
||||
DistroAdvisories: p.DistroAdvisories,
|
||||
})
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
|
||||
// unscoredCves
|
||||
if p.CveDetail.CvssScore(config.Conf.Lang) <= 0 {
|
||||
unscoredCves = append(unscoredCves, models.CveInfo{
|
||||
CveDetail: p.CveDetail,
|
||||
Packages: p.Packs,
|
||||
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
|
||||
DistroAdvisories: p.DistroAdvisories,
|
||||
})
|
||||
continue
|
||||
}
|
||||
@@ -220,10 +259,11 @@ func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
models.CpeName{Name: cpename})
|
||||
}
|
||||
|
||||
// scoredCves
|
||||
cve := models.CveInfo{
|
||||
CveDetail: p.CveDetail,
|
||||
Packages: p.Packs,
|
||||
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
|
||||
DistroAdvisories: p.DistroAdvisories,
|
||||
CpeNames: cpenames,
|
||||
}
|
||||
scoredCves = append(scoredCves, cve)
|
||||
@@ -236,15 +276,18 @@ func (l *base) convertToModel() (models.ScanResult, error) {
|
||||
|
||||
sort.Sort(scoredCves)
|
||||
sort.Sort(unscoredCves)
|
||||
sort.Sort(ignoredCves)
|
||||
|
||||
return models.ScanResult{
|
||||
ServerName: l.ServerInfo.ServerName,
|
||||
Family: l.Family,
|
||||
Release: l.Release,
|
||||
ScannedAt: time.Now(),
|
||||
Family: l.Distro.Family,
|
||||
Release: l.Distro.Release,
|
||||
Container: container,
|
||||
Platform: l.Platform,
|
||||
KnownCves: scoredCves,
|
||||
UnknownCves: unscoredCves,
|
||||
IgnoredCves: ignoredCves,
|
||||
Optional: l.ServerInfo.Optional,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -56,3 +56,25 @@ f570ae647edc agitated_lovelace`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAwsInstanceID(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected bool
|
||||
}{
|
||||
{"i-1234567a", true},
|
||||
{"i-1234567890abcdef0", true},
|
||||
{"i-1234567890abcdef0000000", true},
|
||||
{"e-1234567890abcdef0", false},
|
||||
{"i-1234567890abcdef0 foo bar", false},
|
||||
{"no data", false},
|
||||
}
|
||||
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := r.isAwsInstanceID(tt.in)
|
||||
if tt.expected != actual {
|
||||
t.Errorf("expected %t, actual %t, str: %s", tt.expected, actual, tt.in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
366
scan/debian.go
366
scan/debian.go
@@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/cveapi"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -39,6 +40,7 @@ type debian struct {
|
||||
func newDebian(c config.ServerInfo) *debian {
|
||||
d := &debian{}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -46,7 +48,6 @@ func newDebian(c config.ServerInfo) *debian {
|
||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
|
||||
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
|
||||
deb = newDebian(c)
|
||||
deb.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
|
||||
if r.Error != nil {
|
||||
@@ -65,17 +66,16 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// root@fa3ec524be43:/# lsb_release -ir
|
||||
// Distributor ID: Ubuntu
|
||||
// Release: 14.04
|
||||
re, _ := regexp.Compile(
|
||||
`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
|
||||
re := regexp.MustCompile(`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
|
||||
if len(result) == 0 {
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
deb.setDistributionInfo(distro, trim(result[2]))
|
||||
deb.setDistro(distro, trim(result[2]))
|
||||
}
|
||||
return true, deb, nil
|
||||
}
|
||||
@@ -86,16 +86,15 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// DISTRIB_RELEASE=14.04
|
||||
// DISTRIB_CODENAME=trusty
|
||||
// DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
|
||||
re, _ := regexp.Compile(
|
||||
`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
|
||||
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
if len(result) == 0 {
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
deb.setDistributionInfo(distro, trim(result[2]))
|
||||
deb.setDistro(distro, trim(result[2]))
|
||||
}
|
||||
return true, deb, nil
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// Debian
|
||||
cmd := "cat /etc/debian_version"
|
||||
if r := sshExec(c, cmd, noSudo); r.isSuccess() {
|
||||
deb.setDistributionInfo("debian", trim(r.Stdout))
|
||||
deb.setDistro("debian", trim(r.Stdout))
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
@@ -125,7 +124,30 @@ func (o *debian) checkIfSudoNoPasswd() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *debian) checkDependencies() error {
|
||||
switch o.Distro.Family {
|
||||
case "ubuntu":
|
||||
return nil
|
||||
|
||||
case "debian":
|
||||
// Debian needs aptitude to get changelogs.
|
||||
// Because unable to get changelogs via apt-get changelog on Debian.
|
||||
name := "aptitude"
|
||||
cmd := name + " -h"
|
||||
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
|
||||
o.lackDependencies = []string{name}
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Not implemented yet: %s", o.Distro)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *debian) install() error {
|
||||
if len(o.lackDependencies) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// apt-get update
|
||||
o.log.Infof("apt-get update...")
|
||||
@@ -136,37 +158,15 @@ func (o *debian) install() error {
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
if o.Family == "debian" {
|
||||
// install aptitude
|
||||
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
|
||||
for _, name := range o.lackDependencies {
|
||||
cmd = util.PrependProxyEnv("apt-get install " + name)
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
o.log.Infof("Installed: aptitude")
|
||||
o.log.Infof("Installed: " + name)
|
||||
}
|
||||
|
||||
// install unattended-upgrades
|
||||
if !config.Conf.UseUnattendedUpgrades {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r := o.ssh("type unattended-upgrade", noSudo); r.isSuccess() {
|
||||
o.log.Infof(
|
||||
"Ignored: unattended-upgrade already installed")
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd = util.PrependProxyEnv(
|
||||
"apt-get install --force-yes -y unattended-upgrades")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
o.log.Infof("Installed: unattended-upgrades")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -214,12 +214,16 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
|
||||
return
|
||||
}
|
||||
|
||||
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
|
||||
|
||||
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
|
||||
re, _ := regexp.Compile(`^([^\t']+)\t(.+)$`)
|
||||
result := re.FindStringSubmatch(line)
|
||||
result := packageLinePattern.FindStringSubmatch(line)
|
||||
if len(result) == 3 {
|
||||
// remove :amd64, i386...
|
||||
name = regexp.MustCompile(":.+").ReplaceAllString(result[1], "")
|
||||
name = result[1]
|
||||
if i := strings.IndexRune(name, ':'); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
version = result[2]
|
||||
return
|
||||
}
|
||||
@@ -227,49 +231,27 @@ func (o *debian) parseScannedPackagesLine(line string) (name, version string, er
|
||||
return "", "", fmt.Errorf("Unknown format: %s", line)
|
||||
}
|
||||
|
||||
// unattended-upgrade command need to check security upgrades).
|
||||
func (o *debian) checkRequiredPackagesInstalled() error {
|
||||
|
||||
if o.Family == "debian" {
|
||||
if o.Distro.Family == "debian" {
|
||||
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("aptitude is not installed: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
if !config.Conf.UseUnattendedUpgrades {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r := o.ssh("type unattended-upgrade", noSudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("unattended-upgrade is not installed: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO return whether already expired.
|
||||
func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInfo, error) {
|
||||
// cmd := prependProxyEnv(conf.HTTPProxy, "apt-get update | cat; echo 1")
|
||||
o.log.Infof("apt-get update...")
|
||||
cmd := util.PrependProxyEnv("apt-get update")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
var upgradablePackNames []string
|
||||
var err error
|
||||
if config.Conf.UseUnattendedUpgrades {
|
||||
upgradablePackNames, err = o.GetUnsecurePackNamesUsingUnattendedUpgrades()
|
||||
if err != nil {
|
||||
return []CvePacksInfo{}, err
|
||||
}
|
||||
} else {
|
||||
upgradablePackNames, err = o.GetUpgradablePackNames()
|
||||
if err != nil {
|
||||
return []CvePacksInfo{}, err
|
||||
}
|
||||
upgradablePackNames, err := o.GetUpgradablePackNames()
|
||||
if err != nil {
|
||||
return []CvePacksInfo{}, err
|
||||
}
|
||||
|
||||
// Convert package name to PackageInfo struct
|
||||
@@ -282,12 +264,21 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsecurePacks, err = o.fillCandidateVersion(unsecurePacks)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
|
||||
}
|
||||
|
||||
current := cache.Meta{
|
||||
Name: o.getServerInfo().GetServerName(),
|
||||
Distro: o.getServerInfo().Distro,
|
||||
Packs: unsecurePacks,
|
||||
}
|
||||
o.log.Debugf("Ensure changelog cache: %s", current.Name)
|
||||
if err := o.ensureChangelogCache(current); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collect CVE information of upgradable packages
|
||||
cvePacksInfos, err := o.scanPackageCveInfos(unsecurePacks)
|
||||
if err != nil {
|
||||
@@ -297,103 +288,65 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
|
||||
return cvePacksInfos, nil
|
||||
}
|
||||
|
||||
func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.PackageInfo, error) {
|
||||
reqChan := make(chan models.PackageInfo, len(packs))
|
||||
resChan := make(chan models.PackageInfo, len(packs))
|
||||
errChan := make(chan error, len(packs))
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
defer close(reqChan)
|
||||
|
||||
go func() {
|
||||
for _, pack := range packs {
|
||||
reqChan <- pack
|
||||
func (o *debian) ensureChangelogCache(current cache.Meta) error {
|
||||
// Search from cache
|
||||
old, found, err := cache.DB.GetMeta(current.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get meta. err: %s", err)
|
||||
}
|
||||
if !found {
|
||||
o.log.Debugf("Not found in meta: %s", current.Name)
|
||||
err = cache.DB.EnsureBuckets(current)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ensure buckets. err: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
timeout := time.After(5 * 60 * time.Second)
|
||||
concurrency := 5
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range packs {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case pack := <-reqChan:
|
||||
func(p models.PackageInfo) {
|
||||
cmd := fmt.Sprintf("LANG=en_US.UTF-8 apt-cache policy %s", p.Name)
|
||||
r := o.ssh(cmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
errChan <- fmt.Errorf("Failed to SSH: %s.", r)
|
||||
return
|
||||
}
|
||||
ver, err := o.parseAptCachePolicy(r.Stdout, p.Name)
|
||||
if err != nil {
|
||||
errChan <- fmt.Errorf("Failed to parse %s", err)
|
||||
}
|
||||
p.NewVersion = ver.Candidate
|
||||
resChan <- p
|
||||
}(pack)
|
||||
} else {
|
||||
if current.Distro.Family != old.Distro.Family ||
|
||||
current.Distro.Release != old.Distro.Release {
|
||||
o.log.Debugf("Need to refesh meta: %s", current.Name)
|
||||
err = cache.DB.EnsureBuckets(current)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to ensure buckets. err: %s", err)
|
||||
}
|
||||
} else {
|
||||
o.log.Debugf("Reuse meta: %s", current.Name)
|
||||
}
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
result := []models.PackageInfo{}
|
||||
for i := 0; i < len(packs); i++ {
|
||||
select {
|
||||
case pack := <-resChan:
|
||||
result = append(result, pack)
|
||||
o.log.Infof("(%d/%d) Upgradable: %s-%s -> %s",
|
||||
i+1, len(packs), pack.Name, pack.Version, pack.NewVersion)
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, fmt.Errorf("Timeout fillCandidateVersion")
|
||||
}
|
||||
if config.Conf.Debug {
|
||||
cache.DB.PrettyPrint(current)
|
||||
}
|
||||
if 0 < len(errs) {
|
||||
return nil, fmt.Errorf("%v", errs)
|
||||
}
|
||||
return result, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *debian) GetUnsecurePackNamesUsingUnattendedUpgrades() (packNames []string, err error) {
|
||||
cmd := util.PrependProxyEnv("unattended-upgrades --dry-run -d 2>&1 ")
|
||||
release, err := strconv.ParseFloat(o.Release, 64)
|
||||
if err != nil {
|
||||
return packNames, fmt.Errorf(
|
||||
"OS Release Version is invalid, %s, %s", o.Family, o.Release)
|
||||
func (o *debian) fillCandidateVersion(before models.PackageInfoList) (filled []models.PackageInfo, err error) {
|
||||
names := []string{}
|
||||
for _, p := range before {
|
||||
names = append(names, p.Name)
|
||||
}
|
||||
switch {
|
||||
case release < 12:
|
||||
return packNames, fmt.Errorf(
|
||||
"Support expired. %s, %s", o.Family, o.Release)
|
||||
|
||||
case 12 < release && release < 14:
|
||||
cmd += `| grep 'pkgs that look like they should be upgraded:' |
|
||||
sed -e 's/pkgs that look like they should be upgraded://g'`
|
||||
|
||||
case 14 < release:
|
||||
cmd += `| grep 'Packages that will be upgraded:' |
|
||||
sed -e 's/Packages that will be upgraded://g'`
|
||||
|
||||
default:
|
||||
return packNames, fmt.Errorf(
|
||||
"Not supported yet. %s, %s", o.Family, o.Release)
|
||||
}
|
||||
|
||||
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
|
||||
r := o.ssh(cmd, sudo)
|
||||
if r.isSuccess(0, 1) {
|
||||
packNames = strings.Split(strings.TrimSpace(r.Stdout), " ")
|
||||
return packNames, nil
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s.", r)
|
||||
}
|
||||
|
||||
return packNames, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
packChangelog := o.splitAptCachePolicy(r.Stdout)
|
||||
for k, v := range packChangelog {
|
||||
ver, err := o.parseAptCachePolicy(v, k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse %s", err)
|
||||
}
|
||||
p, found := before.FindByName(k)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Not found: %s", k)
|
||||
}
|
||||
p.NewVersion = ver.Candidate
|
||||
filled = append(filled, p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
|
||||
cmd := util.PrependProxyEnv("LANG=en_US.UTF-8 apt-get upgrade --dry-run")
|
||||
cmd := util.PrependProxyEnv("LANGUAGE=en_US.UTF-8 apt-get upgrade --dry-run")
|
||||
r := o.ssh(cmd, sudo)
|
||||
if r.isSuccess(0, 1) {
|
||||
return o.parseAptGetUpgrade(r.Stdout)
|
||||
@@ -404,8 +357,8 @@ func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
|
||||
}
|
||||
|
||||
func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, err error) {
|
||||
startRe, _ := regexp.Compile(`The following packages will be upgraded:`)
|
||||
stopRe, _ := regexp.Compile(`^(\d+) upgraded.*`)
|
||||
startRe := regexp.MustCompile(`The following packages will be upgraded:`)
|
||||
stopRe := regexp.MustCompile(`^(\d+) upgraded.*`)
|
||||
startLineFound, stopLineFound := false, false
|
||||
|
||||
lines := strings.Split(stdout, "\n")
|
||||
@@ -446,9 +399,11 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
|
||||
}
|
||||
|
||||
func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePacksList CvePacksList, err error) {
|
||||
|
||||
// { CVE ID: [packageInfo] }
|
||||
cvePackages := make(map[string][]models.PackageInfo)
|
||||
meta := cache.Meta{
|
||||
Name: o.getServerInfo().GetServerName(),
|
||||
Distro: o.getServerInfo().Distro,
|
||||
Packs: unsecurePacks,
|
||||
}
|
||||
|
||||
type strarray []string
|
||||
resChan := make(chan struct {
|
||||
@@ -475,6 +430,18 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
select {
|
||||
case pack := <-reqChan:
|
||||
func(p models.PackageInfo) {
|
||||
changelog := o.getChangelogCache(meta, p)
|
||||
if 0 < len(changelog) {
|
||||
cveIDs := o.getCveIDFromChangelog(changelog, p.Name, p.Version)
|
||||
resChan <- struct {
|
||||
models.PackageInfo
|
||||
strarray
|
||||
}{p, cveIDs}
|
||||
return
|
||||
}
|
||||
|
||||
// if the changelog is not in cache or failed to get from local cache,
|
||||
// get the changelog of the package via internet.
|
||||
if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
@@ -488,6 +455,8 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
}
|
||||
}
|
||||
|
||||
// { CVE ID: [packageInfo] }
|
||||
cvePackages := make(map[string][]models.PackageInfo)
|
||||
errs := []error{}
|
||||
for i := 0; i < len(unsecurePacks); i++ {
|
||||
select {
|
||||
@@ -506,7 +475,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
return nil, fmt.Errorf("Timeout scanPackageCveIDs")
|
||||
}
|
||||
}
|
||||
|
||||
if 0 < len(errs) {
|
||||
return nil, fmt.Errorf("%v", errs)
|
||||
}
|
||||
@@ -515,7 +483,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
for k := range cvePackages {
|
||||
cveIDs = append(cveIDs, k)
|
||||
}
|
||||
|
||||
o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
|
||||
|
||||
o.log.Info("Fetching CVE details...")
|
||||
@@ -536,9 +503,32 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) getChangelogCache(meta cache.Meta, pack models.PackageInfo) string {
|
||||
cachedPack, found := meta.FindPack(pack.Name)
|
||||
if !found {
|
||||
return ""
|
||||
}
|
||||
if cachedPack.NewVersion != pack.NewVersion {
|
||||
return ""
|
||||
}
|
||||
changelog, err := cache.DB.GetChangelog(meta.Name, pack.Name)
|
||||
if err != nil {
|
||||
o.log.Warnf("Failed to get changelog. bucket: %s, key:%s, err: %s",
|
||||
meta.Name, pack.Name, err)
|
||||
return ""
|
||||
}
|
||||
if len(changelog) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
o.log.Debugf("Cache hit: %s, len: %d, %s...",
|
||||
meta.Name, len(changelog), util.Truncate(changelog, 30))
|
||||
return changelog
|
||||
}
|
||||
|
||||
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
|
||||
cmd := ""
|
||||
switch o.Family {
|
||||
switch o.Distro.Family {
|
||||
case "ubuntu":
|
||||
cmd = fmt.Sprintf(`apt-get changelog %s | grep '\(urgency\|CVE\)'`, pack.Name)
|
||||
case "debian":
|
||||
@@ -551,37 +541,42 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
|
||||
o.log.Warnf("Failed to SSH: %s", r)
|
||||
// Ignore this Error.
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, pack.Version)
|
||||
|
||||
if 0 < len(strings.TrimSpace(r.Stdout)) {
|
||||
err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, r.Stdout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to put changelog into cache")
|
||||
}
|
||||
}
|
||||
// No error will be returned. Only logging.
|
||||
return o.getCveIDFromChangelog(r.Stdout, pack.Name, pack.Version), nil
|
||||
}
|
||||
|
||||
func (o *debian) getCveIDParsingChangelog(changelog string,
|
||||
packName string, versionOrLater string) (cveIDs []string, err error) {
|
||||
func (o *debian) getCveIDFromChangelog(changelog string,
|
||||
packName string, versionOrLater string) []string {
|
||||
|
||||
cveIDs, err = o.parseChangelog(changelog, packName, versionOrLater)
|
||||
if err == nil {
|
||||
return
|
||||
if cveIDs, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil {
|
||||
return cveIDs
|
||||
}
|
||||
|
||||
ver := strings.Split(versionOrLater, "ubuntu")[0]
|
||||
cveIDs, err = o.parseChangelog(changelog, packName, ver)
|
||||
if err == nil {
|
||||
return
|
||||
if cveIDs, err := o.parseChangelog(changelog, packName, ver); err == nil {
|
||||
return cveIDs
|
||||
}
|
||||
|
||||
splittedByColon := strings.Split(versionOrLater, ":")
|
||||
if 1 < len(splittedByColon) {
|
||||
ver = splittedByColon[1]
|
||||
}
|
||||
cveIDs, err = o.parseChangelog(changelog, packName, ver)
|
||||
cveIDs, err := o.parseChangelog(changelog, packName, ver)
|
||||
if err == nil {
|
||||
return
|
||||
return cveIDs
|
||||
}
|
||||
|
||||
//TODO report as unable to parse changelog.
|
||||
// Only logging the error.
|
||||
o.log.Error(err)
|
||||
return []string{}, nil
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Collect CVE-IDs included in the changelog.
|
||||
@@ -589,8 +584,8 @@ func (o *debian) getCveIDParsingChangelog(changelog string,
|
||||
func (o *debian) parseChangelog(changelog string,
|
||||
packName string, versionOrLater string) (cveIDs []string, err error) {
|
||||
|
||||
cveRe, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
|
||||
stopRe, _ := regexp.Compile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
|
||||
cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
|
||||
stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
|
||||
stopLineFound := false
|
||||
lines := strings.Split(changelog, "\n")
|
||||
for _, line := range lines {
|
||||
@@ -598,7 +593,7 @@ func (o *debian) parseChangelog(changelog string,
|
||||
o.log.Debugf("Found the stop line. line: %s", line)
|
||||
stopLineFound = true
|
||||
break
|
||||
} else if matches := cveRe.FindAllString(line, -1); len(matches) > 0 {
|
||||
} else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
|
||||
for _, m := range matches {
|
||||
cveIDs = util.AppendIfMissing(cveIDs, m)
|
||||
}
|
||||
@@ -614,6 +609,29 @@ func (o *debian) parseChangelog(changelog string,
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
|
||||
// re := regexp.MustCompile(`(?m:^[^ \t]+:$)`)
|
||||
re := regexp.MustCompile(`(?m:^[^ \t]+:\r\n)`)
|
||||
ii := re.FindAllStringIndex(stdout, -1)
|
||||
ri := []int{}
|
||||
for i := len(ii) - 1; 0 <= i; i-- {
|
||||
ri = append(ri, ii[i][0])
|
||||
}
|
||||
splitted := []string{}
|
||||
lasti := len(stdout)
|
||||
for _, i := range ri {
|
||||
splitted = append(splitted, stdout[i:lasti])
|
||||
lasti = i
|
||||
}
|
||||
|
||||
packChangelog := map[string]string{}
|
||||
for _, r := range splitted {
|
||||
packName := r[:strings.Index(r, ":")]
|
||||
packChangelog[packName] = r
|
||||
}
|
||||
return packChangelog
|
||||
}
|
||||
|
||||
type packCandidateVer struct {
|
||||
Name string
|
||||
Installed string
|
||||
|
||||
@@ -18,10 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package scan
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
@@ -54,7 +58,7 @@ func TestParseScannedPackagesLineDebian(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestgetCveIDParsingChangelog(t *testing.T) {
|
||||
func TestGetCveIDParsingChangelog(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
in []string
|
||||
@@ -86,12 +90,11 @@ systemd (227-1) unstable; urgency=medium`,
|
||||
"CVE-2015-3210",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// ver
|
||||
[]string{
|
||||
"libpcre3",
|
||||
"2:8.38-1ubuntu1",
|
||||
"2:8.35-7.1ubuntu1",
|
||||
`pcre3 (2:8.38-2) unstable; urgency=low
|
||||
pcre3 (2:8.38-1) unstable; urgency=low
|
||||
pcre3 (2:8.35-8) unstable; urgency=low
|
||||
@@ -110,7 +113,6 @@ pcre3 (2:8.35-7) unstable; urgency=medium`,
|
||||
"CVE-2015-3210",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// ver-ubuntu3
|
||||
[]string{
|
||||
@@ -151,7 +153,7 @@ sysvinit (2.88dsf-57) unstable; urgency=low`,
|
||||
util-linux (2.27.1-3) unstable; urgency=medium
|
||||
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
|
||||
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
|
||||
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
|
||||
CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
|
||||
util-linux (2.27.1-2) unstable; urgency=medium
|
||||
util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
|
||||
util-linux (2.27.1-1ubuntu3) xenial; urgency=medium
|
||||
@@ -178,15 +180,17 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
|
||||
"CVE-2015-2325",
|
||||
"CVE-2015-2326",
|
||||
"CVE-2015-3210",
|
||||
"CVE-2016-1000000",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual, _ := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], tt.in[1])
|
||||
actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1])
|
||||
if len(actual) != len(tt.expected) {
|
||||
t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual)
|
||||
t.Errorf(pp.Sprintf("%s", tt.in))
|
||||
continue
|
||||
}
|
||||
for i := range tt.expected {
|
||||
@@ -195,13 +199,6 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
_, err := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], "version number do'nt match case")
|
||||
if err != nil {
|
||||
t.Errorf("Returning error is unexpected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUpdatablePackNames(t *testing.T) {
|
||||
@@ -520,6 +517,95 @@ Calculating upgrade... Done
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChangelogCache(t *testing.T) {
|
||||
const servername = "server1"
|
||||
pack := models.PackageInfo{
|
||||
Name: "apt",
|
||||
Version: "1.0.0",
|
||||
NewVersion: "1.0.1",
|
||||
}
|
||||
var meta = cache.Meta{
|
||||
Name: servername,
|
||||
Distro: config.Distro{
|
||||
Family: "ubuntu",
|
||||
Release: "16.04",
|
||||
},
|
||||
Packs: []models.PackageInfo{pack},
|
||||
}
|
||||
|
||||
const path = "/tmp/vuls-test-cache-11111111.db"
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := cache.SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := cache.DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
actual := d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("Failed to get empty stirng from cache:")
|
||||
}
|
||||
|
||||
clog := "changelog-text"
|
||||
if err := cache.DB.PutChangelog(servername, "apt", clog); err != nil {
|
||||
t.Errorf("Failed to put changelog: %s", err)
|
||||
}
|
||||
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != clog {
|
||||
t.Errorf("Failed to get changelog from cache: %s", actual)
|
||||
}
|
||||
|
||||
// increment a version of the pack
|
||||
pack.NewVersion = "1.0.2"
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("The changelog is not invalidated: %s", actual)
|
||||
}
|
||||
|
||||
// change a name of the pack
|
||||
pack.Name = "bash"
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("The changelog is not invalidated: %s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitAptCachePolicy(t *testing.T) {
|
||||
var tests = []struct {
|
||||
stdout string
|
||||
expected map[string]string
|
||||
}{
|
||||
// This function parse apt-cache policy by using Regexp multi-line mode.
|
||||
// So, test data includes "\r\n"
|
||||
{
|
||||
"apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\napt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\nbase-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
map[string]string{
|
||||
"apt": "apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
"apt-utils": "apt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
"base-files": "base-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := d.splitAptCachePolicy(tt.stdout)
|
||||
if !reflect.DeepEqual(tt.expected, actual) {
|
||||
e := pp.Sprintf("%v", tt.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAptCachePolicy(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
@@ -19,18 +36,22 @@ type bsd struct {
|
||||
func newBsd(c config.ServerInfo) *bsd {
|
||||
d := &bsd{}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
|
||||
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
|
||||
bsd = newBsd(c)
|
||||
c.Family = "FreeBSD"
|
||||
|
||||
// Prevent from adding `set -o pipefail` option
|
||||
c.Distro = config.Distro{Family: "FreeBSD"}
|
||||
|
||||
if r := sshExec(c, "uname", noSudo); r.isSuccess() {
|
||||
if strings.Contains(r.Stdout, "FreeBSD") == true {
|
||||
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
|
||||
bsd.setDistributionInfo("FreeBSD", strings.TrimSpace(b.Stdout))
|
||||
bsd.setServerInfo(c)
|
||||
rel := strings.TrimSpace(b.Stdout)
|
||||
bsd.setDistro("FreeBSD", rel)
|
||||
return true, bsd
|
||||
}
|
||||
}
|
||||
@@ -45,6 +66,10 @@ func (o *bsd) checkIfSudoNoPasswd() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) checkDependencies() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) install() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -139,17 +139,17 @@ WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
|
||||
|
||||
d := newBsd(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
aName, aCveIDs, aVunlnID := d.parseBlock(tt.in)
|
||||
aName, aCveIDs, aVulnID := d.parseBlock(tt.in)
|
||||
if tt.name != aName {
|
||||
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVunlnID)
|
||||
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVulnID)
|
||||
}
|
||||
for i := range tt.cveIDs {
|
||||
if tt.cveIDs[i] != aCveIDs[i] {
|
||||
t.Errorf("expected cveID: %s, actual %s", tt.cveIDs[i], aCveIDs[i])
|
||||
}
|
||||
}
|
||||
if tt.vulnID != aVunlnID {
|
||||
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVunlnID)
|
||||
if tt.vulnID != aVulnID {
|
||||
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVulnID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
254
scan/redhat.go
254
scan/redhat.go
@@ -42,16 +42,16 @@ type redhat struct {
|
||||
func newRedhat(c config.ServerInfo) *redhat {
|
||||
r := &redhat{}
|
||||
r.log = util.NewCustomLogger(c)
|
||||
r.setServerInfo(c)
|
||||
return r
|
||||
}
|
||||
|
||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb
|
||||
func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
red = newRedhat(c)
|
||||
red.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
|
||||
red.setDistributionInfo("fedora", "unknown")
|
||||
red.setDistro("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet: %s", r)
|
||||
return true, red
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
// $ cat /etc/redhat-release
|
||||
// CentOS release 6.5 (Final)
|
||||
if r := sshExec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
|
||||
re, _ := regexp.Compile(`(.*) release (\d[\d.]*)`)
|
||||
re := regexp.MustCompile(`(.*) release (\d[\d.]*)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
@@ -72,9 +72,9 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
release := result[2]
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "centos", "centos linux":
|
||||
red.setDistributionInfo("centos", release)
|
||||
red.setDistro("centos", release)
|
||||
default:
|
||||
red.setDistributionInfo("rhel", release)
|
||||
red.setDistro("rhel", release)
|
||||
}
|
||||
return true, red
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
release = fields[4]
|
||||
}
|
||||
}
|
||||
red.setDistributionInfo(family, release)
|
||||
red.setDistro(family, release)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
}
|
||||
|
||||
func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
r := o.ssh("yum --version", sudo)
|
||||
r := o.ssh("yum --version", o.sudo())
|
||||
if !r.isSuccess() {
|
||||
o.log.Errorf("sudo error on %s", r)
|
||||
return fmt.Errorf("Failed to sudo: %s", r)
|
||||
@@ -108,104 +108,66 @@ func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CentOS 5 ... yum-plugin-security, yum-changelog
|
||||
// CentOS 6 ... yum-plugin-security, yum-plugin-changelog
|
||||
// CentOS 7 ... yum-plugin-security, yum-plugin-changelog
|
||||
// CentOS 5 ... yum-changelog
|
||||
// CentOS 6 ... yum-plugin-changelog
|
||||
// CentOS 7 ... yum-plugin-changelog
|
||||
// RHEL, Amazon ... no additinal packages needed
|
||||
func (o *redhat) install() error {
|
||||
|
||||
switch o.Family {
|
||||
func (o *redhat) checkDependencies() error {
|
||||
switch o.Distro.Family {
|
||||
case "rhel", "amazon":
|
||||
o.log.Infof("Nothing to do")
|
||||
// o.log.Infof("Nothing to do")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := o.installYumPluginSecurity(); err != nil {
|
||||
return err
|
||||
}
|
||||
return o.installYumChangelog()
|
||||
}
|
||||
|
||||
func (o *redhat) installYumPluginSecurity() error {
|
||||
|
||||
if r := o.ssh("rpm -q yum-plugin-security", noSudo); r.isSuccess() {
|
||||
o.log.Infof("Ignored: yum-plugin-security already installed")
|
||||
return nil
|
||||
}
|
||||
|
||||
o.log.Info("Installing yum-plugin-security...")
|
||||
cmd := util.PrependProxyEnv("yum install -y yum-plugin-security")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhat) installYumChangelog() error {
|
||||
|
||||
if o.Family == "centos" {
|
||||
case "centos":
|
||||
var majorVersion int
|
||||
if 0 < len(o.Release) {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
|
||||
if 0 < len(o.Distro.Release) {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
|
||||
} else {
|
||||
return fmt.Errorf(
|
||||
"Not implemented yet. family: %s, release: %s",
|
||||
o.Family, o.Release)
|
||||
return fmt.Errorf("Not implemented yet: %s", o.Distro)
|
||||
}
|
||||
|
||||
var packName = ""
|
||||
var name = "yum-plugin-changelog"
|
||||
if majorVersion < 6 {
|
||||
packName = "yum-changelog"
|
||||
} else {
|
||||
packName = "yum-plugin-changelog"
|
||||
name = "yum-changelog"
|
||||
}
|
||||
|
||||
cmd := "rpm -q " + packName
|
||||
cmd := "rpm -q " + name
|
||||
if r := o.ssh(cmd, noSudo); r.isSuccess() {
|
||||
o.log.Infof("Ignored: %s already installed", packName)
|
||||
return nil
|
||||
}
|
||||
o.lackDependencies = []string{name}
|
||||
return nil
|
||||
|
||||
o.log.Infof("Installing %s...", packName)
|
||||
cmd = util.PrependProxyEnv("yum install -y " + packName)
|
||||
default:
|
||||
return fmt.Errorf("Not implemented yet: %s", o.Distro)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *redhat) install() error {
|
||||
for _, name := range o.lackDependencies {
|
||||
cmd := util.PrependProxyEnv("yum install -y " + name)
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
o.log.Infof("Installed: %s", packName)
|
||||
o.log.Infof("Installed: %s", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhat) checkRequiredPackagesInstalled() error {
|
||||
if config.Conf.UseYumPluginSecurity {
|
||||
// check if yum-plugin-security is installed.
|
||||
// Amazon Linux, REHL can execute 'yum updateinfo --security updates' without yum-plugin-security
|
||||
if o.Family == "centos" {
|
||||
cmd := "rpm -q yum-plugin-security"
|
||||
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
|
||||
msg := "yum-plugin-security is not installed"
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if o.Family == "centos" {
|
||||
if o.Distro.Family == "centos" {
|
||||
var majorVersion int
|
||||
if 0 < len(o.Release) {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
|
||||
if 0 < len(o.Distro.Release) {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
|
||||
} else {
|
||||
msg := fmt.Sprintf("Not implemented yet. family: %s, release: %s", o.Family, o.Release)
|
||||
msg := fmt.Sprintf("Not implemented yet: %s", o.Distro)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
var packName = ""
|
||||
var packName = "yum-plugin-changelog"
|
||||
if majorVersion < 6 {
|
||||
packName = "yum-changelog"
|
||||
} else {
|
||||
packName = "yum-plugin-changelog"
|
||||
}
|
||||
|
||||
cmd := "rpm -q " + packName
|
||||
@@ -274,7 +236,7 @@ func (o *redhat) parseScannedPackagesLine(line string) (models.PackageInfo, erro
|
||||
}
|
||||
|
||||
func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
|
||||
if o.Family != "centos" || config.Conf.UseYumPluginSecurity {
|
||||
if o.Distro.Family != "centos" {
|
||||
// Amazon, RHEL has yum updateinfo as default
|
||||
// yum updateinfo can collenct vendor advisory information.
|
||||
return o.scanUnsecurePackagesUsingYumPluginSecurity()
|
||||
@@ -284,10 +246,15 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
|
||||
return o.scanUnsecurePackagesUsingYumCheckUpdate()
|
||||
}
|
||||
|
||||
//TODO return whether already expired.
|
||||
// For CentOS
|
||||
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
|
||||
cmd := "LANGUAGE=en_US.UTF-8 yum --color=never %s check-update"
|
||||
if o.getServerInfo().Enablerepo != "" {
|
||||
cmd = fmt.Sprintf(cmd, "--enablerepo="+o.getServerInfo().Enablerepo)
|
||||
} else {
|
||||
cmd = fmt.Sprintf(cmd, "")
|
||||
}
|
||||
|
||||
cmd := "LANG=en_US.UTF-8 yum --color=never check-update"
|
||||
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
if !r.isSuccess(0, 100) {
|
||||
//returns an exit code of 100 if there are available updates.
|
||||
@@ -416,7 +383,9 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
|
||||
continue
|
||||
}
|
||||
if needToParse {
|
||||
if strings.HasPrefix(line, "Obsoleting") {
|
||||
if strings.HasPrefix(line, "Obsoleting") ||
|
||||
strings.HasPrefix(line, "Security:") {
|
||||
// see https://github.com/future-architect/vuls/issues/165
|
||||
continue
|
||||
}
|
||||
candidate, err := o.parseYumCheckUpdateLine(line)
|
||||
@@ -433,6 +402,7 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
|
||||
}
|
||||
installed.NewVersion = candidate.NewVersion
|
||||
installed.NewRelease = candidate.NewRelease
|
||||
installed.Repository = candidate.Repository
|
||||
results = append(results, installed)
|
||||
}
|
||||
}
|
||||
@@ -441,7 +411,7 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
|
||||
|
||||
func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 3 {
|
||||
if len(fields) < 3 {
|
||||
return models.PackageInfo{}, fmt.Errorf("Unknown format: %s", line)
|
||||
}
|
||||
splitted := strings.Split(fields[0], ".")
|
||||
@@ -452,16 +422,19 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
|
||||
packName = strings.Join(strings.Split(fields[0], ".")[0:(len(splitted)-1)], ".")
|
||||
}
|
||||
|
||||
fields = strings.Split(fields[1], "-")
|
||||
if len(fields) != 2 {
|
||||
verfields := strings.Split(fields[1], "-")
|
||||
if len(verfields) != 2 {
|
||||
return models.PackageInfo{}, fmt.Errorf("Unknown format: %s", line)
|
||||
}
|
||||
version := o.regexpReplace(fields[0], `^[0-9]+:`, "")
|
||||
release := fields[1]
|
||||
version := o.regexpReplace(verfields[0], `^[0-9]+:`, "")
|
||||
release := verfields[1]
|
||||
repos := strings.Join(fields[2:len(fields)], " ")
|
||||
|
||||
return models.PackageInfo{
|
||||
Name: packName,
|
||||
NewVersion: version,
|
||||
NewRelease: release,
|
||||
Repository: repos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -471,18 +444,19 @@ func (o *redhat) mkPstring() *string {
|
||||
}
|
||||
|
||||
func (o *redhat) regexpReplace(src string, pat string, rep string) string {
|
||||
r := regexp.MustCompile(pat)
|
||||
return r.ReplaceAllString(src, rep)
|
||||
re := regexp.MustCompile(pat)
|
||||
return re.ReplaceAllString(src, rep)
|
||||
}
|
||||
|
||||
var changeLogCVEPattern = regexp.MustCompile(`CVE-[0-9]+-[0-9]+`)
|
||||
|
||||
func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo models.PackageInfo) string {
|
||||
rpm := fmt.Sprintf("%s-%s-%s", packInfo.Name, packInfo.NewVersion, packInfo.NewRelease)
|
||||
retLine := ""
|
||||
if rpm2changelog[rpm] != nil {
|
||||
lines := strings.Split(*rpm2changelog[rpm], "\n")
|
||||
for _, line := range lines {
|
||||
match, _ := regexp.MatchString("CVE-[0-9]+-[0-9]+", line)
|
||||
if match {
|
||||
if changeLogCVEPattern.MatchString(line) {
|
||||
retLine += fmt.Sprintf("%s\n", line)
|
||||
}
|
||||
}
|
||||
@@ -492,12 +466,10 @@ func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo
|
||||
|
||||
func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, error) {
|
||||
var majorVersion int
|
||||
if 0 < len(o.Release) && o.Family == "centos" {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
|
||||
if 0 < len(o.Distro.Release) && o.Distro.Family == "centos" {
|
||||
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
|
||||
} else {
|
||||
return nil, fmt.Errorf(
|
||||
"Not implemented yet. family: %s, release: %s",
|
||||
o.Family, o.Release)
|
||||
return nil, fmt.Errorf("Not implemented yet: %s", o.getDistro())
|
||||
}
|
||||
|
||||
orglines := strings.Split(allChangelog, "\n")
|
||||
@@ -510,7 +482,7 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
|
||||
/* for CentOS5 (yum-util < 1.1.20) */
|
||||
prev = false
|
||||
now = false
|
||||
if i > 0 {
|
||||
if 0 < i {
|
||||
prev, err = o.isRpmPackageNameLine(orglines[i-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -560,8 +532,7 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
|
||||
rpm2changelog[rpm] = pNewString
|
||||
}
|
||||
} else {
|
||||
stop, _ := regexp.MatchString("^Dependencies Resolved", line)
|
||||
if stop {
|
||||
if strings.HasPrefix(line, "Dependencies Resolved") {
|
||||
return rpm2changelog, nil
|
||||
}
|
||||
*writePointer += fmt.Sprintf("%s\n", line)
|
||||
@@ -570,22 +541,27 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
|
||||
return rpm2changelog, nil
|
||||
}
|
||||
|
||||
// CentOS
|
||||
func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout string, err error) {
|
||||
packageNames := ""
|
||||
for _, packInfo := range packInfoList {
|
||||
packageNames += fmt.Sprintf("%s ", packInfo.Name)
|
||||
}
|
||||
|
||||
command := ""
|
||||
if o.ServerInfo.User == "root" {
|
||||
command = "echo N | "
|
||||
}
|
||||
command := "echo N | "
|
||||
if 0 < len(config.Conf.HTTPProxy) {
|
||||
command += util.ProxyEnv()
|
||||
}
|
||||
|
||||
yumopts := ""
|
||||
if o.getServerInfo().Enablerepo != "" {
|
||||
yumopts = " --enablerepo=" + o.getServerInfo().Enablerepo
|
||||
}
|
||||
if config.Conf.SkipBroken {
|
||||
yumopts += " --skip-broken"
|
||||
}
|
||||
// yum update --changelog doesn't have --color option.
|
||||
command += fmt.Sprintf(" LANG=en_US.UTF-8 yum update --changelog %s", packageNames)
|
||||
command += fmt.Sprintf(" LANGUAGE=en_US.UTF-8 yum %s --changelog update ", yumopts) + packageNames
|
||||
|
||||
r := o.ssh(command, sudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
@@ -602,9 +578,9 @@ type distroAdvisoryCveIDs struct {
|
||||
}
|
||||
|
||||
// Scaning unsecure packages using yum-plugin-security.
|
||||
//TODO return whether already expired.
|
||||
// Amazon, RHEL
|
||||
func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, error) {
|
||||
if o.Family == "centos" {
|
||||
if o.Distro.Family == "centos" {
|
||||
// CentOS has no security channel.
|
||||
// So use yum check-update && parse changelog
|
||||
return CvePacksList{}, fmt.Errorf(
|
||||
@@ -612,14 +588,14 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
}
|
||||
|
||||
cmd := "yum --color=never repolist"
|
||||
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
r := o.ssh(util.PrependProxyEnv(cmd), o.sudo())
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// get advisoryID(RHSA, ALAS) - package name,version
|
||||
cmd = "yum --color=never updateinfo list available --security"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
@@ -627,8 +603,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
|
||||
// get package name, version, rel to be upgrade.
|
||||
// cmd = "yum check-update --security"
|
||||
cmd = "LANG=en_US.UTF-8 yum --color=never check-update"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
cmd = "LANGUAGE=en_US.UTF-8 yum --color=never check-update"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
|
||||
if !r.isSuccess(0, 100) {
|
||||
//returns an exit code of 100 if there are available updates.
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
@@ -656,7 +632,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
|
||||
// get advisoryID(RHSA, ALAS) - CVE IDs
|
||||
cmd = "yum --color=never updateinfo --security update"
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
|
||||
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
@@ -706,6 +682,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var horizontalRulePattern = regexp.MustCompile(`^=+$`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveIDs, err error) {
|
||||
sectionState := Outside
|
||||
lines := strings.Split(stdout, "\n")
|
||||
@@ -723,7 +701,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// find the new section pattern
|
||||
if match, _ := o.isHorizontalRule(line); match {
|
||||
if horizontalRulePattern.MatchString(line) {
|
||||
|
||||
// set previous section's result to return-variable
|
||||
if sectionState == Content {
|
||||
@@ -750,7 +728,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
|
||||
|
||||
switch sectionState {
|
||||
case Header:
|
||||
switch o.Family {
|
||||
switch o.Distro.Family {
|
||||
case "centos":
|
||||
// CentOS has no security channel.
|
||||
// So use yum check-update && parse changelog
|
||||
@@ -817,9 +795,8 @@ func (o *redhat) changeSectionState(state int) (newState int) {
|
||||
return newState
|
||||
}
|
||||
|
||||
func (o *redhat) isHorizontalRule(line string) (bool, error) {
|
||||
return regexp.MatchString("^=+$", line)
|
||||
}
|
||||
var rpmPackageArchPattern = regexp.MustCompile(
|
||||
`^[^ ]+\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`)
|
||||
|
||||
func (o *redhat) isRpmPackageNameLine(line string) (bool, error) {
|
||||
s := strings.TrimPrefix(line, "ChangeLog for: ")
|
||||
@@ -829,10 +806,8 @@ func (o *redhat) isRpmPackageNameLine(line string) (bool, error) {
|
||||
}
|
||||
for _, s := range ss {
|
||||
s = strings.TrimRight(s, " \r\n")
|
||||
ok, err := regexp.MatchString(
|
||||
`^[^ ]+\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`, s)
|
||||
if !ok {
|
||||
return false, err
|
||||
if !rpmPackageArchPattern.MatchString(s) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
@@ -855,9 +830,10 @@ func (o *redhat) parseYumUpdateinfoHeaderCentOS(line string) (packs []models.Pac
|
||||
return
|
||||
}
|
||||
|
||||
var yumHeaderPattern = regexp.MustCompile(`(ALAS-.+): (.+) priority package update for (.+)$`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdvisory, names []string, err error) {
|
||||
re, _ := regexp.Compile(`(ALAS-.+): (.+) priority package update for (.+)$`)
|
||||
result := re.FindStringSubmatch(line)
|
||||
result := yumHeaderPattern.FindStringSubmatch(line)
|
||||
if len(result) == 4 {
|
||||
a.AdvisoryID = result[1]
|
||||
a.Severity = result[2]
|
||||
@@ -869,31 +845,36 @@ func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdv
|
||||
return
|
||||
}
|
||||
|
||||
var yumCveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoLineToGetCveIDs(line string) []string {
|
||||
re, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
|
||||
return re.FindAllString(line, -1)
|
||||
return yumCveIDPattern.FindAllString(line, -1)
|
||||
}
|
||||
|
||||
var yumAdvisoryIDPattern = regexp.MustCompile(`^ *Update ID : (.*)$`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoToGetAdvisoryID(line string) (advisoryID string, found bool) {
|
||||
re, _ := regexp.Compile(`^ *Update ID : (.*)$`)
|
||||
result := re.FindStringSubmatch(line)
|
||||
result := yumAdvisoryIDPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(result[1]), true
|
||||
}
|
||||
|
||||
var yumIssuedPattern = regexp.MustCompile(`^\s*Issued : (\d{4}-\d{2}-\d{2})`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoLineToGetIssued(line string) (date time.Time, found bool) {
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Issued : (\d{4}-\d{2}-\d{2})`)
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, yumIssuedPattern)
|
||||
}
|
||||
|
||||
var yumUpdatedPattern = regexp.MustCompile(`^\s*Updated : (\d{4}-\d{2}-\d{2})`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoLineToGetUpdated(line string) (date time.Time, found bool) {
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Updated : (\d{4}-\d{2}-\d{2})`)
|
||||
return o.parseYumUpdateinfoLineToGetDate(line, yumUpdatedPattern)
|
||||
}
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (date time.Time, found bool) {
|
||||
re, _ := regexp.Compile(regexpFormat)
|
||||
result := re.FindStringSubmatch(line)
|
||||
func (o *redhat) parseYumUpdateinfoLineToGetDate(line string, regexpPattern *regexp.Regexp) (date time.Time, found bool) {
|
||||
result := regexpPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return date, false
|
||||
}
|
||||
@@ -904,14 +885,16 @@ func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (dat
|
||||
return t, true
|
||||
}
|
||||
|
||||
var yumDescriptionPattern = regexp.MustCompile(`^\s*Description : `)
|
||||
|
||||
func (o *redhat) isDescriptionLine(line string) bool {
|
||||
re, _ := regexp.Compile(`^\s*Description : `)
|
||||
return re.MatchString(line)
|
||||
return yumDescriptionPattern.MatchString(line)
|
||||
}
|
||||
|
||||
var yumSeverityPattern = regexp.MustCompile(`^ *Severity : (.*)$`)
|
||||
|
||||
func (o *redhat) parseYumUpdateinfoToGetSeverity(line string) (severity string, found bool) {
|
||||
re, _ := regexp.Compile(`^ *Severity : (.*)$`)
|
||||
result := re.FindStringSubmatch(line)
|
||||
result := yumSeverityPattern.FindStringSubmatch(line)
|
||||
if len(result) != 2 {
|
||||
return "", false
|
||||
}
|
||||
@@ -990,3 +973,12 @@ func (o *redhat) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacks
|
||||
func (o *redhat) clone() osTypeInterface {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *redhat) sudo() bool {
|
||||
switch o.Distro.Family {
|
||||
case "amazon":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,11 +143,11 @@ func TestParseYumUpdateinfoLineToGetCveIDs(t *testing.T) {
|
||||
[]string{"CVE-2015-0278"},
|
||||
},
|
||||
{
|
||||
": 1195457 - nodejs-0.10.35 causes undefined symbolsCVE-2015-0278, CVE-2015-0278, CVE-2015-0277",
|
||||
": 1195457 - nodejs-0.10.35 causes undefined symbolsCVE-2015-0278, CVE-2015-0278, CVE-2015-02770000000 ",
|
||||
[]string{
|
||||
"CVE-2015-0278",
|
||||
"CVE-2015-0278",
|
||||
"CVE-2015-0277",
|
||||
"CVE-2015-02770000000",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -451,7 +451,7 @@ Description : The Berkeley Internet Name Domain (BIND) is an implementation of
|
||||
updated, _ := time.Parse("2006-01-02", "2015-09-04")
|
||||
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "redhat"
|
||||
r.Distro = config.Distro{Family: "redhat"}
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
@@ -511,7 +511,7 @@ Description : The Berkeley Internet Name Domain (BIND) is an implementation of
|
||||
func TestParseYumUpdateinfoAmazon(t *testing.T) {
|
||||
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "amazon"
|
||||
r.Distro = config.Distro{Family: "redhat"}
|
||||
|
||||
issued, _ := time.Parse("2006-01-02", "2015-12-15")
|
||||
updated, _ := time.Parse("2006-01-02", "2015-12-16")
|
||||
@@ -601,7 +601,7 @@ Description : Package updates are available for Amazon Linux AMI that fix the
|
||||
|
||||
func TestParseYumCheckUpdateLines(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "centos"
|
||||
r.Distro = config.Distro{Family: "centos"}
|
||||
stdout := `Loaded plugins: changelog, fastestmirror, keys, protect-packages, protectbase, security
|
||||
Loading mirror speeds from cached hostfile
|
||||
* base: mirror.fairway.ne.jp
|
||||
@@ -616,6 +616,7 @@ Obsoleting Packages
|
||||
python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases
|
||||
python-ordereddict.noarch 1.1-3.el6ev installed
|
||||
bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
pytalloc.x86_64 2.0.7-2.el6 @CentOS 6.5/6.5
|
||||
`
|
||||
|
||||
r.Packages = []models.PackageInfo{
|
||||
@@ -644,6 +645,11 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Version: "1.0",
|
||||
Release: "1",
|
||||
},
|
||||
{
|
||||
Name: "pytalloc",
|
||||
Version: "2.0.1",
|
||||
Release: "0",
|
||||
},
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
@@ -658,6 +664,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Release: "4.el6",
|
||||
NewVersion: "2.3.7",
|
||||
NewRelease: "5.el6",
|
||||
Repository: "base",
|
||||
},
|
||||
{
|
||||
Name: "bash",
|
||||
@@ -665,6 +672,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Release: "33",
|
||||
NewVersion: "4.1.2",
|
||||
NewRelease: "33.el6_7.1",
|
||||
Repository: "updates",
|
||||
},
|
||||
{
|
||||
Name: "python-libs",
|
||||
@@ -672,6 +680,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Release: "1.1-0",
|
||||
NewVersion: "2.6.6",
|
||||
NewRelease: "64.el6",
|
||||
Repository: "rhui-REGION-rhel-server-releases",
|
||||
},
|
||||
{
|
||||
Name: "python-ordereddict",
|
||||
@@ -679,6 +688,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Release: "1",
|
||||
NewVersion: "1.1",
|
||||
NewRelease: "3.el6ev",
|
||||
Repository: "installed",
|
||||
},
|
||||
{
|
||||
Name: "bind-utils",
|
||||
@@ -686,6 +696,15 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
Release: "1",
|
||||
NewVersion: "9.3.6",
|
||||
NewRelease: "25.P1.el5_11.8",
|
||||
Repository: "updates",
|
||||
},
|
||||
{
|
||||
Name: "pytalloc",
|
||||
Version: "2.0.1",
|
||||
Release: "0",
|
||||
NewVersion: "2.0.7",
|
||||
NewRelease: "2.el6",
|
||||
Repository: "@CentOS 6.5/6.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -709,7 +728,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
|
||||
|
||||
func TestParseYumCheckUpdateLinesAmazon(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "amazon"
|
||||
r.Distro = config.Distro{Family: "amazon"}
|
||||
stdout := `Loaded plugins: priorities, update-motd, upgrade-helper
|
||||
34 package(s) needed for security, out of 71 available
|
||||
|
||||
@@ -747,6 +766,7 @@ if-not-architecture 100-200 amzn-main
|
||||
Release: "0.33.rc1.45.amzn1",
|
||||
NewVersion: "9.8.2",
|
||||
NewRelease: "0.37.rc1.45.amzn1",
|
||||
Repository: "amzn-main",
|
||||
},
|
||||
{
|
||||
Name: "java-1.7.0-openjdk",
|
||||
@@ -754,6 +774,7 @@ if-not-architecture 100-200 amzn-main
|
||||
Release: "2.6.4.0.0.amzn1",
|
||||
NewVersion: "1.7.0.95",
|
||||
NewRelease: "2.6.4.0.65.amzn1",
|
||||
Repository: "amzn-main",
|
||||
},
|
||||
{
|
||||
Name: "if-not-architecture",
|
||||
@@ -761,6 +782,7 @@ if-not-architecture 100-200 amzn-main
|
||||
Release: "20",
|
||||
NewVersion: "100",
|
||||
NewRelease: "200",
|
||||
Repository: "amzn-main",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1110,8 +1132,10 @@ func TestGetChangelogCVELines(t *testing.T) {
|
||||
}
|
||||
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "centos"
|
||||
r.Release = "6.7"
|
||||
r.Distro = config.Distro{
|
||||
Family: "centos",
|
||||
Release: "6.7",
|
||||
}
|
||||
for _, tt := range testsCentos6 {
|
||||
rpm2changelog, err := r.parseAllChangelog(stdoutCentos6)
|
||||
if err != nil {
|
||||
@@ -1194,7 +1218,10 @@ func TestGetChangelogCVELines(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
r.Release = "5.6"
|
||||
r.Distro = config.Distro{
|
||||
Family: "centos",
|
||||
Release: "5.6",
|
||||
}
|
||||
for _, tt := range testsCentos5 {
|
||||
rpm2changelog, err := r.parseAllChangelog(stdoutCentos5)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
@@ -20,8 +41,12 @@ type osTypeInterface interface {
|
||||
setServerInfo(config.ServerInfo)
|
||||
getServerInfo() config.ServerInfo
|
||||
|
||||
setDistributionInfo(string, string)
|
||||
getDistributionInfo() string
|
||||
setDistro(string, string)
|
||||
getDistro() config.Distro
|
||||
|
||||
// checkDependencies checks if dependencies are installed on the target server.
|
||||
checkDependencies() error
|
||||
getLackDependencies() []string
|
||||
|
||||
checkIfSudoNoPasswd() error
|
||||
detectPlatform() error
|
||||
@@ -41,7 +66,7 @@ type osTypeInterface interface {
|
||||
setErrs([]error)
|
||||
}
|
||||
|
||||
// osPackages included by linux struct
|
||||
// osPackages is included by base struct
|
||||
type osPackages struct {
|
||||
// installed packages
|
||||
Packages models.PackageInfoList
|
||||
@@ -151,11 +176,20 @@ func PrintSSHableServerNames() {
|
||||
}
|
||||
|
||||
// InitServers detect the kind of OS distribution of target servers
|
||||
func InitServers(localLogger *logrus.Entry) {
|
||||
func InitServers(localLogger *logrus.Entry) error {
|
||||
Log = localLogger
|
||||
servers = detectServerOSes()
|
||||
if len(servers) == 0 {
|
||||
return fmt.Errorf("No scannable servers")
|
||||
}
|
||||
|
||||
containers := detectContainerOSes()
|
||||
servers = append(servers, containers...)
|
||||
if config.Conf.ContainersOnly {
|
||||
servers = containers
|
||||
} else {
|
||||
servers = append(servers, containers...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
@@ -188,7 +222,7 @@ func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getDistributionInfo())
|
||||
res.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timed out while detecting servers"
|
||||
@@ -228,7 +262,7 @@ func detectContainerOSes() (actives []osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
p, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}()
|
||||
osTypesChan <- detectContainerOSesOnServer(s)
|
||||
@@ -248,7 +282,7 @@ func detectContainerOSes() (actives []osTypeInterface) {
|
||||
}
|
||||
oses = append(oses, res...)
|
||||
Log.Infof("Detected: %s@%s: %s",
|
||||
sinfo.Container.Name, sinfo.ServerName, osi.getDistributionInfo())
|
||||
sinfo.Container.Name, sinfo.ServerName, osi.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timed out while detecting containers"
|
||||
@@ -350,7 +384,7 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
|
||||
|
||||
// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH
|
||||
func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error {
|
||||
timeoutSec := 1 * 15
|
||||
timeoutSec := 15
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.checkIfSudoNoPasswd()
|
||||
}, timeoutSec)
|
||||
@@ -397,12 +431,65 @@ func detectPlatforms() []error {
|
||||
|
||||
// Prepare installs requred packages to scan vulnerabilities.
|
||||
func Prepare() []error {
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
if err := o.checkDependencies(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
var targets []osTypeInterface
|
||||
for _, s := range servers {
|
||||
deps := s.getLackDependencies()
|
||||
if len(deps) != 0 {
|
||||
targets = append(targets, s)
|
||||
}
|
||||
}
|
||||
if len(targets) == 0 {
|
||||
Log.Info("No need to install dependencies")
|
||||
return nil
|
||||
}
|
||||
|
||||
Log.Info("Below servers are needed to install dependencies")
|
||||
for _, s := range targets {
|
||||
for _, d := range s.getLackDependencies() {
|
||||
Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}
|
||||
Log.Info("Is this ok to install dependencies on the servers? [y/N]")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
switch strings.TrimSpace(text) {
|
||||
case "", "N", "n":
|
||||
return nil
|
||||
case "y", "Y":
|
||||
goto yes
|
||||
default:
|
||||
Log.Info("Please enter y or N")
|
||||
}
|
||||
}
|
||||
|
||||
yes:
|
||||
servers = targets
|
||||
errs = parallelSSHExec(func(o osTypeInterface) error {
|
||||
if err := o.install(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(errs) != 0 {
|
||||
return errs
|
||||
}
|
||||
Log.Info("All dependencies were installed correctly")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scan scan
|
||||
@@ -417,6 +504,16 @@ func Scan() []error {
|
||||
return errs
|
||||
}
|
||||
|
||||
if err := setupCangelogCache(); err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if cache.DB != nil {
|
||||
cache.DB.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
Log.Info("Scanning vulnerable OS packages...")
|
||||
if errs := scanPackages(); errs != nil {
|
||||
return errs
|
||||
@@ -429,6 +526,23 @@ func Scan() []error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupCangelogCache() error {
|
||||
needToSetupCache := false
|
||||
for _, s := range servers {
|
||||
switch s.getDistro().Family {
|
||||
case "ubuntu", "debian":
|
||||
needToSetupCache = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if needToSetupCache {
|
||||
if err := cache.SetupBolt(config.Conf.CacheDBPath, Log); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRequiredPackagesInstalled() []error {
|
||||
timeoutSec := 30 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
|
||||
@@ -89,7 +89,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
logrus.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
p, s.getServerInfo().GetServerName())
|
||||
}
|
||||
}()
|
||||
if err := fn(s); err != nil {
|
||||
@@ -100,7 +100,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
err,
|
||||
)
|
||||
} else {
|
||||
resChan <- s.getServerInfo().ServerName
|
||||
resChan <- s.getServerInfo().GetServerName()
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
var timedoutSnames []string
|
||||
if isTimedout {
|
||||
for _, s := range servers {
|
||||
name := s.getServerInfo().ServerName
|
||||
name := s.getServerInfo().GetServerName()
|
||||
found := false
|
||||
for _, t := range snames {
|
||||
if name == t {
|
||||
@@ -299,9 +299,10 @@ func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
|
||||
func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
|
||||
if sudo && c.User != "root" && !c.IsContainer() {
|
||||
cmd = fmt.Sprintf("sudo -S %s", cmd)
|
||||
cmd = strings.Replace(cmd, "|", "| sudo ", -1)
|
||||
}
|
||||
|
||||
if c.Family != "FreeBSD" {
|
||||
if c.Distro.Family != "FreeBSD" {
|
||||
// set pipefail option. Bash only
|
||||
// http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
|
||||
cmd = fmt.Sprintf("set -o pipefail; %s", cmd)
|
||||
@@ -318,7 +319,7 @@ func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
|
||||
}
|
||||
|
||||
func getAgentAuth() (auth ssh.AuthMethod, ok bool) {
|
||||
if sock := os.Getenv("SSH_AUTH_SOCK"); len(sock) > 0 {
|
||||
if sock := os.Getenv("SSH_AUTH_SOCK"); 0 < len(sock) {
|
||||
if agconn, err := net.Dial("unix", sock); err == nil {
|
||||
ag := agent.NewClient(agconn)
|
||||
auth = ssh.PublicKeysCallback(ag.Signers)
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
# Vuls on Docker
|
||||
|
||||
## What's Vuls-On-Docker
|
||||
|
||||
- 数個のコマンドを実行するだけでVulsとvulsrepoのセットアップが出来るスクリプト
|
||||
- Dockerコンテナ上にVulsと[vulsrepo](https://github.com/usiusi360/vulsrepo)をセットアップ可能
|
||||
- スキャン結果をvulsrepoでブラウザで分析可能
|
||||
- 脆弱性データベースの更新が可能
|
||||
- モジュールのアップデートが可能
|
||||
|
||||
## Setting up your machine
|
||||
|
||||
1. [Install Docker](https://docs.docker.com/engine/installation/)
|
||||
2. [Install Docker-Compose](https://docs.docker.com/compose/install/)
|
||||
3. 実行前に以下のコマンドが実行可能なことを確認する
|
||||
|
||||
```
|
||||
$ docker version
|
||||
$ docker-compose version
|
||||
```
|
||||
|
||||
4. Vulsをgit clone
|
||||
```
|
||||
mkdir work
|
||||
cd work
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
cd vuls/setup/docker
|
||||
```
|
||||
|
||||
## Start A Vuls Container
|
||||
|
||||
- 以下のコマンドを実行してコンテナをビルドする
|
||||
|
||||
```
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
## Setting up Vuls
|
||||
|
||||
1. スキャン対象サーバのSSH秘密鍵を保存(vuls/setup/docker/conf/)する
|
||||
2. config.toml(vuls/docker/conf/config.toml) を環境に合わせて作成する
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "conf/id_rsa"
|
||||
```
|
||||
|
||||
## Fetch Vulnerability database
|
||||
|
||||
- NVDから脆弱性データベースを取得する
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_nvd_all.sh
|
||||
```
|
||||
|
||||
- レポートを日本語化する場合は、JVNから脆弱性データを取得する
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_jvn_all.sh
|
||||
```
|
||||
|
||||
## Scan servers with Vuls-On-Docker
|
||||
|
||||
- スキャンを実行する
|
||||
|
||||
```
|
||||
$ docker exec -t vuls vuls prepare -config=conf/config.toml
|
||||
$ docker exec -t vuls scripts/scan_for_vulsrepo.sh
|
||||
```
|
||||
|
||||
## See the results in a browser
|
||||
|
||||
```
|
||||
http://${Vuls_Host}/vulsrepo/
|
||||
```
|
||||
|
||||
# Update modules
|
||||
|
||||
- vuls, go-cve-dictionary, vulsrepoのモジュールをアップデートする
|
||||
```
|
||||
$ docker exec -t vuls scripts/update_modules.sh
|
||||
```
|
||||
|
||||
# Update Vulnerability database
|
||||
|
||||
- NVDの過去2年分の脆弱性データベースを更新する
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_nvd_last2y.sh
|
||||
```
|
||||
|
||||
- JVNの過去1ヶ月分の脆弱性データベースを更新する
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_jvn_month.sh
|
||||
```
|
||||
|
||||
- JVNの過去1週間分の脆弱性データベースを更新する
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_jvn_week.sh
|
||||
```
|
||||
@@ -1,87 +1,183 @@
|
||||
# Vuls on Docker
|
||||
# Vuls Docker components
|
||||
|
||||
## What's Vuls-On-Docker
|
||||
This is the Git repo of the official Docker image for vuls.
|
||||
|
||||
- This is a dockernized-Vuls with vulsrepo UI in it.
|
||||
- It's designed to reduce the cost of installation and the dependencies that vuls requires.
|
||||
- You can run install and run Vuls on your machine with only a few commands.
|
||||
- The result can be viewed with a browser
|
||||
# Supported tags and respective `Dockerfile` links
|
||||
|
||||
## Setting up your machine
|
||||
|
||||
1. [Install Docker](https://docs.docker.com/engine/installation/)
|
||||
2. [Install Docker-Compose](https://docs.docker.com/compose/install/)
|
||||
3. Make sure that you can run the following commands before you move on.
|
||||
- go-cve-dictionary
|
||||
- [`latest` (*go-cve-dictionary:latest Dockerfile*)]()
|
||||
- vuls
|
||||
- [`latest` (*vuls:latest Dockerfile*)]()
|
||||
- vulsrepo
|
||||
- [`latest` (*vulsrepo:latest Dockerfile*)]()
|
||||
|
||||
```
|
||||
$ docker version
|
||||
$ docker-compose version
|
||||
```
|
||||
|
||||
4. git clone vuls
|
||||
```
|
||||
mkdir work
|
||||
cd work
|
||||
git clone https://github.com/future-architect/vuls.git
|
||||
cd vuls/setup/docker
|
||||
```
|
||||
This image version is same as the github repository version.
|
||||
|
||||
# Caution
|
||||
|
||||
This image is built per commit.
|
||||
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
|
||||
|
||||
## Start A Vuls Container
|
||||
1. Confirm your vuls version
|
||||
|
||||
- Execute the following command to build and run a Vuls Container
|
||||
- go-cve-dictionary
|
||||
|
||||
```
|
||||
$ docker-compose up -d
|
||||
```
|
||||
```console
|
||||
$ docker run --rm vuls/go-cve-dictionary -v
|
||||
|
||||
## Setting up Vuls
|
||||
|
||||
1. Locate ssh-keys of targer servers in (vuls/docker/conf/)
|
||||
2. Create and ajust config.toml(vuls/docker/conf/config.toml) to your environment
|
||||
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "conf/id_rsa"
|
||||
```
|
||||
|
||||
## Fetch Vulnerability database
|
||||
|
||||
- Fetch Vulnerability database from NVD
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_nvd_all.sh
|
||||
```
|
||||
|
||||
## Scan servers with Vuls-On-Docker
|
||||
|
||||
- Use the embedded script to scan servers for vulsrepo(or run whatever with docker exec)
|
||||
|
||||
```
|
||||
$ docker exec -t vuls vuls prepare -config=conf/config.toml
|
||||
$ docker exec -t vuls scripts/scan_for_vulsrepo.sh
|
||||
```
|
||||
|
||||
## See the results in a browser
|
||||
|
||||
```
|
||||
http://${Vuls_Host}/vulsrepo/
|
||||
go-cve-dictionary v0.0.xxx xxxx
|
||||
```
|
||||
|
||||
# Update modules
|
||||
- vuls
|
||||
|
||||
- update vuls, go-cve-dictionary, vulsrepo
|
||||
```
|
||||
$ docker exec -t vuls scripts/update_modules.sh
|
||||
```
|
||||
```console
|
||||
$ docker run --rm vuls/vuls -v
|
||||
|
||||
# Update Vulnerability database
|
||||
vuls v0.0.xxx xxxx
|
||||
```
|
||||
|
||||
- Fetch Vulnerability database from NVD
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_nvd_last2y.sh
|
||||
```
|
||||
2. Remove your old docker images
|
||||
|
||||
- go-cve-dictionary
|
||||
|
||||
```
|
||||
$ docker rmi vuls/go-cve-dictionary
|
||||
```
|
||||
|
||||
```
|
||||
$ docker rmi vuls/vuls
|
||||
```
|
||||
|
||||
- vuls
|
||||
|
||||
```
|
||||
$ docker rmi vuls/vuls
|
||||
```
|
||||
|
||||
3. Pull new vuls docker images
|
||||
|
||||
- go-cve-dictionary
|
||||
|
||||
```
|
||||
$ docker pull vuls/go-cve-dictionary
|
||||
```
|
||||
|
||||
- vuls
|
||||
|
||||
```
|
||||
$ docker pull vuls/vuls
|
||||
```
|
||||
|
||||
4. Confirm your vuls version
|
||||
|
||||
```console
|
||||
$ docker run --rm vuls/go-cve-dictionary -v
|
||||
|
||||
go-cve-dictionary v0.1.xxx xxxx
|
||||
```
|
||||
|
||||
- vuls
|
||||
|
||||
```console
|
||||
$ docker run --rm vuls/vuls -v
|
||||
|
||||
vuls v0.1.xxx xxxx
|
||||
```
|
||||
|
||||
|
||||
# How to use this image
|
||||
|
||||
1. fetch nvd (vuls/go-cve-dictionary)
|
||||
1. configuration (vuls/vuls)
|
||||
1. prepare (vuls/vuls)
|
||||
1. scan (vuls/vuls)
|
||||
1. vulsrepo (vuls/vulsrepo)
|
||||
|
||||
## Step1. Fetch NVD
|
||||
|
||||
```console
|
||||
$ for i in {2002..2016}; do \
|
||||
docker run --rm -it \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
|
||||
vuls/go-cve-dictionary fetchnvd -years $i; \
|
||||
done
|
||||
```
|
||||
|
||||
## Step2. Configuration
|
||||
|
||||
Create config.toml referring to [this](https://github.com/future-architect/vuls#configuration).
|
||||
|
||||
```toml
|
||||
[servers]
|
||||
|
||||
[servers.amazon]
|
||||
host = "54.249.93.16"
|
||||
port = "22"
|
||||
user = "vuls-user"
|
||||
keyPath = "/root/.ssh/id_rsa" # path to ssh private key in docker
|
||||
```
|
||||
|
||||
|
||||
```console
|
||||
$ docker run --rm \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
vuls/vuls configtest \
|
||||
-config=./config.toml # path to config.toml in docker
|
||||
```
|
||||
|
||||
## Step3. Prepare
|
||||
|
||||
```console
|
||||
$ docker run --rm \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
vuls/vuls prepare \
|
||||
-config=./config.toml # path to config.toml in docker
|
||||
```
|
||||
|
||||
## Step4. Scan
|
||||
|
||||
```console
|
||||
$ docker run --rm -it \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
-e "TZ=Asia/Tokyo" \
|
||||
vuls/vuls scan \
|
||||
-cve-dictionary-dbpath=/vuls/cve.sqlite3 \
|
||||
-report-json \
|
||||
-config=./config.toml # path to config.toml in docker
|
||||
```
|
||||
|
||||
## Step5. vulsrepo
|
||||
|
||||
```console
|
||||
$docker run -dt \
|
||||
-v $PWD:/vuls \
|
||||
-p 80:80 \
|
||||
vuls/vulsrepo
|
||||
```
|
||||
|
||||
# User Feedback
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this image is stored in the [`docker/` directory]() of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
|
||||
|
||||
## Issues
|
||||
|
||||
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
|
||||
|
||||
## Contributing
|
||||
|
||||
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
|
||||
1. get original code: go get github.com/future-architect/vuls
|
||||
1. work on original code
|
||||
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
|
||||
1. push your changes: git push myfork
|
||||
1. create a new Pull Request
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
version: '2'
|
||||
services:
|
||||
vuls:
|
||||
container_name: vuls
|
||||
build: ./dockerfile
|
||||
image: vuls-docker:0.1
|
||||
volumes:
|
||||
- ./conf:/opt/vuls/conf
|
||||
ports:
|
||||
- "80:80"
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
FROM buildpack-deps:jessie-scm
|
||||
|
||||
# golang Install
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
g++ \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
make \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV GOLANG_VERSION 1.6.2
|
||||
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
|
||||
ENV GOLANG_DOWNLOAD_SHA256 e40c36ae71756198478624ed1bb4ce17597b3c19d243f3f0899bb5740d56212a
|
||||
|
||||
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
|
||||
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
|
||||
&& tar -C /usr/local -xzf golang.tar.gz \
|
||||
&& rm golang.tar.gz
|
||||
|
||||
ENV GOPATH /go
|
||||
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
|
||||
|
||||
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
|
||||
|
||||
# glide install
|
||||
ENV GLIDE_VERSION 0.10.2
|
||||
ENV GLIDE_DOWNLOAD_URL https://github.com/Masterminds/glide/releases/download/$GLIDE_VERSION/glide-$GLIDE_VERSION-linux-amd64.tar.gz
|
||||
RUN curl -fsSL "$GLIDE_DOWNLOAD_URL" -o glide.tar.gz \
|
||||
&& mkdir /usr/local/glide \
|
||||
&& tar -C /usr/local/glide -xzf glide.tar.gz \
|
||||
&& ln -s /usr/local/glide/linux-amd64/glide /usr/local/bin/ \
|
||||
&& rm glide.tar.gz
|
||||
|
||||
# nginx Install
|
||||
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \
|
||||
&& echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends --no-install-suggests -y \
|
||||
ca-certificates \
|
||||
nginx \
|
||||
nginx-module-xslt \
|
||||
nginx-module-geoip \
|
||||
nginx-module-image-filter \
|
||||
nginx-module-perl \
|
||||
nginx-module-njs \
|
||||
gettext-base \
|
||||
wget \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
|
||||
&& ln -sf /dev/stderr /var/log/nginx/error.log
|
||||
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
#Vuls Install
|
||||
ENV VULS_ROOT /opt/vuls
|
||||
RUN mkdir -p /var/log/vuls ${VULS_ROOT}/conf /root/.ssh/
|
||||
RUN chmod 700 -R /var/log/vuls $VULS_ROOT
|
||||
# RUN go get github.com/kotakanbe/go-cve-dictionary
|
||||
# RUN go get github.com/future-architect/vuls
|
||||
|
||||
RUN go get -v -d github.com/kotakanbe/go-cve-dictionary \
|
||||
&& cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary \
|
||||
&& glide install \
|
||||
&& go install
|
||||
|
||||
RUN go get -v -d github.com/future-architect/vuls \
|
||||
&& cd $GOPATH/src/github.com/future-architect/vuls \
|
||||
&& glide install \
|
||||
&& go install
|
||||
|
||||
# Copy custom Scripts
|
||||
COPY ./scripts/ ${VULS_ROOT}/scripts
|
||||
RUN chmod 755 ${VULS_ROOT}/scripts/*
|
||||
|
||||
|
||||
#Vulrepo Install
|
||||
RUN git clone https://github.com/usiusi360/vulsrepo /tmp/vulsrepo
|
||||
RUN mkdir /usr/share/nginx/html/vulsrepo/
|
||||
RUN cp -rp /tmp/vulsrepo/src/* /usr/share/nginx/html/vulsrepo
|
||||
RUN rm -rf /tmp/vulsrepo
|
||||
|
||||
#Home
|
||||
WORKDIR /opt/vuls
|
||||
EXPOSE 80 443
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
user root;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchjvn -entire
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchjvn -month
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchjvn -week
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchnvd -last2y
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
VULS_CONF=${VULS_ROOT}/conf
|
||||
NGINX_VULSREPO_ROOT=/usr/share/nginx/html/vulsrepo
|
||||
cd $VULS_ROOT
|
||||
vuls scan -report-json --cve-dictionary-dbpath=${VULS_ROOT}/cve.sqlite3 -config=${VULS_CONF}/config.toml
|
||||
ln -sf ${VULS_ROOT}/results/current ${NGINX_VULSREPO_ROOT}/current
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd $GOPATH/src/github.com/future-architect/vuls
|
||||
git pull origin master
|
||||
glide install
|
||||
go install
|
||||
|
||||
|
||||
cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
git pull origin master
|
||||
glide install
|
||||
go install
|
||||
|
||||
git clone https://github.com/usiusi360/vulsrepo /tmp/vulsrepo
|
||||
cp -rp /tmp/vulsrepo/src/* /usr/share/nginx/html/vulsrepo
|
||||
rm -rf /tmp/vulsrepo
|
||||
|
||||
19
setup/docker/go-cve-dictionary/latest/Dockerfile
Normal file
19
setup/docker/go-cve-dictionary/latest/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM golang:latest
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
|
||||
ENV REPOSITORY github.com/kotakanbe/go-cve-dictionary
|
||||
ENV LOGDIR /var/log/vuls
|
||||
ENV WORKDIR /vuls
|
||||
# go-cve-dictionary install
|
||||
RUN git clone https://$REPOSITORY.git $GOPATH/src/$REPOSITORY \
|
||||
&& cd $GOPATH/src/$REPOSITORY \
|
||||
&& make install \
|
||||
&& mkdir -p $LOGDIR
|
||||
|
||||
VOLUME [$WORKDIR, $LOGDIR]
|
||||
WORKDIR $WORKDIR
|
||||
ENV PWD $WORKDIR
|
||||
|
||||
ENTRYPOINT ["go-cve-dictionary"]
|
||||
CMD ["--help"]
|
||||
89
setup/docker/go-cve-dictionary/latest/README.md
Normal file
89
setup/docker/go-cve-dictionary/latest/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# go-cve-dictionary-Docker
|
||||
|
||||
This is the Git repo of the official Docker image for go-cve-dictionary.
|
||||
See the [Hub page](https://hub.docker.com/r/vuls/go-cve-dictionary/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
|
||||
|
||||
# Supported tags and respective `Dockerfile` links
|
||||
|
||||
- [`latest` (*go-cve-dictionary:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/go-cve-dictionary/latest/Dockerfile)
|
||||
|
||||
# Caution
|
||||
|
||||
This image is built per commit.
|
||||
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
|
||||
|
||||
- Remove old docker image
|
||||
|
||||
```
|
||||
$ docker rmi vuls/go-cve-dictionary
|
||||
```
|
||||
|
||||
- Pull new docker image
|
||||
|
||||
```
|
||||
$ docker pull vuls/go-cve-dictionary
|
||||
```
|
||||
|
||||
# What is go-cve-dictionary?
|
||||
|
||||
This is tool to build a local copy of the NVD (National Vulnerabilities Database) [1] and the Japanese JVN [2], which contain security vulnerabilities according to their CVE identifiers [3] including exhaustive information and a risk score. The local copy is generated in sqlite format, and the tool has a server mode for easy querying.
|
||||
|
||||
[1] https://en.wikipedia.org/wiki/National_Vulnerability_Database
|
||||
[2] https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures
|
||||
[3] http://jvndb.jvn.jp/apis/termsofuse.html
|
||||
|
||||
# How to use this image
|
||||
|
||||
## check vuls version
|
||||
|
||||
```
|
||||
$ docker run --rm vuls/go-cve-dictionary -v
|
||||
```
|
||||
|
||||
## fetchnvd
|
||||
|
||||
```console
|
||||
$ for i in {2002..2016}; do \
|
||||
docker run --rm -it \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
|
||||
vuls/go-cve-dictionary fetchnvd -years $i; \
|
||||
done
|
||||
```
|
||||
|
||||
## server
|
||||
|
||||
```console
|
||||
$ docker run -dt \
|
||||
--name go-cve-dictionary \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
|
||||
--expose 1323 \
|
||||
-p 1323:1323 \
|
||||
vuls/go-cve-dictionary server --bind=0.0.0.0
|
||||
```
|
||||
|
||||
Prease refer to [this](https://hub.docker.com/r/vuls/go-cve-dictionary).
|
||||
|
||||
## vuls
|
||||
|
||||
Please refer to [this](https://hub.docker.com/r/vuls/vuls/).
|
||||
|
||||
# User Feedback
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
|
||||
|
||||
## Issues
|
||||
|
||||
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
|
||||
|
||||
## Contributing
|
||||
|
||||
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
|
||||
1. get original code: go get github.com/future-architect/vuls
|
||||
1. work on original code
|
||||
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
|
||||
1. push your changes: git push myfork
|
||||
1. create a new Pull Request
|
||||
19
setup/docker/vuls/latest/Dockerfile
Normal file
19
setup/docker/vuls/latest/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM golang:latest
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
|
||||
ENV REPOSITORY github.com/future-architect/vuls
|
||||
ENV LOGDIR /var/log/vuls
|
||||
ENV WORKDIR /vuls
|
||||
# go-cve-dictionary install
|
||||
RUN git clone https://$REPOSITORY.git $GOPATH/src/$REPOSITORY \
|
||||
&& cd $GOPATH/src/$REPOSITORY \
|
||||
&& make install \
|
||||
&& mkdir -p $LOGDIR
|
||||
|
||||
VOLUME [$WORKDIR, $LOGDIR]
|
||||
WORKDIR $WORKDIR
|
||||
ENV PWD $WORKDIR
|
||||
|
||||
ENTRYPOINT ["vuls"]
|
||||
CMD ["--help"]
|
||||
121
setup/docker/vuls/latest/README.md
Normal file
121
setup/docker/vuls/latest/README.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Vuls-Docker
|
||||
|
||||
This is the Git repo of the official Docker image for vuls.
|
||||
See the [Hub page](https://hub.docker.com/r/vuls/vuls/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
|
||||
|
||||
# Supported tags and respective `Dockerfile` links
|
||||
|
||||
- [`latest` (*vuls:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/vuls/latest/Dockerfile)
|
||||
|
||||
# Caution
|
||||
|
||||
This image is built per commit.
|
||||
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
|
||||
|
||||
- Remove old docker image
|
||||
|
||||
```
|
||||
$ docker rmi vuls/vuls
|
||||
```
|
||||
|
||||
- Pull new docker image
|
||||
|
||||
```
|
||||
$ docker pull vuls/vuls
|
||||
```
|
||||
|
||||
# What is Vuls?
|
||||
|
||||
Vuls is the Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
|
||||
Please see the [Documentation](https://github.com/future-architect/vuls)
|
||||
|
||||

|
||||
|
||||
# How to use this image
|
||||
|
||||
## check vuls version
|
||||
|
||||
```
|
||||
$ docker run --rm vuls/vuls -v
|
||||
```
|
||||
|
||||
## configtest
|
||||
|
||||
Create config.toml referring to [this](https://github.com/future-architect/vuls#configuration).
|
||||
|
||||
```toml
|
||||
[servers]
|
||||
|
||||
[servers.amazon]
|
||||
host = "54.249.93.16"
|
||||
port = "22"
|
||||
user = "vuls-user"
|
||||
keyPath = "/root/.ssh/id_rsa" # path to ssh private key in docker
|
||||
```
|
||||
|
||||
|
||||
```console
|
||||
$ docker run --rm \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
vuls/vuls configtest
|
||||
```
|
||||
|
||||
|
||||
## prepare
|
||||
|
||||
```console
|
||||
$ docker run --rm \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
vuls/vuls prepare \
|
||||
-config=./config.toml # path to config.toml in docker
|
||||
```
|
||||
|
||||
## scan
|
||||
|
||||
```console
|
||||
$ docker run --rm -it \
|
||||
-v ~/.ssh:/root/.ssh:ro \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
vuls/vuls scan \
|
||||
-cve-dictionary-dbpath=/vuls/cve.sqlite3 \
|
||||
-config=./config.toml \ # path to config.toml in docker
|
||||
-report-json
|
||||
```
|
||||
|
||||
## tui
|
||||
|
||||
```console
|
||||
$ docker run --rm -it \
|
||||
-v $PWD:/vuls \
|
||||
-v $PWD/vuls-log:/var/log/vuls \
|
||||
vuls/vuls tui
|
||||
```
|
||||
|
||||
## vulsrepo
|
||||
|
||||
Prease refer to [this](https://hub.docker.com/r/vuls/vulsrepo/).
|
||||
|
||||
# User Feedback
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
|
||||
|
||||
## Issues
|
||||
|
||||
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
|
||||
|
||||
## Contributing
|
||||
|
||||
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
|
||||
1. get original code: go get github.com/future-architect/vuls
|
||||
1. work on original code
|
||||
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
|
||||
1. push your changes: git push myfork
|
||||
1. create a new Pull Request
|
||||
31
setup/docker/vulsrepo/latest/Dockerfile
Normal file
31
setup/docker/vulsrepo/latest/Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
||||
FROM httpd:2.4
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
# install packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
vim \
|
||||
git \
|
||||
libcgi-pm-perl \
|
||||
libjson-perl \
|
||||
&& rm -r /var/lib/apt/lists/*
|
||||
|
||||
# env
|
||||
ENV HTTPD_PREFIX /usr/local/apache2
|
||||
|
||||
VOLUME /vuls
|
||||
|
||||
WORKDIR ${HTTPD_PREFIX}/htdocs
|
||||
RUN git clone https://github.com/usiusi360/vulsrepo.git \
|
||||
&& echo "LoadModule cgid_module modules/mod_cgid.so" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo "<Directory \"$HTTPD_PREFIX/htdocs/vulsrepo/dist/cgi\">" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo " Options +ExecCGI +FollowSymLinks" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo " AddHandler cgi-script cgi" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo "</Directory>" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& sed -i -e 's/User daemon/#User/g' $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& sed -i -e 's/Group daemon/#Group/g' $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& ln -snf /vuls/results /usr/local/apache2/htdocs/vulsrepo/results
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["httpd-foreground"]
|
||||
47
setup/docker/vulsrepo/latest/README.md
Normal file
47
setup/docker/vulsrepo/latest/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# VulsRepo-Docker
|
||||
|
||||
This is the Git repo of the official Docker image for vulsrepo.
|
||||
See the [Hub page](https://hub.docker.com/r/vuls/vulsrepo/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
|
||||
|
||||
# Supported tags and respective `Dockerfile` links
|
||||
|
||||
- [`latest` (*vulsrepo:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/vulsrepo/latest/Dockerfile)
|
||||
|
||||
# Caution
|
||||
|
||||
This image is built per commit.
|
||||
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
|
||||
|
||||
# What is vulsrepo?
|
||||
|
||||
VulsRepo is visualized based on the json report output in [vuls](https://github.com/future-architect/vuls).
|
||||
|
||||
# How to use this image
|
||||
|
||||
## vulsrepo
|
||||
|
||||
```console
|
||||
$docker run -dt \
|
||||
-v $PWD:/vuls \
|
||||
-p 80:80 \
|
||||
vuls/vulsrepo
|
||||
```
|
||||
|
||||
# User Feedback
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
|
||||
|
||||
## Issues
|
||||
|
||||
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
|
||||
|
||||
## Contributing
|
||||
|
||||
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
|
||||
1. get original code: go get github.com/future-architect/vuls
|
||||
1. work on original code
|
||||
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
|
||||
1. push your changes: git push myfork
|
||||
1. create a new Pull Request
|
||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -53,12 +52,7 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
|
||||
|
||||
whereami := "localhost"
|
||||
if 0 < len(c.ServerName) {
|
||||
if 0 < len(c.Container.ContainerID) {
|
||||
whereami = fmt.Sprintf(
|
||||
"%s_%s", c.ServerName, c.Container.Name)
|
||||
} else {
|
||||
whereami = fmt.Sprintf("%s", c.ServerName)
|
||||
}
|
||||
whereami = c.GetServerName()
|
||||
}
|
||||
|
||||
if _, err := os.Stat(logDir); err == nil {
|
||||
|
||||
13
util/util.go
13
util/util.go
@@ -111,7 +111,7 @@ func ProxyEnv() string {
|
||||
|
||||
// PrependProxyEnv prepends proxy enviroment variable
|
||||
func PrependProxyEnv(cmd string) string {
|
||||
if config.Conf.HTTPProxy == "" {
|
||||
if len(config.Conf.HTTPProxy) == 0 {
|
||||
return cmd
|
||||
}
|
||||
return fmt.Sprintf("%s %s", ProxyEnv(), cmd)
|
||||
@@ -124,3 +124,14 @@ func PrependProxyEnv(cmd string) string {
|
||||
// }
|
||||
// return time.Unix(i, 0), nil
|
||||
// }
|
||||
|
||||
// Truncate truncates string to the length
|
||||
func Truncate(str string, length int) string {
|
||||
if length < 0 {
|
||||
return str
|
||||
}
|
||||
if length <= len(str) {
|
||||
return str[:length]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -131,3 +131,43 @@ func TestPrependHTTPProxyEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
length int
|
||||
out string
|
||||
}{
|
||||
{
|
||||
in: "abcde",
|
||||
length: 3,
|
||||
out: "abc",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 5,
|
||||
out: "abcde",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 10,
|
||||
out: "abcdefg",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 0,
|
||||
out: "",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: -1,
|
||||
out: "abcdefg",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := Truncate(tt.in, tt.length)
|
||||
if actual != tt.out {
|
||||
t.Errorf("\nexpected: %s\n actual: %s", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
// Name is Vuls
|
||||
const Name string = "vuls"
|
||||
|
||||
// Version of Vuls
|
||||
const Version string = "0.1.5"
|
||||
Reference in New Issue
Block a user