Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
8b264a564a | ||
|
|
227da93c13 | ||
|
|
f939041606 | ||
|
|
e5b1a0bef8 | ||
|
|
b9404d0880 | ||
|
|
d6f12868be | ||
|
|
b79e96f6cf | ||
|
|
b066cc819e | ||
|
|
4b669a0d49 | ||
|
|
5e9de5d91a | ||
|
|
da68b061e3 | ||
|
|
6c3802071f | ||
|
|
ad84f09bce | ||
|
|
04166632d3 | ||
|
|
376238b1ad | ||
|
|
4f0dbff059 | ||
|
|
f506e2b50a | ||
|
|
88d2fbf5e2 | ||
|
|
7fd8cc5449 | ||
|
|
d033463b34 | ||
|
|
740208cf74 | ||
|
|
0036c0b10e | ||
|
|
834c832390 | ||
|
|
5bc99dfd25 | ||
|
|
c92d2d064a | ||
|
|
a60c21323c | ||
|
|
34d6d6e709 | ||
|
|
f2ddafc718 | ||
|
|
267afdd15d | ||
|
|
48b7b82e33 | ||
|
|
84e5e5432e | ||
|
|
201e18eac2 | ||
|
|
3f3f0b1fec | ||
|
|
ca697c5038 | ||
|
|
5aeeb4e8b4 | ||
|
|
c285f9f587 | ||
|
|
d046608426 | ||
|
|
b91ed9cff5 | ||
|
|
185d85bfdd | ||
|
|
44b2c1464a | ||
|
|
a0762a0a6c | ||
|
|
2ad7660c09 | ||
|
|
d8b8c38182 | ||
|
|
1d50e5126a | ||
|
|
aa55e30358 | ||
|
|
f662de50db | ||
|
|
24c798ad3a | ||
|
|
0e304ae546 | ||
|
|
cd604cbfe7 | ||
|
|
b8e66d9df0 | ||
|
|
a2c738e57b | ||
|
|
ae16cd708c | ||
|
|
2ed0443f88 | ||
|
|
38f1c5075d | ||
|
|
55043a6348 | ||
|
|
1f6eb55b86 | ||
|
|
d9d8500484 | ||
|
|
0fca75c2db | ||
|
|
a7dcccbdf9 | ||
|
|
396eb5aec2 | ||
|
|
79d2076e09 | ||
|
|
693dca4ca2 | ||
|
|
4047076033 | ||
|
|
acb0b71f1b | ||
|
|
32d9352048 | ||
|
|
0246556f7c | ||
|
|
a17284681f | ||
|
|
adb66e3298 | ||
|
|
ad062d777d | ||
|
|
ffe1ff73a5 | ||
|
|
54f9202d74 | ||
|
|
ef3e173fb2 | ||
|
|
1aeec2ae51 | ||
|
|
1f50bfd801 | ||
|
|
d3466eabe5 | ||
|
|
8aff1af939 | ||
|
|
af35303432 | ||
|
|
0ef1a5a3ce | ||
|
|
e958bc8212 | ||
|
|
e0ca6e89d1 | ||
|
|
55d8ae124a | ||
|
|
5e28ec22e1 | ||
|
|
c3deb93489 | ||
|
|
a9aca94848 | ||
|
|
f3c06890dd | ||
|
|
d9d0e629fd | ||
|
|
17181405e3 | ||
|
|
c209564945 | ||
|
|
2da01db438 | ||
|
|
8c4913d411 | ||
|
|
e7ffc24844 | ||
|
|
259f23f6ee | ||
|
|
0de38b99c2 | ||
|
|
1044fb8574 | ||
|
|
e5bfa1bd6f | ||
|
|
a29b2a2ad9 | ||
|
|
b6899ce461 | ||
|
|
32c11af07c | ||
|
|
6ff55d24d0 | ||
|
|
055aacd7f6 | ||
|
|
5ecf58fd56 | ||
|
|
8a9106052f | ||
|
|
91264547c9 | ||
|
|
3190b877ae | ||
|
|
f8a8cc4676 | ||
|
|
93ee329315 | ||
|
|
b45163388d | ||
|
|
6029784f76 | ||
|
|
058ab55a6f | ||
|
|
1005d241b8 | ||
|
|
33b1ccba67 | ||
|
|
a5549fb500 | ||
|
|
b057ed3e77 | ||
|
|
1e88cc10e7 | ||
|
|
2f8634383e | ||
|
|
86f9e5ce96 | ||
|
|
9ae42d647c | ||
|
|
54d6217b93 | ||
|
|
150b1c2406 | ||
|
|
51b6f1b5f3 | ||
|
|
3eae14cef6 | ||
|
|
cc6dc1ca69 | ||
|
|
7f2361f58c | ||
|
|
7cb02d77ae | ||
|
|
52cc9b0cc0 | ||
|
|
d91bf61038 | ||
|
|
d5f81674f8 | ||
|
|
9381883835 |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,9 +1,14 @@
|
||||
vuls
|
||||
.vscode
|
||||
*.txt
|
||||
*.json
|
||||
*.sqlite3
|
||||
*.db
|
||||
tags
|
||||
.gitmodules
|
||||
coverage.out
|
||||
issues/
|
||||
*.txt
|
||||
vendor/
|
||||
log/
|
||||
.gitmodules
|
||||
vuls
|
||||
*.sqlite3
|
||||
results/
|
||||
*config.toml
|
||||
|
||||
111
CHANGELOG.md
111
CHANGELOG.md
@@ -1,5 +1,116 @@
|
||||
# Change Log
|
||||
|
||||
## [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)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Enable to scan without running go-cve-dictionary as server mode [\#84](https://github.com/future-architect/vuls/issues/84)
|
||||
- Support high-speed scanning for CentOS [\#138](https://github.com/future-architect/vuls/pull/138) ([tai-ga](https://github.com/tai-ga))
|
||||
- Add configtest subcommand. skip un-ssh-able servers. [\#134](https://github.com/future-architect/vuls/pull/134) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support -report-azure-blob option [\#130](https://github.com/future-architect/vuls/pull/130) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add optional key-values that will be outputted to JSON in config [\#117](https://github.com/future-architect/vuls/pull/117) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Change dir structure [\#115](https://github.com/future-architect/vuls/pull/115) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add some validation of loading config. user, host and port [\#113](https://github.com/future-architect/vuls/pull/113) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Support scanning with external ssh command [\#101](https://github.com/future-architect/vuls/pull/101) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Detect Platform and get instance-id of amazon ec2 [\#95](https://github.com/future-architect/vuls/pull/95) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add -report-s3 option [\#92](https://github.com/future-architect/vuls/pull/92) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Added FreeBSD support. [\#90](https://github.com/future-architect/vuls/pull/90) ([justyntemme](https://github.com/justyntemme))
|
||||
- Add glide files for vendoring [\#89](https://github.com/future-architect/vuls/pull/89) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix README, change -cvedbpath to -cve-dictionary-dbpath \#84 [\#85](https://github.com/future-architect/vuls/pull/85) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add option for it get cve detail from cve.sqlite3. [\#81](https://github.com/future-architect/vuls/pull/81) ([ymd38](https://github.com/ymd38))
|
||||
- Add -report-text option, Fix small bug of report in japanese [\#78](https://github.com/future-architect/vuls/pull/78) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add JSONWriter, Fix CVE sort order of report [\#77](https://github.com/future-architect/vuls/pull/77) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Docker: Panic [\#76](https://github.com/future-architect/vuls/issues/76)
|
||||
- Fix apt command to scan correctly when system locale is not english [\#149](https://github.com/future-architect/vuls/pull/149) ([kit494way](https://github.com/kit494way))
|
||||
- Disable -ask-sudo-password for security reasons [\#148](https://github.com/future-architect/vuls/pull/148) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix no tty error while executing with -external-ssh option [\#143](https://github.com/future-architect/vuls/pull/143) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- wrong log packages [\#141](https://github.com/future-architect/vuls/pull/141) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix platform detection. [\#137](https://github.com/future-architect/vuls/pull/137) ([Rompei](https://github.com/Rompei))
|
||||
- Fix nil pointer when scan with -cve-dictionary-dbpath and cpeNames [\#111](https://github.com/future-architect/vuls/pull/111) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove vulndb file before pkg audit [\#110](https://github.com/future-architect/vuls/pull/110) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add error handling when unable to connect via ssh. status code: 255 [\#108](https://github.com/future-architect/vuls/pull/108) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Enable to detect vulnerabilities on FreeBSD [\#98](https://github.com/future-architect/vuls/pull/98) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix unknown format err while check-update on RHEL6.5 [\#93](https://github.com/future-architect/vuls/pull/93) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix type of SMTP Port of discovery command's output [\#88](https://github.com/future-architect/vuls/pull/88) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error msg when go-cve-dictionary is unavailable \#84 [\#86](https://github.com/future-architect/vuls/pull/86) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix error handling to avoid nil pointer err on debian [\#83](https://github.com/future-architect/vuls/pull/83) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix nil pointer while doing apt-cache policy on ubuntu \#76 [\#82](https://github.com/future-architect/vuls/pull/82) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- fix log import url [\#79](https://github.com/future-architect/vuls/pull/79) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
- Fix error handling of gorequest [\#75](https://github.com/future-architect/vuls/pull/75) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix freezing forever when no args specified in TUI mode [\#73](https://github.com/future-architect/vuls/pull/73) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- mv version.go version/version.go to run main.go without compile [\#71](https://github.com/future-architect/vuls/pull/71) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- SSh password authentication failed on FreeBSD [\#99](https://github.com/future-architect/vuls/issues/99)
|
||||
- BUG: -o pipefail is not work on FreeBSD's /bin/sh. because it isn't bash [\#91](https://github.com/future-architect/vuls/issues/91)
|
||||
- Use ~/.ssh/config [\#62](https://github.com/future-architect/vuls/issues/62)
|
||||
- SSH ciphers [\#37](https://github.com/future-architect/vuls/issues/37)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Update README \#138 [\#144](https://github.com/future-architect/vuls/pull/144) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix a typo [\#142](https://github.com/future-architect/vuls/pull/142) ([dtan4](https://github.com/dtan4))
|
||||
- Remove unnecessary step in readme of docker setup [\#140](https://github.com/future-architect/vuls/pull/140) ([mikkame](https://github.com/mikkame))
|
||||
- Update logo [\#139](https://github.com/future-architect/vuls/pull/139) ([chanomaru](https://github.com/chanomaru))
|
||||
- Update README.ja.md to fix wrong tips. [\#135](https://github.com/future-architect/vuls/pull/135) ([a2atsu](https://github.com/a2atsu))
|
||||
- add tips about NVD JVN issue [\#133](https://github.com/future-architect/vuls/pull/133) ([a2atsu](https://github.com/a2atsu))
|
||||
- Fix README wrong links [\#129](https://github.com/future-architect/vuls/pull/129) ([aomoriringo](https://github.com/aomoriringo))
|
||||
- Add logo [\#126](https://github.com/future-architect/vuls/pull/126) ([chanomaru](https://github.com/chanomaru))
|
||||
- Improve setup/docker [\#125](https://github.com/future-architect/vuls/pull/125) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Fix scan command help [\#124](https://github.com/future-architect/vuls/pull/124) ([aomoriringo](https://github.com/aomoriringo))
|
||||
- added dockernized-vuls with vulsrepo [\#121](https://github.com/future-architect/vuls/pull/121) ([hikachan](https://github.com/hikachan))
|
||||
- Fix detect platform on azure and degital ocean [\#119](https://github.com/future-architect/vuls/pull/119) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Remove json marshall-indent [\#118](https://github.com/future-architect/vuls/pull/118) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Improve Readme.ja [\#116](https://github.com/future-architect/vuls/pull/116) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Add architecture diag to README.md [\#114](https://github.com/future-architect/vuls/pull/114) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Rename linux.go to base.go [\#100](https://github.com/future-architect/vuls/pull/100) ([kotakanbe](https://github.com/kotakanbe))
|
||||
- Update README.md [\#74](https://github.com/future-architect/vuls/pull/74) ([yoshi-taka](https://github.com/yoshi-taka))
|
||||
- Refactoring debian.go [\#72](https://github.com/future-architect/vuls/pull/72) ([kotakanbe](https://github.com/kotakanbe))
|
||||
|
||||
## [v0.1.4](https://github.com/future-architect/vuls/tree/v0.1.4) (2016-05-24)
|
||||
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.3...v0.1.4)
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@
|
||||
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
|
||||
|
||||
all: test
|
||||
|
||||
|
||||
434
README.fr.md
434
README.fr.md
@@ -7,6 +7,7 @@ Scanneur de vulnérabilité Linux, sans agent, écrit en golang
|
||||
|
||||
Nous avons une équipe Slack. [Rejoignez notre Slack Team](http://goo.gl/forms/xm5KFo35tu)
|
||||
|
||||
[README en English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
[README en Japonais](https://github.com/future-architect/vuls/blob/master/README.ja.md)
|
||||
|
||||
[](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
|
||||
@@ -144,22 +145,6 @@ Démarrez go-cve-dictionary en mode serveur.
|
||||
Lors de son premier démarrage go-cve-dictionary récupère la liste des vulnérabilités depuis NVD
|
||||
Cette opération prend environ 10 minutes (sur AWS).
|
||||
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
... Fetching ...
|
||||
$ ls -alh cve.sqlite3
|
||||
-rw-r--r-- 1 ec2-user ec2-user 7.0M Mar 24 13:20 cve.sqlite3
|
||||
```
|
||||
|
||||
Une fois les informations de vulnérabilités collectées redémarrez le mode serveur.
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
[Mar 24 15:21:55] INFO Opening DB. datafile: /home/ec2-user/cve.sqlite3
|
||||
[Mar 24 15:21:55] INFO Migrating DB
|
||||
[Mar 24 15:21:56] INFO Starting HTTP Sever...
|
||||
[Mar 24 15:21:56] INFO Listening on 127.0.0.1:1323
|
||||
```
|
||||
|
||||
## Step5. Déploiement de Vuls
|
||||
|
||||
Ouvrez un second terminal, connectez vous à l'instance ec2 via SSH
|
||||
@@ -193,7 +178,7 @@ $ vuls prepare
|
||||
## Step8. Scan
|
||||
|
||||
```
|
||||
$ vuls scan
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
|
||||
|
||||
... snip ...
|
||||
@@ -236,417 +221,4 @@ $ vuls tui
|
||||
|
||||
----
|
||||
|
||||
# Architecture
|
||||
|
||||

|
||||
|
||||
## go-cve-dictinary
|
||||
- Collecte les informations de vulnérabilités depuis NVD, JVN(Japonais), et les envoie dans SQLite.
|
||||
|
||||
## Vuls
|
||||
- Scan de vulnérabilités sur serveurs et création d'une liste contenant les CVE ID
|
||||
- Pour des informations plus détaillés sur une CVE, envoie une requete HTTP à go-cve-dictinary
|
||||
- Rapport à Slack et par Email
|
||||
- L'administrateur système peut voir les résultats du dernier rapport dans le terminal
|
||||
|
||||
----
|
||||
|
||||
# Exemples d'utilisation
|
||||
|
||||
## Scan de tous les serverus
|
||||
|
||||

|
||||
|
||||
## Scan d'un seul serveur
|
||||
|
||||
web/app server in the same configuration under the load balancer
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# OS supportés
|
||||
|
||||
| Distribution| Release |
|
||||
|:------------|-------------------:|
|
||||
| Ubuntu | 12, 14, 16|
|
||||
| Debian | 7, 8|
|
||||
| RHEL | 4, 5, 6, 7|
|
||||
| CentOS | 5, 6, 7|
|
||||
| Amazon Linux| All |
|
||||
|
||||
----
|
||||
|
||||
|
||||
# Usage: Détection Automatique de Serveurs
|
||||
|
||||
La sous-commande Discovery permet de détecter les serveurs actifs dans un range d'IP CIDR, les résultas sont directement affichés dans le terminal en respectant le format du fichier de configuration (TOML format).
|
||||
|
||||
```
|
||||
$ vuls discover -help
|
||||
discover:
|
||||
discover 192.168.0.0/24
|
||||
```
|
||||
|
||||
## Exemple
|
||||
|
||||
```
|
||||
$ vuls discover 172.31.4.0/24
|
||||
# Create config.toml using below and then ./vuls --config=/path/to/config.toml
|
||||
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "${servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
|
||||
Vous pouvez customiser votre configuration en utilisant ce modèle.
|
||||
|
||||
----
|
||||
|
||||
# Configuration
|
||||
|
||||
- Slack section
|
||||
```
|
||||
[slack]
|
||||
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
|
||||
channel = "#channel-name"
|
||||
#channel = "${servername}"
|
||||
iconEmoji = ":ghost:"
|
||||
authUser = "username"
|
||||
notifyUsers = ["@username"]
|
||||
```
|
||||
|
||||
- hookURL : Incomming 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.
|
||||
Be sure to create these channels before scanning.
|
||||
```
|
||||
[slack]
|
||||
channel = "${servername}"
|
||||
...snip...
|
||||
|
||||
[servers]
|
||||
|
||||
[servers.server1]
|
||||
host = "172.31.4.82"
|
||||
...snip...
|
||||
|
||||
[servers.server2]
|
||||
host = "172.31.4.83"
|
||||
...snip...
|
||||
```
|
||||
|
||||
- iconEmoji: emoji
|
||||
- authUser: username of the slack team
|
||||
- notifyUsers: a list of Slack usernames to send Slack notifications.
|
||||
If you set ["@foo", "@bar"] to notifyUsers, @foo @bar will be included in text.
|
||||
So @foo, @bar can receive mobile push notifications on their smartphone.
|
||||
|
||||
- Mail section
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
to = ["to@address.com"]
|
||||
cc = ["cc@address.com"]
|
||||
subjectPrefix = "[vuls]"
|
||||
```
|
||||
|
||||
- Default section
|
||||
```
|
||||
[default]
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
```
|
||||
Items of the default section will be used if not specified.
|
||||
|
||||
- servers section
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
#port = "22"
|
||||
#user = "root"
|
||||
#password = "password"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#keyPassword = "password"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
```
|
||||
Vous pouvez remplacer les valeurs par défaut indiquées en modifiant la section default
|
||||
Vuls supporte plusieurs méthodes d'authentification SSH :
|
||||
- SSH agent
|
||||
- SSH authentication par clés (avec mot de passe ou sans mot de passe)
|
||||
- Authentification par mot de passe
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Prepare
|
||||
|
||||
La sous-commande prepare installe tous les paquets nécessaires sur chaque serveur.
|
||||
|
||||
| Distribution| Release | Requirements |
|
||||
|:------------|-------------------:|:-------------|
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8| apptitude |
|
||||
| CentOS | 5| yum-plugin-security, yum-changelog |
|
||||
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 4, 5, 6, 7 | - |
|
||||
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare:
|
||||
prepare [-config=/path/to/config.toml] [-debug]
|
||||
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Scan
|
||||
|
||||
```
|
||||
$ vuls scan -help
|
||||
scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cvss-over=7]
|
||||
[-report-slack]
|
||||
[-report-mail]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-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
|
||||
SQL debug mode
|
||||
-http-proxy string
|
||||
http://proxy-url:port (default: empty)
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-report-mail
|
||||
Email report
|
||||
-report-slack
|
||||
Slack report
|
||||
-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)
|
||||
|
||||
```
|
||||
|
||||
## exemple
|
||||
|
||||
Lancez go-cve-dictionary en mode serveur avant de lancer un scan
|
||||
```
|
||||
$ go-cve-dictionary server
|
||||
```
|
||||
|
||||
### Scan tous les serveurs identifiés dans le fichier de configuration
|
||||
```
|
||||
$ vuls scan --report-slack --report-mail --cvss-over=7
|
||||
```
|
||||
Via cette simple commande Vuls va : ..
|
||||
- Scanner tous les serveurs identifiés dans le fichier de configuration
|
||||
- Envoyer les résultas du scan à slack et par email
|
||||
- Ne rapporter que les CVE dont la note CVSS est au dessus de 7
|
||||
- Afficher les résultats du scan dans le terminal
|
||||
|
||||
### Scan de serveurs spécifiques
|
||||
```
|
||||
$ vuls scan server1 server2
|
||||
```
|
||||
Via cette simple commande Vuls va : ..
|
||||
- Scanner seulement 2 serveurs. (server1, server2)
|
||||
- Afficher les résultats du scan dans le terminal
|
||||
|
||||
----
|
||||
|
||||
# Utilisation : Recherche de vulnérabilités sur des paquets non compris dans l'OS
|
||||
|
||||
Il est possible de détecter des vulnérabilités sur des programmes que vous avez compilés, des lors que les libraries et frameworks ont été enregistré dans [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
|
||||
- Comment rechercher dans CPE via le nom du programme
|
||||
- [NVD: Search Common Platform Enumerations (CPE)](https://web.nvd.nist.gov/view/cpe/search)
|
||||
**Check CPE Naming Format: 2.2**
|
||||
|
||||
- Configuration
|
||||
Pour détecter des vulnérabilités sur Ruby on Rails v4.2.1, cpeNames doit etre déclaré dans la section servers.
|
||||
```
|
||||
[servers]
|
||||
|
||||
[servers.172-31-4-82]
|
||||
host = "172.31.4.82"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/username/.ssh/id_rsa"
|
||||
cpeNames = [
|
||||
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
]
|
||||
```
|
||||
|
||||
# Utilisation : Mise à jour des données NVD.
|
||||
|
||||
```
|
||||
$ 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.
|
||||
```
|
||||
|
||||
- Récupérer toutes les données jusqu'à aujourd'hui
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -entire
|
||||
```
|
||||
|
||||
- Reçupérer les données des 2 denières années
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -last2y
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Misc
|
||||
|
||||
- HTTP Proxy Support
|
||||
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.
|
||||
Use job scheduler like Cron (with -last2y option).
|
||||
|
||||
- How to cross compile
|
||||
```bash
|
||||
$ cd /path/to/your/local-git-reporsitory/vuls
|
||||
$ GOOS=linux GOARCH=amd64 go build -o vuls.amd64
|
||||
```
|
||||
|
||||
- Logging
|
||||
Log wrote to under /var/log/vuls/
|
||||
|
||||
- Debug
|
||||
Run with --debug, --sql-debug option.
|
||||
|
||||
- Ajusting Open File Limit
|
||||
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
|
||||
|
||||
- Does Vuls accept ssh connections with fish-shell or old zsh as the login shell?
|
||||
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
|
||||
|
||||
- Windows
|
||||
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)
|
||||
|
||||
----
|
||||
|
||||
# Data Source
|
||||
|
||||
- [NVD](https://nvd.nist.gov/)
|
||||
- [JVN(Japanese)](http://jvndb.jvn.jp/apis/myjvn/)
|
||||
|
||||
|
||||
# Authors
|
||||
|
||||
kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.
|
||||
|
||||
----
|
||||
|
||||
# Contribute
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
|
||||
----
|
||||
|
||||
# Change Log
|
||||
|
||||
Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHANGELOG.md).
|
||||
|
||||
----
|
||||
|
||||
# Licence
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
|
||||
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
|
||||
1144
README.ja.md
1144
README.ja.md
File diff suppressed because it is too large
Load Diff
520
README.md
520
README.md
@@ -5,7 +5,9 @@
|
||||
[](https://github.com/future-architect/vuls/blob/master/LICENSE.txt)
|
||||
|
||||
|
||||
Vulnerability scanner for Linux, agentless, written in golang.
|
||||

|
||||
|
||||
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
|
||||
|
||||
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
|
||||
@@ -25,7 +27,7 @@ We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
|
||||
For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.
|
||||
To avoid downtime in production environment, it is common for system administrator to choose not to use the automatic update option provided by package manager and to perform update manually.
|
||||
This leads to the following problems.
|
||||
- System administrator will have to constantly watch out for any new vulnerabilities in NVD(National Vulnerability Database) and etc.
|
||||
- System administrator will have to constantly watch out for any new vulnerabilities in NVD(National Vulnerability Database) or similar databases.
|
||||
- It might be impossible for the system administrator to monitor all the software if there are a large number of software installed in server.
|
||||
- It is expensive to perform analysis to determine the servers affected by new vulnerabilities. The possibility of overlooking a server or two during analysis is there.
|
||||
|
||||
@@ -34,7 +36,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
- Informs users of the vulnerabilities that are related to the system.
|
||||
- Informs users of the servers that are affected.
|
||||
- Vulnerability detection is done automatically to prevent any oversight.
|
||||
- Report is generated on regular basis using CRON etc. to manage vulnerability.
|
||||
- Report is generated on regular basis using CRON or other methods. to manage vulnerability.
|
||||
|
||||

|
||||
|
||||
@@ -42,8 +44,8 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
|
||||
# Main Features
|
||||
|
||||
- Scan for any vulnerabilities in Linux Server
|
||||
- Supports Ubuntu, Debian, CentOS, Amazon Linux, RHEL
|
||||
- Scan for any vulnerabilities in Linux/FreeBSD Server
|
||||
- Supports Ubuntu, Debian, CentOS, Amazon Linux, RHEL, FreeBSD
|
||||
- Cloud, on-premise, Docker
|
||||
- Scan middleware that are not included in OS package management
|
||||
- Scan middleware, programming language libraries and framework for vulnerability
|
||||
@@ -53,7 +55,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
- 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)).
|
||||
|
||||
----
|
||||
|
||||
@@ -63,9 +65,26 @@ Vuls is a tool created to solve the problems listed above. It has the following
|
||||
|
||||
----
|
||||
|
||||
# Hello Vuls
|
||||
# Setup Vuls
|
||||
|
||||
This tutorial will let you scan the vulnerabilities on the localhost with vuls.
|
||||
There are 3 ways to setup Vuls.
|
||||
|
||||
- Docker container
|
||||
Dockernized-Vuls with vulsrepo UI in it.
|
||||
You can run install and run Vuls on your machine with only a few commands.
|
||||
see https://github.com/future-architect/vuls/tree/master/setup/docker
|
||||
|
||||
- Chef
|
||||
see https://github.com/sadayuki-matsuno/vuls-cookbook
|
||||
|
||||
- Manually
|
||||
Hello Vuls Tutorial shows how to setup vuls manually.
|
||||
|
||||
----
|
||||
|
||||
# Tutorial: Hello Vuls
|
||||
|
||||
This tutorial will let you scan the vulnerabilities on the localhost with Vuls.
|
||||
This can be done in the following steps.
|
||||
|
||||
1. Launch Amazon Linux
|
||||
@@ -77,6 +96,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
|
||||
|
||||
@@ -101,12 +121,15 @@ $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||
$ chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
Vuls doesn't support SSH password authentication. So you have to use SSH key-based authentication.
|
||||
And also, SUDO with password is not supported for security reasons. So you have to define NOPASSWORD in /etc/sudoers on target servers.
|
||||
|
||||
## Step3. Install requirements
|
||||
|
||||
Vuls requires the following packages.
|
||||
|
||||
- SQLite3
|
||||
- git
|
||||
- git v2
|
||||
- gcc
|
||||
- go v1.6
|
||||
- https://golang.org/doc/install
|
||||
@@ -142,6 +165,10 @@ $ sudo chmod 700 /var/log/vuls
|
||||
$ go get github.com/kotakanbe/go-cve-dictionary
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
Fetch vulnerability data from NVD.
|
||||
It takes about 10 minutes (on AWS).
|
||||
|
||||
@@ -152,27 +179,23 @@ $ ls -alh cve.sqlite3
|
||||
-rw-r--r-- 1 ec2-user ec2-user 7.0M Mar 24 13:20 cve.sqlite3
|
||||
```
|
||||
|
||||
Now we successfully collected vulnerbility data, then start as server.
|
||||
```bash
|
||||
$ go-cve-dictionary server
|
||||
[Mar 24 15:21:55] INFO Opening DB. datafile: /home/ec2-user/cve.sqlite3
|
||||
[Mar 24 15:21:55] INFO Migrating DB
|
||||
[Mar 24 15:21:56] INFO Starting HTTP Sever...
|
||||
[Mar 24 15:21:56] INFO Listening on 127.0.0.1:1323
|
||||
```
|
||||
## Step5. Deploy Vuls
|
||||
|
||||
## Step5. Deploy vuls
|
||||
|
||||
Launch a new terminal, SSH to the ec2 instance.
|
||||
Launch a new terminal and SSH to the ec2 instance.
|
||||
|
||||
go get
|
||||
```
|
||||
$ go get github.com/future-architect/vuls
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
## Step6. Config
|
||||
|
||||
Create a config file(TOML format).
|
||||
Create a config file(TOML format).
|
||||
Then check the config.
|
||||
|
||||
```
|
||||
$ cat config.toml
|
||||
@@ -183,9 +206,11 @@ host = "172.31.4.82"
|
||||
port = "22"
|
||||
user = "ec2-user"
|
||||
keyPath = "/home/ec2-user/.ssh/id_rsa"
|
||||
|
||||
$ vuls configtest
|
||||
```
|
||||
|
||||
## Step7. Setting up target servers for vuls
|
||||
## Step7. Setting up target servers for Vuls
|
||||
|
||||
```
|
||||
$ vuls prepare
|
||||
@@ -195,8 +220,12 @@ see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
|
||||
## Step8. Start Scanning
|
||||
|
||||
```
|
||||
$ vuls scan
|
||||
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
|
||||
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
INFO[0000] Start scanning (config: /home/ec2-user/config.toml)
|
||||
INFO[0000] Start scanning
|
||||
INFO[0000] config: /home/ec2-user/config.toml
|
||||
INFO[0000] cve-dictionary: /home/ec2-user/cve.sqlite3
|
||||
|
||||
|
||||
... snip ...
|
||||
|
||||
@@ -235,12 +264,16 @@ $ 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/).
|
||||
|
||||
----
|
||||
|
||||
# Hello Vuls in a docker container
|
||||
# Setup Vuls in a Docker Container
|
||||
|
||||
see https://github.com/future-architect/vuls/tree/master/docker
|
||||
see https://github.com/future-architect/vuls/tree/master/setup/docker
|
||||
|
||||
----
|
||||
|
||||
@@ -249,15 +282,42 @@ see https://github.com/future-architect/vuls/tree/master/docker
|
||||

|
||||
|
||||
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
|
||||
- Fetch vulnerability information from NVD, JVN(Japanese), then insert into SQLite3.
|
||||
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3.
|
||||
|
||||
## Vuls
|
||||
- Scan vulnerabilities on the servers and create a list of the CVE ID
|
||||
- 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, Email
|
||||
- System operator can view the latest report by terminal
|
||||
- Send a report by Slack and Email
|
||||
- Show the latest report on your terminal
|
||||
|
||||

|
||||
|
||||
----
|
||||
# Performance Considerations
|
||||
|
||||
- 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.
|
||||
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
|
||||
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 |
|
||||
|:------------|:-------------------|:-------------|
|
||||
| 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 |
|
||||
|
||||
----
|
||||
|
||||
@@ -281,16 +341,17 @@ 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 |
|
||||
| Amazon Linux| All|
|
||||
| FreeBSD | 10|
|
||||
|
||||
----
|
||||
|
||||
|
||||
# Usage: Automatic Server Discovery
|
||||
|
||||
Discovery subcommand discovers active servers specified in CIDR range, then print the template of config file(TOML format) to terminal.
|
||||
Discovery subcommand discovers active servers specified in CIDR range, then display the template of config file(TOML format) to terminal.
|
||||
|
||||
```
|
||||
$ vuls discover -help
|
||||
@@ -314,7 +375,7 @@ notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
smtpPort = "465"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -326,6 +387,13 @@ subjectPrefix = "[vuls]"
|
||||
#port = "22"
|
||||
#user = "username"
|
||||
#keyPath = "/home/username/.ssh/id_rsa"
|
||||
#cpeNames = [
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
|
||||
[servers]
|
||||
|
||||
@@ -338,6 +406,9 @@ host = "172.31.4.82"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
```
|
||||
|
||||
You can customize your configuration using this template.
|
||||
@@ -359,8 +430,8 @@ You can customize your configuration using this template.
|
||||
|
||||
- hookURL : Incomming webhook's URL
|
||||
- channel : channel name.
|
||||
If you set ${servername} to channel, the report will be sent to each channel.
|
||||
In the following example, the report will be sent to the #server1 and #server2.
|
||||
If you set `${servername}` to channel, the report will be sent to each channel.
|
||||
In the following example, the report will be sent to the `#server1` and `#server2`.
|
||||
Be sure to create these channels before scanning.
|
||||
```
|
||||
[slack]
|
||||
@@ -381,14 +452,14 @@ You can customize your configuration using this template.
|
||||
- iconEmoji: emoji
|
||||
- authUser: username of the slack team
|
||||
- notifyUsers: a list of Slack usernames to send Slack notifications.
|
||||
If you set ["@foo", "@bar"] to notifyUsers, @foo @bar will be included in text.
|
||||
If you set `["@foo", "@bar"]` to notifyUsers, @foo @bar will be included in text.
|
||||
So @foo, @bar can receive mobile push notifications on their smartphone.
|
||||
|
||||
- Mail section
|
||||
```
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
smtpPort = "465"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -407,6 +478,9 @@ You can customize your configuration using this template.
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
```
|
||||
Items of the default section will be used if not specified.
|
||||
|
||||
@@ -423,12 +497,67 @@ You can customize your configuration using this template.
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
```
|
||||
|
||||
You can overwrite the default value specified in default section.
|
||||
Vuls supports multiple SSH authentication methods.
|
||||
|
||||
- host: IP address or hostname of target server
|
||||
- port: SSH Port number
|
||||
- user: SSH username
|
||||
- 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)
|
||||
- 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)
|
||||
|
||||
Multiple SSH authentication methods are supported.
|
||||
- SSH agent
|
||||
- SSH public key authentication (with password, empty password)
|
||||
- Password authentication
|
||||
- SSH public key authentication (with password and empty password)
|
||||
Password authentication is not supported.
|
||||
|
||||
----
|
||||
|
||||
# Usage: Configtest
|
||||
|
||||
Configtest subcommand check if vuls is able to connect via ssh to servers/containers defined in the config.toml.
|
||||
```
|
||||
$ vuls configtest --help
|
||||
configtest:
|
||||
configtest
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-key-password]
|
||||
[-ssh-external]
|
||||
[-debug]
|
||||
|
||||
[SERVER]...
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-config string
|
||||
/path/to/toml (default "/Users/kotakanbe/go/src/github.com/future-architect/vuls/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
```
|
||||
|
||||
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, RHEL
|
||||
```
|
||||
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.
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
@@ -440,29 +569,26 @@ 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 | - |
|
||||
|
||||
|
||||
```
|
||||
$ vuls prepare -help
|
||||
prepare
|
||||
[-config=/path/to/config.toml] [-debug]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[SERVER]...
|
||||
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-ask-sudo-password
|
||||
Ask sudo password of target servers before scanning
|
||||
-config string
|
||||
/path/to/toml (default "$PWD/config.toml")
|
||||
-debug
|
||||
debug mode
|
||||
-use-unattended-upgrades
|
||||
[Deprecated] For Ubuntu, install unattended-upgrades
|
||||
```
|
||||
|
||||
----
|
||||
@@ -476,29 +602,56 @@ scan:
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cache-dbpath=/path/to/cache.db]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-report-slack]
|
||||
[-ssh-external]
|
||||
[-report-azure-blob]
|
||||
[-report-json]
|
||||
[-report-mail]
|
||||
[-report-s3]
|
||||
[-report-slack]
|
||||
[-report-text]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-aws-profile=default]
|
||||
[-aws-region=us-west-2]
|
||||
[-aws-s3-bucket=bucket_name]
|
||||
[-azure-account=accout]
|
||||
[-azure-key=key]
|
||||
[-azure-container=container]
|
||||
[SERVER]...
|
||||
|
||||
|
||||
-ask-key-password
|
||||
Ask ssh privatekey password before scanning
|
||||
-ask-sudo-password
|
||||
Ask sudo password of target servers before scanning
|
||||
-aws-profile string
|
||||
AWS Profile to use (default "default")
|
||||
-aws-region string
|
||||
AWS Region to use (default "us-east-1")
|
||||
-aws-s3-bucket string
|
||||
S3 bucket name
|
||||
-azure-account string
|
||||
Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified
|
||||
-azure-container string
|
||||
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")
|
||||
-cve-dictionary-dbpath string
|
||||
/path/to/sqlite3 (For get cve detail from cve.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
|
||||
@@ -509,65 +662,168 @@ scan:
|
||||
Don't report the unscored CVEs
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-report-json
|
||||
Write report to JSON files ($PWD/results/current)
|
||||
-report-mail
|
||||
Email report
|
||||
Send report via Email
|
||||
-report-s3
|
||||
Write report to S3 (bucket/yyyyMMdd_HHmm)
|
||||
-report-slack
|
||||
Slack report
|
||||
-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)
|
||||
|
||||
Send report via Slack
|
||||
-report-text
|
||||
Write report to text files ($PWD/results/current)
|
||||
-results-dir string
|
||||
/path/to/results (default "$PWD/results")
|
||||
-ssh-external
|
||||
Use external ssh command. Default: Use the Go native implementation
|
||||
```
|
||||
|
||||
## ask-key-password option
|
||||
## -ssh-external option
|
||||
|
||||
Vuls supports different types of SSH.
|
||||
|
||||
By Defaut, 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.
|
||||
Don't forget to add below line to /etc/sudoers on the target servers. (username: vuls)
|
||||
```
|
||||
Defaults:vuls !requiretty
|
||||
```
|
||||
|
||||
|
||||
## -ask-key-password option
|
||||
|
||||
| SSH key password | -ask-key-password | |
|
||||
|:-----------------|:-------------------|:----|
|
||||
| empty password | - | |
|
||||
| with password | required | or use ssh-agent |
|
||||
|
||||
## ask-sudo-password option
|
||||
## -report-json , -report-text option
|
||||
|
||||
| sudo password on target servers | -ask-sudo-password | |
|
||||
|:-----------------|:-------|:------|
|
||||
| NOPASSWORD | - | defined as NOPASSWORD in /etc/sudoers on target servers |
|
||||
| with password | required | . |
|
||||
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.
|
||||
|
||||
|
||||
## example
|
||||
|
||||
Run go-cve-dictionary as server mode before scanning.
|
||||
## Example: Scan all servers defined in config file
|
||||
```
|
||||
$ go-cve-dictionary server
|
||||
```
|
||||
|
||||
### Scan all servers defined in config file
|
||||
```
|
||||
$ vuls scan --report-slack --report-mail --cvss-over=7 -ask-sudo-password -ask-key-password
|
||||
$ vuls scan \
|
||||
--report-slack \
|
||||
--report-mail \
|
||||
--cvss-over=7 \
|
||||
-ask-key-password \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Ask sudo password and ssh key passsword before scanning
|
||||
- Ask SSH key passsword 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
|
||||
- Print scan result to terminal
|
||||
|
||||
### Scan specific servers
|
||||
## Example: Scan specific servers
|
||||
```
|
||||
$ vuls scan server1 server2
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3 \
|
||||
server1 server2
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
|
||||
- Sudo with no password (without -ask-sudo-password option)
|
||||
- Scan only 2 servers (server1, server2)
|
||||
- Print scan result to terminal
|
||||
|
||||
## Example: Put results in S3 bucket
|
||||
To put results in S3 bucket, configure following settings in AWS before scanning.
|
||||
- Create S3 bucket. see [Creating a Bucket](http://docs.aws.amazon.com/AmazonS3/latest/UG/CreatingaBucket.html)
|
||||
- Create access key. The access key must have read and write access to the AWS S3 bucket. see [Managing Access Keys for IAM Users](http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
|
||||
- Configure the security credentials. see [Configuring the AWS Command Line Interface](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html)
|
||||
|
||||
```
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3 \
|
||||
-report-s3 \
|
||||
-aws-region=ap-northeast-1 \
|
||||
-aws-s3-bucket=vuls \
|
||||
-aws-profile=default
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
|
||||
- Scan all servers defined in config file
|
||||
- Put scan result(JSON) in S3 bucket. The bucket name is "vuls" in ap-northeast-1 and profile is "default"
|
||||
|
||||
## Example: Put results in Azure Blob storage
|
||||
|
||||
To put results in Azure Blob Storage, configure following settings in Azure before scanning.
|
||||
- Create a container
|
||||
|
||||
```
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3 \
|
||||
-report-azure-blob \
|
||||
-azure-container=vuls \
|
||||
-azure-account=test \
|
||||
-azure-key=access-key-string
|
||||
```
|
||||
With this sample command, it will ..
|
||||
- Use SSH Key-Based authentication with empty password (without -ask-key-password option)
|
||||
- Scan all servers defined in config file
|
||||
- Put scan result(JSON) in Azure Blob Storage. The container name is "vuls", storage account is "test" and accesskey is "access-key-string"
|
||||
|
||||
account and access key can be defined in environment variables.
|
||||
```
|
||||
$ export AZURE_STORAGE_ACCOUNT=test
|
||||
$ export AZURE_STORAGE_ACCESS_KEY=access-key-string
|
||||
$ vuls scan \
|
||||
-cve-dictionary-dbpath=$PWD/cve.sqlite3 \
|
||||
-report-azure-blob \
|
||||
-azure-container=vuls
|
||||
```
|
||||
|
||||
## Example: Add optional key-value pairs to JSON
|
||||
|
||||
Optional key-value can be outputted to JSON.
|
||||
The key-value in the default section will be overwritten by servers section's key-value.
|
||||
For instance, you can use this field for Azure ResourceGroup name, Azure VM Name and so on.
|
||||
|
||||
- config.toml
|
||||
```toml
|
||||
[default]
|
||||
optional = [
|
||||
["key1", "default_value"],
|
||||
["key3", "val3"],
|
||||
]
|
||||
|
||||
[servers.bsd]
|
||||
host = "192.168.11.11"
|
||||
user = "kanbe"
|
||||
optional = [
|
||||
["key1", "val1"],
|
||||
["key2", "val2"],
|
||||
]
|
||||
```
|
||||
|
||||
- bsd.json
|
||||
```json
|
||||
[
|
||||
{
|
||||
"ServerName": "bsd",
|
||||
"Family": "FreeBSD",
|
||||
"Release": "10.3-RELEASE",
|
||||
.... snip ...
|
||||
"Optional": [
|
||||
[ "key1", "val1" ],
|
||||
[ "key2", "val2" ],
|
||||
[ "key3", "val3" ]
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Usage: Scan vulnerability of non-OS package
|
||||
|
||||
It is possible to detect vulnerabilities something you compiled by yourself, the language libraries and the frameworks that have been registered in the [CPE](https://nvd.nist.gov/cpe.cfm).
|
||||
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).
|
||||
|
||||
- How to search CPE name by software name
|
||||
- [NVD: Search Common Platform Enumerations (CPE)](https://web.nvd.nist.gov/view/cpe/search)
|
||||
@@ -599,7 +855,7 @@ Vuls scans Docker containers via `docker exec` instead of SSH.
|
||||
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
|
||||
|
||||
- To scan all of running containers
|
||||
"${running}" needs to be set in the containers item.
|
||||
`"${running}"` needs to be set in the containers item.
|
||||
```
|
||||
[servers]
|
||||
|
||||
@@ -612,9 +868,9 @@ For more details, see [Architecture section](https://github.com/future-architect
|
||||
|
||||
- To scan specific containers
|
||||
The container ID or container name needs to be set in the containers item.
|
||||
In the following example, only "container_name_a" and "4aa37a8b63b9" will be scanned.
|
||||
In the following example, only `container_name_a` and `4aa37a8b63b9` will be scanned.
|
||||
Be sure to check these containers are running state before scanning.
|
||||
If specified containers are exited, vuls gives up scanning with printing error message.
|
||||
If specified containers are not running, Vuls gives up scanning with printing error message.
|
||||
```
|
||||
[servers]
|
||||
|
||||
@@ -632,10 +888,10 @@ 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
|
||||
|
||||
@@ -679,8 +935,20 @@ $ ./vuls history | peco | ./vuls tui
|
||||
|
||||
[](https://asciinema.org/a/emi7y7docxr60bq080z10t7v8)
|
||||
|
||||
# Usage: go-cve-dictionary on different server
|
||||
|
||||
# Usage: Update NVD Data.
|
||||
Run go-cve-dictionary as server mode before scanning on 192.168.10.1
|
||||
```
|
||||
$ go-cve-dictionary server -bind=192.168.10.1 -port=1323
|
||||
```
|
||||
|
||||
Run Vuls with -cve-dictionary-url option.
|
||||
|
||||
```
|
||||
$ vuls scan -cve-dictionary-url=http://192.168.0.1:1323
|
||||
```
|
||||
|
||||
# Usage: Update NVD Data
|
||||
|
||||
```
|
||||
$ go-cve-dictionary fetchnvd -h
|
||||
@@ -715,6 +983,50 @@ $ 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
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
|
||||
- Update vuls
|
||||
```
|
||||
$ cd $GOPATH/src/github.com/future-architect/vuls
|
||||
$ git pull
|
||||
$ glide install
|
||||
$ go install
|
||||
```
|
||||
- The binaries are created under $GOPARH/bin
|
||||
- If the DB schema was changed, please specify new SQLite3 DB file.
|
||||
|
||||
---
|
||||
|
||||
# Misc
|
||||
|
||||
- Unable to go get vuls
|
||||
@@ -733,7 +1045,7 @@ Use job scheduler like Cron (with -last2y option).
|
||||
- How to Enable Automatic-Scan.
|
||||
Use job scheduler like Cron.
|
||||
Set NOPASSWORD option in /etc/sudoers on target servers.
|
||||
Use SSH Key-Based Authentication with empty password or ssh-agent.
|
||||
Use SSH Key-Based Authentication with no passphrase or ssh-agent.
|
||||
|
||||
- How to cross compile
|
||||
```bash
|
||||
@@ -742,12 +1054,12 @@ Use SSH Key-Based Authentication with empty password or ssh-agent.
|
||||
```
|
||||
|
||||
- Logging
|
||||
Log wrote to under /var/log/vuls/
|
||||
Log is under /var/log/vuls/
|
||||
|
||||
- Debug
|
||||
Run with --debug, --sql-debug option.
|
||||
|
||||
- Ajusting Open File Limit
|
||||
- Adjusting Open File Limit
|
||||
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
|
||||
|
||||
- Does Vuls accept ssh connections with fish-shell or old zsh as the login shell?
|
||||
@@ -763,6 +1075,12 @@ Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/e
|
||||
- [k1LoW/ssh_config_to_vuls_config](https://github.com/k1LoW/ssh_config_to_vuls_config)
|
||||
ssh_config to vuls config TOML format
|
||||
|
||||
- [usiusi360/vulsrepo](https://github.com/usiusi360/vulsrepo)
|
||||
VulsRepo is visualized based on the json report output in vuls.
|
||||
Youtube
|
||||
[](https://www.youtube.com/watch?v=DIBPoik4owc)
|
||||
|
||||
|
||||
----
|
||||
|
||||
# Data Source
|
||||
@@ -779,11 +1097,14 @@ kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these
|
||||
|
||||
# Contribute
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
|
||||
2. get original code: go get github.com/future-architect/vuls
|
||||
3. work on original code
|
||||
4. add remote to your repo: git remote add myfork https://github.com/you/repo.git
|
||||
5. push your changes: git push myfork
|
||||
6. create a new Pull Request
|
||||
|
||||
- see [GitHub and Go: forking, pull requests, and go-getting](http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html)
|
||||
|
||||
----
|
||||
|
||||
@@ -797,6 +1118,3 @@ Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHAN
|
||||
|
||||
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).
|
||||
|
||||
|
||||
[](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
162
commands/configtest.go
Normal file
162
commands/configtest.go
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// ConfigtestCmd is Subcommand
|
||||
type ConfigtestCmd struct {
|
||||
configPath string
|
||||
askKeyPassword bool
|
||||
sshExternal bool
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
func (*ConfigtestCmd) Name() string { return "configtest" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*ConfigtestCmd) Synopsis() string { return "Test configuration" }
|
||||
|
||||
// Usage return usage
|
||||
func (*ConfigtestCmd) Usage() string {
|
||||
return `configtest:
|
||||
configtest
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-key-password]
|
||||
[-ssh-external]
|
||||
[-debug]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
// SetFlags set flag
|
||||
func (p *ConfigtestCmd) SetFlags(f *flag.FlagSet) {
|
||||
wd, _ := os.Getwd()
|
||||
defaultConfPath := filepath.Join(wd, "config.toml")
|
||||
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
|
||||
|
||||
f.BoolVar(&p.debug, "debug", false, "debug mode")
|
||||
|
||||
f.BoolVar(
|
||||
&p.askKeyPassword,
|
||||
"ask-key-password",
|
||||
false,
|
||||
"Ask ssh privatekey password before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.sshExternal,
|
||||
"ssh-external",
|
||||
false,
|
||||
"Use external ssh command. Default: Use the Go native implementation")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
if keyPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
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)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
var servernames []string
|
||||
if 0 < len(f.Args()) {
|
||||
servernames = f.Args()
|
||||
} else {
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to read stdin: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
servernames = fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range servernames {
|
||||
found := false
|
||||
for servername, info := range c.Conf.Servers {
|
||||
if servername == arg {
|
||||
target[servername] = info
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("%s is not in config", arg)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
// logger
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
if !c.Conf.Validate() {
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server/Contianer OS... ")
|
||||
scan.InitServers(Log)
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
scan.PrintSSHableServerNames()
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
@@ -100,7 +100,7 @@ notifyUsers = ["@username"]
|
||||
|
||||
[mail]
|
||||
smtpAddr = "smtp.gmail.com"
|
||||
smtpPort = 465
|
||||
smtpPort = "465"
|
||||
user = "username"
|
||||
password = "password"
|
||||
from = "from@address.com"
|
||||
@@ -116,6 +116,9 @@ subjectPrefix = "[vuls]"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
|
||||
[servers]
|
||||
{{- $names:= .Names}}
|
||||
@@ -129,6 +132,9 @@ host = "{{$ip}}"
|
||||
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
|
||||
#]
|
||||
#containers = ["${running}"]
|
||||
#optional = [
|
||||
# ["key", "value"],
|
||||
#]
|
||||
{{end}}
|
||||
|
||||
`
|
||||
|
||||
@@ -20,25 +20,22 @@ package commands
|
||||
import (
|
||||
"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"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// HistoryCmd is Subcommand of list scanned results
|
||||
type HistoryCmd struct {
|
||||
debug bool
|
||||
debugSQL bool
|
||||
|
||||
dbpath string
|
||||
debug bool
|
||||
debugSQL bool
|
||||
jsonBaseDir 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,45 @@ 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")
|
||||
defaultJSONBaseDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/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.JSONBaseDir = p.jsonBaseDir
|
||||
|
||||
// _, 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 {
|
||||
// TODO this "if block" will be deleted in a future release
|
||||
if f.Name() == "all.json" {
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ type PrepareCmd struct {
|
||||
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useUnattendedUpgrades bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -46,7 +44,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
|
||||
@@ -61,10 +58,10 @@ func (*PrepareCmd) Usage() string {
|
||||
return `prepare:
|
||||
prepare
|
||||
[-config=/path/to/config.toml]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
@@ -89,20 +86,13 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"Ask sudo password of target servers before scanning",
|
||||
)
|
||||
|
||||
f.BoolVar(
|
||||
&p.useUnattendedUpgrades,
|
||||
"use-unattended-upgrades",
|
||||
false,
|
||||
"[Deprecated] For Ubuntu, install unattended-upgrades",
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
|
||||
)
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
var keyPass, sudoPass string
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
@@ -112,14 +102,11 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
prompt := "sudo password: "
|
||||
if sudoPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
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")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
err = c.Load(p.configPath, keyPass, sudoPass)
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
@@ -146,17 +133,12 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
|
||||
}
|
||||
|
||||
c.Conf.Debug = p.debug
|
||||
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
|
||||
|
||||
// Set up custom logger
|
||||
logger := util.NewCustomLogger(c.ServerInfo{})
|
||||
|
||||
logger.Info("Detecting OS... ")
|
||||
err = scan.InitServers(logger)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to init servers. err: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
scan.InitServers(logger)
|
||||
|
||||
logger.Info("Installing...")
|
||||
if errs := scan.Prepare(); 0 < len(errs) {
|
||||
|
||||
253
commands/scan.go
253
commands/scan.go
@@ -19,13 +19,16 @@ package commands
|
||||
|
||||
import (
|
||||
"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"
|
||||
@@ -41,23 +44,35 @@ type ScanCmd struct {
|
||||
|
||||
configPath string
|
||||
|
||||
dbpath string
|
||||
jsonBaseDir string
|
||||
cvedbpath string
|
||||
cveDictionaryURL string
|
||||
cacheDBPath string
|
||||
|
||||
cvssScoreOver float64
|
||||
ignoreUnscoredCves bool
|
||||
|
||||
httpProxy string
|
||||
|
||||
// reporting
|
||||
reportSlack bool
|
||||
reportMail bool
|
||||
|
||||
httpProxy string
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useYumPluginSecurity bool
|
||||
useUnattendedUpgrades bool
|
||||
// reporting
|
||||
reportSlack bool
|
||||
reportMail bool
|
||||
reportJSON bool
|
||||
reportText bool
|
||||
reportS3 bool
|
||||
reportAzureBlob bool
|
||||
|
||||
awsProfile string
|
||||
awsS3Bucket string
|
||||
awsRegion string
|
||||
|
||||
azureAccount string
|
||||
azureKey string
|
||||
azureContainer string
|
||||
|
||||
sshExternal bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -72,17 +87,31 @@ func (*ScanCmd) Usage() string {
|
||||
scan
|
||||
[-lang=en|ja]
|
||||
[-config=/path/to/config.toml]
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
|
||||
[-cve-dictionary-url=http://127.0.0.1:1323]
|
||||
[-cache-dbpath=/path/to/cache.db]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-report-slack]
|
||||
[-ssh-external]
|
||||
[-report-azure-blob]
|
||||
[-report-json]
|
||||
[-report-mail]
|
||||
[-report-s3]
|
||||
[-report-slack]
|
||||
[-report-text]
|
||||
[-http-proxy=http://192.168.0.1:8080]
|
||||
[-ask-sudo-password]
|
||||
[-ask-key-password]
|
||||
[-debug]
|
||||
[-debug-sql]
|
||||
[-aws-profile=default]
|
||||
[-aws-region=us-west-2]
|
||||
[-aws-s3-bucket=bucket_name]
|
||||
[-azure-account=accout]
|
||||
[-azure-key=key]
|
||||
[-azure-container=container]
|
||||
|
||||
[SERVER]...
|
||||
`
|
||||
}
|
||||
|
||||
@@ -97,8 +126,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")
|
||||
defaultJSONBaseDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/path/to/results")
|
||||
|
||||
f.StringVar(
|
||||
&p.cvedbpath,
|
||||
"cve-dictionary-dbpath",
|
||||
"",
|
||||
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
|
||||
|
||||
defaultURL := "http://127.0.0.1:1323"
|
||||
f.StringVar(
|
||||
@@ -107,6 +142,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",
|
||||
@@ -119,6 +161,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.BoolVar(
|
||||
&p.sshExternal,
|
||||
"ssh-external",
|
||||
false,
|
||||
"Use external ssh command. Default: Use the Go native implementation")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
@@ -126,8 +174,36 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
"http://proxy-url:port (default: empty)",
|
||||
)
|
||||
|
||||
f.BoolVar(&p.reportSlack, "report-slack", false, "Slack report")
|
||||
f.BoolVar(&p.reportMail, "report-mail", false, "Email report")
|
||||
f.BoolVar(&p.reportSlack, "report-slack", false, "Send report via Slack")
|
||||
f.BoolVar(&p.reportMail, "report-mail", false, "Send report via Email")
|
||||
f.BoolVar(&p.reportJSON,
|
||||
"report-json",
|
||||
false,
|
||||
fmt.Sprintf("Write report to JSON files (%s/results/current)", wd),
|
||||
)
|
||||
f.BoolVar(&p.reportText,
|
||||
"report-text",
|
||||
false,
|
||||
fmt.Sprintf("Write report to text files (%s/results/current)", wd),
|
||||
)
|
||||
|
||||
f.BoolVar(&p.reportS3,
|
||||
"report-s3",
|
||||
false,
|
||||
"Write report to S3 (bucket/yyyyMMdd_HHmm/servername.json)",
|
||||
)
|
||||
f.StringVar(&p.awsProfile, "aws-profile", "default", "AWS profile to use")
|
||||
f.StringVar(&p.awsRegion, "aws-region", "us-east-1", "AWS region to use")
|
||||
f.StringVar(&p.awsS3Bucket, "aws-s3-bucket", "", "S3 bucket name")
|
||||
|
||||
f.BoolVar(&p.reportAzureBlob,
|
||||
"report-azure-blob",
|
||||
false,
|
||||
"Write report to S3 (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")
|
||||
f.StringVar(&p.azureContainer, "azure-container", "", "Azure storage container name")
|
||||
|
||||
f.BoolVar(
|
||||
&p.askKeyPassword,
|
||||
@@ -140,28 +216,13 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
|
||||
&p.askSudoPassword,
|
||||
"ask-sudo-password",
|
||||
false,
|
||||
"Ask sudo password of target servers before scanning",
|
||||
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget 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
|
||||
func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
var keyPass, sudoPass string
|
||||
var keyPass string
|
||||
var err error
|
||||
if p.askKeyPassword {
|
||||
prompt := "SSH key password: "
|
||||
@@ -171,22 +232,44 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
}
|
||||
if p.askSudoPassword {
|
||||
prompt := "sudo password: "
|
||||
if sudoPass, err = getPasswd(prompt); err != nil {
|
||||
logrus.Error(err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
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")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
err = c.Load(p.configPath, keyPass, sudoPass)
|
||||
err = c.Load(p.configPath, keyPass)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error loading %s, %s", p.configPath, err)
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
logrus.Infof("Start scanning (config: %s)", p.configPath)
|
||||
logrus.Info("Start scanning")
|
||||
logrus.Infof("config: %s", p.configPath)
|
||||
if p.cvedbpath != "" {
|
||||
logrus.Infof("cve-dictionary: %s", p.cvedbpath)
|
||||
} else {
|
||||
logrus.Infof("cve-dictionary: %s", p.cveDictionaryURL)
|
||||
}
|
||||
|
||||
var servernames []string
|
||||
if 0 < len(f.Args()) {
|
||||
servernames = f.Args()
|
||||
} else {
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to read stdin: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
servernames = fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target := make(map[string]c.ServerInfo)
|
||||
for _, arg := range f.Args() {
|
||||
for _, arg := range servernames {
|
||||
found := false
|
||||
for servername, info := range c.Conf.Servers {
|
||||
if servername == arg {
|
||||
@@ -200,7 +283,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
}
|
||||
if 0 < len(f.Args()) {
|
||||
if 0 < len(servernames) {
|
||||
c.Conf.Servers = target
|
||||
}
|
||||
|
||||
@@ -210,10 +293,11 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
|
||||
// logger
|
||||
Log := util.NewCustomLogger(c.ServerInfo{})
|
||||
scannedAt := time.Now()
|
||||
|
||||
// report
|
||||
reports := []report.ResultWriter{
|
||||
report.TextWriter{},
|
||||
report.StdoutWriter{},
|
||||
report.LogrusWriter{},
|
||||
}
|
||||
if p.reportSlack {
|
||||
@@ -222,14 +306,55 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
if p.reportMail {
|
||||
reports = append(reports, report.MailWriter{})
|
||||
}
|
||||
if p.reportJSON {
|
||||
reports = append(reports, report.JSONWriter{ScannedAt: scannedAt})
|
||||
}
|
||||
if p.reportText {
|
||||
reports = append(reports, report.TextFileWriter{ScannedAt: scannedAt})
|
||||
}
|
||||
if p.reportS3 {
|
||||
c.Conf.AwsRegion = p.awsRegion
|
||||
c.Conf.AwsProfile = p.awsProfile
|
||||
c.Conf.S3Bucket = p.awsS3Bucket
|
||||
if err := report.CheckIfBucketExists(); err != nil {
|
||||
Log.Errorf("Failed to access to the S3 bucket. err: %s", err)
|
||||
Log.Error("Ensure the bucket or check AWS config before scanning")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.S3Writer{})
|
||||
}
|
||||
if p.reportAzureBlob {
|
||||
c.Conf.AzureAccount = p.azureAccount
|
||||
if len(c.Conf.AzureAccount) == 0 {
|
||||
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
|
||||
}
|
||||
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.AzureKey = p.azureKey
|
||||
if len(c.Conf.AzureKey) == 0 {
|
||||
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
|
||||
}
|
||||
|
||||
c.Conf.AzureContainer = p.azureContainer
|
||||
if len(c.Conf.AzureContainer) == 0 {
|
||||
Log.Error("Azure storage container name is requied with --azure-container option")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
if err := report.CheckIfAzureContainerExists(); err != nil {
|
||||
Log.Errorf("Failed to access to the Azure Blob container. err: %s", err)
|
||||
Log.Error("Ensure the container or check Azure config before scanning")
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
reports = append(reports, report.AzureBlobWriter{})
|
||||
}
|
||||
|
||||
c.Conf.JSONBaseDir = p.jsonBaseDir
|
||||
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
|
||||
|
||||
Log.Info("Validating Config...")
|
||||
if !c.Conf.Validate() {
|
||||
@@ -237,18 +362,23 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
}
|
||||
|
||||
if ok, err := cveapi.CveClient.CheckHealth(); !ok {
|
||||
Log.Errorf("CVE HTTP server is not running. %#v", cveapi.CveClient)
|
||||
Log.Fatal(err)
|
||||
Log.Errorf("CVE HTTP server is not running. err: %s", err)
|
||||
Log.Errorf("Run go-cve-dictionary as server mode or specify -cve-dictionary-dbpath option")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Detecting Server OS... ")
|
||||
err = scan.InitServers(Log)
|
||||
if err != nil {
|
||||
Log.Errorf("Failed to init servers. Check the configuration. err: %s", err)
|
||||
Log.Info("Detecting Server/Contianer OS... ")
|
||||
scan.InitServers(Log)
|
||||
|
||||
Log.Info("Checking sudo configuration... ")
|
||||
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
|
||||
Log.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
|
||||
Log.Info("Detecting Platforms... ")
|
||||
scan.DetectPlatforms(Log)
|
||||
|
||||
Log.Info("Scanning vulnerabilities... ")
|
||||
if errs := scan.Scan(); 0 < len(errs) {
|
||||
for _, e := range errs {
|
||||
@@ -267,25 +397,10 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
||||
filtered := scanResults.FilterByCvssOver()
|
||||
for _, w := range reports {
|
||||
if err := w.Write(filtered); err != nil {
|
||||
Log.Fatalf("Failed to output report, err: %s", err)
|
||||
Log.Fatalf("Failed to report, err: %s", err)
|
||||
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
|
||||
}
|
||||
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
@@ -19,25 +19,23 @@ package commands
|
||||
|
||||
import (
|
||||
"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"
|
||||
"github.com/labstack/gommon/log"
|
||||
"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
|
||||
jsonBaseDir 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,34 +59,47 @@ 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))
|
||||
defaultJSONBaseDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/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.JSONBaseDir = p.jsonBaseDir
|
||||
|
||||
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 {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read stdin: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
historyID = fields[0]
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read stdin: %s", err)
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
jsonDirName = fields[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return report.RunTui(historyID)
|
||||
return report.RunTui(jsonDirName)
|
||||
}
|
||||
|
||||
@@ -44,22 +44,47 @@ type Config struct {
|
||||
CvssScoreOver float64
|
||||
IgnoreUnscoredCves bool
|
||||
|
||||
HTTPProxy string `valid:"url"`
|
||||
DBPath string
|
||||
SSHExternal bool
|
||||
|
||||
HTTPProxy string `valid:"url"`
|
||||
JSONBaseDir string
|
||||
CveDBPath string
|
||||
CacheDBPath string
|
||||
|
||||
AwsProfile string
|
||||
AwsRegion string
|
||||
S3Bucket string
|
||||
|
||||
AzureAccount string
|
||||
AzureKey string
|
||||
AzureContainer string
|
||||
|
||||
// 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.JSONBaseDir) != 0 {
|
||||
if ok, _ := valid.IsFilePath(c.JSONBaseDir); !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.JSONBaseDir))
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", c.CacheDBPath))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +222,6 @@ func (c *SlackConf) Validate() (errs []error) {
|
||||
type ServerInfo struct {
|
||||
ServerName string
|
||||
User string
|
||||
Password string
|
||||
Host string
|
||||
Port string
|
||||
KeyPath string
|
||||
@@ -208,10 +232,23 @@ type ServerInfo struct {
|
||||
// Container Names or IDs
|
||||
Containers []string
|
||||
|
||||
// userd internal
|
||||
// Optional key-value set that will be outputted to JSON
|
||||
Optional [][]interface{}
|
||||
|
||||
// used internal
|
||||
LogMsgAnsiColor string // DebugLog Color
|
||||
SudoOpt SudoOption
|
||||
Container Container
|
||||
Distro Distro
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -230,13 +267,3 @@ type Container struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// SudoOption is flag of sudo option.
|
||||
type SudoOption struct {
|
||||
|
||||
// echo pass | sudo -S ls
|
||||
ExecBySudo bool
|
||||
|
||||
// echo pass | sudo sh -C 'ls'
|
||||
ExecBySudoSh bool
|
||||
}
|
||||
|
||||
@@ -18,14 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package config
|
||||
|
||||
// Load loads configuration
|
||||
func Load(path, keyPass, sudoPass string) error {
|
||||
func Load(path, keyPass string) error {
|
||||
var loader Loader
|
||||
loader = TOMLLoader{}
|
||||
|
||||
return loader.Load(path, keyPass, sudoPass)
|
||||
return loader.Load(path, keyPass)
|
||||
}
|
||||
|
||||
// Loader is interface of concrete loader
|
||||
type Loader interface {
|
||||
Load(string, string, string) error
|
||||
Load(string, string) error
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ type TOMLLoader struct {
|
||||
}
|
||||
|
||||
// Load load the configuraiton TOML file specified by path arg.
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
|
||||
var conf Config
|
||||
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
|
||||
log.Error("Load config failed", err)
|
||||
@@ -49,50 +49,52 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
d.KeyPassword = keyPass
|
||||
}
|
||||
|
||||
if sudoPass != "" {
|
||||
d.Password = sudoPass
|
||||
}
|
||||
|
||||
i := 0
|
||||
for name, v := range conf.Servers {
|
||||
|
||||
if 0 < len(v.KeyPassword) || 0 < len(v.Password) {
|
||||
log.Warn("[Deprecated] password and keypassword in config file are unsecure. Remove them immediately for a security reason. They will be removed in a future release.")
|
||||
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.")
|
||||
}
|
||||
|
||||
s := ServerInfo{ServerName: name}
|
||||
s.User = v.User
|
||||
if s.User == "" {
|
||||
s.User = d.User
|
||||
}
|
||||
|
||||
// s.Password = sudoPass
|
||||
s.Password = v.Password
|
||||
if s.Password == "" {
|
||||
s.Password = d.Password
|
||||
switch {
|
||||
case v.User != "":
|
||||
s.User = v.User
|
||||
case d.User != "":
|
||||
s.User = d.User
|
||||
default:
|
||||
return fmt.Errorf("%s is invalid. User is empty", name)
|
||||
}
|
||||
|
||||
s.Host = v.Host
|
||||
if len(s.Host) == 0 {
|
||||
return fmt.Errorf("%s is invalid. host is empty", name)
|
||||
}
|
||||
|
||||
s.Port = v.Port
|
||||
if s.Port == "" {
|
||||
switch {
|
||||
case v.Port != "":
|
||||
s.Port = v.Port
|
||||
case d.Port != "":
|
||||
s.Port = d.Port
|
||||
default:
|
||||
s.Port = "22"
|
||||
}
|
||||
|
||||
s.KeyPath = v.KeyPath
|
||||
if s.KeyPath == "" {
|
||||
if len(s.KeyPath) == 0 {
|
||||
s.KeyPath = d.KeyPath
|
||||
}
|
||||
if s.KeyPath != "" {
|
||||
if _, err := os.Stat(s.KeyPath); err != nil {
|
||||
return fmt.Errorf(
|
||||
"config.toml is invalid. keypath: %s not exists", s.KeyPath)
|
||||
"%s is invalid. keypath: %s not exists", name, s.KeyPath)
|
||||
}
|
||||
}
|
||||
|
||||
// s.KeyPassword = keyPass
|
||||
s.KeyPassword = v.KeyPassword
|
||||
if s.KeyPassword == "" {
|
||||
if len(s.KeyPassword) == 0 {
|
||||
s.KeyPassword = d.KeyPassword
|
||||
}
|
||||
|
||||
@@ -106,6 +108,20 @@ func (c TOMLLoader) Load(pathToToml, keyPass, sudoPass string) (err error) {
|
||||
s.Containers = d.Containers
|
||||
}
|
||||
|
||||
s.Optional = v.Optional
|
||||
for _, dkv := range d.Optional {
|
||||
found := false
|
||||
for _, kv := range s.Optional {
|
||||
if dkv[0] == kv[0] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.Optional = append(s.Optional, dkv)
|
||||
}
|
||||
}
|
||||
|
||||
s.LogMsgAnsiColor = Colors[i%len(Colors)]
|
||||
i++
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/util"
|
||||
cveconfig "github.com/kotakanbe/go-cve-dictionary/config"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
@@ -46,17 +48,19 @@ func (api *cvedictClient) initialize() {
|
||||
}
|
||||
|
||||
func (api cvedictClient) CheckHealth() (ok bool, err error) {
|
||||
if config.Conf.CveDBPath != "" {
|
||||
log.Debugf("get cve-dictionary from sqlite3")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
api.initialize()
|
||||
url := fmt.Sprintf("%s/health", api.baseURL)
|
||||
var errs []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.StatusCode != 200 {
|
||||
return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v",
|
||||
url,
|
||||
errs,
|
||||
)
|
||||
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
|
||||
}
|
||||
@@ -67,6 +71,10 @@ type response struct {
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDetails, err error) {
|
||||
if config.Conf.CveDBPath != "" {
|
||||
return api.FetchCveDetailsFromCveDB(cveIDs)
|
||||
}
|
||||
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
reqChan := make(chan string, len(cveIDs))
|
||||
resChan := make(chan response, len(cveIDs))
|
||||
@@ -126,6 +134,29 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDet
|
||||
return
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails cve.CveDetails, err error) {
|
||||
log.Debugf("open cve-dictionary db")
|
||||
cveconfig.Conf.DBPath = config.Conf.CveDBPath
|
||||
if err := cvedb.OpenDB(); err != nil {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to open DB. err: %s", err)
|
||||
}
|
||||
for _, cveID := range cveIDs {
|
||||
cveDetail := cvedb.Get(cveID)
|
||||
if len(cveDetail.CveID) == 0 {
|
||||
cveDetails = append(cveDetails, cve.CveDetail{
|
||||
CveID: cveID,
|
||||
})
|
||||
} else {
|
||||
cveDetails = append(cveDetails, cveDetail)
|
||||
}
|
||||
}
|
||||
|
||||
// order by CVE ID desc
|
||||
sort.Sort(cveDetails)
|
||||
return
|
||||
}
|
||||
|
||||
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
@@ -155,49 +186,17 @@ 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
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsByCpeName(cpeName string) ([]cve.CveDetail, error) {
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
if config.Conf.CveDBPath != "" {
|
||||
return api.FetchCveDetailsByCpeNameFromDB(cpeName)
|
||||
}
|
||||
|
||||
api.baseURL = config.Conf.CveDictionaryURL
|
||||
url, err := util.URLPathJoin(api.baseURL, "cpes")
|
||||
if err != nil {
|
||||
return []cve.CveDetail{}, err
|
||||
@@ -218,8 +217,8 @@ 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.StatusCode != 200 {
|
||||
return fmt.Errorf("HTTP POST errors: %v, code: %d, url: %s", errs, resp.StatusCode, url)
|
||||
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
|
||||
}
|
||||
@@ -238,3 +237,13 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
|
||||
}
|
||||
return cveDetails, nil
|
||||
}
|
||||
|
||||
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.CveDetail, error) {
|
||||
log.Debugf("open cve-dictionary db")
|
||||
cveconfig.Conf.DBPath = config.Conf.CveDBPath
|
||||
if err := cvedb.OpenDB(); err != nil {
|
||||
return []cve.CveDetail{},
|
||||
fmt.Errorf("Failed to open DB. err: %s", err)
|
||||
}
|
||||
return cvedb.GetByCpeName(cpeName), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
FROM golang:1.6
|
||||
RUN apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y git openssh-client gcc nmap
|
||||
WORKDIR /app
|
||||
RUN go get github.com/kotakanbe/go-cve-dictionary
|
||||
RUN go get github.com/future-architect/vuls
|
||||
COPY fetch.sh .
|
||||
RUN /bin/bash /app/fetch.sh
|
||||
COPY config.toml .
|
||||
COPY run.sh .
|
||||
ENTRYPOINT ["/bin/bash", "/app/run.sh"]
|
||||
COPY id_rsa .
|
||||
COPY id_rsa.pub .
|
||||
@@ -1,7 +0,0 @@
|
||||
# Before building the docker
|
||||
|
||||
Since it's not on docker hub because blablabla, you have to :
|
||||
* Edit your [config.toml](https://github.com/future-architect/vuls#step6-config) to match your infrastructure
|
||||
* generate a keypair dedicated to this docker : ```ssh-keygen -t rsa -b 4096 -C "your_email@example.com"```
|
||||
* it's **highly** recommanded to use a restrained `authorized_keys` files with this key to be sure that it will be only usable from a single IP (after all it's a root executed software) : ```from="1.2.3.4,1.2.3.5" ssh-rsa [...] your_email@example.com```
|
||||
* Deploy your ssh key on the targetted machines
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i ; done
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
tries=0
|
||||
|
||||
function isopen {
|
||||
tries=$1
|
||||
nmap -Pn -T4 -p 1323 127.0.0.1|grep -iq open
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ $tries -lt 5 ]; then
|
||||
let tries++
|
||||
startserver $tries
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
function startserver {
|
||||
tries=$1
|
||||
go-cve-dictionary server &
|
||||
sleep 2
|
||||
isopen $tries
|
||||
}
|
||||
|
||||
startserver $tries
|
||||
if [ $? -ne 1 ]; then
|
||||
vuls scan -config /app/config.toml -report-slack
|
||||
fi
|
||||
121
glide.lock
generated
Normal file
121
glide.lock
generated
Normal file
@@ -0,0 +1,121 @@
|
||||
hash: 28d14f88e90c0765c1b660ddde796e51e197239d353bb79bfc5d8f8cf9b5f9ee
|
||||
updated: 2016-09-08T19:35:45.581570944+09:00
|
||||
imports:
|
||||
- name: github.com/asaskevich/govalidator
|
||||
version: 593d64559f7600f29581a3ee42177f5dbded27a9
|
||||
- name: github.com/aws/aws-sdk-go
|
||||
version: bc572378d109481c50d45d9dba4490d80386e98e
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/credentials
|
||||
- aws/session
|
||||
- service/s3
|
||||
- aws/awserr
|
||||
- aws/client
|
||||
- aws/corehandlers
|
||||
- aws/credentials/stscreds
|
||||
- aws/defaults
|
||||
- aws/request
|
||||
- private/endpoints
|
||||
- aws/awsutil
|
||||
- aws/client/metadata
|
||||
- aws/signer/v4
|
||||
- private/protocol
|
||||
- private/protocol/restxml
|
||||
- private/waiter
|
||||
- service/sts
|
||||
- aws/credentials/ec2rolecreds
|
||||
- aws/credentials/endpointcreds
|
||||
- aws/ec2metadata
|
||||
- private/protocol/rest
|
||||
- private/protocol/query
|
||||
- private/protocol/xml/xmlutil
|
||||
- private/protocol/query/queryutil
|
||||
- name: github.com/Azure/azure-sdk-for-go
|
||||
version: 34467930a15f0d2872168deb11435b8ac3d863bb
|
||||
subpackages:
|
||||
- storage
|
||||
- name: github.com/BurntSushi/toml
|
||||
version: 99064174e013895bbd9b025c31100bd1d9b590ca
|
||||
- name: github.com/cenkalti/backoff
|
||||
version: 8edc80b07f38c27352fb186d971c628a6c32552b
|
||||
- name: github.com/cheggaaa/pb
|
||||
version: ad4efe000aa550bb54918c06ebbadc0ff17687b9
|
||||
- name: github.com/go-ini/ini
|
||||
version: 6e4869b434bd001f6983749881c7ead3545887d8
|
||||
- name: github.com/google/subcommands
|
||||
version: 1c7173745a6001f67d8d96ab4e178284c77f7759
|
||||
- name: github.com/gosuri/uitable
|
||||
version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
|
||||
subpackages:
|
||||
- util/strutil
|
||||
- util/wordwrap
|
||||
- name: github.com/howeyc/gopass
|
||||
version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
|
||||
- name: github.com/jinzhu/gorm
|
||||
version: 02f6ae3c4ed211472b0492cee02ff3ddfdc1830d
|
||||
- name: github.com/jinzhu/inflection
|
||||
version: 74387dc39a75e970e7a3ae6a3386b5bd2e5c5cff
|
||||
- name: github.com/jmespath/go-jmespath
|
||||
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
|
||||
- name: github.com/jroimartin/gocui
|
||||
version: 30f7d65597dc2c421ce452b164c36b7014ef94be
|
||||
- name: github.com/k0kubun/pp
|
||||
version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
|
||||
- name: github.com/kotakanbe/go-cve-dictionary
|
||||
version: f9f68fee57dca8e60fb5d9d6b34d3215d854fc06
|
||||
subpackages:
|
||||
- config
|
||||
- models
|
||||
- db
|
||||
- log
|
||||
- jvn
|
||||
- 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: ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
|
||||
- name: github.com/mattn/go-runewidth
|
||||
version: d6bea18f789704b5f83375793155289da36a3c7f
|
||||
- name: github.com/mattn/go-sqlite3
|
||||
version: 3fb7a0e792edd47bf0cf1e919dfc14e2be412e15
|
||||
- name: github.com/mgutz/ansi
|
||||
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
|
||||
- name: github.com/moul/http2curl
|
||||
version: b1479103caacaa39319f75e7f57fc545287fca0d
|
||||
- name: github.com/nsf/termbox-go
|
||||
version: e8f6d27f72a2f2bb598eb3579afd5ea364ef67f7
|
||||
- name: github.com/parnurzeal/gorequest
|
||||
version: 2aea80ce763523ecc6452e61c3727ae9595a5809
|
||||
- name: github.com/rifflock/lfshook
|
||||
version: f9d14dda07b109a7aa56f135c31b34062eb14392
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
|
||||
- name: golang.org/x/crypto
|
||||
version: 9e590154d2353f3f5e1b24da7275686040dcf491
|
||||
subpackages:
|
||||
- ssh
|
||||
- ssh/agent
|
||||
- ssh/terminal
|
||||
- curve25519
|
||||
- ed25519
|
||||
- ed25519/internal/edwards25519
|
||||
- name: golang.org/x/net
|
||||
version: 9313baa13d9262e49d07b20ed57dceafcd7240cc
|
||||
subpackages:
|
||||
- context
|
||||
- publicsuffix
|
||||
- name: golang.org/x/sys
|
||||
version: 30de6d19a3bd89a5f38ae4028e23aaa5582648af
|
||||
subpackages:
|
||||
- unix
|
||||
- name: gopkg.in/alexcesaro/quotedprintable.v3
|
||||
version: 2caba252f4dc53eaf6b553000885530023f54623
|
||||
- name: gopkg.in/gomail.v2
|
||||
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
|
||||
devImports: []
|
||||
38
glide.yaml
Normal file
38
glide.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
package: github.com/future-architect/vuls
|
||||
import:
|
||||
- package: github.com/Azure/azure-sdk-for-go
|
||||
subpackages:
|
||||
- storage
|
||||
- package: github.com/BurntSushi/toml
|
||||
- package: github.com/Sirupsen/logrus
|
||||
- package: github.com/asaskevich/govalidator
|
||||
- package: github.com/aws/aws-sdk-go
|
||||
subpackages:
|
||||
- aws
|
||||
- aws/credentials
|
||||
- aws/session
|
||||
- service/s3
|
||||
- package: github.com/cenkalti/backoff
|
||||
- package: github.com/google/subcommands
|
||||
- package: github.com/gosuri/uitable
|
||||
- package: github.com/howeyc/gopass
|
||||
- package: github.com/jinzhu/gorm
|
||||
- package: github.com/jroimartin/gocui
|
||||
- package: github.com/k0kubun/pp
|
||||
- package: github.com/kotakanbe/go-cve-dictionary
|
||||
subpackages:
|
||||
- config
|
||||
- models
|
||||
- package: github.com/kotakanbe/go-pingscanner
|
||||
- package: github.com/kotakanbe/logrus-prefixed-formatter
|
||||
- package: github.com/mattn/go-sqlite3
|
||||
- package: github.com/parnurzeal/gorequest
|
||||
- package: github.com/rifflock/lfshook
|
||||
- package: golang.org/x/crypto
|
||||
subpackages:
|
||||
- ssh
|
||||
- ssh/agent
|
||||
- package: golang.org/x/net
|
||||
subpackages:
|
||||
- context
|
||||
- package: gopkg.in/gomail.v2
|
||||
@@ -37,14 +37,14 @@
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="289.5891316731771" width="137.0" x="1274.5282340049735" y="-47.136413574218864"/>
|
||||
<y:Geometry height="289.5891316731771" width="171.0" x="1274.5282340049735" y="-47.136413574218864"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="159.0390625" x="-11.01953125" y="0.0">Vulnerbility Database</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="171.0" x="0.0" y="0.0">Vulnerbility Database</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="29" bottomF="28.96858723958337" left="9" leftF="9.35411262512207" right="13" rightF="12.64588737487793" top="27" topF="27.242065429687557"/>
|
||||
<y:BorderInsets bottom="29" bottomF="28.96858723958337" left="29" leftF="29.35411262512207" right="27" rightF="26.64588737487793" top="27" topF="27.242065429687557"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -63,7 +63,7 @@
|
||||
<node id="n1::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="70.0" width="85.0" x="1298.8823466300955" y="128.4841308593749"/>
|
||||
<y:Geometry height="70.0" width="85.0" x="1318.8823466300955" y="128.4841308593749"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.279296875" x="10.8603515625" y="18.8671875">JVN
|
||||
@@ -81,7 +81,7 @@
|
||||
<node id="n1::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="70.0" width="85.0" x="1298.8823466300955" y="16.771667480468693"/>
|
||||
<y:Geometry height="70.0" width="85.0" x="1318.8823466300955" y="16.771667480468693"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.69921875" x="27.650390625" y="25.93359375">NVD<y:LabelModel>
|
||||
@@ -103,14 +103,14 @@
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="357.5982869466146" width="137.0" x="1274.5282340049735" y="285.984130859375"/>
|
||||
<y:Geometry height="336.1141560872396" width="171.61765336990447" x="1271.3823466300955" y="296.7261962890625"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="137.0" x="0.0" y="0.0">Linux Support</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="171.61765336990447" x="0.0" y="0.0">Distribution Support</y:NodeLabel>
|
||||
<y: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="15" bottomF="14.801595052083258" left="5" leftF="4.85411262512207" right="8" rightF="8.14588737487793" top="11" topF="10.747945149739394"/>
|
||||
<y:BorderInsets bottom="12" bottomF="11.879109700520985" left="27" leftF="26.617653369904474" right="13" rightF="13.0" top="13" topF="12.569030761718636"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -129,10 +129,10 @@
|
||||
<node id="n2::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="443.21842447916663"/>
|
||||
<y:Geometry height="50.0" width="100.0" x="1313.0" y="416.42173258463527"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">apptitude
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">apptitude
|
||||
changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -147,10 +147,10 @@ changelog<y:LabelModel>
|
||||
<node id="n2::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="333.3980916341144"/>
|
||||
<y:Geometry height="50.0" width="100.0" x="1314.0" y="345.96124267578114"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="14.9208984375" y="19.6092529296875">yum
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="64.158203125" x="17.9208984375" y="8.8671875">yum
|
||||
changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -165,10 +165,10 @@ changelog<y:LabelModel>
|
||||
<node id="n2::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="71.484130859375" width="94.0" x="1294.3823466300955" y="542.2966918945314"/>
|
||||
<y:Geometry height="50.0" width="100.0" x="1315.0" y="485.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="92.828125" x="0.5859375" y="19.6092529296875">RHSA (RedHat)
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="92.828125" x="3.5859375" y="8.8671875">RHSA (RedHat)
|
||||
ALAS (Amazon)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -180,6 +180,23 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2::n3">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="100.0" x="1314.3823466300955" y="555.9612426757811"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="100.650390625" x="-0.3251953125" y="15.93359375">FreeBSD Support<y:LabelModel>
|
||||
<y: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="n3">
|
||||
@@ -205,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"/>
|
||||
@@ -231,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>
|
||||
@@ -279,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">
|
||||
@@ -302,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"/>
|
||||
@@ -339,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"/>
|
||||
@@ -362,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="29" bottomF="28.96858723958337" left="12" leftF="12.249999999999773" right="0" rightF="0.0" top="49" topF="49.0"/>
|
||||
<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"/>
|
||||
@@ -390,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="747.1249999999995" y="34.484130859374886"/>
|
||||
<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>
|
||||
@@ -415,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>
|
||||
@@ -432,10 +443,10 @@ 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="802.4999999999995" y="148.4841308593749"/>
|
||||
<y:Geometry height="50.0" width="80.0" x="800.0" y="35.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="46.796875" x="16.6015625" y="15.93359375">Fetcher<y:LabelModel>
|
||||
@@ -451,9 +462,8 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<node id="n9" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
@@ -480,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"/>
|
||||
@@ -498,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"/>
|
||||
@@ -518,9 +528,8 @@ Container<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n11" yfiles.foldertype="group">
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d5"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
@@ -528,7 +537,7 @@ Container<y:LabelModel>
|
||||
<y:Geometry height="131.666015625" width="192.109991788863" x="964.6223485469814" y="516.3727416992189"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="192.109991788863" x="0.0" y="0.0">Linux Servers</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="192.109991788863" x="0.0" y="0.0">Linux/FreeBSD Servers</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
@@ -547,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"/>
|
||||
@@ -565,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"/>
|
||||
@@ -584,13 +593,95 @@ 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"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.240234375" x="98.64874426818972" y="-10.207796256635163">Fetch
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.240234375" x="28.568307252002455" y="48.099340559462576">Fetch
|
||||
Vulnerability data<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -609,7 +700,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="8.29332590103104" y="20.93360392252606">HTTP<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="6.7270960807795745" y="20.93360392252606">HTTP<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -621,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>
|
||||
@@ -645,7 +736,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="65.78936398029236" y="21.58855026779011">HTTP<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="65.80254757404236" y="21.547962712535252">HTTP<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -663,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>
|
||||
@@ -681,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>
|
||||
@@ -699,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>
|
||||
@@ -711,8 +802,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n4::n2" target="n11::n0">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -730,8 +820,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n4::n2" target="n10::n0">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -749,8 +838,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n10::e0" source="n10::n0" target="n10::n1">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -768,8 +856,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n10::n1" target="n3">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -779,8 +866,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n11::n0" target="n3">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -790,14 +876,13 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e0" source="n9::n2" target="n9::n0">
|
||||
<data key="d9"/>
|
||||
<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="-0.014769665799690301" y="-35.98834449405365">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>
|
||||
@@ -809,66 +894,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e1" source="n9::n0" target="n9::n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-65.17704859198193" y="14.912883039360338">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n4::n2" target="n6">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="70.275390625" x="-75.4169220517914" y="5.2924810128525905">Insert
|
||||
Scan Result<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n6" target="n4::n1">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-48.96875000000057" y="19.334788004557254">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e12" source="n4::n0" target="n7">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -878,8 +904,7 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n7" target="n5">
|
||||
<data key="d9"/>
|
||||
<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"/>
|
||||
@@ -897,6 +922,85 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<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" 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.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>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n8::e1" source="n8::n1" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
|
||||
<y: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="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: 73 KiB After Width: | Height: | Size: 77 KiB |
443
img/vuls-scan-flow.graphml
Normal file
443
img/vuls-scan-flow.graphml
Normal file
@@ -0,0 +1,443 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.14.2-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="0.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="83.482421875" x="92.2587890625" y="18.93359375">Detect the OS<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.decision">
|
||||
<y:Geometry height="40.0" width="80.0" x="403.6849206349206" y="206.44247787610618"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="38.0" y="18.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" modelName="custom" textColor="#000000" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
|
||||
Debian/Ubuntu: dpkg-query
|
||||
Amazon/RHEL/CentOS: rpm
|
||||
FreeBSD: pkg<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimit">
|
||||
<y:Geometry height="51.10998735777497" width="137.19216182048035" x="75.40391908975982" y="376.28592169721867"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="131.751953125" x="2.7201043477401754" y="9.422181178887513">foreach
|
||||
upgradable packages<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="5.551115123125783E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="459.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="213.619140625" x="27.1904296875" y="11.8671875">Parse changelog and get CVE IDs
|
||||
Debian/Ubuntu: aptitude changelog<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.loopLimitEnd">
|
||||
<y:Geometry height="50.0" width="137.0" x="75.5" y="545.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="55.24609375" x="40.876953125" y="15.93359375">end loop<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="625.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="194.904296875" x="36.5478515625" y="18.93359375">Select the CVE detail information<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" modelName="custom" textColor="#000000" visible="true" width="232.744140625" x="17.6279296875" y="4.80078125">Get CVE IDs by using package manager
|
||||
Amazon/RHEL: yum plugin security
|
||||
FreeBSD: pkg audit<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.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"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="117.970703125" x="9.434370308549205" y="23.548005886535975">CVE DB (NVD / JVN)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-8.326672684688674E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11">
|
||||
<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
|
||||
Reporting<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="n12">
|
||||
<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="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>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n13">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.process">
|
||||
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="373.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="205.52734375" x="31.236328125000057" y="18.93359375">Parse changelogs and get CVE IDs <y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="2.220446049250313E-16" nodeRatioY="0.1619001116071429" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="45.22123893805309" tx="0.0" ty="-20.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="144.0" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.697265625" x="-56.79057374984495" y="-34.26562148912808">Debian
|
||||
Ubuntu<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.02215389573439544" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n3" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.554993678887485"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n4" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="25.554993678887485" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n5" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-25.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n6" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="68.5" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="570.8409153761062"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n1" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="40.0" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="226.44247787610618"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
|
||||
RHEL
|
||||
FreeBSD<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.022401276994204813" 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="e7" source="n8" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n0" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-45.22123893805309"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n7" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n7" target="n10">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-134.01566143419018" sy="6.159084623893818" tx="0.0" ty="-29.333162136535975">
|
||||
<y:Point x="480.0" y="660.0"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="none"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="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="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="46.708984375" x="-53.35447755843876" y="11.632816010871807">CentOS<y:LabelModel>
|
||||
<y: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="e13" source="n12" target="n13">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n13" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">
|
||||
<y:Point x="743.3698412698412" y="401.8409153761062"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
||||
BIN
img/vuls-scan-flow.png
Normal file
BIN
img/vuls-scan-flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
img/vuls_icon.png
Normal file
BIN
img/vuls_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
img/vuls_logo.png
Normal file
BIN
img/vuls_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
BIN
img/vuls_logo_large.png
Normal file
BIN
img/vuls_logo_large.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
8
main.go
8
main.go
@@ -25,6 +25,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/future-architect/vuls/commands"
|
||||
"github.com/future-architect/vuls/version"
|
||||
"github.com/google/subcommands"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
@@ -39,13 +40,14 @@ func main() {
|
||||
subcommands.Register(&commands.ScanCmd{}, "scan")
|
||||
subcommands.Register(&commands.PrepareCmd{}, "prepare")
|
||||
subcommands.Register(&commands.HistoryCmd{}, "history")
|
||||
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
|
||||
|
||||
var version = flag.Bool("v", false, "Show version")
|
||||
var v = flag.Bool("v", false, "Show version")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *version {
|
||||
fmt.Printf("%s %s\n", Name, Version)
|
||||
if *v {
|
||||
fmt.Printf("%s %s\n", version.Name, version.Version)
|
||||
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
|
||||
ScanHistoryID uint
|
||||
gorm.Model `json:"-"`
|
||||
ScanHistoryID uint `json:"-"`
|
||||
ScannedAt time.Time
|
||||
|
||||
ServerName string // TOML Section key
|
||||
// Hostname string
|
||||
@@ -82,16 +83,20 @@ type ScanResult struct {
|
||||
|
||||
Container Container
|
||||
|
||||
Platform Platform
|
||||
|
||||
// Fqdn string
|
||||
// NWLinks []NWLink
|
||||
KnownCves []CveInfo
|
||||
UnknownCves []CveInfo
|
||||
|
||||
Optional [][]interface{} `gorm:"-"`
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -114,7 +119,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,
|
||||
@@ -161,8 +166,8 @@ func (r ScanResult) CveSummary() string {
|
||||
|
||||
// NWLink has network link information.
|
||||
type NWLink struct {
|
||||
gorm.Model
|
||||
ScanResultID uint
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
|
||||
IPAddress string
|
||||
Netmask string
|
||||
@@ -183,13 +188,16 @@ func (c CveInfos) Swap(i, j int) {
|
||||
|
||||
func (c CveInfos) Less(i, j int) bool {
|
||||
lang := config.Conf.Lang
|
||||
return c[i].CveDetail.CvssScore(lang) > c[j].CveDetail.CvssScore(lang)
|
||||
if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
|
||||
return c[i].CveDetail.CveID < c[j].CveDetail.CveID
|
||||
}
|
||||
return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
|
||||
}
|
||||
|
||||
// CveInfo has Cve Information.
|
||||
type CveInfo struct {
|
||||
gorm.Model
|
||||
ScanResultID uint
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
|
||||
CveDetail cve.CveDetail
|
||||
Packages []PackageInfo
|
||||
@@ -199,8 +207,8 @@ type CveInfo struct {
|
||||
|
||||
// CpeName has CPE name
|
||||
type CpeName struct {
|
||||
gorm.Model
|
||||
CveInfoID uint
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
|
||||
Name string
|
||||
}
|
||||
@@ -265,8 +273,8 @@ func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found boo
|
||||
|
||||
// PackageInfo has installed packages.
|
||||
type PackageInfo struct {
|
||||
gorm.Model
|
||||
CveInfoID uint
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
|
||||
Name string
|
||||
Version string
|
||||
@@ -300,10 +308,10 @@ func (p PackageInfo) ToStringNewVersion() string {
|
||||
return str
|
||||
}
|
||||
|
||||
// DistroAdvisory has Amazon Linux AMI Security Advisory information.
|
||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
|
||||
type DistroAdvisory struct {
|
||||
gorm.Model
|
||||
CveInfoID uint
|
||||
gorm.Model `json:"-"`
|
||||
CveInfoID uint `json:"-"`
|
||||
|
||||
AdvisoryID string
|
||||
Severity string
|
||||
@@ -313,9 +321,18 @@ type DistroAdvisory struct {
|
||||
|
||||
// Container has Container information
|
||||
type Container struct {
|
||||
gorm.Model
|
||||
ScanResultID uint
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
|
||||
ContainerID string
|
||||
Name string
|
||||
}
|
||||
|
||||
// Platform has platform information
|
||||
type Platform struct {
|
||||
gorm.Model `json:"-"`
|
||||
ScanResultID uint `json:"-"`
|
||||
|
||||
Name string // aws or azure or gcp or other...
|
||||
InstanceID string
|
||||
}
|
||||
|
||||
140
report/azureblob.go
Normal file
140
report/azureblob.go
Normal file
@@ -0,0 +1,140 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/storage"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// AzureBlobWriter writes results to AzureBlob
|
||||
type AzureBlobWriter struct{}
|
||||
|
||||
// CheckIfAzureContainerExists check the existence of Azure storage container
|
||||
func CheckIfAzureContainerExists() error {
|
||||
cli, err := getBlobClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ok, err := cli.ContainerExists(c.Conf.AzureContainer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("Container not found. Container: %s", c.Conf.AzureContainer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBlobClient() (storage.BlobStorageClient, error) {
|
||||
api, err := storage.NewBasicClient(c.Conf.AzureAccount, c.Conf.AzureKey)
|
||||
if err != nil {
|
||||
return storage.BlobStorageClient{}, err
|
||||
}
|
||||
return api.GetBlobService(), nil
|
||||
}
|
||||
|
||||
// Write results to Azure Blob storage
|
||||
func (w AzureBlobWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
reqChan := make(chan models.ScanResult, len(scanResults))
|
||||
resChan := make(chan bool)
|
||||
errChan := make(chan error, len(scanResults))
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
defer close(reqChan)
|
||||
|
||||
timeout := time.After(10 * 60 * time.Second)
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
|
||||
go func() {
|
||||
for _, r := range scanResults {
|
||||
reqChan <- r
|
||||
}
|
||||
}()
|
||||
|
||||
for range scanResults {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case sresult := <-reqChan:
|
||||
func(r models.ScanResult) {
|
||||
err := w.upload(r)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
resChan <- true
|
||||
}(sresult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
for i := 0; i < len(scanResults); i++ {
|
||||
select {
|
||||
case <-resChan:
|
||||
case err := <-errChan:
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
errs = append(errs, fmt.Errorf("Timeout while uploading to azure Blob"))
|
||||
}
|
||||
}
|
||||
|
||||
if 0 < len(errs) {
|
||||
return fmt.Errorf("Failed to upload json to Azure Blob: %v", errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w AzureBlobWriter) upload(res models.ScanResult) (err error) {
|
||||
cli, err := getBlobClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timestr := time.Now().Format("20060102_1504")
|
||||
name := ""
|
||||
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)
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
||||
}
|
||||
|
||||
if err = cli.CreateBlockBlobFromReader(
|
||||
c.Conf.AzureContainer,
|
||||
name,
|
||||
uint64(len(jsonBytes)),
|
||||
bytes.NewReader(jsonBytes),
|
||||
map[string]string{},
|
||||
); err != nil {
|
||||
return fmt.Errorf("%s/%s, %s",
|
||||
c.Conf.AzureContainer, name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
138
report/json.go
138
report/json.go
@@ -20,18 +20,144 @@ package report
|
||||
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"
|
||||
)
|
||||
|
||||
// JSONWriter writes report as JSON format
|
||||
type JSONWriter struct{}
|
||||
// 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 {
|
||||
ScannedAt time.Time
|
||||
}
|
||||
|
||||
func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
var j []byte
|
||||
if j, err = json.MarshalIndent(scanResults, "", " "); err != nil {
|
||||
return
|
||||
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 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 len(r.Container.ContainerID) == 0 {
|
||||
jsonPath = filepath.Join(path, fmt.Sprintf("%s.json", r.ServerName))
|
||||
} else {
|
||||
jsonPath = filepath.Join(path,
|
||||
fmt.Sprintf("%s_%s.json", r.ServerName, r.Container.Name))
|
||||
}
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", jsonPath, err)
|
||||
}
|
||||
}
|
||||
fmt.Println(string(j))
|
||||
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.JSONBaseDir); err != nil {
|
||||
err = fmt.Errorf("Failed to read %s: %s", c.Conf.JSONBaseDir, err)
|
||||
return
|
||||
}
|
||||
for _, d := range dirInfo {
|
||||
if d.IsDir() && JSONDirPattern.MatchString(d.Name()) {
|
||||
jsonDir := filepath.Join(c.Conf.JSONBaseDir, 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 {
|
||||
// TODO this "if block" will be deleted in a future release
|
||||
if file.Name() == "all.json" {
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
112
report/s3.go
Normal file
112
report/s3.go
Normal file
@@ -0,0 +1,112 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// CheckIfBucketExists check the existence of S3 bucket
|
||||
func CheckIfBucketExists() error {
|
||||
svc := getS3()
|
||||
result, err := svc.ListBuckets(&s3.ListBucketsInput{})
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"Failed to list buckets. err: %s, profile: %s, region: %s",
|
||||
err, c.Conf.AwsProfile, c.Conf.AwsRegion)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, bucket := range result.Buckets {
|
||||
if *bucket.Name == c.Conf.S3Bucket {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf(
|
||||
"Failed to find the buckets. profile: %s, region: %s, bukdet: %s",
|
||||
c.Conf.AwsProfile, c.Conf.AwsRegion, c.Conf.S3Bucket)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// S3Writer writes results to S3
|
||||
type S3Writer struct{}
|
||||
|
||||
func getS3() *s3.S3 {
|
||||
return s3.New(session.New(&aws.Config{
|
||||
Region: aws.String(c.Conf.AwsRegion),
|
||||
Credentials: credentials.NewSharedCredentials("", c.Conf.AwsProfile),
|
||||
}))
|
||||
}
|
||||
|
||||
// Write results to S3
|
||||
func (w S3Writer) Write(scanResults []models.ScanResult) (err error) {
|
||||
|
||||
var jsonBytes []byte
|
||||
if jsonBytes, err = json.Marshal(scanResults); err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
||||
}
|
||||
|
||||
// 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 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)
|
||||
}
|
||||
|
||||
if jsonBytes, err = json.Marshal(r); err != nil {
|
||||
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
|
||||
}
|
||||
_, 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)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -80,7 +80,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]
|
||||
}
|
||||
}
|
||||
@@ -113,9 +113,8 @@ func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
|
||||
if !config.Conf.IgnoreUnscoredCves {
|
||||
cves = append(cves, scanResult.UnknownCves...)
|
||||
}
|
||||
scanResult.KnownCves = cves
|
||||
|
||||
for _, cveInfo := range scanResult.KnownCves {
|
||||
for _, cveInfo := range cves {
|
||||
cveID := cveInfo.CveDetail.CveID
|
||||
|
||||
curentPackages := []string{}
|
||||
@@ -176,16 +175,15 @@ func attachmentText(cveInfo models.CveInfo, osFamily string) string {
|
||||
|
||||
switch {
|
||||
case config.Conf.Lang == "ja" &&
|
||||
cveInfo.CveDetail.Jvn.ID != 0 &&
|
||||
0 < cveInfo.CveDetail.CvssScore("ja"):
|
||||
0 < cveInfo.CveDetail.Jvn.CvssScore():
|
||||
|
||||
jvn := cveInfo.CveDetail.Jvn
|
||||
return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s",
|
||||
cveInfo.CveDetail.CvssScore(config.Conf.Lang),
|
||||
jvn.Severity,
|
||||
fmt.Sprintf(cvssV2CalcURLTemplate, cveInfo.CveDetail.CveID, jvn.Vector),
|
||||
jvn.Vector,
|
||||
jvn.Title,
|
||||
jvn.CvssSeverity(),
|
||||
fmt.Sprintf(cvssV2CalcURLTemplate, cveInfo.CveDetail.CveID, jvn.CvssVector()),
|
||||
jvn.CvssVector(),
|
||||
jvn.CveTitle(),
|
||||
linkText,
|
||||
)
|
||||
|
||||
@@ -193,20 +191,31 @@ func attachmentText(cveInfo models.CveInfo, osFamily string) string {
|
||||
nvd := cveInfo.CveDetail.Nvd
|
||||
return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s",
|
||||
cveInfo.CveDetail.CvssScore(config.Conf.Lang),
|
||||
nvd.Severity(),
|
||||
nvd.CvssSeverity(),
|
||||
fmt.Sprintf(cvssV2CalcURLTemplate, cveInfo.CveDetail.CveID, nvd.CvssVector()),
|
||||
nvd.CvssVector(),
|
||||
nvd.Summary,
|
||||
nvd.CveSummary(),
|
||||
linkText,
|
||||
)
|
||||
default:
|
||||
nvd := cveInfo.CveDetail.Nvd
|
||||
return fmt.Sprintf("?\n%s\n%s", nvd.Summary, linkText)
|
||||
return fmt.Sprintf("?\n%s\n%s", nvd.CveSummary(), linkText)
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// TextWriter write to stdout
|
||||
type TextWriter struct{}
|
||||
// StdoutWriter write to stdout
|
||||
type StdoutWriter struct{}
|
||||
|
||||
func (w TextWriter) Write(scanResults []models.ScanResult) error {
|
||||
func (w StdoutWriter) Write(scanResults []models.ScanResult) error {
|
||||
for _, s := range scanResults {
|
||||
text, err := toPlainText(s)
|
||||
if err != nil {
|
||||
|
||||
64
report/textfile.go
Normal file
64
report/textfile.go
Normal file
@@ -0,0 +1,64 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// TextFileWriter writes results to file.
|
||||
type TextFileWriter struct {
|
||||
ScannedAt time.Time
|
||||
}
|
||||
|
||||
func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
|
||||
path, err := ensureResultDir(w.ScannedAt)
|
||||
all := []string{}
|
||||
for _, r := range scanResults {
|
||||
textFilePath := ""
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
textFilePath = filepath.Join(path, fmt.Sprintf("%s.txt", r.ServerName))
|
||||
} else {
|
||||
textFilePath = filepath.Join(path,
|
||||
fmt.Sprintf("%s_%s.txt", r.ServerName, r.Container.Name))
|
||||
}
|
||||
text, err := toPlainText(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
all = append(all, text)
|
||||
b := []byte(text)
|
||||
if err := ioutil.WriteFile(textFilePath, b, 0644); err != nil {
|
||||
return fmt.Errorf("Failed to write text files. path: %s, err: %s", textFilePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
text := strings.Join(all, "\n\n")
|
||||
b := []byte(text)
|
||||
allPath := filepath.Join(path, "all.txt")
|
||||
if err := ioutil.WriteFile(allPath, b, 0644); err != nil {
|
||||
return fmt.Errorf("Failed to write text files. path: %s, err: %s", allPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -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,9 +40,9 @@ 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)
|
||||
return subcommands.ExitFailure
|
||||
@@ -70,12 +70,20 @@ func RunTui(historyID string) subcommands.ExitStatus {
|
||||
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.JSONBaseDir, jsonDirName)
|
||||
} else {
|
||||
var jsonDirs JSONDirs
|
||||
if jsonDirs, err = GetValidJSONDirs(); err != nil {
|
||||
return
|
||||
}
|
||||
jsonDir = jsonDirs[0]
|
||||
}
|
||||
if latest, err = LoadOneScanHistory(jsonDir); err != nil {
|
||||
return
|
||||
}
|
||||
latest, err = db.SelectScanHistory(historyID)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -332,7 +340,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
|
||||
}
|
||||
@@ -564,19 +572,19 @@ func summaryLines(data models.ScanResult) string {
|
||||
// packs = append(packs, pack.Name)
|
||||
// }
|
||||
if config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore() {
|
||||
summary := d.CveDetail.Jvn.Title
|
||||
summary := d.CveDetail.Jvn.CveTitle()
|
||||
cols = []string{
|
||||
fmt.Sprintf(indexFormat, i+1),
|
||||
d.CveDetail.CveID,
|
||||
fmt.Sprintf("| %-4.1f(%s)",
|
||||
d.CveDetail.CvssScore(config.Conf.Lang),
|
||||
d.CveDetail.Jvn.Severity,
|
||||
d.CveDetail.Jvn.CvssSeverity(),
|
||||
),
|
||||
// strings.Join(packs, ","),
|
||||
summary,
|
||||
}
|
||||
} else {
|
||||
summary := d.CveDetail.Nvd.Summary
|
||||
summary := d.CveDetail.Nvd.CveSummary()
|
||||
|
||||
var cvssScore string
|
||||
if d.CveDetail.CvssScore("en") <= 0 {
|
||||
@@ -584,7 +592,7 @@ func summaryLines(data models.ScanResult) string {
|
||||
} else {
|
||||
cvssScore = fmt.Sprintf("| %-4.1f(%s)",
|
||||
d.CveDetail.CvssScore(config.Conf.Lang),
|
||||
d.CveDetail.Nvd.Severity(),
|
||||
d.CveDetail.Nvd.CvssSeverity(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -643,6 +651,7 @@ type dataForTmpl struct {
|
||||
CvssVector string
|
||||
CvssSeverity string
|
||||
Summary string
|
||||
CweURL string
|
||||
VulnSiteLinks []string
|
||||
References []cve.Reference
|
||||
Packages []string
|
||||
@@ -670,18 +679,20 @@ func detailLines() (string, error) {
|
||||
case config.Conf.Lang == "ja" &&
|
||||
0 < cveInfo.CveDetail.Jvn.CvssScore():
|
||||
jvn := cveInfo.CveDetail.Jvn
|
||||
cvssSeverity = jvn.Severity
|
||||
cvssVector = jvn.Vector
|
||||
summary = fmt.Sprintf("%s\n%s", jvn.Title, jvn.Summary)
|
||||
refs = jvn.References
|
||||
cvssSeverity = jvn.CvssSeverity()
|
||||
cvssVector = jvn.CvssVector()
|
||||
summary = fmt.Sprintf("%s\n%s", jvn.CveTitle(), jvn.CveSummary())
|
||||
refs = jvn.VulnSiteReferences()
|
||||
default:
|
||||
nvd := cveInfo.CveDetail.Nvd
|
||||
cvssSeverity = nvd.Severity()
|
||||
cvssSeverity = nvd.CvssSeverity()
|
||||
cvssVector = nvd.CvssVector()
|
||||
summary = nvd.Summary
|
||||
refs = nvd.References
|
||||
summary = nvd.CveSummary()
|
||||
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 +726,7 @@ func detailLines() (string, error) {
|
||||
CvssSeverity: cvssSeverity,
|
||||
CvssVector: cvssVector,
|
||||
Summary: summary,
|
||||
CweURL: cweURL,
|
||||
VulnSiteLinks: links,
|
||||
References: refs,
|
||||
Packages: packages,
|
||||
@@ -746,6 +758,11 @@ Summary
|
||||
|
||||
{{.Summary }}
|
||||
|
||||
CWE
|
||||
--------------
|
||||
|
||||
{{.CweURL }}
|
||||
|
||||
Package/CPE
|
||||
--------------
|
||||
|
||||
|
||||
@@ -20,13 +20,44 @@ package report
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/gosuri/uitable"
|
||||
)
|
||||
|
||||
func ensureResultDir(scannedAt time.Time) (path string, err error) {
|
||||
if resultDirPath != "" {
|
||||
return resultDirPath, nil
|
||||
}
|
||||
|
||||
const timeLayout = "20060102_1504"
|
||||
timedir := scannedAt.Format(timeLayout)
|
||||
wd, _ := os.Getwd()
|
||||
dir := filepath.Join(wd, "results", timedir)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", fmt.Errorf("Failed to create dir: %s", err)
|
||||
}
|
||||
|
||||
symlinkPath := filepath.Join(wd, "results", "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 {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func toPlainText(scanResult models.ScanResult) (string, error) {
|
||||
serverInfo := scanResult.ServerInfo()
|
||||
|
||||
@@ -83,24 +114,24 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
|
||||
switch {
|
||||
case config.Conf.Lang == "ja" &&
|
||||
d.CveDetail.Jvn.ID != 0 &&
|
||||
0 < d.CveDetail.CvssScore("ja"):
|
||||
0 < d.CveDetail.Jvn.CvssScore():
|
||||
|
||||
summary := d.CveDetail.Jvn.Title
|
||||
summary := d.CveDetail.Jvn.CveTitle()
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
fmt.Sprintf("%-4.1f (%s)",
|
||||
d.CveDetail.CvssScore(config.Conf.Lang),
|
||||
d.CveDetail.Jvn.Severity,
|
||||
d.CveDetail.Jvn.CvssSeverity(),
|
||||
),
|
||||
summary,
|
||||
}
|
||||
case 0 < d.CveDetail.CvssScore("en"):
|
||||
summary := d.CveDetail.Nvd.Summary
|
||||
summary := d.CveDetail.Nvd.CveSummary()
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
fmt.Sprintf("%-4.1f",
|
||||
fmt.Sprintf("%-4.1f (%s)",
|
||||
d.CveDetail.CvssScore(config.Conf.Lang),
|
||||
d.CveDetail.Nvd.CvssSeverity(),
|
||||
),
|
||||
summary,
|
||||
}
|
||||
@@ -108,7 +139,7 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
"?",
|
||||
d.CveDetail.Nvd.Summary,
|
||||
d.CveDetail.Nvd.CveSummary(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,12 +152,11 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
return fmt.Sprintf("%s", stable)
|
||||
}
|
||||
|
||||
//TODO Distro Advisory
|
||||
func toPlainTextDetails(data models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) {
|
||||
for _, cve := range data.KnownCves {
|
||||
switch config.Conf.Lang {
|
||||
case "en":
|
||||
if cve.CveDetail.Nvd.ID != 0 {
|
||||
if 0 < cve.CveDetail.Nvd.CvssScore() {
|
||||
scoredReport = append(
|
||||
scoredReport, toPlainTextDetailsLangEn(cve, osFamily))
|
||||
} else {
|
||||
@@ -134,10 +164,10 @@ func toPlainTextDetails(data models.ScanResult, osFamily string) (scoredReport,
|
||||
scoredReport, toPlainTextUnknownCve(cve, osFamily))
|
||||
}
|
||||
case "ja":
|
||||
if cve.CveDetail.Jvn.ID != 0 {
|
||||
if 0 < cve.CveDetail.Jvn.CvssScore() {
|
||||
scoredReport = append(
|
||||
scoredReport, toPlainTextDetailsLangJa(cve, osFamily))
|
||||
} else if cve.CveDetail.Nvd.ID != 0 {
|
||||
} else if 0 < cve.CveDetail.Nvd.CvssScore() {
|
||||
scoredReport = append(
|
||||
scoredReport, toPlainTextDetailsLangEn(cve, osFamily))
|
||||
} else {
|
||||
@@ -175,13 +205,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)
|
||||
@@ -190,14 +218,16 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
|
||||
dtable.AddRow("Score",
|
||||
fmt.Sprintf("%4.1f (%s)",
|
||||
cveDetail.Jvn.CvssScore(),
|
||||
jvn.Severity,
|
||||
jvn.CvssSeverity(),
|
||||
))
|
||||
} else {
|
||||
dtable.AddRow("Score", "?")
|
||||
}
|
||||
dtable.AddRow("Vector", jvn.Vector)
|
||||
dtable.AddRow("Title", jvn.Title)
|
||||
dtable.AddRow("Description", jvn.Summary)
|
||||
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))
|
||||
@@ -222,7 +252,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)
|
||||
@@ -232,14 +261,16 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
|
||||
dtable.AddRow("Score",
|
||||
fmt.Sprintf("%4.1f (%s)",
|
||||
cveDetail.Nvd.CvssScore(),
|
||||
nvd.Severity(),
|
||||
nvd.CvssSeverity(),
|
||||
))
|
||||
} else {
|
||||
dtable.AddRow("Score", "?")
|
||||
}
|
||||
|
||||
dtable.AddRow("Vector", nvd.CvssVector())
|
||||
dtable.AddRow("Summary", nvd.Summary)
|
||||
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))
|
||||
@@ -311,6 +342,15 @@ func distroLinks(cveInfo models.CveInfo, osFamily string) []distroLink {
|
||||
},
|
||||
// TODO Debian dsa
|
||||
}
|
||||
case "FreeBSD":
|
||||
links := []distroLink{}
|
||||
for _, advisory := range cveInfo.DistroAdvisories {
|
||||
links = append(links, distroLink{
|
||||
"FreeBSD-VuXML",
|
||||
fmt.Sprintf(freeBSDVuXMLBaseURL, advisory.AdvisoryID),
|
||||
})
|
||||
}
|
||||
return links
|
||||
default:
|
||||
return []distroLink{}
|
||||
}
|
||||
@@ -337,3 +377,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)
|
||||
}
|
||||
|
||||
@@ -31,9 +31,13 @@ const (
|
||||
|
||||
ubuntuSecurityBaseURL = "http://people.ubuntu.com/~ubuntu-security/cve"
|
||||
debianTrackerBaseURL = "https://security-tracker.debian.org/tracker"
|
||||
|
||||
freeBSDVuXMLBaseURL = "https://vuxml.freebsd.org/freebsd/%s.html"
|
||||
)
|
||||
|
||||
// ResultWriter Interface
|
||||
type ResultWriter interface {
|
||||
Write([]models.ScanResult) error
|
||||
}
|
||||
|
||||
var resultDirPath string
|
||||
|
||||
@@ -19,8 +19,10 @@ package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
@@ -28,39 +30,54 @@ import (
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
type linux struct {
|
||||
type base struct {
|
||||
ServerInfo config.ServerInfo
|
||||
Distro config.Distro
|
||||
|
||||
Family string
|
||||
Release string
|
||||
Platform models.Platform
|
||||
osPackages
|
||||
log *logrus.Entry
|
||||
|
||||
log *logrus.Entry
|
||||
errs []error
|
||||
}
|
||||
|
||||
func (l *linux) ssh(cmd string, sudo bool) sshResult {
|
||||
func (l *base) ssh(cmd string, sudo bool) sshResult {
|
||||
return sshExec(l.ServerInfo, cmd, sudo, l.log)
|
||||
}
|
||||
|
||||
func (l *linux) setServerInfo(c config.ServerInfo) {
|
||||
func (l *base) setServerInfo(c config.ServerInfo) {
|
||||
l.ServerInfo = c
|
||||
}
|
||||
|
||||
func (l linux) getServerInfo() config.ServerInfo {
|
||||
func (l base) getServerInfo() config.ServerInfo {
|
||||
return l.ServerInfo
|
||||
}
|
||||
|
||||
func (l *linux) 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 *linux) getDistributionInfo() string {
|
||||
return fmt.Sprintf("%s %s", l.Family, l.Release)
|
||||
func (l base) getDistro() config.Distro {
|
||||
return l.Distro
|
||||
}
|
||||
|
||||
func (l linux) allContainers() (containers []config.Container, err error) {
|
||||
func (l *base) setPlatform(p models.Platform) {
|
||||
l.Platform = p
|
||||
}
|
||||
|
||||
func (l base) getPlatform() models.Platform {
|
||||
return l.Platform
|
||||
}
|
||||
|
||||
func (l base) allContainers() (containers []config.Container, err error) {
|
||||
switch l.ServerInfo.Container.Type {
|
||||
case "", "docker":
|
||||
stdout, err := l.dockerPs("-a --format '{{.ID}} {{.Names}}'")
|
||||
@@ -74,7 +91,7 @@ func (l linux) allContainers() (containers []config.Container, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *linux) runningContainers() (containers []config.Container, err error) {
|
||||
func (l *base) runningContainers() (containers []config.Container, err error) {
|
||||
switch l.ServerInfo.Container.Type {
|
||||
case "", "docker":
|
||||
stdout, err := l.dockerPs("--format '{{.ID}} {{.Names}}'")
|
||||
@@ -88,7 +105,7 @@ func (l *linux) runningContainers() (containers []config.Container, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *linux) exitedContainers() (containers []config.Container, err error) {
|
||||
func (l *base) exitedContainers() (containers []config.Container, err error) {
|
||||
switch l.ServerInfo.Container.Type {
|
||||
case "", "docker":
|
||||
stdout, err := l.dockerPs("--filter 'status=exited' --format '{{.ID}} {{.Names}}'")
|
||||
@@ -102,18 +119,16 @@ func (l *linux) exitedContainers() (containers []config.Container, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *linux) dockerPs(option string) (string, error) {
|
||||
func (l *base) dockerPs(option string) (string, error) {
|
||||
cmd := fmt.Sprintf("docker ps %s", option)
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return "", fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return r.Stdout, nil
|
||||
}
|
||||
|
||||
func (l *linux) parseDockerPs(stdout string) (containers []config.Container, err error) {
|
||||
func (l *base) parseDockerPs(stdout string) (containers []config.Container, err error) {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
@@ -131,11 +146,83 @@ func (l *linux) parseDockerPs(stdout string) (containers []config.Container, err
|
||||
return
|
||||
}
|
||||
|
||||
func (l *linux) convertToModel() (models.ScanResult, error) {
|
||||
var cves, unknownScoreCves []models.CveInfo
|
||||
func (l *base) detectPlatform() error {
|
||||
ok, instanceID, err := l.detectRunningOnAws()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
l.setPlatform(models.Platform{
|
||||
Name: "aws",
|
||||
InstanceID: instanceID,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO Azure, GCP...
|
||||
l.setPlatform(models.Platform{
|
||||
Name: "other",
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
|
||||
if r := l.ssh("type curl", noSudo); r.isSuccess() {
|
||||
cmd := "curl --max-time 1 --retry 3 --noproxy 169.254.169.254 http://169.254.169.254/latest/meta-data/instance-id"
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
id := strings.TrimSpace(r.Stdout)
|
||||
if !l.isAwsInstanceID(id) {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, id, nil
|
||||
}
|
||||
|
||||
switch r.ExitStatus {
|
||||
case 28, 7:
|
||||
// Not running on AWS
|
||||
// 7 Failed to connect to host.
|
||||
// 28 operation timeout.
|
||||
return false, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
if r := l.ssh("type wget", noSudo); r.isSuccess() {
|
||||
cmd := "wget --tries=3 --timeout=1 --no-proxy -q -O - http://169.254.169.254/latest/meta-data/instance-id"
|
||||
r := l.ssh(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
id := strings.TrimSpace(r.Stdout)
|
||||
if !l.isAwsInstanceID(id) {
|
||||
return false, "", nil
|
||||
}
|
||||
return true, id, nil
|
||||
}
|
||||
|
||||
switch r.ExitStatus {
|
||||
case 4, 8:
|
||||
// Not running on AWS
|
||||
// 4 Network failure
|
||||
// 8 Server issued an error response.
|
||||
return false, "", nil
|
||||
}
|
||||
}
|
||||
return false, "", fmt.Errorf(
|
||||
"Failed to curl or wget to AWS instance metadata on %s. container: %s",
|
||||
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
|
||||
for _, p := range l.UnsecurePackages {
|
||||
if p.CveDetail.CvssScore(config.Conf.Lang) < 0 {
|
||||
unknownScoreCves = append(unknownScoreCves, models.CveInfo{
|
||||
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
|
||||
@@ -155,7 +242,7 @@ func (l *linux) convertToModel() (models.ScanResult, error) {
|
||||
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
|
||||
CpeNames: cpenames,
|
||||
}
|
||||
cves = append(cves, cve)
|
||||
scoredCves = append(scoredCves, cve)
|
||||
}
|
||||
|
||||
container := models.Container{
|
||||
@@ -163,18 +250,24 @@ func (l *linux) convertToModel() (models.ScanResult, error) {
|
||||
Name: l.ServerInfo.Container.Name,
|
||||
}
|
||||
|
||||
sort.Sort(scoredCves)
|
||||
sort.Sort(unscoredCves)
|
||||
|
||||
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,
|
||||
KnownCves: cves,
|
||||
UnknownCves: unknownScoreCves,
|
||||
Platform: l.Platform,
|
||||
KnownCves: scoredCves,
|
||||
UnknownCves: unscoredCves,
|
||||
Optional: l.ServerInfo.Optional,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// scanVulnByCpeName search vulnerabilities that specified in config file.
|
||||
func (l *linux) scanVulnByCpeName() error {
|
||||
func (l *base) scanVulnByCpeName() error {
|
||||
unsecurePacks := CvePacksList{}
|
||||
|
||||
serverInfo := l.getServerInfo()
|
||||
@@ -208,15 +301,14 @@ func (l *linux) scanVulnByCpeName() error {
|
||||
unsecurePacks = append(unsecurePacks, set[key])
|
||||
}
|
||||
unsecurePacks = append(unsecurePacks, l.UnsecurePackages...)
|
||||
sort.Sort(CvePacksList(unsecurePacks))
|
||||
l.setUnsecurePackages(unsecurePacks)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linux) setErrs(errs []error) {
|
||||
func (l *base) setErrs(errs []error) {
|
||||
l.errs = errs
|
||||
}
|
||||
|
||||
func (l linux) getErrs() []error {
|
||||
func (l base) getErrs() []error {
|
||||
return l.errs
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
442
scan/debian.go
442
scan/debian.go
@@ -20,11 +20,11 @@ package scan
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"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"
|
||||
@@ -33,29 +33,32 @@ import (
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type debian struct {
|
||||
linux
|
||||
base
|
||||
}
|
||||
|
||||
// NewDebian is constructor
|
||||
func newDebian(c config.ServerInfo) *debian {
|
||||
d := &debian{}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
// Ubuntu, Debian
|
||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
|
||||
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {
|
||||
|
||||
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
|
||||
deb = newDebian(c)
|
||||
|
||||
// set sudo option flag
|
||||
c.SudoOpt = config.SudoOption{ExecBySudo: true}
|
||||
deb.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
|
||||
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return false, deb
|
||||
if r.Error != nil {
|
||||
return false, deb, r.Error
|
||||
}
|
||||
if r.ExitStatus == 255 {
|
||||
return false, deb, fmt.Errorf(
|
||||
"Unable to connect via SSH. Check SSH settings. %s", r)
|
||||
}
|
||||
Log.Debugf("Not Debian like Linux. %s", r)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
if r := sshExec(c, "lsb_release -ir", noSudo); r.isSuccess() {
|
||||
@@ -63,20 +66,18 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {
|
||||
// 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, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
"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
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
if r := sshExec(c, "cat /etc/lsb-release", noSudo); r.isSuccess() {
|
||||
@@ -85,81 +86,64 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {
|
||||
// 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, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
|
||||
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
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
// Debian
|
||||
cmd := "cat /etc/debian_version"
|
||||
if r := sshExec(c, cmd, noSudo); r.isSuccess() {
|
||||
deb.setDistributionInfo("debian", trim(r.Stdout))
|
||||
return true, deb
|
||||
deb.setDistro("debian", trim(r.Stdout))
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
Log.Debugf("Not Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return false, deb
|
||||
Log.Debugf("Not Debian like Linux: %s", c.ServerName)
|
||||
return false, deb, nil
|
||||
}
|
||||
|
||||
func trim(str string) string {
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
func (o *debian) install() error {
|
||||
func (o *debian) checkIfSudoNoPasswd() error {
|
||||
r := o.ssh("apt-get -v", sudo)
|
||||
if !r.isSuccess() {
|
||||
o.log.Errorf("sudo error on %s", r)
|
||||
return fmt.Errorf("Failed to sudo: %s", r)
|
||||
}
|
||||
o.log.Infof("sudo ... OK")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *debian) install() error {
|
||||
// apt-get update
|
||||
o.log.Infof("apt-get update...")
|
||||
cmd := util.PrependProxyEnv("apt-get update")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
if o.Family == "debian" {
|
||||
if o.Distro.Family == "debian" {
|
||||
// install aptitude
|
||||
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
msg := fmt.Sprintf("Failed to SSH: %s", r)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
o.log.Infof("Installed: aptitude")
|
||||
}
|
||||
|
||||
// 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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
o.log.Errorf(msg)
|
||||
return fmt.Errorf(msg)
|
||||
}
|
||||
|
||||
o.log.Infof("Installed: unattended-upgrades")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -184,9 +168,7 @@ func (o *debian) scanPackages() error {
|
||||
func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error) {
|
||||
r := o.ssh("dpkg-query -W", noSudo)
|
||||
if !r.isSuccess() {
|
||||
return packs, fmt.Errorf(
|
||||
"Failed to scan packages. status: %d, stdout:%s, stderr: %s",
|
||||
r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return packs, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// e.g.
|
||||
@@ -195,7 +177,7 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
|
||||
lines := strings.Split(r.Stdout, "\n")
|
||||
for _, line := range lines {
|
||||
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
|
||||
name, version, err := o.parseScanedPackagesLine(trimmed)
|
||||
name, version, err := o.parseScannedPackagesLine(trimmed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Debian: Failed to parse package line: %s", line)
|
||||
@@ -209,12 +191,16 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) parseScanedPackagesLine(line string) (name, version string, err error) {
|
||||
re, _ := regexp.Compile(`^([^\t']+)\t(.+)$`)
|
||||
result := re.FindStringSubmatch(line)
|
||||
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
|
||||
|
||||
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
|
||||
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
|
||||
}
|
||||
@@ -222,52 +208,27 @@ func (o *debian) parseScanedPackagesLine(line string) (name, version string, err
|
||||
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 := "aptitude is not installed"
|
||||
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 := "unattended-upgrade is not installed"
|
||||
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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr,
|
||||
)
|
||||
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
|
||||
@@ -280,9 +241,18 @@ 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().ServerName,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -295,101 +265,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("apt-cache policy %s", p.Name)
|
||||
r := o.ssh(cmd, sudo)
|
||||
if !r.isSuccess() {
|
||||
errChan <- fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
return nil, err
|
||||
case <-timeout:
|
||||
return nil, fmt.Errorf("Timeout fillCandidateVersion")
|
||||
}
|
||||
if config.Conf.Debug {
|
||||
cache.DB.PrettyPrint(current)
|
||||
}
|
||||
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("LANG=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("apt-get upgrade --dry-run")
|
||||
cmd := util.PrependProxyEnv("LANG=en_US.UTF-8 apt-get upgrade --dry-run")
|
||||
r := o.ssh(cmd, sudo)
|
||||
if r.isSuccess(0, 1) {
|
||||
return o.parseAptGetUpgrade(r.Stdout)
|
||||
@@ -400,8 +334,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")
|
||||
@@ -442,9 +376,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().ServerName,
|
||||
Distro: o.getServerInfo().Distro,
|
||||
Packs: unsecurePacks,
|
||||
}
|
||||
|
||||
type strarray []string
|
||||
resChan := make(chan struct {
|
||||
@@ -464,7 +400,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
}()
|
||||
|
||||
timeout := time.After(30 * 60 * time.Second)
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range unsecurePacks {
|
||||
@@ -472,47 +407,63 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
select {
|
||||
case pack := <-reqChan:
|
||||
func(p models.PackageInfo) {
|
||||
if cveIds, err := o.scanPackageCveIds(p); err != nil {
|
||||
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 {
|
||||
resChan <- struct {
|
||||
models.PackageInfo
|
||||
strarray
|
||||
}{p, cveIds}
|
||||
}{p, cveIDs}
|
||||
}
|
||||
}(pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// { CVE ID: [packageInfo] }
|
||||
cvePackages := make(map[string][]models.PackageInfo)
|
||||
errs := []error{}
|
||||
for i := 0; i < len(unsecurePacks); i++ {
|
||||
select {
|
||||
case pair := <-resChan:
|
||||
pack := pair.PackageInfo
|
||||
cveIds := pair.strarray
|
||||
for _, cveID := range cveIds {
|
||||
cveIDs := pair.strarray
|
||||
for _, cveID := range cveIDs {
|
||||
cvePackages[cveID] = appendPackIfMissing(cvePackages[cveID], pack)
|
||||
}
|
||||
o.log.Infof("(%d/%d) Scanned %s-%s : %s",
|
||||
i+1, len(unsecurePacks), pair.Name, pair.PackageInfo.Version, cveIds)
|
||||
i+1, len(unsecurePacks), pair.Name, pair.PackageInfo.Version, cveIDs)
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
errs = append(errs, err)
|
||||
case <-timeout:
|
||||
return nil, fmt.Errorf("Timeout scanPackageCveIds")
|
||||
//TODO append to errs
|
||||
return nil, fmt.Errorf("Timeout scanPackageCveIDs")
|
||||
}
|
||||
}
|
||||
|
||||
var cveIds []string
|
||||
for k := range cvePackages {
|
||||
cveIds = append(cveIds, k)
|
||||
if 0 < len(errs) {
|
||||
return nil, fmt.Errorf("%v", errs)
|
||||
}
|
||||
|
||||
o.log.Debugf("%d Cves are found. cves: %v", len(cveIds), cveIds)
|
||||
var cveIDs []string
|
||||
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...")
|
||||
cveDetails, err := cveapi.CveClient.FetchCveDetails(cveIds)
|
||||
cveDetails, err := cveapi.CveClient.FetchCveDetails(cveIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -526,13 +477,35 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
|
||||
// CvssScore: cinfo.CvssScore(conf.Lang),
|
||||
})
|
||||
}
|
||||
sort.Sort(CvePacksList(cvePacksList))
|
||||
return
|
||||
}
|
||||
|
||||
func (o *debian) scanPackageCveIds(pack models.PackageInfo) (cveIds []string, err error) {
|
||||
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 chnagelog. 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":
|
||||
@@ -542,47 +515,43 @@ func (o *debian) scanPackageCveIds(pack models.PackageInfo) (cveIds []string, er
|
||||
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
o.log.Warnf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
o.log.Warnf("Failed to SSH: %s", r)
|
||||
// Ignore this Error.
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
cveIds, err = o.getCveIDParsingChangelog(r.Stdout, pack.Name, pack.Version)
|
||||
err := cache.DB.PutChangelog(o.getServerInfo().ServerName, pack.Name, r.Stdout)
|
||||
if err != nil {
|
||||
trimUbuntu := strings.Split(pack.Version, "ubuntu")[0]
|
||||
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, trimUbuntu)
|
||||
return nil, fmt.Errorf("Failed to put changelog into cache")
|
||||
}
|
||||
return
|
||||
// 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.
|
||||
o.log.Warn(err)
|
||||
return []string{}, nil
|
||||
// Only logging the error.
|
||||
o.log.Error(err)
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Collect CVE-IDs included in the changelog.
|
||||
@@ -590,8 +559,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 {
|
||||
@@ -599,7 +568,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)
|
||||
}
|
||||
@@ -615,6 +584,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,14 +18,18 @@ 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"
|
||||
)
|
||||
|
||||
func TestParseScanedPackagesLineDebian(t *testing.T) {
|
||||
func TestParseScannedPackagesLineDebian(t *testing.T) {
|
||||
|
||||
var packagetests = []struct {
|
||||
in string
|
||||
@@ -43,7 +47,7 @@ func TestParseScanedPackagesLineDebian(t *testing.T) {
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range packagetests {
|
||||
n, v, _ := d.parseScanedPackagesLine(tt.in)
|
||||
n, v, _ := d.parseScannedPackagesLine(tt.in)
|
||||
if n != tt.name {
|
||||
t.Errorf("name: expected %s, actual %s", tt.name, n)
|
||||
}
|
||||
@@ -54,7 +58,7 @@ func TestParseScanedPackagesLineDebian(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 {
|
||||
|
||||
255
scan/freebsd.go
Normal file
255
scan/freebsd.go
Normal file
@@ -0,0 +1,255 @@
|
||||
/* 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/cveapi"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type bsd struct {
|
||||
base
|
||||
}
|
||||
|
||||
// NewBSD constructor
|
||||
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)
|
||||
if r := sshExec(c, "uname", noSudo); r.isSuccess() {
|
||||
if strings.Contains(r.Stdout, "FreeBSD") == true {
|
||||
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
|
||||
rel := strings.TrimSpace(b.Stdout)
|
||||
bsd.setDistro("FreeBSD", rel)
|
||||
return true, bsd
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.Debugf("Not FreeBSD. servernam: %s", c.ServerName)
|
||||
return false, bsd
|
||||
}
|
||||
|
||||
func (o *bsd) checkIfSudoNoPasswd() error {
|
||||
// FreeBSD doesn't need root privilege
|
||||
o.log.Infof("sudo ... OK")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) install() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) checkRequiredPackagesInstalled() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) scanPackages() error {
|
||||
var err error
|
||||
var packs []models.PackageInfo
|
||||
if packs, err = o.scanInstalledPackages(); err != nil {
|
||||
o.log.Errorf("Failed to scan installed packages")
|
||||
return err
|
||||
}
|
||||
o.setPackages(packs)
|
||||
|
||||
var unsecurePacks []CvePacksInfo
|
||||
if unsecurePacks, err = o.scanUnsecurePackages(); err != nil {
|
||||
o.log.Errorf("Failed to scan vulnerable packages")
|
||||
return err
|
||||
}
|
||||
o.setUnsecurePackages(unsecurePacks)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *bsd) scanInstalledPackages() ([]models.PackageInfo, error) {
|
||||
cmd := util.PrependProxyEnv("pkg version -v")
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
return o.parsePkgVersion(r.Stdout), nil
|
||||
}
|
||||
|
||||
func (o *bsd) scanUnsecurePackages() (cvePacksList []CvePacksInfo, err error) {
|
||||
const vulndbPath = "/tmp/vuln.db"
|
||||
cmd := "rm -f " + vulndbPath
|
||||
r := o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess(0) {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
cmd = util.PrependProxyEnv("pkg audit -F -r -f " + vulndbPath)
|
||||
r = o.ssh(cmd, noSudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
if r.ExitStatus == 0 {
|
||||
// no vulnerabilities
|
||||
return []CvePacksInfo{}, nil
|
||||
}
|
||||
|
||||
var packAdtRslt []pkgAuditResult
|
||||
blocks := o.splitIntoBlocks(r.Stdout)
|
||||
for _, b := range blocks {
|
||||
name, cveIDs, vulnID := o.parseBlock(b)
|
||||
if len(cveIDs) == 0 {
|
||||
continue
|
||||
}
|
||||
pack, found := o.Packages.FindByName(name)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Vulnerable package: %s is not found", name)
|
||||
}
|
||||
packAdtRslt = append(packAdtRslt, pkgAuditResult{
|
||||
pack: pack,
|
||||
vulnIDCveIDs: vulnIDCveIDs{
|
||||
vulnID: vulnID,
|
||||
cveIDs: cveIDs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// { CVE ID: []pkgAuditResult }
|
||||
cveIDAdtMap := make(map[string][]pkgAuditResult)
|
||||
for _, p := range packAdtRslt {
|
||||
for _, cid := range p.vulnIDCveIDs.cveIDs {
|
||||
cveIDAdtMap[cid] = append(cveIDAdtMap[cid], p)
|
||||
}
|
||||
}
|
||||
|
||||
cveIDs := []string{}
|
||||
for k := range cveIDAdtMap {
|
||||
cveIDs = append(cveIDs, k)
|
||||
}
|
||||
|
||||
cveDetails, err := cveapi.CveClient.FetchCveDetails(cveIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
o.log.Info("Done")
|
||||
|
||||
for _, d := range cveDetails {
|
||||
packs := []models.PackageInfo{}
|
||||
for _, r := range cveIDAdtMap[d.CveID] {
|
||||
packs = append(packs, r.pack)
|
||||
}
|
||||
|
||||
disAdvs := []models.DistroAdvisory{}
|
||||
for _, r := range cveIDAdtMap[d.CveID] {
|
||||
disAdvs = append(disAdvs, models.DistroAdvisory{
|
||||
AdvisoryID: r.vulnIDCveIDs.vulnID,
|
||||
})
|
||||
}
|
||||
|
||||
cvePacksList = append(cvePacksList, CvePacksInfo{
|
||||
CveID: d.CveID,
|
||||
CveDetail: d,
|
||||
Packs: packs,
|
||||
DistroAdvisories: disAdvs,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *bsd) parsePkgVersion(stdout string) (packs []models.PackageInfo) {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
for _, l := range lines {
|
||||
fields := strings.Fields(l)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
packVer := fields[0]
|
||||
splitted := strings.Split(packVer, "-")
|
||||
ver := splitted[len(splitted)-1]
|
||||
name := strings.Join(splitted[:len(splitted)-1], "-")
|
||||
|
||||
switch fields[1] {
|
||||
case "?", "=":
|
||||
packs = append(packs, models.PackageInfo{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
})
|
||||
case "<":
|
||||
candidate := strings.TrimSuffix(fields[6], ")")
|
||||
packs = append(packs, models.PackageInfo{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
NewVersion: candidate,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type vulnIDCveIDs struct {
|
||||
vulnID string
|
||||
cveIDs []string
|
||||
}
|
||||
|
||||
type pkgAuditResult struct {
|
||||
pack models.PackageInfo
|
||||
vulnIDCveIDs vulnIDCveIDs
|
||||
}
|
||||
|
||||
func (o *bsd) splitIntoBlocks(stdout string) (blocks []string) {
|
||||
lines := strings.Split(stdout, "\n")
|
||||
block := []string{}
|
||||
for _, l := range lines {
|
||||
if len(strings.TrimSpace(l)) == 0 {
|
||||
if 0 < len(block) {
|
||||
blocks = append(blocks, strings.Join(block, "\n"))
|
||||
block = []string{}
|
||||
}
|
||||
continue
|
||||
}
|
||||
block = append(block, strings.TrimSpace(l))
|
||||
}
|
||||
if 0 < len(block) {
|
||||
blocks = append(blocks, strings.Join(block, "\n"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *bsd) parseBlock(block string) (packName string, cveIDs []string, vulnID string) {
|
||||
lines := strings.Split(block, "\n")
|
||||
for _, l := range lines {
|
||||
if strings.HasSuffix(l, " is vulnerable:") {
|
||||
packVer := strings.Fields(l)[0]
|
||||
splitted := strings.Split(packVer, "-")
|
||||
packName = strings.Join(splitted[:len(splitted)-1], "-")
|
||||
} else if strings.HasPrefix(l, "CVE:") {
|
||||
cveIDs = append(cveIDs, strings.Fields(l)[1])
|
||||
} else if strings.HasPrefix(l, "WWW:") {
|
||||
splitted := strings.Split(l, "/")
|
||||
vulnID = strings.TrimSuffix(splitted[len(splitted)-1], ".html")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
155
scan/freebsd_test.go
Normal file
155
scan/freebsd_test.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
func TestParsePkgVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected []models.PackageInfo
|
||||
}{
|
||||
{
|
||||
`Updating FreeBSD repository catalogue...
|
||||
FreeBSD repository is up-to-date.
|
||||
All repositories are up-to-date.
|
||||
bash-4.2.45 < needs updating (remote has 4.3.42_1)
|
||||
gettext-0.18.3.1 < needs updating (remote has 0.19.7)
|
||||
tcl84-8.4.20_2,1 = up-to-date with remote
|
||||
teTeX-base-3.0_25 ? orphaned: print/teTeX-base`,
|
||||
|
||||
[]models.PackageInfo{
|
||||
{
|
||||
Name: "bash",
|
||||
Version: "4.2.45",
|
||||
NewVersion: "4.3.42_1",
|
||||
},
|
||||
{
|
||||
Name: "gettext",
|
||||
Version: "0.18.3.1",
|
||||
NewVersion: "0.19.7",
|
||||
},
|
||||
{
|
||||
Name: "tcl84",
|
||||
Version: "8.4.20_2,1",
|
||||
},
|
||||
{
|
||||
Name: "teTeX-base",
|
||||
Version: "3.0_25",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newBsd(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := d.parsePkgVersion(tt.in)
|
||||
if !reflect.DeepEqual(tt.expected, actual) {
|
||||
e := pp.Sprintf("%v", tt.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitIntoBlocks(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
`
|
||||
block1
|
||||
|
||||
block2
|
||||
block2
|
||||
block2
|
||||
|
||||
block3
|
||||
block3`,
|
||||
[]string{
|
||||
`block1`,
|
||||
"block2\nblock2\nblock2",
|
||||
"block3\nblock3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newBsd(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := d.splitIntoBlocks(tt.in)
|
||||
if !reflect.DeepEqual(tt.expected, actual) {
|
||||
e := pp.Sprintf("%v", tt.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestParseBlock(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
name string
|
||||
cveIDs []string
|
||||
vulnID string
|
||||
}{
|
||||
{
|
||||
|
||||
in: `vulnxml file up-to-date
|
||||
bind96-9.6.3.2.ESV.R10_2 is vulnerable:
|
||||
bind -- denial of service vulnerability
|
||||
CVE: CVE-2014-0591
|
||||
WWW: https://vuxml.FreeBSD.org/freebsd/cb252f01-7c43-11e3-b0a6-005056a37f68.html`,
|
||||
name: "bind96",
|
||||
cveIDs: []string{"CVE-2014-0591"},
|
||||
vulnID: "cb252f01-7c43-11e3-b0a6-005056a37f68",
|
||||
},
|
||||
{
|
||||
in: `bind96-9.6.3.2.ESV.R10_2 is vulnerable:
|
||||
bind -- denial of service vulnerability
|
||||
CVE: CVE-2014-8680
|
||||
CVE: CVE-2014-8500
|
||||
WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html`,
|
||||
name: "bind96",
|
||||
cveIDs: []string{"CVE-2014-8680", "CVE-2014-8500"},
|
||||
vulnID: "ab3e98d9-8175-11e4-907d-d050992ecde8",
|
||||
},
|
||||
{
|
||||
in: `hoge-hoge-9.6.3.2.ESV.R10_2 is vulnerable:
|
||||
bind -- denial of service vulnerability
|
||||
CVE: CVE-2014-8680
|
||||
CVE: CVE-2014-8500
|
||||
WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html`,
|
||||
name: "hoge-hoge",
|
||||
cveIDs: []string{"CVE-2014-8680", "CVE-2014-8500"},
|
||||
vulnID: "ab3e98d9-8175-11e4-907d-d050992ecde8",
|
||||
},
|
||||
{
|
||||
in: `1 problem(s) in the installed packages found.`,
|
||||
cveIDs: []string{},
|
||||
vulnID: "",
|
||||
},
|
||||
}
|
||||
|
||||
d := newBsd(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
aName, aCveIDs, aVunlnID := d.parseBlock(tt.in)
|
||||
if tt.name != aName {
|
||||
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVunlnID)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
353
scan/redhat.go
353
scan/redhat.go
@@ -35,28 +35,24 @@ import (
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type redhat struct {
|
||||
linux
|
||||
base
|
||||
}
|
||||
|
||||
// NewRedhat is constructor
|
||||
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)
|
||||
|
||||
// set sudo option flag
|
||||
c.SudoOpt = config.SudoOption{ExecBySudo: true}
|
||||
red.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
|
||||
red.setDistributionInfo("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet. Host: %s:%s", c.Host, c.Port)
|
||||
red.setDistro("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -66,21 +62,19 @@ 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. stdout: %s, Host: %s:%s",
|
||||
r.Stdout, c.Host, c.Port)
|
||||
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
return true, red
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -96,59 +90,46 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
release = fields[4]
|
||||
}
|
||||
}
|
||||
red.setDistributionInfo(family, release)
|
||||
red.setDistro(family, release)
|
||||
return true, red
|
||||
}
|
||||
|
||||
Log.Debugf("Not RedHat like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
Log.Debugf("Not RedHat like Linux. servername: %s", c.ServerName)
|
||||
return false, red
|
||||
}
|
||||
|
||||
// CentOS 5 ... yum-plugin-security, yum-changelog
|
||||
// CentOS 6 ... yum-plugin-security, yum-plugin-changelog
|
||||
// CentOS 7 ... yum-plugin-security, yum-plugin-changelog
|
||||
func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
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)
|
||||
}
|
||||
o.log.Infof("sudo ... OK")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch o.Distro.Family {
|
||||
case "rhel", "amazon":
|
||||
o.log.Infof("Nothing to do")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := o.installYumPluginSecurity(); err != nil {
|
||||
return err
|
||||
}
|
||||
// CentOS
|
||||
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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *redhat) installYumChangelog() error {
|
||||
|
||||
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 {
|
||||
return fmt.Errorf(
|
||||
"Not implemented yet. family: %s, release: %s",
|
||||
o.Family, o.Release)
|
||||
"Not implemented yet: %s", o.Distro)
|
||||
}
|
||||
|
||||
var packName = ""
|
||||
@@ -167,9 +148,7 @@ func (o *redhat) installYumChangelog() error {
|
||||
o.log.Infof("Installing %s...", packName)
|
||||
cmd = util.PrependProxyEnv("yum install -y " + packName)
|
||||
if r := o.ssh(cmd, sudo); !r.isSuccess() {
|
||||
return fmt.Errorf(
|
||||
"Failed to install %s. status: %d, stdout: %s, stderr: %s",
|
||||
packName, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
o.log.Infof("Installed: %s", packName)
|
||||
}
|
||||
@@ -177,26 +156,12 @@ func (o *redhat) installYumChangelog() error {
|
||||
}
|
||||
|
||||
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
|
||||
cmd := "rpm -q yum-plugin-security"
|
||||
if o.Family == "centos" {
|
||||
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)
|
||||
}
|
||||
@@ -274,7 +239,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,16 +249,13 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
|
||||
return o.scanUnsecurePackagesUsingYumCheckUpdate()
|
||||
}
|
||||
|
||||
//TODO return whether already expired.
|
||||
// For CentOS
|
||||
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
|
||||
|
||||
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.
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// get Updateble package name, installed, candidate version.
|
||||
@@ -308,13 +270,22 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
|
||||
PackInfo models.PackageInfo
|
||||
CveIDs []string
|
||||
}
|
||||
|
||||
// { packageName: changelog-lines }
|
||||
var rpm2changelog map[string]*string
|
||||
allChangelog, err := o.getAllChangelog(packInfoList)
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to getAllchangelog. err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
rpm2changelog, err = o.parseAllChangelog(allChangelog)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parseAllChangelog. err: %s", err)
|
||||
}
|
||||
|
||||
var results []PackInfoCveIDs
|
||||
for i, packInfo := range packInfoList {
|
||||
changelog, err := o.getChangelog(packInfo.Name)
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to collect CVE IDs. err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
changelog := o.getChangelogCVELines(rpm2changelog, packInfo)
|
||||
|
||||
// Collect unique set of CVE-ID in each changelog
|
||||
uniqueCveIDMap := make(map[string]bool)
|
||||
@@ -395,7 +366,6 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
|
||||
// CvssScore: cinfo.CvssScore(conf.Lang),
|
||||
})
|
||||
}
|
||||
sort.Sort(CvePacksList(cvePacksList))
|
||||
return cvePacksList, nil
|
||||
}
|
||||
|
||||
@@ -410,7 +380,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)
|
||||
@@ -450,7 +422,7 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
|
||||
if len(fields) != 2 {
|
||||
return models.PackageInfo{}, fmt.Errorf("Unknown format: %s", line)
|
||||
}
|
||||
version := fields[0]
|
||||
version := o.regexpReplace(fields[0], `^[0-9]+:`, "")
|
||||
release := fields[1]
|
||||
return models.PackageInfo{
|
||||
Name: packName,
|
||||
@@ -459,17 +431,123 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *redhat) getChangelog(packageNames string) (stdout string, err error) {
|
||||
command := ""
|
||||
if o.ServerInfo.User == "root" {
|
||||
command = "echo N | "
|
||||
func (o *redhat) mkPstring() *string {
|
||||
str := ""
|
||||
return &str
|
||||
}
|
||||
|
||||
func (o *redhat) regexpReplace(src string, pat string, rep string) string {
|
||||
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 {
|
||||
if changeLogCVEPattern.MatchString(line) {
|
||||
retLine += fmt.Sprintf("%s\n", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
return retLine
|
||||
}
|
||||
|
||||
func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, error) {
|
||||
var majorVersion int
|
||||
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: %s", o.getDistro())
|
||||
}
|
||||
|
||||
orglines := strings.Split(allChangelog, "\n")
|
||||
tmpline := ""
|
||||
var lines []string
|
||||
var prev, now bool
|
||||
var err error
|
||||
for i := range orglines {
|
||||
if majorVersion == 5 {
|
||||
/* for CentOS5 (yum-util < 1.1.20) */
|
||||
prev = false
|
||||
now = false
|
||||
if 0 < i {
|
||||
prev, err = o.isRpmPackageNameLine(orglines[i-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
now, err = o.isRpmPackageNameLine(orglines[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prev && now {
|
||||
tmpline = fmt.Sprintf("%s, %s", tmpline, orglines[i])
|
||||
continue
|
||||
}
|
||||
if !prev && now {
|
||||
tmpline = fmt.Sprintf("%s%s", tmpline, orglines[i])
|
||||
continue
|
||||
}
|
||||
if tmpline != "" {
|
||||
lines = append(lines, fmt.Sprintf("%s", tmpline))
|
||||
tmpline = ""
|
||||
}
|
||||
lines = append(lines, fmt.Sprintf("%s", orglines[i]))
|
||||
} else {
|
||||
/* for CentOS6,7 (yum-util >= 1.1.20) */
|
||||
line := orglines[i]
|
||||
line = o.regexpReplace(line, `^ChangeLog for: `, "")
|
||||
line = o.regexpReplace(line, `^\*\*\sNo\sChangeLog\sfor:.*`, "")
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
|
||||
rpm2changelog := make(map[string]*string)
|
||||
writePointer := o.mkPstring()
|
||||
for _, line := range lines {
|
||||
match, err := o.isRpmPackageNameLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
rpms := strings.Split(line, ",")
|
||||
pNewString := o.mkPstring()
|
||||
writePointer = pNewString
|
||||
for _, rpm := range rpms {
|
||||
rpm = strings.TrimSpace(rpm)
|
||||
rpm = o.regexpReplace(rpm, `^[0-9]+:`, "")
|
||||
rpm = o.regexpReplace(rpm, `\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`, "")
|
||||
rpm2changelog[rpm] = pNewString
|
||||
}
|
||||
} else {
|
||||
if strings.HasPrefix(line, "Dependencies Resolved") {
|
||||
return rpm2changelog, nil
|
||||
}
|
||||
*writePointer += fmt.Sprintf("%s\n", line)
|
||||
}
|
||||
}
|
||||
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 := "echo N | "
|
||||
if 0 < len(config.Conf.HTTPProxy) {
|
||||
command += util.ProxyEnv()
|
||||
}
|
||||
|
||||
// yum update --changelog doesn't have --color option.
|
||||
command += fmt.Sprintf(" yum update --changelog %s | grep CVE", packageNames)
|
||||
command += fmt.Sprintf(" LANG=en_US.UTF-8 yum update --changelog %s", packageNames)
|
||||
|
||||
r := o.ssh(command, sudo)
|
||||
if !r.isSuccess(0, 1) {
|
||||
@@ -486,9 +564,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(
|
||||
@@ -496,32 +574,26 @@ 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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout)
|
||||
|
||||
// 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)
|
||||
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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
updatable, err := o.parseYumCheckUpdateLines(r.Stdout)
|
||||
if err != nil {
|
||||
@@ -546,11 +618,9 @@ 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 %s. status: %d, stdout: %s, stderr: %s",
|
||||
cmd, r.ExitStatus, r.Stdout, r.Stderr)
|
||||
return nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
advisoryCveIDsList, err := o.parseYumUpdateinfo(r.Stdout)
|
||||
if err != nil {
|
||||
@@ -598,6 +668,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")
|
||||
@@ -615,7 +687,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 {
|
||||
@@ -642,7 +714,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
|
||||
@@ -709,8 +781,22 @@ 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: ")
|
||||
ss := strings.Split(s, ", ")
|
||||
if len(ss) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
for _, s := range ss {
|
||||
s = strings.TrimRight(s, " \r\n")
|
||||
if !rpmPackageArchPattern.MatchString(s) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// see test case
|
||||
@@ -730,9 +816,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]
|
||||
@@ -744,31 +831,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
|
||||
}
|
||||
@@ -779,14 +871,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
|
||||
}
|
||||
@@ -865,3 +959,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",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -302,6 +302,54 @@ func TestIsDescriptionLine(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsRpmPackageNameLine(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
var tests = []struct {
|
||||
in string
|
||||
found bool
|
||||
}{
|
||||
{
|
||||
"stunnel-4.15-2.el5.2.i386",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"iproute-2.6.18-15.el5.i386",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"1:yum-updatesd-0.9-6.el5_10.noarch",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"glibc-2.12-1.192.el6.x86_64",
|
||||
true,
|
||||
},
|
||||
{
|
||||
" glibc-2.12-1.192.el6.x86_64",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"glibc-2.12-1.192.el6.x86_64, iproute-2.6.18-15.el5.i386",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"k6 hoge.i386",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"triathlon",
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
found, err := r.isRpmPackageNameLine(tt.in)
|
||||
if tt.found != found {
|
||||
t.Errorf("[%d] line: %s, expected %t, actual %t, err %v", i, tt.in, tt.found, found, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseYumUpdateinfoToGetSeverity(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
var tests = []struct {
|
||||
@@ -403,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
|
||||
@@ -463,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")
|
||||
@@ -553,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
|
||||
@@ -567,6 +615,7 @@ bash.x86_64 4.1.2-33.el6_7.1 updates
|
||||
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
|
||||
`
|
||||
|
||||
r.Packages = []models.PackageInfo{
|
||||
@@ -590,6 +639,11 @@ python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases
|
||||
Version: "1.0",
|
||||
Release: "1",
|
||||
},
|
||||
{
|
||||
Name: "bind-utils",
|
||||
Version: "1.0",
|
||||
Release: "1",
|
||||
},
|
||||
}
|
||||
var tests = []struct {
|
||||
in string
|
||||
@@ -626,6 +680,13 @@ python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases
|
||||
NewVersion: "1.1",
|
||||
NewRelease: "3.el6ev",
|
||||
},
|
||||
{
|
||||
Name: "bind-utils",
|
||||
Version: "1.0",
|
||||
Release: "1",
|
||||
NewVersion: "9.3.6",
|
||||
NewRelease: "25.P1.el5_11.8",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -648,23 +709,23 @@ python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases
|
||||
|
||||
func TestParseYumCheckUpdateLinesAmazon(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Family = "amzon"
|
||||
r.Distro = config.Distro{Family: "amazon"}
|
||||
stdout := `Loaded plugins: priorities, update-motd, upgrade-helper
|
||||
34 package(s) needed for security, out of 71 available
|
||||
|
||||
bind-libs.x86_64 32:9.8.2-0.37.rc1.45.amzn1 amzn-main
|
||||
java-1.7.0-openjdk.x86_64 1:1.7.0.95-2.6.4.0.65.amzn1 amzn-main
|
||||
java-1.7.0-openjdk.x86_64 1.7.0.95-2.6.4.0.65.amzn1 amzn-main
|
||||
if-not-architecture 100-200 amzn-main
|
||||
`
|
||||
r.Packages = []models.PackageInfo{
|
||||
{
|
||||
Name: "bind-libs",
|
||||
Version: "32:9.8.0",
|
||||
Version: "9.8.0",
|
||||
Release: "0.33.rc1.45.amzn1",
|
||||
},
|
||||
{
|
||||
Name: "java-1.7.0-openjdk",
|
||||
Version: "1:1.7.0.0",
|
||||
Version: "1.7.0.0",
|
||||
Release: "2.6.4.0.0.amzn1",
|
||||
},
|
||||
{
|
||||
@@ -682,16 +743,16 @@ if-not-architecture 100-200 amzn-main
|
||||
models.PackageInfoList{
|
||||
{
|
||||
Name: "bind-libs",
|
||||
Version: "32:9.8.0",
|
||||
Version: "9.8.0",
|
||||
Release: "0.33.rc1.45.amzn1",
|
||||
NewVersion: "32:9.8.2",
|
||||
NewVersion: "9.8.2",
|
||||
NewRelease: "0.37.rc1.45.amzn1",
|
||||
},
|
||||
{
|
||||
Name: "java-1.7.0-openjdk",
|
||||
Version: "1:1.7.0.0",
|
||||
Version: "1.7.0.0",
|
||||
Release: "2.6.4.0.0.amzn1",
|
||||
NewVersion: "1:1.7.0.95",
|
||||
NewVersion: "1.7.0.95",
|
||||
NewRelease: "2.6.4.0.65.amzn1",
|
||||
},
|
||||
{
|
||||
@@ -844,3 +905,309 @@ func TestExtractPackNameVerRel(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
/* for CentOS6,7 (yum-util >= 1.1.20) */
|
||||
stdoutCentos6 = `---> Package libaio.x86_64 0:0.3.107-10.el6 will be installed
|
||||
--> Finished Dependency Resolution
|
||||
|
||||
Changes in packages about to be updated:
|
||||
|
||||
ChangeLog for: binutils-2.20.51.0.2-5.44.el6.x86_64
|
||||
* Mon Dec 7 21:00:00 2015 Nick Clifton <nickc@redhat.com> - 2.20.51.0.2-5.44
|
||||
- Backport upstream RELRO fixes. (#1227839)
|
||||
|
||||
** No ChangeLog for: chkconfig-1.3.49.5-1.el6.x86_64
|
||||
|
||||
ChangeLog for: coreutils-8.4-43.el6.x86_64, coreutils-libs-8.4-43.el6.x86_64
|
||||
* Wed Feb 10 21:00:00 2016 Ondrej Vasik <ovasik@redhat.com> - 8.4-43
|
||||
- sed should actually be /bin/sed (related #1222140)
|
||||
|
||||
* Wed Jan 6 21:00:00 2016 Ondrej Vasik <ovasik@redhat.com> - 8.4-41
|
||||
- colorls.sh,colorls.csh - call utilities with complete path (#1222140)
|
||||
- mkdir, mkfifo, mknod - respect default umask/acls when
|
||||
COREUTILS_CHILD_DEFAULT_ACLS envvar is set (to match rhel 7 behaviour,
|
||||
|
||||
ChangeLog for: centos-release-6-8.el6.centos.12.3.x86_64
|
||||
* Wed May 18 21:00:00 2016 Johnny Hughes <johnny@centos.org> 6-8.el6.centos.12.3
|
||||
- CentOS-6.8 Released
|
||||
- TESTSTRING CVE-0000-0000
|
||||
|
||||
ChangeLog for: 12:dhclient-4.1.1-51.P1.el6.centos.x86_64, 12:dhcp-common-4.1.1-51.P1.el6.centos.x86_64
|
||||
* Tue May 10 21:00:00 2016 Johnny Hughes <johnny@centos.org> - 12:4.1.1-51.P1
|
||||
- created patch 1000 for CentOS Branding
|
||||
- replaced vvendor variable with CentOS in the SPEC file
|
||||
- TESTSTRING CVE-1111-1111
|
||||
|
||||
* Mon Jan 11 21:00:00 2016 Jiri Popelka <jpopelka@redhat.com> - 12:4.1.1-51.P1
|
||||
- send unicast request/release via correct interface (#1297445)
|
||||
|
||||
* Thu Dec 3 21:00:00 2015 Jiri Popelka <jpopelka@redhat.com> - 12:4.1.1-50.P1
|
||||
- Lease table overflow crash. (#1133917)
|
||||
- Add ignore-client-uids option. (#1196768)
|
||||
- dhclient-script: it's OK if the arping reply comes from our system. (#1204095)
|
||||
- VLAN ID is only bottom 12-bits of TCI. (#1259552)
|
||||
- dhclient: Make sure link-local address is ready in stateless mode. (#1263466)
|
||||
- dhclient-script: make_resolv_conf(): Keep old nameservers
|
||||
if server sends domain-name/search, but no nameservers. (#1269595)
|
||||
|
||||
ChangeLog for: file-5.04-30.el6.x86_64, file-libs-5.04-30.el6.x86_64
|
||||
* Tue Feb 16 21:00:00 2016 Jan Kaluza <jkaluza@redhat.com> 5.04-30
|
||||
- fix CVE-2014-3538 (unrestricted regular expression matching)
|
||||
|
||||
* Tue Jan 5 21:00:00 2016 Jan Kaluza <jkaluza@redhat.com> 5.04-29
|
||||
- fix #1284826 - try to read ELF header to detect corrupted one
|
||||
|
||||
* Wed Dec 16 21:00:00 2015 Jan Kaluza <jkaluza@redhat.com> 5.04-28
|
||||
- fix #1263987 - fix bugs found by coverity in the patch
|
||||
|
||||
* Thu Nov 26 21:00:00 2015 Jan Kaluza <jkaluza@redhat.com> 5.04-27
|
||||
- fix CVE-2014-3587 (incomplete fix for CVE-2012-1571)
|
||||
- fix CVE-2014-3710 (out-of-bounds read in elf note headers)
|
||||
- fix CVE-2014-8116 (multiple DoS issues (resource consumption))
|
||||
- fix CVE-2014-8117 (denial of service issue (resource consumption))
|
||||
- fix CVE-2014-9620 (limit the number of ELF notes processed)
|
||||
- fix CVE-2014-9653 (malformed elf file causes access to uninitialized memory)
|
||||
|
||||
|
||||
Dependencies Resolved
|
||||
|
||||
`
|
||||
/* for CentOS5 (yum-util < 1.1.20) */
|
||||
stdoutCentos5 = `---> Package portmap.i386 0:4.0-65.2.2.1 set to be updated
|
||||
--> Finished Dependency Resolution
|
||||
|
||||
Changes in packages about to be updated:
|
||||
|
||||
libuser-0.54.7-3.el5.i386
|
||||
nss_db-2.2-38.el5_11.i386
|
||||
* Thu Nov 20 23:00:00 2014 Nalin Dahyabhai <nalin@redhat.com> - 2.2-38
|
||||
- build without strict aliasing (internal build tooling)
|
||||
|
||||
* Sat Nov 15 23:00:00 2014 Nalin Dahyabhai <nalin@redhat.com> - 2.2-37
|
||||
- pull in fix for a memory leak in nss_db (#1163493)
|
||||
|
||||
acpid-1.0.4-12.el5.i386
|
||||
* Thu Oct 6 00:00:00 2011 Jiri Skala <jskala@redhat.com> - 1.0.4-12
|
||||
- Resolves: #729769 - acpid dumping useless info to log
|
||||
|
||||
nash-5.1.19.6-82.el5.i386, mkinitrd-5.1.19.6-82.el5.i386
|
||||
* Tue Apr 15 00:00:00 2014 Brian C. Lane <bcl@redhat.com> 5.1.19.6-82
|
||||
- Use ! instead of / when searching sysfs for ccis device
|
||||
Resolves: rhbz#988020
|
||||
- Always include ahci module (except on s390) (bcl)
|
||||
Resolves: rhbz#978245
|
||||
- Prompt to recreate default initrd (karsten)
|
||||
Resolves: rhbz#472764
|
||||
|
||||
util-linux-2.13-0.59.el5_8.i386
|
||||
* Wed Oct 17 00:00:00 2012 Karel Zak <kzak@redhat.com> 2.13-0.59.el5_8
|
||||
- fix #865791 - fdisk fails to partition disk not in use
|
||||
|
||||
* Wed Dec 21 23:00:00 2011 Karel Zak <kzak@redhat.com> 2.13-0.59
|
||||
- fix #768382 - CVE-2011-1675 CVE-2011-1677 util-linux various flaws
|
||||
|
||||
* Wed Oct 26 00:00:00 2011 Karel Zak <kzak@redhat.com> 2.13-0.58
|
||||
- fix #677452 - util-linux fails to build with gettext-0.17
|
||||
|
||||
30:bind-utils-9.3.6-25.P1.el5_11.8.i386, 30:bind-libs-9.3.6-25.P1.el5_11.8.i386
|
||||
* Mon Mar 14 23:00:00 2016 Tomas Hozza <thozza@redhat.com> - 30:9.3.6-25.P1.8
|
||||
- Fix issue with patch for CVE-2016-1285 and CVE-2016-1286 found by test suite
|
||||
|
||||
* Wed Mar 9 23:00:00 2016 Tomas Hozza <thozza@redhat.com> - 30:9.3.6-25.P1.7
|
||||
- Fix CVE-2016-1285 and CVE-2016-1286
|
||||
|
||||
* Mon Jan 18 23:00:00 2016 Tomas Hozza <thozza@redhat.com> - 30:9.3.6-25.P1.6
|
||||
- Fix CVE-2015-8704
|
||||
|
||||
* Thu Sep 3 00:00:00 2015 Tomas Hozza <thozza@redhat.com> - 30:9.3.6-25.P1.5
|
||||
- Fix CVE-2015-8000
|
||||
|
||||
|
||||
Dependencies Resolved
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
func TestGetChangelogCVELines(t *testing.T) {
|
||||
var testsCentos6 = []struct {
|
||||
in models.PackageInfo
|
||||
out string
|
||||
}{
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "binutils",
|
||||
NewVersion: "2.20.51.0.2",
|
||||
NewRelease: "5.44.el6",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "centos-release",
|
||||
NewVersion: "6",
|
||||
NewRelease: "8.el6.centos.12.3",
|
||||
},
|
||||
`- TESTSTRING CVE-0000-0000
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "dhclient",
|
||||
NewVersion: "4.1.1",
|
||||
NewRelease: "51.P1.el6.centos",
|
||||
},
|
||||
`- TESTSTRING CVE-1111-1111
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "dhcp-common",
|
||||
NewVersion: "4.1.1",
|
||||
NewRelease: "51.P1.el6.centos",
|
||||
},
|
||||
`- TESTSTRING CVE-1111-1111
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "coreutils-libs",
|
||||
NewVersion: "8.4",
|
||||
NewRelease: "43.el6",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "file",
|
||||
NewVersion: "5.04",
|
||||
NewRelease: "30.el6",
|
||||
},
|
||||
`- fix CVE-2014-3538 (unrestricted regular expression matching)
|
||||
- fix CVE-2014-3587 (incomplete fix for CVE-2012-1571)
|
||||
- fix CVE-2014-3710 (out-of-bounds read in elf note headers)
|
||||
- fix CVE-2014-8116 (multiple DoS issues (resource consumption))
|
||||
- fix CVE-2014-8117 (denial of service issue (resource consumption))
|
||||
- fix CVE-2014-9620 (limit the number of ELF notes processed)
|
||||
- fix CVE-2014-9653 (malformed elf file causes access to uninitialized memory)
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "file-libs",
|
||||
NewVersion: "5.04",
|
||||
NewRelease: "30.el6",
|
||||
},
|
||||
`- fix CVE-2014-3538 (unrestricted regular expression matching)
|
||||
- fix CVE-2014-3587 (incomplete fix for CVE-2012-1571)
|
||||
- fix CVE-2014-3710 (out-of-bounds read in elf note headers)
|
||||
- fix CVE-2014-8116 (multiple DoS issues (resource consumption))
|
||||
- fix CVE-2014-8117 (denial of service issue (resource consumption))
|
||||
- fix CVE-2014-9620 (limit the number of ELF notes processed)
|
||||
- fix CVE-2014-9653 (malformed elf file causes access to uninitialized memory)
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Distro = config.Distro{
|
||||
Family: "centos",
|
||||
Release: "6.7",
|
||||
}
|
||||
for _, tt := range testsCentos6 {
|
||||
rpm2changelog, err := r.parseAllChangelog(stdoutCentos6)
|
||||
if err != nil {
|
||||
t.Errorf("err: %s", err)
|
||||
}
|
||||
changelog := r.getChangelogCVELines(rpm2changelog, tt.in)
|
||||
if tt.out != changelog {
|
||||
t.Errorf("line: expected %s, actual %s, tt: %#v", tt.out, changelog, tt)
|
||||
}
|
||||
}
|
||||
|
||||
var testsCentos5 = []struct {
|
||||
in models.PackageInfo
|
||||
out string
|
||||
}{
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "libuser",
|
||||
NewVersion: "0.54.7",
|
||||
NewRelease: "3.el5",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "nss_db",
|
||||
NewVersion: "2.2",
|
||||
NewRelease: "38.el5_11",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "acpid",
|
||||
NewVersion: "1.0.4",
|
||||
NewRelease: "82.el5",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "mkinitrd",
|
||||
NewVersion: "5.1.19.6",
|
||||
NewRelease: "82.el5",
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "util-linux",
|
||||
NewVersion: "2.13",
|
||||
NewRelease: "0.59.el5_8",
|
||||
},
|
||||
`- fix #768382 - CVE-2011-1675 CVE-2011-1677 util-linux various flaws
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "bind-libs",
|
||||
NewVersion: "9.3.6",
|
||||
NewRelease: "25.P1.el5_11.8",
|
||||
},
|
||||
`- Fix issue with patch for CVE-2016-1285 and CVE-2016-1286 found by test suite
|
||||
- Fix CVE-2016-1285 and CVE-2016-1286
|
||||
- Fix CVE-2015-8704
|
||||
- Fix CVE-2015-8000
|
||||
`,
|
||||
},
|
||||
{
|
||||
models.PackageInfo{
|
||||
Name: "bind-utils",
|
||||
NewVersion: "9.3.6",
|
||||
NewRelease: "25.P1.el5_11.8",
|
||||
},
|
||||
`- Fix issue with patch for CVE-2016-1285 and CVE-2016-1286 found by test suite
|
||||
- Fix CVE-2016-1285 and CVE-2016-1286
|
||||
- Fix CVE-2015-8704
|
||||
- Fix CVE-2015-8000
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
r.Distro = config.Distro{
|
||||
Family: "centos",
|
||||
Release: "5.6",
|
||||
}
|
||||
for _, tt := range testsCentos5 {
|
||||
rpm2changelog, err := r.parseAllChangelog(stdoutCentos5)
|
||||
if err != nil {
|
||||
t.Errorf("err: %s", err)
|
||||
}
|
||||
changelog := r.getChangelogCVELines(rpm2changelog, tt.in)
|
||||
if tt.out != changelog {
|
||||
t.Errorf("line: expected %s, actual %s, tt: %#v", tt.out, changelog, tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
@@ -5,6 +22,7 @@ import (
|
||||
"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"
|
||||
@@ -15,12 +33,19 @@ var Log *logrus.Entry
|
||||
|
||||
var servers []osTypeInterface
|
||||
|
||||
// Base Interface of redhat, debian
|
||||
// Base Interface of redhat, debian, freebsd
|
||||
type osTypeInterface interface {
|
||||
setServerInfo(config.ServerInfo)
|
||||
getServerInfo() config.ServerInfo
|
||||
setDistributionInfo(string, string)
|
||||
getDistributionInfo() string
|
||||
|
||||
setDistro(string, string)
|
||||
getDistro() config.Distro
|
||||
// getFamily() string
|
||||
|
||||
checkIfSudoNoPasswd() error
|
||||
detectPlatform() error
|
||||
getPlatform() models.Platform
|
||||
|
||||
checkRequiredPackagesInstalled() error
|
||||
scanPackages() error
|
||||
scanVulnByCpeName() error
|
||||
@@ -59,10 +84,9 @@ type CvePacksList []CvePacksInfo
|
||||
type CvePacksInfo struct {
|
||||
CveID string
|
||||
CveDetail cve.CveDetail
|
||||
Packs []models.PackageInfo
|
||||
DistroAdvisories []models.DistroAdvisory // for Aamazon, RHEL
|
||||
Packs models.PackageInfoList
|
||||
DistroAdvisories []models.DistroAdvisory // for Aamazon, RHEL, FreeBSD
|
||||
CpeNames []string
|
||||
// CvssScore float64
|
||||
}
|
||||
|
||||
// FindByCveID find by CVEID
|
||||
@@ -98,67 +122,95 @@ func (s CvePacksList) Swap(i, j int) {
|
||||
|
||||
// Less implement Sort Interface
|
||||
func (s CvePacksList) Less(i, j int) bool {
|
||||
return s[i].CveDetail.CvssScore("en") > s[j].CveDetail.CvssScore("en")
|
||||
return s[i].CveDetail.CvssScore(config.Conf.Lang) >
|
||||
s[j].CveDetail.CvssScore(config.Conf.Lang)
|
||||
}
|
||||
|
||||
func detectOS(c config.ServerInfo) (osType osTypeInterface) {
|
||||
var itsMe bool
|
||||
itsMe, osType = detectDebian(c)
|
||||
if itsMe {
|
||||
var fatalErr error
|
||||
|
||||
itsMe, osType, fatalErr = detectDebian(c)
|
||||
if fatalErr != nil {
|
||||
osType.setServerInfo(c)
|
||||
osType.setErrs([]error{fatalErr})
|
||||
return
|
||||
}
|
||||
itsMe, osType = detectRedhat(c)
|
||||
if itsMe {
|
||||
} else if itsMe {
|
||||
Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
if itsMe, osType = detectRedhat(c); itsMe {
|
||||
Log.Debugf("Redhat like Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
if itsMe, osType = detectFreebsd(c); itsMe {
|
||||
Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
osType.setServerInfo(c)
|
||||
osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
|
||||
return
|
||||
}
|
||||
|
||||
// InitServers detect the kind of OS distribution of target servers
|
||||
func InitServers(localLogger *logrus.Entry) error {
|
||||
Log = localLogger
|
||||
|
||||
hosts, err := detectServerOSes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to detect server OSes. err: %s", err)
|
||||
// PrintSSHableServerNames print SSH-able servernames
|
||||
func PrintSSHableServerNames() {
|
||||
Log.Info("SSH-able servers are below...")
|
||||
for _, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
fmt.Printf("%s@%s ",
|
||||
s.getServerInfo().Container.Name,
|
||||
s.getServerInfo().ServerName,
|
||||
)
|
||||
} else {
|
||||
fmt.Printf("%s ", s.getServerInfo().ServerName)
|
||||
}
|
||||
}
|
||||
servers = hosts
|
||||
|
||||
Log.Info("Detecting Container OS...")
|
||||
containers, err := detectContainerOSes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to detect Container OSes. err: %s", err)
|
||||
}
|
||||
servers = append(servers, containers...)
|
||||
return nil
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func detectServerOSes() (oses []osTypeInterface, err error) {
|
||||
// InitServers detect the kind of OS distribution of target servers
|
||||
func InitServers(localLogger *logrus.Entry) {
|
||||
Log = localLogger
|
||||
servers = detectServerOSes()
|
||||
containers := detectContainerOSes()
|
||||
servers = append(servers, containers...)
|
||||
}
|
||||
|
||||
func detectServerOSes() (sshAbleOses []osTypeInterface) {
|
||||
Log.Info("Detecting OS of servers... ")
|
||||
osTypeChan := make(chan osTypeInterface, len(config.Conf.Servers))
|
||||
defer close(osTypeChan)
|
||||
for _, s := range config.Conf.Servers {
|
||||
go func(s config.ServerInfo) {
|
||||
//TODO handling Unknown OS
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s", p, s.ServerName)
|
||||
}
|
||||
}()
|
||||
osTypeChan <- detectOS(s)
|
||||
}(s)
|
||||
}
|
||||
|
||||
timeout := time.After(300 * time.Second)
|
||||
var oses []osTypeInterface
|
||||
timeout := time.After(30 * time.Second)
|
||||
for i := 0; i < len(config.Conf.Servers); i++ {
|
||||
select {
|
||||
case res := <-osTypeChan:
|
||||
if 0 < len(res.getErrs()) {
|
||||
continue
|
||||
}
|
||||
Log.Infof("(%d/%d) Detected %s: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getDistributionInfo())
|
||||
oses = append(oses, res)
|
||||
if 0 < len(res.getErrs()) {
|
||||
Log.Errorf("(%d/%d) Failed: %s, err: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getErrs())
|
||||
} else {
|
||||
Log.Infof("(%d/%d) Detected: %s: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
res.getServerInfo().ServerName,
|
||||
res.getDistro())
|
||||
}
|
||||
case <-timeout:
|
||||
msg := "Timeout occurred while detecting"
|
||||
msg := "Timed out while detecting servers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
found := false
|
||||
@@ -169,55 +221,56 @@ func detectServerOSes() (oses []osTypeInterface, err error) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("Failed to detect. servername: %s", servername)
|
||||
Log.Errorf("(%d/%d) Timed out: %s",
|
||||
i+1, len(config.Conf.Servers),
|
||||
servername)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return oses, fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
errorOccurred := false
|
||||
for _, osi := range oses {
|
||||
if errs := osi.getErrs(); 0 < len(errs) {
|
||||
errorOccurred = true
|
||||
Log.Errorf("Some errors occurred on %s",
|
||||
osi.getServerInfo().ServerName)
|
||||
for _, err := range errs {
|
||||
Log.Error(err)
|
||||
}
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
sshAbleOses = append(sshAbleOses, o)
|
||||
}
|
||||
}
|
||||
if errorOccurred {
|
||||
return oses, fmt.Errorf("Some errors occurred")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detectContainerOSes() (oses []osTypeInterface, err error) {
|
||||
func detectContainerOSes() (actives []osTypeInterface) {
|
||||
Log.Info("Detecting OS of containers... ")
|
||||
osTypesChan := make(chan []osTypeInterface, len(servers))
|
||||
defer close(osTypesChan)
|
||||
for _, s := range servers {
|
||||
go func(s osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
Log.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
}
|
||||
}()
|
||||
osTypesChan <- detectContainerOSesOnServer(s)
|
||||
}(s)
|
||||
}
|
||||
|
||||
timeout := time.After(300 * time.Second)
|
||||
for i := 0; i < len(config.Conf.Servers); i++ {
|
||||
var oses []osTypeInterface
|
||||
timeout := time.After(30 * time.Second)
|
||||
for i := 0; i < len(servers); i++ {
|
||||
select {
|
||||
case res := <-osTypesChan:
|
||||
for _, osi := range res {
|
||||
sinfo := osi.getServerInfo()
|
||||
if 0 < len(osi.getErrs()) {
|
||||
Log.Errorf("Failed: %s err: %s", sinfo.ServerName, osi.getErrs())
|
||||
continue
|
||||
}
|
||||
sinfo := osi.getServerInfo()
|
||||
Log.Infof("Detected %s/%s on %s: %s",
|
||||
sinfo.Container.ContainerID, sinfo.Container.Name,
|
||||
sinfo.ServerName, osi.getDistributionInfo())
|
||||
oses = append(oses, res...)
|
||||
Log.Infof("Detected: %s@%s: %s",
|
||||
sinfo.Container.Name, sinfo.ServerName, osi.getDistro())
|
||||
}
|
||||
oses = append(oses, res...)
|
||||
case <-timeout:
|
||||
msg := "Timeout occurred while detecting"
|
||||
msg := "Timed out while detecting containers"
|
||||
Log.Error(msg)
|
||||
for servername := range config.Conf.Servers {
|
||||
found := false
|
||||
@@ -228,27 +281,17 @@ func detectContainerOSes() (oses []osTypeInterface, err error) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
Log.Errorf("Failed to detect. servername: %s", servername)
|
||||
Log.Errorf("Timed out: %s", servername)
|
||||
|
||||
}
|
||||
}
|
||||
return oses, fmt.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
errorOccurred := false
|
||||
for _, osi := range oses {
|
||||
if errs := osi.getErrs(); 0 < len(errs) {
|
||||
errorOccurred = true
|
||||
Log.Errorf("Some errors occurred on %s",
|
||||
osi.getServerInfo().ServerName)
|
||||
for _, err := range errs {
|
||||
Log.Error(err)
|
||||
}
|
||||
for _, o := range oses {
|
||||
if len(o.getErrs()) == 0 {
|
||||
actives = append(actives, o)
|
||||
}
|
||||
}
|
||||
if errorOccurred {
|
||||
return oses, fmt.Errorf("Some errors occurred")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -324,6 +367,53 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
|
||||
return oses
|
||||
}
|
||||
|
||||
// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH
|
||||
func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error {
|
||||
timeoutSec := 1 * 15
|
||||
errs := parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.checkIfSudoNoPasswd()
|
||||
}, timeoutSec)
|
||||
|
||||
if 0 < len(errs) {
|
||||
return fmt.Errorf(fmt.Sprintf("%s", errs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DetectPlatforms detects the platform of each servers.
|
||||
func DetectPlatforms(localLogger *logrus.Entry) {
|
||||
errs := detectPlatforms()
|
||||
if 0 < len(errs) {
|
||||
// Only logging
|
||||
Log.Warnf("Failed to detect platforms. err: %v", errs)
|
||||
}
|
||||
for i, s := range servers {
|
||||
if s.getServerInfo().IsContainer() {
|
||||
Log.Infof("(%d/%d) %s on %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().Container.Name,
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
|
||||
} else {
|
||||
Log.Infof("(%d/%d) %s is running on %s",
|
||||
i+1, len(servers),
|
||||
s.getServerInfo().ServerName,
|
||||
s.getPlatform().Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func detectPlatforms() []error {
|
||||
timeoutSec := 1 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.detectPlatform()
|
||||
}, timeoutSec)
|
||||
}
|
||||
|
||||
// Prepare installs requred packages to scan vulnerabilities.
|
||||
func Prepare() []error {
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
@@ -346,6 +436,13 @@ func Scan() []error {
|
||||
return errs
|
||||
}
|
||||
|
||||
if err := setupCangelogCache(); err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
if cache.DB != nil {
|
||||
defer cache.DB.Close()
|
||||
}
|
||||
|
||||
Log.Info("Scanning vulnerable OS packages...")
|
||||
if errs := scanPackages(); errs != nil {
|
||||
return errs
|
||||
@@ -358,6 +455,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 {
|
||||
@@ -366,7 +480,7 @@ func checkRequiredPackagesInstalled() []error {
|
||||
}
|
||||
|
||||
func scanPackages() []error {
|
||||
timeoutSec := 30 * 60
|
||||
timeoutSec := 120 * 60
|
||||
return parallelSSHExec(func(o osTypeInterface) error {
|
||||
return o.scanPackages()
|
||||
}, timeoutSec)
|
||||
|
||||
264
scan/sshutil.go
264
scan/sshutil.go
@@ -25,7 +25,10 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -34,18 +37,30 @@ import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/cenkalti/backoff"
|
||||
conf "github.com/future-architect/vuls/config"
|
||||
"github.com/k0kubun/pp"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
type sshResult struct {
|
||||
Servername string
|
||||
Host string
|
||||
Port string
|
||||
Cmd string
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitStatus int
|
||||
Error error
|
||||
}
|
||||
|
||||
func (s sshResult) String() string {
|
||||
return fmt.Sprintf(
|
||||
"SSHResult: servername: %s, cmd: %s, exitstatus: %d, stdout: %s, stderr: %s, err: %s",
|
||||
s.Servername, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error)
|
||||
}
|
||||
|
||||
func (s sshResult) isSuccess(expectedStatusCodes ...int) bool {
|
||||
if s.Error != nil {
|
||||
return false
|
||||
}
|
||||
if len(expectedStatusCodes) == 0 {
|
||||
return s.ExitStatus == 0
|
||||
}
|
||||
@@ -64,10 +79,19 @@ const sudo = true
|
||||
const noSudo = false
|
||||
|
||||
func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []error) {
|
||||
resChan := make(chan string, len(servers))
|
||||
errChan := make(chan error, len(servers))
|
||||
defer close(errChan)
|
||||
defer close(resChan)
|
||||
|
||||
for _, s := range servers {
|
||||
go func(s osTypeInterface) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
logrus.Debugf("Panic: %s on %s",
|
||||
p, s.getServerInfo().ServerName)
|
||||
}
|
||||
}()
|
||||
if err := fn(s); err != nil {
|
||||
errChan <- fmt.Errorf("%s@%s:%s: %s",
|
||||
s.getServerInfo().User,
|
||||
@@ -76,7 +100,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
err,
|
||||
)
|
||||
} else {
|
||||
errChan <- nil
|
||||
resChan <- s.getServerInfo().ServerName
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
@@ -88,74 +112,78 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
|
||||
timeout = timeoutSec[0]
|
||||
}
|
||||
|
||||
var snames []string
|
||||
isTimedout := false
|
||||
for i := 0; i < len(servers); i++ {
|
||||
select {
|
||||
case s := <-resChan:
|
||||
snames = append(snames, s)
|
||||
case err := <-errChan:
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
logrus.Debug("Parallel SSH Success")
|
||||
}
|
||||
errs = append(errs, err)
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
logrus.Errorf("Parallel SSH Timeout")
|
||||
errs = append(errs, fmt.Errorf("Timed out"))
|
||||
isTimedout = true
|
||||
}
|
||||
}
|
||||
|
||||
// collect timed out servernames
|
||||
var timedoutSnames []string
|
||||
if isTimedout {
|
||||
for _, s := range servers {
|
||||
name := s.getServerInfo().ServerName
|
||||
found := false
|
||||
for _, t := range snames {
|
||||
if name == t {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
timedoutSnames = append(timedoutSnames, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if isTimedout {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"Timed out: %s", timedoutSnames))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (result sshResult) {
|
||||
// Setup Logger
|
||||
var logger *logrus.Entry
|
||||
if len(log) == 0 {
|
||||
level := logrus.InfoLevel
|
||||
if conf.Conf.Debug == true {
|
||||
level = logrus.DebugLevel
|
||||
}
|
||||
l := &logrus.Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: new(logrus.TextFormatter),
|
||||
Hooks: make(logrus.LevelHooks),
|
||||
Level: level,
|
||||
}
|
||||
logger = logrus.NewEntry(l)
|
||||
if isSSHExecNative() {
|
||||
result = sshExecNative(c, cmd, sudo)
|
||||
} else {
|
||||
logger = log[0]
|
||||
result = sshExecExternal(c, cmd, sudo)
|
||||
}
|
||||
c.SudoOpt.ExecBySudo = true
|
||||
var err error
|
||||
if sudo && c.User != "root" && !c.IsContainer() {
|
||||
switch {
|
||||
case c.SudoOpt.ExecBySudo:
|
||||
cmd = fmt.Sprintf("echo %s | sudo -S %s", c.Password, cmd)
|
||||
case c.SudoOpt.ExecBySudoSh:
|
||||
cmd = fmt.Sprintf("echo %s | sudo sh -c '%s'", c.Password, cmd)
|
||||
default:
|
||||
logger.Panicf("sudoOpt is invalid. SudoOpt: %v", c.SudoOpt)
|
||||
}
|
||||
}
|
||||
// set pipefail option.
|
||||
// http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
|
||||
cmd = fmt.Sprintf("set -o pipefail; %s", cmd)
|
||||
logger.Debugf("Command: %s",
|
||||
strings.Replace(maskPassword(cmd, c.Password), "\n", "", -1))
|
||||
|
||||
if c.IsContainer() {
|
||||
switch c.Container.Type {
|
||||
case "", "docker":
|
||||
cmd = fmt.Sprintf(`docker exec %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
|
||||
}
|
||||
}
|
||||
logger := getSSHLogger(log...)
|
||||
logger.Debug(result)
|
||||
return
|
||||
}
|
||||
|
||||
func isSSHExecNative() bool {
|
||||
return runtime.GOOS == "windows" || !conf.Conf.SSHExternal
|
||||
}
|
||||
|
||||
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
|
||||
result.Servername = c.ServerName
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
|
||||
var client *ssh.Client
|
||||
client, err = sshConnect(c)
|
||||
var err error
|
||||
if client, err = sshConnect(c); err != nil {
|
||||
result.Error = err
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
var session *ssh.Session
|
||||
if session, err = client.NewSession(); err != nil {
|
||||
logger.Errorf("Failed to new session. err: %s, c: %s",
|
||||
err,
|
||||
pp.Sprintf("%v", c))
|
||||
result.Error = fmt.Errorf(
|
||||
"Failed to create a new session. servername: %s, err: %s",
|
||||
c.ServerName, err)
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
@@ -167,11 +195,10 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
if err = session.RequestPty("xterm", 400, 120, modes); err != nil {
|
||||
logger.Errorf("Failed to request for pseudo terminal. err: %s, c: %s",
|
||||
err,
|
||||
pp.Sprintf("%v", c))
|
||||
|
||||
if err = session.RequestPty("xterm", 400, 256, modes); err != nil {
|
||||
result.Error = fmt.Errorf(
|
||||
"Failed to request for pseudo terminal. servername: %s, err: %s",
|
||||
c.ServerName, err)
|
||||
result.ExitStatus = 999
|
||||
return
|
||||
}
|
||||
@@ -180,6 +207,7 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
|
||||
session.Stdout = &stdoutBuf
|
||||
session.Stderr = &stderrBuf
|
||||
|
||||
cmd = decolateCmd(c, cmd, sudo)
|
||||
if err := session.Run(cmd); err != nil {
|
||||
if exitErr, ok := err.(*ssh.ExitError); ok {
|
||||
result.ExitStatus = exitErr.ExitStatus()
|
||||
@@ -192,18 +220,106 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
|
||||
|
||||
result.Stdout = stdoutBuf.String()
|
||||
result.Stderr = stderrBuf.String()
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
|
||||
logger.Debugf(
|
||||
"SSH executed. cmd: %s, status: %#v\nstdout: \n%s\nstderr: \n%s",
|
||||
maskPassword(cmd, c.Password), err, result.Stdout, result.Stderr)
|
||||
|
||||
result.Cmd = strings.Replace(cmd, "\n", "", -1)
|
||||
return
|
||||
}
|
||||
|
||||
func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result sshResult) {
|
||||
sshBinaryPath, err := exec.LookPath("ssh")
|
||||
if err != nil {
|
||||
return sshExecNative(c, cmd, sudo)
|
||||
}
|
||||
|
||||
defaultSSHArgs := []string{
|
||||
"-t",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "LogLevel=quiet",
|
||||
"-o", "ConnectionAttempts=3",
|
||||
"-o", "ConnectTimeout=10",
|
||||
"-o", "ControlMaster=no",
|
||||
"-o", "ControlPath=none",
|
||||
|
||||
// TODO ssh session multiplexing
|
||||
// "-o", "ControlMaster=auto",
|
||||
// "-o", `ControlPath=~/.ssh/controlmaster-%r-%h.%p`,
|
||||
// "-o", "Controlpersist=30m",
|
||||
}
|
||||
args := append(defaultSSHArgs, fmt.Sprintf("%s@%s", c.User, c.Host))
|
||||
args = append(args, "-p", c.Port)
|
||||
|
||||
// if conf.Conf.Debug {
|
||||
// args = append(args, "-v")
|
||||
// }
|
||||
|
||||
if 0 < len(c.KeyPath) {
|
||||
args = append(args, "-i", c.KeyPath)
|
||||
args = append(args, "-o", "PasswordAuthentication=no")
|
||||
}
|
||||
|
||||
cmd = decolateCmd(c, cmd, sudo)
|
||||
// cmd = fmt.Sprintf("stty cols 256; set -o pipefail; %s", cmd)
|
||||
|
||||
args = append(args, cmd)
|
||||
execCmd := exec.Command(sshBinaryPath, args...)
|
||||
|
||||
var stdoutBuf, stderrBuf bytes.Buffer
|
||||
execCmd.Stdout = &stdoutBuf
|
||||
execCmd.Stderr = &stderrBuf
|
||||
if err := execCmd.Run(); err != nil {
|
||||
if e, ok := err.(*exec.ExitError); ok {
|
||||
if s, ok := e.Sys().(syscall.WaitStatus); ok {
|
||||
result.ExitStatus = s.ExitStatus()
|
||||
} else {
|
||||
result.ExitStatus = 998
|
||||
}
|
||||
} else {
|
||||
result.ExitStatus = 999
|
||||
}
|
||||
} else {
|
||||
result.ExitStatus = 0
|
||||
}
|
||||
|
||||
result.Stdout = stdoutBuf.String()
|
||||
result.Stderr = stderrBuf.String()
|
||||
result.Servername = c.ServerName
|
||||
result.Host = c.Host
|
||||
result.Port = c.Port
|
||||
result.Cmd = fmt.Sprintf("%s %s", sshBinaryPath, strings.Join(args, " "))
|
||||
return
|
||||
}
|
||||
|
||||
func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
|
||||
if len(log) == 0 {
|
||||
return util.NewCustomLogger(conf.ServerInfo{})
|
||||
}
|
||||
return log[0]
|
||||
}
|
||||
|
||||
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.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)
|
||||
}
|
||||
|
||||
if c.IsContainer() {
|
||||
switch c.Container.Type {
|
||||
case "", "docker":
|
||||
cmd = fmt.Sprintf(`docker exec %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
|
||||
}
|
||||
}
|
||||
// cmd = fmt.Sprintf("set -x; %s", cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -226,19 +342,13 @@ func tryAgentConnect(c conf.ServerInfo) *ssh.Client {
|
||||
}
|
||||
|
||||
func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
|
||||
|
||||
if client = tryAgentConnect(c); client != nil {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
var auths = []ssh.AuthMethod{}
|
||||
if auths, err = addKeyAuth(auths, c.KeyPath, c.KeyPassword); err != nil {
|
||||
logrus.Fatalf("Failed to add keyAuth. %s@%s:%s err: %s",
|
||||
c.User, c.Host, c.Port, err)
|
||||
}
|
||||
|
||||
if c.Password != "" {
|
||||
auths = append(auths, ssh.Password(c.Password))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// http://blog.ralch.com/tutorial/golang-ssh-connection/
|
||||
@@ -248,8 +358,9 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
|
||||
}
|
||||
|
||||
notifyFunc := func(e error, t time.Duration) {
|
||||
logrus.Warnf("Failed to ssh %s@%s:%s err: %s, Retrying in %s...",
|
||||
c.User, c.Host, c.Port, e, t)
|
||||
logger := getSSHLogger()
|
||||
logger.Debugf("Failed to Dial to %s, err: %s, Retrying in %s...",
|
||||
c.ServerName, e, t)
|
||||
}
|
||||
err = backoff.RetryNotify(func() error {
|
||||
if client, err = ssh.Dial("tcp", c.Host+":"+c.Port, config); err != nil {
|
||||
@@ -313,11 +424,6 @@ func parsePemBlock(block *pem.Block) (interface{}, error) {
|
||||
case "DSA PRIVATE KEY":
|
||||
return ssh.ParseDSAPrivateKey(block.Bytes)
|
||||
default:
|
||||
return nil, fmt.Errorf("rtop: unsupported key type %q", block.Type)
|
||||
return nil, fmt.Errorf("Unsupported key type %q", block.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// ref golang.org/x/crypto/ssh/keys.go#ParseRawPrivateKey.
|
||||
func maskPassword(cmd, sudoPass string) string {
|
||||
return strings.Replace(cmd, fmt.Sprintf("echo %s", sudoPass), "echo *****", -1)
|
||||
}
|
||||
|
||||
101
setup/docker/README.ja.md
Normal file
101
setup/docker/README.ja.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# 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
|
||||
```
|
||||
87
setup/docker/README.md
Normal file
87
setup/docker/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Vuls on Docker
|
||||
|
||||
## What's Vuls-On-Docker
|
||||
|
||||
- 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
|
||||
|
||||
## 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.
|
||||
|
||||
```
|
||||
$ 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
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Start A Vuls Container
|
||||
|
||||
- Execute the following command to build and run a Vuls Container
|
||||
|
||||
```
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
## 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/
|
||||
```
|
||||
|
||||
# Update modules
|
||||
|
||||
- update vuls, go-cve-dictionary, vulsrepo
|
||||
```
|
||||
$ docker exec -t vuls scripts/update_modules.sh
|
||||
```
|
||||
|
||||
# Update Vulnerability database
|
||||
|
||||
- Fetch Vulnerability database from NVD
|
||||
```
|
||||
$ docker exec -t vuls scripts/fetch_nvd_last2y.sh
|
||||
```
|
||||
0
setup/docker/conf/.gitkeep
Normal file
0
setup/docker/conf/.gitkeep
Normal file
11
setup/docker/docker-compose.yml
Normal file
11
setup/docker/docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
version: '2'
|
||||
services:
|
||||
vuls:
|
||||
container_name: vuls
|
||||
build: ./dockerfile
|
||||
image: vuls-docker:0.1
|
||||
volumes:
|
||||
- ./conf:/opt/vuls/conf
|
||||
ports:
|
||||
- "80:80"
|
||||
|
||||
73
setup/docker/dockerfile/Dockerfile
Normal file
73
setup/docker/dockerfile/Dockerfile
Normal file
@@ -0,0 +1,73 @@
|
||||
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
|
||||
|
||||
#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 apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
apache2 \
|
||||
libcgi-pm-perl \
|
||||
libjson-perl \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& cd /var/www/html/ \
|
||||
&& git clone https://github.com/usiusi360/vulsrepo \
|
||||
&& mkdir /var/www/html/vulsrepo/results \
|
||||
&& cp /var/www/html/vulsrepo/dist/cgi/vulsrepo.conf.sample /etc/apache2/conf-enabled/vulsrepo.conf \
|
||||
&& a2enmod cgid
|
||||
|
||||
#Home
|
||||
WORKDIR /opt/vuls
|
||||
EXPOSE 80 443
|
||||
ENTRYPOINT service apache2 start && tail -f /dev/null
|
||||
6
setup/docker/dockerfile/scripts/fetch_jvn_all.sh
Normal file
6
setup/docker/dockerfile/scripts/fetch_jvn_all.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
for i in {2002..2016}; do go-cve-dictionary fetchjvn -years $i; done
|
||||
|
||||
6
setup/docker/dockerfile/scripts/fetch_jvn_last2y.sh
Normal file
6
setup/docker/dockerfile/scripts/fetch_jvn_last2y.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchjvn -last2y
|
||||
|
||||
5
setup/docker/dockerfile/scripts/fetch_jvn_latest.sh
Normal file
5
setup/docker/dockerfile/scripts/fetch_jvn_latest.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchjvn -latest
|
||||
6
setup/docker/dockerfile/scripts/fetch_nvd_all.sh
Normal file
6
setup/docker/dockerfile/scripts/fetch_nvd_all.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/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
|
||||
|
||||
6
setup/docker/dockerfile/scripts/fetch_nvd_last2y.sh
Normal file
6
setup/docker/dockerfile/scripts/fetch_nvd_last2y.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
#VULS_CONF=${VULS_ROOT}/conf
|
||||
cd $VULS_ROOT
|
||||
go-cve-dictionary fetchnvd -last2y
|
||||
|
||||
8
setup/docker/dockerfile/scripts/scan_for_vulsrepo.sh
Normal file
8
setup/docker/dockerfile/scripts/scan_for_vulsrepo.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
VULS_ROOT=/opt/vuls
|
||||
VULS_CONF=${VULS_ROOT}/conf
|
||||
APACHE_VULSREPO_ROOT=/var/www/html/vulsrepo
|
||||
cd $VULS_ROOT
|
||||
vuls scan -report-json --cve-dictionary-dbpath=${VULS_ROOT}/cve.sqlite3 -config=${VULS_CONF}/config.toml
|
||||
rm ${APACHE_VULSREPO_ROOT}/results/*
|
||||
cp ${VULS_ROOT}/results/current/* ${APACHE_VULSREPO_ROOT}/results
|
||||
16
setup/docker/dockerfile/scripts/update_modules.sh
Normal file
16
setup/docker/dockerfile/scripts/update_modules.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/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
|
||||
|
||||
|
||||
cd /var/www/html/vulsrepo
|
||||
git pull origin master
|
||||
19
util/util.go
19
util/util.go
@@ -31,6 +31,12 @@ func GenWorkers(num int) chan<- func() {
|
||||
tasks := make(chan func())
|
||||
for i := 0; i < num; i++ {
|
||||
go func() {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
log := NewCustomLogger(config.ServerInfo{})
|
||||
log.Debugf("Panic: %s")
|
||||
}
|
||||
}()
|
||||
for f := range tasks {
|
||||
f()
|
||||
}
|
||||
@@ -105,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)
|
||||
@@ -118,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ 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 main
|
||||
package version
|
||||
|
||||
// Name is Vuls
|
||||
const Name string = "vuls"
|
||||
|
||||
// Version of Vuls
|
||||
const Version string = "0.1.4"
|
||||
const Version string = "0.1.6"
|
||||
Reference in New Issue
Block a user