Compare commits

...

233 Commits

Author SHA1 Message Date
kota kanbe
8b264a564a Bump up version 2016-08-16 20:24:33 +09:00
Kota Kanbe
227da93c13 Merge pull request #148 from future-architect/remove-ask-sudo-password
Disable -ask-sudo-password for security reasons
2016-08-16 11:12:02 +09:00
kota kanbe
f939041606 Disable -ask-sudo-password for security reasons 2016-08-16 11:09:01 +09:00
Kota Kanbe
e5b1a0bef8 Merge pull request #149 from kit494way/fix-scan-debian
Fix apt command to scan correctly when system locale is not english
2016-08-15 11:31:58 +09:00
KITAGAWA Yasutaka
b9404d0880 Fix apt command to scan correctly when system locale is not english 2016-08-14 01:05:23 +09:00
Kota Kanbe
d6f12868be Merge pull request #144 from future-architect/update-readme
Update README #138
2016-08-01 08:35:24 +09:00
kota kanbe
b79e96f6cf Update README #138 2016-08-01 08:33:44 +09:00
Kota Kanbe
b066cc819e Merge pull request #143 from future-architect/sudo-require-tty
Fix no tty error while executing with -external-ssh option
2016-07-29 17:59:21 +09:00
kota kanbe
4b669a0d49 Fix no tty error while executing with -external-ssh option 2016-07-29 17:56:20 +09:00
Kota Kanbe
5e9de5d91a Merge pull request #138 from tai-ga/master
Support high-speed scanning for CentOS
2016-07-27 18:43:28 +09:00
Masahiro Ono
da68b061e3 Fix release string that contains "centos" 2016-07-27 13:15:13 +09:00
Masahiro Ono
6c3802071f Add error handling to getChangelogCVELines 2016-07-27 13:13:07 +09:00
Masahiro Ono
ad84f09bce Merge pull request #1 from tai-ga/fix-parse-allchangelog
Fix checklogic of detecting packagename line in changelog.
2016-07-27 13:10:31 +09:00
kota kanbe
04166632d3 Fix checklogic of detecting packagename line in changelog. 2016-07-27 10:58:53 +09:00
Kota Kanbe
376238b1ad Merge pull request #142 from dtan4/fix-typo
Fix a typo
2016-07-26 10:04:21 +09:00
Masahiro Ono
4f0dbff059 Fix golint errors of scan/redhat.go 2016-07-25 14:36:56 +09:00
Daisuke Fujita
f506e2b50a Fix a typo 2016-07-24 16:34:36 +09:00
kota kanbe
88d2fbf5e2 Add performance considerations to README 2016-07-22 18:17:14 +09:00
Kota Kanbe
7fd8cc5449 Merge pull request #141 from sadayuki-matsuno/wrong_log_package
wrong log packages
2016-07-22 12:09:34 +09:00
Sadayuki Matsuno
d033463b34 wrong log packages 2016-07-22 12:07:13 +09:00
Kota Kanbe
740208cf74 Merge pull request #140 from mikkame/fix-docker-howto
Remove unnecessary step in readme of docker setup
2016-07-21 15:49:34 +09:00
mikami
0036c0b10e Remove unnecessary step in readme of docker setup 2016-07-21 15:12:36 +09:00
Kota Kanbe
834c832390 Merge pull request #139 from chanomaru/update_logo
Update logo
2016-07-20 21:25:44 +09:00
chanomaru
5bc99dfd25 Update logo 2016-07-20 20:34:47 +09:00
Masahiro Ono
c92d2d064a Support high-speed scanning for CentOS 2016-07-19 18:51:02 +09:00
Kota Kanbe
a60c21323c Merge pull request #134 from future-architect/add-configtest
Add configtest subcommand. skip un-ssh-able servers.
2016-07-19 13:46:08 +09:00
kota kanbe
34d6d6e709 Add configtest subcommand. skip un-ssh-able servers. 2016-07-19 12:29:20 +09:00
Kota Kanbe
f2ddafc718 Merge pull request #137 from Rompei/fix-detect-platform
Fix platform detection.
2016-07-16 16:39:21 +09:00
Rompei
267afdd15d Fix platform detection. 2016-07-16 15:53:57 +09:00
Kota Kanbe
48b7b82e33 Merge pull request #135 from a2atsu/change-readme
Update README.ja.md to fix wrong tips.
2016-07-15 21:01:28 +09:00
MURATA Atsu
84e5e5432e Update README.ja.md to fix wrong tips. 2016-07-15 20:56:58 +09:00
Kota Kanbe
201e18eac2 Merge pull request #133 from a2atsu/change-readme
add tips about NVD JVN issue
2016-07-15 15:23:09 +09:00
MURATA Atsu
3f3f0b1fec add tips about NVD JVN issue 2016-07-15 13:39:35 +09:00
kota kanbe
ca697c5038 Fix help message of scan subcommand 2016-07-13 19:27:26 +09:00
Kota Kanbe
5aeeb4e8b4 Merge pull request #117 from future-architect/optional_key_value
[WIP]Add optional key-values that will be outputted to JSON in config
2016-07-13 12:39:42 +09:00
kota kanbe
c285f9f587 Add optional key-values that will be outputted to JSON in config 2016-07-13 12:38:41 +09:00
Kota Kanbe
d046608426 Merge pull request #130 from future-architect/azure-blob
Support -report-azure-blob option
2016-07-12 16:44:03 +09:00
kota kanbe
b91ed9cff5 Support -report-azure-blob option 2016-07-12 16:21:45 +09:00
Kota Kanbe
185d85bfdd Update README.md 2016-07-08 16:28:38 +09:00
Kota Kanbe
44b2c1464a Update README.ja.md 2016-07-08 10:56:48 +09:00
Kota Kanbe
a0762a0a6c Update README.md 2016-07-08 10:49:02 +09:00
Kota Kanbe
2ad7660c09 Merge pull request #129 from aomoriringo/master
Fix README wrong links
2016-07-07 15:02:23 +09:00
aomoriringo
d8b8c38182 Fix README wrong links 2016-07-07 14:52:20 +09:00
Kota Kanbe
1d50e5126a Update README.ja.md 2016-07-06 15:16:00 +09:00
Kota Kanbe
aa55e30358 Update README.ja.md 2016-07-06 12:59:05 +09:00
Kota Kanbe
f662de50db Update README.md 2016-07-06 12:54:59 +09:00
Kota Kanbe
24c798ad3a Update README.md 2016-07-06 12:35:27 +09:00
Kota Kanbe
0e304ae546 Merge pull request #126 from chanomaru/add_logo
Add logo
2016-07-06 09:59:51 +09:00
chanomaru
cd604cbfe7 Add logo 2016-07-05 20:49:56 +09:00
Kota Kanbe
b8e66d9df0 Merge pull request #125 from future-architect/setup-docker
Improve setup/docker
2016-07-05 20:04:52 +09:00
kota kanbe
a2c738e57b Improve setup/docker 2016-07-05 20:03:49 +09:00
Kota Kanbe
ae16cd708c Merge pull request #124 from aomoriringo/master
Fix scan command help
2016-07-04 19:42:33 +09:00
aomoriringo
2ed0443f88 Fix README typo 2016-07-04 19:05:06 +09:00
aomoriringo
38f1c5075d Fix missing parameter of scan command 2016-07-04 19:04:42 +09:00
Kota Kanbe
55043a6348 Merge pull request #121 from hikachan/master
added dockernized-vuls with vulsrepo
2016-07-04 15:36:57 +09:00
hikachan
1f6eb55b86 added dockernized-vuls with vulsrepo 2016-07-04 15:32:34 +09:00
Kota Kanbe
d9d8500484 Merge pull request #119 from future-architect/fix_detect_platform
Fix detect platform on azure and degital ocean
2016-07-03 16:19:06 +09:00
kota kanbe
0fca75c2db Fix detect platform on azure and degital ocean 2016-07-03 16:17:54 +09:00
Kota Kanbe
a7dcccbdf9 Merge pull request #118 from future-architect/remove-json-marshall-indent
Remove json marshall-indent
2016-07-03 11:17:46 +09:00
kota kanbe
396eb5aec2 Remove json marshall-indent 2016-07-03 11:16:47 +09:00
Kota Kanbe
79d2076e09 Merge pull request #116 from future-architect/readme-japanese
Improve Readme.ja
2016-07-01 16:01:34 +09:00
kota kanbe
693dca4ca2 Improve README 2016-07-01 16:00:16 +09:00
Kota Kanbe
4047076033 Merge pull request #115 from future-architect/change-dir-structure
Change dir structure
2016-06-30 17:18:48 +09:00
kota kanbe
acb0b71f1b Change dir structure 2016-06-30 17:15:31 +09:00
Kota Kanbe
32d9352048 Update README.md 2016-06-30 13:45:40 +09:00
Kota Kanbe
0246556f7c Merge pull request #114 from future-architect/add-architecture-diag
Add architecture diag to README.md
2016-06-30 13:43:26 +09:00
kota kanbe
a17284681f Add architecture diag to README.md 2016-06-30 13:41:59 +09:00
Kota Kanbe
adb66e3298 Merge pull request #113 from future-architect/add-some-validation-loading-config
Add some validation of loading config. user, host and port
2016-06-30 09:06:09 +09:00
kota kanbe
ad062d777d Add some validation of loading config. user, host and port 2016-06-30 09:01:47 +09:00
Kota Kanbe
ffe1ff73a5 Merge pull request #111 from future-architect/enable-to-search-by-cpenames-from-cvedb
Fix nil pointer when scan with -cve-dictionary-dbpath and cpeNames
2016-06-29 10:47:22 +09:00
kota kanbe
54f9202d74 Fix nil pointer when scan with -cve-dictionary-dbpath and cpeNames 2016-06-29 10:44:13 +09:00
Kota Kanbe
ef3e173fb2 Merge pull request #110 from future-architect/remove_vulndb_before_pkg_audit
Remove vulndb file before pkg audit
2016-06-27 05:34:00 +09:00
kota kanbe
1aeec2ae51 Remove vulndb file before pkg audit 2016-06-27 05:28:08 +09:00
Kota Kanbe
1f50bfd801 Merge pull request #108 from future-architect/handle-ssh-255
Add error handling when unable to connect via ssh. status code: 255
2016-06-26 08:16:46 +09:00
kota kanbe
d3466eabe5 Add error handling when unable to connect via ssh. status code: 255 2016-06-26 08:15:40 +09:00
Kota Kanbe
8aff1af939 Update README.md 2016-06-22 19:32:47 +09:00
Kota Kanbe
af35303432 Merge pull request #101 from future-architect/external_ssh_mode
[WIP]Support scanning with external ssh command
2016-06-22 11:11:10 +09:00
kota kanbe
0ef1a5a3ce Support scanning with external ssh command 2016-06-22 11:00:01 +09:00
Kota Kanbe
e958bc8212 Update README.md 2016-06-17 09:29:45 +09:00
Kota Kanbe
e0ca6e89d1 Update README.md 2016-06-17 09:27:22 +09:00
Kota Kanbe
55d8ae124a Update README.md 2016-06-17 09:13:27 +09:00
Kota Kanbe
5e28ec22e1 Merge pull request #100 from future-architect/rename-linux-to-base
Rename linux.go to base.go
2016-06-16 10:40:35 +09:00
kota kanbe
c3deb93489 Rename linux.go to base.go 2016-06-16 10:37:49 +09:00
Kota Kanbe
a9aca94848 Update README.md 2016-06-16 01:05:32 +09:00
Kota Kanbe
f3c06890dd Update README.md 2016-06-16 01:01:07 +09:00
Kota Kanbe
d9d0e629fd Merge pull request #98 from future-architect/freebsd
Enable to detect vulnerabilities on FreeBSD
2016-06-14 16:40:16 +09:00
kota kanbe
17181405e3 Enable to detect vulnerabilities on FreeBSD 2016-06-14 16:34:11 +09:00
Kota Kanbe
c209564945 Merge pull request #93 from sadayuki-matsuno/redhat
fix rhel check-update format and add *config.toml into .gitignore
2016-06-13 14:03:56 +09:00
Sadayuki Matsuno
2da01db438 change ssh terminal width to fix rhel check-update format error and add *config.toml into .gitignore 2016-06-12 19:05:18 +09:00
Kota Kanbe
8c4913d411 Merge pull request #95 from future-architect/detect-platform
Detect Platform and get instance-id of amazon ec2
2016-06-07 16:18:31 +09:00
kota kanbe
e7ffc24844 Detect platform and get instance-id of amazon ec2 2016-06-07 16:16:55 +09:00
Kota Kanbe
259f23f6ee Merge pull request #92 from future-architect/s3-output
Add -report-s3 option
2016-06-06 09:31:41 +09:00
kota kanbe
0de38b99c2 Add -report-s3 option 2016-06-06 09:29:02 +09:00
Kota Kanbe
1044fb8574 Merge pull request #90 from justyntemme/master
Added FreeBSD support.
2016-06-03 13:52:57 +09:00
justyn
e5bfa1bd6f Added FreeBSD support. 2016-06-02 23:11:00 -05:00
Kota Kanbe
a29b2a2ad9 Update README.md 2016-06-03 09:14:49 +09:00
Kota Kanbe
b6899ce461 Update README.md 2016-06-03 09:08:37 +09:00
Kota Kanbe
32c11af07c Merge pull request #89 from future-architect/add_glide
Add glide files for vendoring
2016-06-02 14:47:33 +09:00
kota kanbe
6ff55d24d0 Add glide files for vendoring 2016-06-02 14:39:33 +09:00
Kota Kanbe
055aacd7f6 Update README.md 2016-06-02 10:16:52 +09:00
Kota Kanbe
5ecf58fd56 Merge pull request #88 from future-architect/fix_smtp_port_in_template
Fix type of SMTP Port of discovery command's output
2016-06-02 10:11:45 +09:00
kota kanbe
8a9106052f Fix type of SMTP Port of discovery command's output 2016-06-02 10:07:56 +09:00
Kota Kanbe
91264547c9 Merge pull request #86 from future-architect/fix-issue-84
Fix error msg when go-cve-dictionary is unavailable #84
2016-06-01 09:31:37 +09:00
kota kanbe
3190b877ae Fix error msg when go-cve-dictionary is unavailable #84 2016-06-01 09:28:52 +09:00
Kota Kanbe
f8a8cc4676 Merge pull request #85 from future-architect/fix-issue-84
Fix README, change -cvedbpath to -cve-dictionary-dbpath #84
2016-06-01 09:21:10 +09:00
kota kanbe
93ee329315 Fix README, change -cvedbpath to -cve-dictionary-dbpath #84 2016-06-01 09:19:53 +09:00
Kota Kanbe
b45163388d Merge pull request #81 from ymd38/master
Add option for it get cve detail from cve.sqlite3.
2016-06-01 08:22:44 +09:00
Kota Kanbe
6029784f76 Merge pull request #83 from future-architect/fix_error_handling_of_genworkers_on_debian
Fix error handling to avoid nil pointer err on debian
2016-05-31 11:32:55 +09:00
kota kanbe
058ab55a6f Fix error handling to avoid nil pointer err on debian 2016-05-31 11:30:33 +09:00
Kota Kanbe
1005d241b8 Merge pull request #82 from future-architect/fix_nil_pointer_while_apt_cahche_policy
Fix nil pointer while doing apt-cache policy on ubuntu #76
2016-05-31 09:51:54 +09:00
kota kanbe
33b1ccba67 Fix nil pointer while doing apt-cache policy on ubuntu #76 2016-05-31 09:46:57 +09:00
hirokazu yamada
a5549fb500 Add option for it get cve detail from cve.sqlite3.
It is an error in go-cve-dictionary API when result of scan is many.
2016-05-31 01:05:02 +09:00
Kota Kanbe
b057ed3e77 Merge pull request #79 from sadayuki-matsuno/logMethod
fix log import url
2016-05-30 15:47:02 +09:00
Sadayuki Matsuno
1e88cc10e7 fix log import url 2016-05-30 12:59:43 +09:00
Kota Kanbe
2f8634383e Merge pull request #78 from future-architect/add_report_text
Add -report-text option, Fix small bug of report in japanese
2016-05-30 12:26:46 +09:00
kota kanbe
86f9e5ce96 Add -report-text option, Fix small bug of report in japanese 2016-05-30 12:23:02 +09:00
Kota Kanbe
9ae42d647c Merge pull request #77 from future-architect/json_writer_sort_order
Add JSONWriter, Fix CVE sort order of report
2016-05-30 08:45:49 +09:00
kota kanbe
54d6217b93 Add JSONWriter, Fix CVE sort order of report 2016-05-29 10:03:22 +09:00
Kota Kanbe
150b1c2406 Merge pull request #75 from future-architect/fix_error_handling_goreuqest
Fix error handling of gorequest
2016-05-27 14:30:14 +09:00
kota kanbe
51b6f1b5f3 Fix error handling of gorequest 2016-05-27 14:28:45 +09:00
Kota Kanbe
3eae14cef6 Merge pull request #74 from yoshi-taka/patch-1
Update README.md
2016-05-26 23:48:54 +09:00
yoshi-taka
cc6dc1ca69 Update README.md
make it a bit professional
2016-05-26 14:58:09 +09:00
Kota Kanbe
7f2361f58c Merge pull request #73 from future-architect/fix_tui_bug_when_no_args_specified
Fix freezing forever when no args specified in TUI mode
2016-05-26 09:04:29 +09:00
kota kanbe
7cb02d77ae Fix freezing forever when no args specified in TUI mode 2016-05-26 09:00:52 +09:00
Kota Kanbe
52cc9b0cc0 Merge pull request #72 from future-architect/refactoring_debian
Refactoring debian.go
2016-05-25 21:00:22 +09:00
kota kanbe
d91bf61038 Refactoring debian.go 2016-05-25 20:58:55 +09:00
Kota Kanbe
d5f81674f8 Merge pull request #71 from sadayuki-matsuno/version2
mv version.go version/version.go to run main.go without compile
2016-05-25 10:12:00 +09:00
Sadayuki Matsuno
9381883835 mv version.go version/version.go to run main.go without compile 2016-05-25 09:49:16 +09:00
kota kanbe
f82e5a281d Update CHANGELOG 2016-05-25 08:30:24 +09:00
kota kanbe
904e6241e4 Bump up version 2016-05-25 08:24:25 +09:00
Kota Kanbe
ce39a3daf9 Update README.md 2016-05-24 20:38:50 +09:00
Kota Kanbe
f2c7f74beb Merge pull request #69 from future-architect/enable_to_show_history
Enable to show previous scan result
2016-05-24 20:35:43 +09:00
kota kanbe
20db997fc2 Enable to show previous scan result 2016-05-24 20:19:56 +09:00
Kota Kanbe
7188e97444 Merge pull request #68 from future-architect/ignore-unscored-cves
Add ignore-unscored-cves option
2016-05-24 09:17:12 +09:00
kota kanbe
6d528e741d Add ignore-unscored-cves option 2016-05-24 08:35:36 +09:00
Kota Kanbe
d356e8370d Merge pull request #67 from future-architect/docker_container
Add docker container support
2016-05-24 08:05:01 +09:00
kota kanbe
5e336b5928 Add docker container support 2016-05-23 09:38:52 +09:00
Kota Kanbe
787ad0629b Merge pull request #57 from theonlydoo/patch-2
Update Dockerfile
2016-05-20 13:45:29 +09:00
Kota Kanbe
53e4adf24e Merge pull request #56 from theonlydoo/patch-1
Update run.sh
2016-05-20 13:45:04 +09:00
Kota Kanbe
6af811d63e Merge pull request #66 from future-architect/pointless_sudo_debian
Fix pointless sudo in debian.go #29
2016-05-19 17:21:41 +09:00
kota kanbe
359dab3380 fix pointless sudo in debian.go #29 2016-05-19 17:20:09 +09:00
Kota Kanbe
97a8e6e965 Merge pull request #65 from future-architect/version
Add version flag
2016-05-19 15:22:04 +09:00
kota kanbe
8ea699aa08 Add version flag 2016-05-19 15:02:17 +09:00
Kota Kanbe
7d924d2b0c Merge pull request #64 from future-architect/fix_nilpointer_when_error_in_cve_client
Fix error handling of httpGet in cve-client #58
2016-05-16 20:06:05 +09:00
kota kanbe
3c85613ada Fix error handling of httpGet in cve-client #58 2016-05-16 20:00:22 +09:00
Kota Kanbe
c536d26db3 Merge pull request #63 from future-architect/fix_nilpointer_when_error_in_cve_client
Fix nil pointer at error handling of cve_client #58
2016-05-16 19:26:53 +09:00
kota kanbe
4350ff2692 Fix nil pointer at error handling of cve_client #58 2016-05-16 19:23:43 +09:00
Kota Kanbe
0b9a1e7bb4 Merge pull request #55 from pabroff/modify
Fix scan on Japanese environment.
2016-05-16 14:48:27 +09:00
Kota Kanbe
714ad18fa0 Merge pull request #54 from jody-frankowski/fix_deprecated_typo
Fix a typo: replace Depricated by Deprecated.
2016-05-16 14:41:16 +09:00
pabroff
f81f785813 Fix again on Japanese environment. 2016-05-16 14:20:57 +09:00
Kota Kanbe
76c32af46f Merge pull request #60 from future-architect/fix_cvss_over
Fix -cvss-over flag #59
2016-05-16 12:31:07 +09:00
kota kanbe
cd108263e1 Fix -cvss-over flag #59 2016-05-16 12:13:21 +09:00
theonlydoo
093c47b59c Update Dockerfile
to comply with https://github.com/future-architect/vuls/compare/master...theonlydoo:patch-1
2016-05-11 17:37:29 +02:00
theonlydoo
56a40ec51a Update run.sh
Update to provide some more reliability on server boot time
2016-05-11 17:36:55 +02:00
pabroff
1337be2b84 Fix scan on Japanese environment. 2016-05-11 20:31:44 +09:00
Jody Frankowski
eecd2c60f5 Fix a typo: replace Depricated by Deprecated. 2016-05-11 11:18:32 +02:00
Kota Kanbe
da071cb120 Merge pull request #50 from pabroff/modify
Fix yes no infinite loop while doing yum update --changelog on root@CentOS #47
2016-05-10 13:58:28 +09:00
Dog of Pavlov
012cfa3cbe Fix #47
Default set  "Is this ok [y/N]:" is N.
2016-05-10 12:22:51 +09:00
Kota Kanbe
21180847dc Update README.fr.md 2016-04-30 11:07:36 +09:00
Kota Kanbe
9e9e538846 Merge pull request #45 from future-architect/fix_prefix_of_servername_slack
Fix $servername in output of discover command
2016-04-30 11:06:26 +09:00
kota kanbe
66025b1ae2 Fix $servername in output of discover command 2016-04-30 11:01:27 +09:00
Kota Kanbe
5999361358 Update README.md 2016-04-30 10:57:16 +09:00
Kota Kanbe
e8699d1cb7 Update README.md 2016-04-25 15:38:56 +09:00
Kota Kanbe
9292448e73 Merge pull request #33 from mattn/windows
Support Windows
2016-04-22 18:37:09 +09:00
kota kanbe
d7e156613d Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix yum to yum --color=never #36
  Update README
2016-04-21 23:24:22 +09:00
kota kanbe
c3604aa66d Update CHANGELOG 2016-04-21 23:23:51 +09:00
kota kanbe
49dd12fef3 Bump up version 2016-04-21 23:19:11 +09:00
Kota Kanbe
5e037b1743 Merge pull request #41 from theonlydoo/patch-1
Update README
2016-04-21 19:17:27 +09:00
Kota Kanbe
ebc79805ed Merge pull request #42 from future-architect/yum_color_never
Fix yum to yum --color=never #36
2016-04-21 19:13:20 +09:00
kota kanbe
c37e56e51d Fix yum to yum --color=never #36 2016-04-21 19:10:36 +09:00
theonlydoo
28a93c02e6 Update README
not so sparse documentation
2016-04-21 11:46:24 +02:00
Kota Kanbe
0996c58894 Merge pull request #40 from future-architect/fix_parse_yum_check_update
Fix parse yum check update
2016-04-21 17:43:24 +09:00
kota kanbe
56ecf32565 Fix yum check-update --security to yum check-update 2016-04-21 17:42:13 +09:00
Kota Kanbe
416fb3c937 Update README.md 2016-04-21 15:50:26 +09:00
Kota Kanbe
d48b8315c9 Merge pull request #38 from theonlydoo/Dockerfile
Sparse dockerization
2016-04-21 15:27:18 +09:00
arnaudb
7c6d1eb585 Sparse dockerization 2016-04-20 15:42:14 +02:00
kota kanbe
fae04dce81 Fix Error while parsing yum check-update if the package not in rpm -qa 2016-04-20 09:02:43 +09:00
Kota Kanbe
86a5433312 Update README.md 2016-04-19 17:49:02 +09:00
Kota Kanbe
d9cf63a9fe Merge pull request #35 from future-architect/fix_to_no_password_in_config
Fix no password in config
2016-04-19 17:35:13 +09:00
kota kanbe
88bf643363 Merge branch 'master' into fix_to_no_password_in_config
* master:
  Update README.md
2016-04-19 17:29:20 +09:00
kota kanbe
e0b680b305 Merge branch 'master' into fix_to_no_password_in_config
* master:
  Update README.md
2016-04-19 16:58:47 +09:00
Kota Kanbe
d6356408b8 Update README.md 2016-04-19 16:07:40 +09:00
kota kanbe
4d28de17b4 No password in config file 2016-04-18 23:03:13 +09:00
Kota Kanbe
fdd918d970 Update README.md 2016-04-16 22:52:02 +09:00
Yasuhiro Matsumoto
da16f9673e Support Windows 2016-04-15 19:25:41 +09:00
Kota Kanbe
b02b7c9081 Merge pull request #31 from blue119/master
fix typo
2016-04-15 01:22:29 +09:00
Yao-Po Wang
ea82149dbe fix typo 2016-04-14 23:40:14 +08:00
Kota Kanbe
9d64f039ab Merge pull request #30 from future-architect/fix_error_while_parse_yum_check_update_obsoleting_line
Fix error while parsing yum check-update #24
2016-04-14 20:49:23 +09:00
kota kanbe
cd9cbd795b Fix error while parsing yum check-update #24 2016-04-14 20:28:01 +09:00
Kota Kanbe
929d561de8 Update README.md 2016-04-14 14:55:22 +09:00
Kota Kanbe
245abe5b6b Update README.md 2016-04-14 14:37:40 +09:00
Kota Kanbe
768364fc77 Update README.md 2016-04-14 01:29:06 +09:00
Kota Kanbe
60a3e9532a Merge pull request #23 from novakin/fr_README_translation
Fr readme translation
2016-04-14 01:27:30 +09:00
Novakin
dcd6ba0a82 Update README.fr.md 2016-04-13 14:52:18 +03:00
Novakin
9f2dc2c6a3 Update README.fr.md 2016-04-13 14:51:27 +03:00
Novakin
7498a540d4 Update README.fr.md 2016-04-13 14:47:19 +03:00
Novakin
26ae01d960 Update README.fr.md 2016-04-13 14:46:15 +03:00
Novakin
f72781c30c Update README.fr.md 2016-04-13 14:37:30 +03:00
Novakin
21e957159d Update README.fr.md 2016-04-13 14:36:21 +03:00
Novakin
a66b425da0 Create README.fr.md 2016-04-13 14:16:57 +03:00
Kota Kanbe
804fffd009 Update README.md 2016-04-13 15:31:06 +09:00
Kota Kanbe
ac77cc1f87 Update README.md 2016-04-13 14:38:41 +09:00
kota kanbe
d0d360a6e7 Update CHANGELOG.md 2016-04-12 15:20:03 +09:00
kota kanbe
9708533565 Bump up version 2016-04-12 15:16:03 +09:00
Kota Kanbe
ac98b908e3 Merge pull request #20 from future-architect/fix_sudo_option_redhat
Fix sudo option on RedHat like Linux, change some messages.
2016-04-12 14:00:36 +09:00
Kota Kanbe
9bacd98577 Merge pull request #19 from Euan-Kerr/typo_fix_and_updated_readme
Typo fix and updated readme
2016-04-12 01:40:23 +09:00
kota kanbe
d750205f31 Fix sudo option on Redhat like Linux, change some messages. 2016-04-12 01:14:40 +09:00
Euan
b4d0aa7532 Typo fix and updated readme
Updated readme to include chmod 600 on the authorized_keys file to force
the correct permissions.
2016-04-11 13:13:19 +01:00
Kota Kanbe
3e846233a3 Merge pull request #18 from future-architect/remove_period_at_end_of_errmsg
remove a period at the end of error messages.
2016-04-10 19:10:32 +09:00
kota kanbe
1a943776c3 remove a period at the end of error messages. 2016-04-10 19:08:46 +09:00
Kota Kanbe
57ef45ebcd Merge pull request #17 from future-architect/fix_error_rhel_on_aws
fix error while yum updateinfo --security update on rhel@aws
2016-04-10 18:42:02 +09:00
kota kanbe
b64115f283 fix error while yum updateinfo --security update on rhel@aws 2016-04-10 18:37:55 +09:00
Kota Kanbe
018eb29ce5 Update README.md 2016-04-10 09:49:06 +09:00
Kota Kanbe
77c7d2fe26 Merge pull request #9 from cpobrien/master
Revise small grammar mistakes in serverapi.go
2016-04-10 08:58:53 +09:00
Kota Kanbe
336b72bbca Merge pull request #15 from radarhere/master
Fixed typos
2016-04-10 08:56:45 +09:00
Andrew Murray
0deb1032cd Fixed typos 2016-04-09 12:31:05 +10:00
Connor O'Brien
34c5644e63 Remove period from error log 2016-04-07 23:35:42 -07:00
Kota Kanbe
1f80738bef Update README.md 2016-04-08 10:42:55 +09:00
Kota Kanbe
66501663a0 Merge pull request #14 from Bregor/issues/typo
Typo fix in error messages
2016-04-08 09:20:05 +09:00
Maxim Filatov
f677939975 Typo fix in error messages 2016-04-07 21:28:04 +03:00
Kota Kanbe
c465faeb6c Merge pull request #13 from future-architect/fix_index_out_of_range_when_7_servers
Fix index out of range error when the number of servers is over 6. #12
2016-04-08 02:13:28 +09:00
kota kanbe
6a6c7bf8a4 Fix index out of range error when the number of servers is over 6. #12 2016-04-08 02:07:02 +09:00
Connor O'Brien
d19afe665f Revise small grammar mistakes in serverapi.go 2016-04-06 21:42:42 -07:00
Kota Kanbe
c62ca7c645 Merge pull request #7 from future-architect/not_send_to_errchan_while_backoff
Fix error handling in HTTP backoff function
2016-04-07 03:27:22 +09:00
kota kanbe
855b48f0c9 Fix error handling in HTTP backoff function 2016-04-07 03:22:51 +09:00
Kota Kanbe
555e34d035 Update README.md 2016-04-07 02:34:13 +09:00
Kota Kanbe
6b12ff35cd Update README.md 2016-04-07 02:33:29 +09:00
Kota Kanbe
d9813e822f Update README.md 2016-04-06 17:20:32 +09:00
kota kanbe
26273e7387 Update CHANGELOG.md 2016-04-06 15:04:00 +09:00
kota kanbe
b52f0120ff Bump up version 2016-04-06 12:34:13 +09:00
kota kanbe
76ade4c3b4 Fix panic: runtime error: index out of range in tui mode #5 2016-04-06 12:09:29 +09:00
Kota Kanbe
110d74a91e Merge pull request #6 from toli/patch-1
Typo in Exapmle
2016-04-06 11:33:27 +09:00
Toli Kuznets
1819edf724 Typo in Exapmle 2016-04-05 13:19:18 -07:00
69 changed files with 6833 additions and 1082 deletions

11
.gitignore vendored
View File

@@ -1,9 +1,12 @@
vuls
.vscode
*.txt
*.json
*.sqlite3
.gitmodules
coverage.out
issues/
*.txt
vendor/
log/
.gitmodules
vuls
*.sqlite3
results/
*config.toml

View File

@@ -1,5 +1,169 @@
# Change Log
0.1.0 (2013-03-23)
## [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)
Initial public release
**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)
**Implemented enhancements:**
- Initial fetch from NVD is too heavy \(2.3 GB of memory consumed\) [\#27](https://github.com/future-architect/vuls/issues/27)
- Enable to show previous scan result [\#69](https://github.com/future-architect/vuls/pull/69) ([kotakanbe](https://github.com/kotakanbe))
- Add ignore-unscored-cves option [\#68](https://github.com/future-architect/vuls/pull/68) ([kotakanbe](https://github.com/kotakanbe))
- Support dynamic scanning docker container [\#67](https://github.com/future-architect/vuls/pull/67) ([kotakanbe](https://github.com/kotakanbe))
- Add version flag [\#65](https://github.com/future-architect/vuls/pull/65) ([kotakanbe](https://github.com/kotakanbe))
- Update Dockerfile [\#57](https://github.com/future-architect/vuls/pull/57) ([theonlydoo](https://github.com/theonlydoo))
- Update run.sh [\#56](https://github.com/future-architect/vuls/pull/56) ([theonlydoo](https://github.com/theonlydoo))
- Support Windows [\#33](https://github.com/future-architect/vuls/pull/33) ([mattn](https://github.com/mattn))
**Fixed bugs:**
- vuls scan -cvss-over does not work. [\#59](https://github.com/future-architect/vuls/issues/59)
- `panic: runtime error: invalid memory address or nil pointer dereference` when scan CentOS5.5 [\#58](https://github.com/future-architect/vuls/issues/58)
- It rans out of memory. [\#47](https://github.com/future-architect/vuls/issues/47)
- BUG: vuls scan on CentOS with Japanese environment. [\#43](https://github.com/future-architect/vuls/issues/43)
- yum --color=never [\#36](https://github.com/future-architect/vuls/issues/36)
- Failed to parse yum check-update [\#32](https://github.com/future-architect/vuls/issues/32)
- Pointless sudo [\#29](https://github.com/future-architect/vuls/issues/29)
- Can't init database in a path having blanks [\#26](https://github.com/future-architect/vuls/issues/26)
- Fix pointless sudo in debian.go \#29 [\#66](https://github.com/future-architect/vuls/pull/66) ([kotakanbe](https://github.com/kotakanbe))
- Fix error handling of httpGet in cve-client \#58 [\#64](https://github.com/future-architect/vuls/pull/64) ([kotakanbe](https://github.com/kotakanbe))
- Fix nil pointer at error handling of cve\_client \#58 [\#63](https://github.com/future-architect/vuls/pull/63) ([kotakanbe](https://github.com/kotakanbe))
- Set language en\_US. [\#61](https://github.com/future-architect/vuls/pull/61) ([pabroff](https://github.com/pabroff))
- Fix -cvss-over flag \#59 [\#60](https://github.com/future-architect/vuls/pull/60) ([kotakanbe](https://github.com/kotakanbe))
- Fix scan on Japanese environment. [\#55](https://github.com/future-architect/vuls/pull/55) ([pabroff](https://github.com/pabroff))
- Fix a typo: replace Depricated by Deprecated. [\#54](https://github.com/future-architect/vuls/pull/54) ([jody-frankowski](https://github.com/jody-frankowski))
- Fix yes no infinite loop while doing yum update --changelog on root@CentOS \#47 [\#50](https://github.com/future-architect/vuls/pull/50) ([pabroff](https://github.com/pabroff))
- Fix $servername in output of discover command [\#45](https://github.com/future-architect/vuls/pull/45) ([kotakanbe](https://github.com/kotakanbe))
## [v0.1.3](https://github.com/future-architect/vuls/tree/v0.1.3) (2016-04-21)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.2...v0.1.3)
**Implemented enhancements:**
- Add sudo support for prepare [\#11](https://github.com/future-architect/vuls/issues/11)
- Dockerfile? [\#10](https://github.com/future-architect/vuls/issues/10)
- Update README [\#41](https://github.com/future-architect/vuls/pull/41) ([theonlydoo](https://github.com/theonlydoo))
- Sparse dockerization [\#38](https://github.com/future-architect/vuls/pull/38) ([theonlydoo](https://github.com/theonlydoo))
- No password in config [\#35](https://github.com/future-architect/vuls/pull/35) ([kotakanbe](https://github.com/kotakanbe))
- Fr readme translation [\#23](https://github.com/future-architect/vuls/pull/23) ([novakin](https://github.com/novakin))
**Fixed bugs:**
- Issues updating CVE database behind https proxy [\#39](https://github.com/future-architect/vuls/issues/39)
- Vuls failed to parse yum check-update [\#24](https://github.com/future-architect/vuls/issues/24)
- Fix yum to yum --color=never \#36 [\#42](https://github.com/future-architect/vuls/pull/42) ([kotakanbe](https://github.com/kotakanbe))
- Fix parse yum check update [\#40](https://github.com/future-architect/vuls/pull/40) ([kotakanbe](https://github.com/kotakanbe))
- fix typo [\#31](https://github.com/future-architect/vuls/pull/31) ([blue119](https://github.com/blue119))
- Fix error while parsing yum check-update \#24 [\#30](https://github.com/future-architect/vuls/pull/30) ([kotakanbe](https://github.com/kotakanbe))
**Closed issues:**
- Unable to scan on ubuntu because changelog.ubuntu.com is down... [\#21](https://github.com/future-architect/vuls/issues/21)
- err: Not initialize\(d\) yet.. [\#16](https://github.com/future-architect/vuls/issues/16)
- Errors when using fish shell [\#8](https://github.com/future-architect/vuls/issues/8)
## [v0.1.2](https://github.com/future-architect/vuls/tree/v0.1.2) (2016-04-12)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.1...v0.1.2)
**Fixed bugs:**
- Maximum 6 nodes available to scan [\#12](https://github.com/future-architect/vuls/issues/12)
- panic: runtime error: index out of range [\#5](https://github.com/future-architect/vuls/issues/5)
- Fix sudo option on RedHat like Linux and change some messages. [\#20](https://github.com/future-architect/vuls/pull/20) ([kotakanbe](https://github.com/kotakanbe))
- Typo fix and updated readme [\#19](https://github.com/future-architect/vuls/pull/19) ([Euan-Kerr](https://github.com/Euan-Kerr))
- remove a period at the end of error messages. [\#18](https://github.com/future-architect/vuls/pull/18) ([kotakanbe](https://github.com/kotakanbe))
- fix error while yum updateinfo --security update on rhel@aws [\#17](https://github.com/future-architect/vuls/pull/17) ([kotakanbe](https://github.com/kotakanbe))
- Fixed typos [\#15](https://github.com/future-architect/vuls/pull/15) ([radarhere](https://github.com/radarhere))
- Typo fix in error messages [\#14](https://github.com/future-architect/vuls/pull/14) ([Bregor](https://github.com/Bregor))
- Fix index out of range error when the number of servers is over 6. \#12 [\#13](https://github.com/future-architect/vuls/pull/13) ([kotakanbe](https://github.com/kotakanbe))
- Revise small grammar mistakes in serverapi.go [\#9](https://github.com/future-architect/vuls/pull/9) ([cpobrien](https://github.com/cpobrien))
- Fix error handling in HTTP backoff function [\#7](https://github.com/future-architect/vuls/pull/7) ([kotakanbe](https://github.com/kotakanbe))
## [v0.1.1](https://github.com/future-architect/vuls/tree/v0.1.1) (2016-04-06)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.0...v0.1.1)
**Fixed bugs:**
- Typo in Exapmle [\#6](https://github.com/future-architect/vuls/pull/6) ([toli](https://github.com/toli))
## [v0.1.0](https://github.com/future-architect/vuls/tree/v0.1.0) (2016-04-04)
**Merged pull requests:**
- English translation [\#4](https://github.com/future-architect/vuls/pull/4) ([hikachan](https://github.com/hikachan))
- English translation [\#3](https://github.com/future-architect/vuls/pull/3) ([chewyinping](https://github.com/chewyinping))
- Add a Bitdeli Badge to README [\#2](https://github.com/future-architect/vuls/pull/2) ([bitdeli-chef](https://github.com/bitdeli-chef))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

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

224
README.fr.md Normal file
View File

@@ -0,0 +1,224 @@
# Vuls: VULnerability Scanner
[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](http://goo.gl/forms/xm5KFo35tu)
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)
[![asciicast](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck.png)](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
![Vuls-slack](img/vuls-slack-en.png)
----
# Résumé
Effectuer des recherches de vulnérabilités et des mises à jour quotidiennes peut etre un fardeau pour un administrateur système.
Afin d'éviter des interruptions systèmes dans un environnement de production, il est fréquent pour un administrateur système de choisir de ne pas utiliser la fonction de mise à jour automatique proposée par le gestionnaire de paquets et d'effecter ces mises à jour manuellement.
Ce qui implique les problèmes suivants :
- L'administrateur système devra surveiller constamment toutes les nouvelles vulnérabilités dans NVD (National Vulnerability Database) etc.
- Il pourrait être impossible pour un administrateur système de surveiller tous les logiciels installés sur un serveur.
- Il est coûteux d'effectuer une analyse pour déterminer quels sont les serveurs affectés par de nouvelles vulnérabilités. La possibilité de négliger un serveur ou deux est bien présente.
Vuls est un outil crée pour palier aux problèmes listés ci-dessus. Voici ses caractéristiques.
- Informer les utilisateurs des vulnérabilités système.
- Informer les utilisateurs des systèmes concernés.
- La détection de vulnérabilités est effectuée automatiquement pour éviter toute négligence.
- Les rapports sont générés régulièrement via CRON pour mieux gérer ces vulnérabilités.
![Vuls-Motivation](img/vuls-motivation.png)
----
# Caractéristiques principales
- Recherche de vulnérabilités sur des serveurs Linux
- Supporte Ubuntu, Debian, CentOS, Amazon Linux, RHEL
- Cloud, auto-hébergement, Docker
- Scan d'intergiciels non inclus dans le gestionnaire de paquets de l'OS
- Scan d'intergiciels, de libraries de language de programmation et framework pour des vulnérabilités
- Supporte les logiciels inscrits au CPE
- Architecture sans agent
- L'utilisateur doit seulement mettre en place VULS sur une seule machine qui se connectera aux autres via SSH
- Génération automatique des fichiers de configuration
- Auto detection de serveurs via CIDR et génération de configuration
- Email et notification Slack possibles (supporte le Japonais)
- Les résultats d'un scan sont accessibles dans un shell via TUI Viewer terminal.
----
# Ce que Vuls ne fait pas
- Vuls ne met pas à jour les programmes affectés par les vulnérabilités découvertes.
----
# Hello Vuls
Ce tutoriel décrit la recherche de vulnérabilités sur une machine locale avec Vuls.
Voici les étapes à suivre.
1. Démrarrage d'Amazon Linux
1. Autoriser les connexions SSH depuis localhost
1. Installation des prérequis
1. Déploiement de go-cve-dictionary
1. Deploiement de Vuls
1. Configuration
1. Préparation
1. Scan
1. TUI(Terminal-Based User Interface)
## Step1. Démrarrage d'Amazon Linux
- Nous utilisons dans cette exemple une vieille AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956)
- Taille de l'instance : t2.medium
- La première fois, t2.medium et plus sont requis pour la récupération des CVE depuis NVD (2.3GB de mémoire utilisé)
- Une fois la récupération initiale des données NVD terminée vous pouvez passer sur une instance t2.nano.
- Ajoutez la configuration suivante au cloud-init, afin d'éviter une mise à jour automatique lors du premier démarrage.
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
```
#cloud-config
repo_upgrade: none
```
## Step2. Paramètres SSH
Il est obligatoire que le serveur puisse se connecter à son propre serveur SSH
Générez une paire de clés SSH et ajoutez la clé publique dans le fichier authorized_keys
```bash
$ ssh-keygen -t rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
```
## Step3. Installation des prérequis
Vuls requiert l'installation des paquets suivants :
- sqlite
- git
- gcc
- go v1.6
- https://golang.org/doc/install
```bash
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
$ sudo yum -y install sqlite git gcc
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
$ mkdir $HOME/go
```
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
```bash
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```
Ajoutons ces nouvelles variables denvironnement au shell
```bash
$ source /etc/profile.d/goenv.sh
```
## Step4. Déploiement de [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
go get
```bash
$ sudo mkdir /var/log/vuls
$ sudo chown ec2-user /var/log/vuls
$ sudo chmod 700 /var/log/vuls
$ go get github.com/kotakanbe/go-cve-dictionary
```
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).
## Step5. Déploiement de Vuls
Ouvrez un second terminal, connectez vous à l'instance ec2 via SSH
go get
```
$ go get github.com/future-architect/vuls
```
## Step6. Configuration
Créez un fichier de configuration (TOML format).
```
$ cat config.toml
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
port = "22"
user = "ec2-user"
keyPath = "/home/ec2-user/.ssh/id_rsa"
```
## Step7. Configuration des serveurs cibles vuls
```
$ vuls prepare
```
## Step8. Scan
```
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
INFO[0000] Begin scanning (config: /home/ec2-user/config.toml)
... snip ...
172-31-4-82 (amazon 2015.09)
============================
CVE-2016-0494 10.0 Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle
Java SE 6u105, 7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to
affect confidentiality, integrity, and availability via unknown vectors related to
2D.
... snip ...
CVE-2016-0494
-------------
Score 10.0 (High)
Vector (AV:N/AC:L/Au:N/C:C/I:C/A:C)
Summary Unspecified vulnerability in the Java SE and Java SE Embedded components in Oracle Java SE 6u105,
7u91, and 8u66 and Java SE Embedded 8u65 allows remote attackers to affect confidentiality,
integrity, and availability via unknown vectors related to 2D.
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
```
## Step9. TUI
Les résultats de Vuls peuvent etre affichés dans un Shell via TUI (Terminal-Based User Interface).
```
$ vuls tui
```
![Vuls-TUI](img/hello-vuls-tui.png)
----
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)

File diff suppressed because it is too large Load Diff

656
README.md
View File

@@ -2,12 +2,17 @@
# Vuls: VULnerability Scanner
[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](http://goo.gl/forms/xm5KFo35tu)
[![License](https://img.shields.io/github/license/future-architect/vuls.svg?style=flat-square)](https://github.com/future-architect/vuls/blob/master/LICENSE.txt)
Vulnerability scanner for Linux, agentless, written in golang.
![Vuls-logo](img/vuls_logo.png)
Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
[README in Japanese](https://github.com/future-architect/vuls/blob/master/README.ja.md)
[README in French](https://github.com/future-architect/vuls/blob/master/README.fr.md)
[![asciicast](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck.png)](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
@@ -22,16 +27,16 @@ 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 anaylsis to determine the servers affected by new vulnerabilities. The possibility of overlooking a server or two during analysis is there.
- 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.
Vuls is a tool created to solve the problems listed above. It has the following characteristics.
- 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.
![Vuls-Motivation](img/vuls-motivation.png)
@@ -39,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
@@ -60,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
@@ -78,17 +100,15 @@ This can be done in the following steps.
## Step1. Launch Amazon Linux
- We are using the old AMI (amzn-ami-hvm-2015.09.1.x86_64-gp2 - ami-383c1956) for this example
- Instance size: t2.medium
- For the first time, t2.medium and above is required for the data fetch from NVD
- You can switch to t2.nano after the initial data fetch.
- Add the following to the cloud-init, to avoid auto-update at the first launch.
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
```
#cloud-config
repo_upgrade: none
```
- [Q: How do I disable the automatic installation of critical and important security updates on initial launch?](https://aws.amazon.com/amazon-linux-ami/faqs/?nc1=h_ls)
## Step2. SSH setting
This is required to ssh to itself.
@@ -97,14 +117,18 @@ Create a keypair then append public key to authorized_keys
```bash
$ ssh-keygen -t rsa
$ 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.
- sqlite
- git
- SQLite3
- git v2
- gcc
- go v1.6
- https://golang.org/doc/install
@@ -129,7 +153,7 @@ Set the OS environment variable to current shell
$ source /etc/profile.d/goenv.sh
```
## Step4. Deploy go-cve-dictionary
## Step4. Deploy [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
go get
@@ -140,38 +164,37 @@ $ sudo chmod 700 /var/log/vuls
$ go get github.com/kotakanbe/go-cve-dictionary
```
Start go-cve-dictionary as server mode.
For the first time, go-cve-dictionary fetches vulnerability data from NVD.
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).
```bash
$ go-cve-dictionary server
... Fetching ...
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
... snip ...
$ 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 mode again.
```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
@@ -182,19 +205,26 @@ 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
```
see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
## Step8. Start Scanning
```
$ vuls scan
INFO[0000] Begin scannig (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 ...
@@ -234,20 +264,53 @@ $ vuls tui
![Vuls-TUI](img/hello-vuls-tui.png)
----
# Setup Vuls in a Docker Container
see https://github.com/future-architect/vuls/tree/master/setup/docker
----
# Architecture
![Vuls-Architecture](img/vuls-architecture.png)
## go-cve-dictinary
- Fetch vulnerbility information from NVD, JVN(Japanese), then insert into SQLite.
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
- 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
- For more detailed information of the detected CVE, send HTTP request to go-cve-dictinary
- Send a report by Slack, Email
- System operator can view the latest report by terminal
- Scan vulnerabilities on the servers via SSH and create a list of the CVE ID
- To scan Docker containers, Vuls connect via ssh to the Docker host and then `docker exec` to the containers. So, no need to run sshd daemon on the containers.
- Fetch more detailed information of the detected CVE from go-cve-dictionary
- Insert scan result into SQLite3
- Send a report by Slack and Email
- Show the latest report on your terminal
![Vuls-Scan-Flow](img/vuls-scan-flow.png)
----
# 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.
- on CentOS
Vuls issues `yum update --changelog` to get changelogs of upgradable packages at once and parse the changelog.
Scan speed is fast and resource usage is light.
- On Amazon, RHEL and FreeBSD
High speed scan and resource usage is light because Vuls can get CVE IDs by using package manager(no need to parse a changelog).
| Distribution| Scan Speed | Resource Usage On Target Server |
|:------------|:-------------------|:-------------|
| Ubuntu | Slow | Heavy |
| Debian | Slow | Heavy |
| CentOS | Fast | Light |
| Amazon | Fast | Light |
| RHEL | Fast | Light |
| FreeBSD | Fast | Light |
----
@@ -273,14 +336,15 @@ web/app server in the same configuration under the load balancer
| Debian | 7, 8|
| RHEL | 4, 5, 6, 7|
| CentOS | 5, 6, 7|
| Amazon Linux| All |
| Amazon Linux| All|
| FreeBSD | 10|
----
# Usage: Automatic Server Discovery
Discovery subcommand discovers active servers specifed 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
@@ -288,7 +352,7 @@ discover:
discover 192.168.0.0/24
```
## Exapmle
## Example
```
$ vuls discover 172.31.4.0/24
@@ -297,14 +361,14 @@ $ vuls discover 172.31.4.0/24
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
[mail]
smtpAddr = "smtp.gmail.com"
smtpPort = 465
smtpPort = "465"
user = "username"
password = "password"
from = "from@address.com"
@@ -315,9 +379,14 @@ subjectPrefix = "[vuls]"
[default]
#port = "22"
#user = "username"
#password = "password"
#keyPath = "/home/username/.ssh/id_rsa"
#keyPassword = "password"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#optional = [
# ["key", "value"],
#]
[servers]
@@ -325,12 +394,14 @@ subjectPrefix = "[vuls]"
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",
#]
#containers = ["${running}"]
#optional = [
# ["key", "value"],
#]
```
You can customize your configuration using this template.
@@ -344,7 +415,7 @@ You can customize your configuration using this template.
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
@@ -352,12 +423,12 @@ You can customize your configuration using this template.
- hookURL : Incomming webhook's URL
- channel : channel name.
If you set #{servername} to channel, the report will be sent to #servername channel.
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]
channel = "#{servername}"
channel = "${servername}"
...snip...
[servers]
@@ -374,14 +445,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"
@@ -390,16 +461,21 @@ You can customize your configuration using this template.
subjectPrefix = "[vuls]"
```
- Defualt section
- Default section
```
[default]
#port = "22"
#user = "username"
#password = "password"
#keyPath = "/home/username/.ssh/id_rsa"
#keyPassword = "password"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#optional = [
# ["key", "value"],
#]
```
Items of the defualt section will be used if not specified.
Items of the default section will be used if not specified.
- servers section
```
@@ -409,18 +485,68 @@ You can customize your configuration using this template.
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",
#]
#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, Amazon Linux, RedHat Enterprise Linux
```
vuls ALL=(root) NOPASSWD: /usr/bin/yum
```
- Ubuntu, Debian
```
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache
```
----
@@ -431,24 +557,29 @@ Prepare subcommand installs required packages on each server.
| Distribution| Release | Requirements |
|:------------|-------------------:|:-------------|
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8| apptitude |
| Debian | 7, 8| aptitude |
| CentOS | 5| yum-plugin-security, yum-changelog |
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
| Amazon | All | - |
| RHEL | 4, 5, 6, 7 | - |
| FreeBSD | 10 | - |
```
$ vuls prepare -help
prepare:
prepare [-config=/path/to/config.toml] [-debug]
prepare
[-config=/path/to/config.toml] [-debug]
[-ask-key-password]
[SERVER]...
-ask-key-password
Ask ssh privatekey password before scanning
-config string
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-use-unattended-upgrades
[Depricated] For Ubuntu, install unattended-upgrades
[Deprecated] For Ubuntu, install unattended-upgrades
```
----
@@ -456,21 +587,55 @@ prepare:
# Usage: Scan
```
$ vuls scan -help
scan:
scan
[-lang=en|ja]
[-config=/path/to/config.toml]
[-dbpath=/path/to/vuls.sqlite3]
[--cve-dictionary-dbpath=/path/to/cve.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cvss-over=7]
[-report-slack]
[-ignore-unscored-cves]
[-ssh-external]
[-report-azure-blob]
[-report-json]
[-report-mail]
[-report-s3]
[-report-slack]
[-report-text]
[-http-proxy=http://192.168.0.1:8080]
[-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
-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
-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
@@ -483,54 +648,183 @@ scan:
SQL debug mode
-http-proxy string
http://proxy-url:port (default: empty)
-ignore-unscored-cves
Don't report the unscored CVEs
-lang string
[en|ja] (default "en")
-report-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
Send report via Slack
-report-text
Write report to text files ($PWD/results/current)
-ssh-external
Use external ssh command. Default: Use the Go native implementation
-use-unattended-upgrades
[Depricated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
-use-yum-plugin-security
[Depricated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
```
## example
## -ssh-external option
Run go-cve-dictionary as server mode before scanning.
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)
```
$ go-cve-dictionary server
Defaults:vuls !requiretty
```
### Scan all servers defined in config file
## -ask-key-password option
| SSH key password | -ask-key-password | |
|:-----------------|:-------------------|:----|
| empty password | - | |
| with password | required | or use ssh-agent |
## -report-json , -report-text option
At the end of the scan, scan results will be available in the `$PWD/result/current/` directory.
`all.(json|txt)` includes the scan results of all servres and `servername.(json|txt)` includes the scan result of the server.
## Example: Scan all servers defined in config file
```
$ vuls scan --report-slack --report-mail --cvss-over=7
$ 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 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 ..
- Scan only 2 servers. (server1, server2)
- Use SSH Key-Based authentication with empty password (without -ask-key-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)
**Check CPE Naming Format: 2.2**
- [go-cpe-dictionary](https://github.com/kotakanbe/go-cpe-dictionary) is a good choice for geeks.
You can search a CPE name by the application name incremenally.
- Configuration
To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
```
@@ -544,8 +838,110 @@ To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
]
```
# Usage: Scan Docker containers
# Usage: Update NVD Data.
It is common that keep Docker containers runnning without SSHd daemon.
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
Vuls scans Docker containers via `docker exec` instead of SSH.
For more details, see [Architecture section](https://github.com/future-architect/vuls#architecture)
- To scan all of running containers
`"${running}"` needs to be set in the containers item.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["${running}"]
```
- To scan specific containers
The container ID or container name needs to be set in the containers item.
In the following example, only `container_name_a` and `4aa37a8b63b9` will be scanned.
Be sure to check these containers are running state before scanning.
If specified containers are not running, Vuls gives up scanning with printing error message.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
containers = ["container_name_a", "4aa37a8b63b9"]
```
# Usage: TUI
## Display the latest scan results
```
$ vuls tui -h
tui:
tui [-dbpath=/path/to/vuls.sqlite3]
-dbpath string
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
-debug-sql
debug SQL
```
Key binding is bellow.
| key | |
|:-----------------|:-------|:------|
| TAB | move cursor among the panes |
| Arrow up/down | move cursor to up/down |
| Ctrl+j, Ctrl+k | move cursor to up/donw |
| Ctrl+u, Ctrl+d | page up/donw |
For details, see https://github.com/future-architect/vuls/blob/master/report/tui.go
## Display the previous scan results
- Display the list of scan results.
```
$ ./vuls history
2 2016-05-24 19:49 scanned 1 servers: amazon2
1 2016-05-24 19:48 scanned 2 servers: amazon1, romantic_goldberg
```
- Display the result of scanID 1
```
$ ./vuls tui 1
```
- Display the result of scanID 2
```
$ ./vuls tui 2
```
# Display the previous scan results using peco
```
$ ./vuls history | peco | ./vuls tui
```
[![asciicast](https://asciinema.org/a/emi7y7docxr60bq080z10t7v8.png)](https://asciinema.org/a/emi7y7docxr60bq080z10t7v8)
# Usage: go-cve-dictionary on different server
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
@@ -580,17 +976,70 @@ $ 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
Update git to the latest version. Old version of git can't get some repositories.
see https://groups.google.com/forum/#!topic/mgo-users/rO1-gUDFo_g
- HTTP Proxy Support
If your system is behind HTTP proxy, you have to specify --http-proxy option.
- How to Daemonize go-cve-dictionary
Use Systemd, Upstart or supervisord, daemontools...
- How to update vulnerbility data automatically.
- How to Enable Automatic-Update of Vunerability Data.
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 no passphrase or ssh-agent.
- How to cross compile
```bash
$ cd /path/to/your/local-git-reporsitory/vuls
@@ -598,13 +1047,32 @@ Use job scheduler like Cron (with -last2y option).
```
- Logging
Log wrote to under /var/log/vuls/
Log is under /var/log/vuls/
- Debug
Run with --debug, --sql-debug option.
- 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?
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
- Windows
Use Microsoft Baseline Secuirty Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)
----
# Related Projects
- [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
[![vulsrepo](http://img.youtube.com/vi/DIBPoik4owc/0.jpg)](https://www.youtube.com/watch?v=DIBPoik4owc)
----
@@ -622,11 +1090,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)
----
@@ -640,6 +1111,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).
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/future-architect/vuls/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

21
commands/cmdutil.go Normal file
View File

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

162
commands/configtest.go Normal file
View 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
}

View File

@@ -39,7 +39,7 @@ type DiscoverCmd struct {
func (*DiscoverCmd) Name() string { return "discover" }
// Synopsis return synopsis
func (*DiscoverCmd) Synopsis() string { return "Host discovery in the CIDR." }
func (*DiscoverCmd) Synopsis() string { return "Host discovery in the CIDR" }
// Usage return usage
func (*DiscoverCmd) Usage() string {
@@ -77,7 +77,7 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
}
if len(hosts) < 1 {
logrus.Errorf("Active hosts not found in %s.", cidr)
logrus.Errorf("Active hosts not found in %s", cidr)
return subcommands.ExitSuccess
} else if err := printConfigToml(hosts); err != nil {
logrus.Errorf("Failed to parse template. err: %s", err)
@@ -93,14 +93,14 @@ func printConfigToml(ips []string) (err error) {
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
channel = "#channel-name"
#channel = "#{servername}"
#channel = "${servername}"
iconEmoji = ":ghost:"
authUser = "username"
notifyUsers = ["@username"]
[mail]
smtpAddr = "smtp.gmail.com"
smtpPort = 465
smtpPort = "465"
user = "username"
password = "password"
from = "from@address.com"
@@ -111,9 +111,14 @@ subjectPrefix = "[vuls]"
[default]
#port = "22"
#user = "username"
#password = "password"
#keyPath = "/home/username/.ssh/id_rsa"
#keyPassword = "password"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#optional = [
# ["key", "value"],
#]
[servers]
{{- $names:= .Names}}
@@ -122,12 +127,14 @@ subjectPrefix = "[vuls]"
host = "{{$ip}}"
#port = "22"
#user = "root"
#password = "password"
#keyPath = "/home/username/.ssh/id_rsa"
#keyPassword = "password"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#optional = [
# ["key", "value"],
#]
{{end}}
`

108
commands/history.go Normal file
View File

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

View File

@@ -20,6 +20,7 @@ package commands
import (
"flag"
"os"
"path/filepath"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
@@ -34,6 +35,9 @@ type PrepareCmd struct {
debug bool
configPath string
askSudoPassword bool
askKeyPassword bool
useUnattendedUpgrades bool
}
@@ -55,8 +59,12 @@ func (*PrepareCmd) Synopsis() string {
// Usage return usage
func (*PrepareCmd) Usage() string {
return `prepare:
prepare [-config=/path/to/config.toml] [-debug]
prepare
[-config=/path/to/config.toml]
[-ask-key-password]
[-debug]
[SERVER]...
`
}
@@ -65,27 +73,56 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
defaultConfPath := os.Getenv("PWD") + "/config.toml"
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
f.BoolVar(
&p.askKeyPassword,
"ask-key-password",
false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.askSudoPassword,
"ask-sudo-password",
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Depricated] For Ubuntu, install unattended-upgrades",
"[Deprecated] For Ubuntu, install unattended-upgrades",
)
}
// Execute execute
func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
logrus.Infof("Begin Preparing (config: %s)", p.configPath)
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
}
}
if p.askSudoPassword {
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
return subcommands.ExitFailure
}
err := c.Load(p.configPath)
err = c.Load(p.configPath, keyPass)
if err != nil {
logrus.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
logrus.Infof("Start Preparing (config: %s)", p.configPath)
target := make(map[string]c.ServerInfo)
for _, arg := range f.Args() {
found := false
@@ -112,16 +149,12 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
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) {
for _, e := range errs {
logger.Errorf("Failed: %s.", e)
logger.Errorf("Failed: %s", e)
}
return subcommands.ExitFailure
}

View File

@@ -19,7 +19,11 @@ package commands
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
@@ -41,23 +45,43 @@ type ScanCmd struct {
configPath string
dbpath string
cvedbpath string
cveDictionaryURL string
cvssScoreOver float64
httpProxy string
cvssScoreOver float64
ignoreUnscoredCves bool
httpProxy string
askSudoPassword bool
askKeyPassword 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
useYumPluginSecurity bool
useUnattendedUpgrades bool
// reporting
reportSlack bool
reportMail bool
sshExternal bool
}
// Name return subcommand name
func (*ScanCmd) Name() string { return "scan" }
// Synopsis return synopsis
func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities." }
func (*ScanCmd) Synopsis() string { return "Scan vulnerabilities" }
// Usage return usage
func (*ScanCmd) Usage() string {
@@ -66,13 +90,29 @@ func (*ScanCmd) Usage() string {
[-lang=en|ja]
[-config=/path/to/config.toml]
[-dbpath=/path/to/vuls.sqlite3]
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cvss-over=7]
[-report-slack]
[-ignore-unscored-cves]
[-ssh-external]
[-report-azure-blob]
[-report-json]
[-report-mail]
[-report-s3]
[-report-slack]
[-report-text]
[-http-proxy=http://192.168.0.1:8080]
[-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]...
`
}
@@ -82,12 +122,20 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debug, "debug", false, "debug mode")
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
defaultConfPath := os.Getenv("PWD") + "/config.toml"
wd, _ := os.Getwd()
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
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(
&p.cveDictionaryURL,
@@ -101,6 +149,18 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(
&p.ignoreUnscoredCves,
"ignore-unscored-cves",
false,
"Don't report the unscored CVEs")
f.BoolVar(
&p.sshExternal,
"ssh-external",
false,
"Use external ssh command. Default: Use the Go native implementation")
f.StringVar(
&p.httpProxy,
"http-proxy",
@@ -108,37 +168,117 @@ 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,
"ask-key-password",
false,
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.askSudoPassword,
"ask-sudo-password",
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useYumPluginSecurity,
"use-yum-plugin-security",
false,
"[Depricated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
"[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Depricated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
"[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
)
}
// Execute execute
func (p *ScanCmd) 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
}
}
if p.askSudoPassword {
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
return subcommands.ExitFailure
}
logrus.Infof("Begin scannig (config: %s)", p.configPath)
err := c.Load(p.configPath)
err = c.Load(p.configPath, keyPass)
if err != nil {
logrus.Errorf("Error loading %s, %s", p.configPath, err)
return subcommands.ExitUsageError
}
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 {
@@ -152,7 +292,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
}
@@ -165,7 +305,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
// report
reports := []report.ResultWriter{
report.TextWriter{},
report.StdoutWriter{},
report.LogrusWriter{},
}
if p.reportSlack {
@@ -174,9 +314,53 @@ 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{})
}
if p.reportText {
reports = append(reports, report.TextFileWriter{})
}
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 c.Conf.AzureAccount == "" {
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
}
c.Conf.AzureKey = p.azureKey
if c.Conf.AzureKey == "" {
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
}
c.Conf.AzureContainer = p.azureContainer
if c.Conf.AzureContainer == "" {
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.DBPath = p.dbpath
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDictionaryURL = p.cveDictionaryURL
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
@@ -187,22 +371,27 @@ 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 OS... ")
err = scan.InitServers(Log)
if err != nil {
Log.Errorf("Failed to init servers. 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 {
Log.Errorf("Failed to scan. err: %s.", e)
Log.Errorf("Failed to scan. err: %s", e)
}
return subcommands.ExitFailure
}
@@ -213,15 +402,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
Log.Info("Reporting...")
filtered := scanResults.FilterByCvssOver()
for _, w := range reports {
if err := w.Write(filtered); err != nil {
Log.Fatalf("Failed to output 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)
@@ -237,5 +417,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
Log.Info("Reporting...")
filtered := scanResults.FilterByCvssOver()
for _, w := range reports {
if err := w.Write(filtered); err != nil {
Log.Fatalf("Failed to report, err: %s", err)
return subcommands.ExitFailure
}
}
return subcommands.ExitSuccess
}

View File

@@ -20,8 +20,13 @@ 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"
@@ -39,7 +44,7 @@ type TuiCmd struct {
func (*TuiCmd) Name() string { return "tui" }
// Synopsis return synopsis
func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites." }
func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites" }
// Usage return usage
func (*TuiCmd) Usage() string {
@@ -54,7 +59,9 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
// f.StringVar(&p.lang, "lang", "en", "[en|ja]")
f.BoolVar(&p.debugSQL, "debug-sql", false, "debug SQL")
defaultDBPath := os.Getenv("PWD") + "/vuls.sqlite3"
wd, _ := os.Getwd()
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
}
@@ -64,5 +71,27 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.Lang = "en"
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
return report.RunTui()
historyID := ""
if 0 < len(f.Args()) {
if _, err := strconv.Atoi(f.Args()[0]); err != nil {
log.Errorf("First Argument have to be scan_histores record ID: %s", err)
return subcommands.ExitFailure
}
historyID = f.Args()[0]
} else {
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) {
historyID = fields[0]
}
}
}
return report.RunTui(historyID)
}

View File

@@ -41,9 +41,23 @@ type Config struct {
CveDictionaryURL string `valid:"url"`
CvssScoreOver float64
HTTPProxy string `valid:"url"`
DBPath string
CvssScoreOver float64
IgnoreUnscoredCves bool
SSHExternal bool
HTTPProxy string `valid:"url"`
DBPath string
CveDBPath string
AwsProfile string
AwsRegion string
S3Bucket string
AzureAccount string
AzureKey string
AzureContainer string
// CpeNames []string
// SummaryMode bool
UseYumPluginSecurity bool
@@ -61,6 +75,13 @@ func (c Config) Validate() bool {
}
}
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. dbpath: %s", c.CveDBPath))
}
}
_, err := valid.ValidateStruct(c)
if err != nil {
errs = append(errs, err)
@@ -188,8 +209,6 @@ func (c *SlackConf) Validate() (errs []error) {
errs = append(errs, err)
}
// TODO check if slack configration is valid
return
}
@@ -197,25 +216,38 @@ func (c *SlackConf) Validate() (errs []error) {
type ServerInfo struct {
ServerName string
User string
Password string
Host string
Port string
KeyPath string
KeyPassword string
SudoOpt SudoOption
CpeNames []string
// DebugLog Color
LogMsgAnsiColor string
// Container Names or IDs
Containers []string
// Optional key-value set that will be outputted to JSON
Optional [][]interface{}
// used internal
LogMsgAnsiColor string // DebugLog Color
Container Container
Family 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
// IsContainer returns whether this ServerInfo is about container
func (s ServerInfo) IsContainer() bool {
return 0 < len(s.Container.ContainerID)
}
// SetContainer set container
func (s *ServerInfo) SetContainer(d Container) {
s.Container = d
}
// Container has Container information.
type Container struct {
ContainerID string
Name string
Type string
}

View File

@@ -24,6 +24,6 @@ type JSONLoader struct {
}
// Load load the configuraiton JSON file specified by path arg.
func (c JSONLoader) Load(path string) (err error) {
func (c JSONLoader) Load(path, sudoPass, keyPass string) (err error) {
return fmt.Errorf("Not implement yet")
}

View File

@@ -18,16 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
// Load loads configuration
func Load(path string) error {
//TODO if path's suffix .toml
func Load(path, keyPass string) error {
var loader Loader
loader = TOMLLoader{}
return loader.Load(path)
return loader.Load(path, keyPass)
}
// Loader is interface of concrete loader
type Loader interface {
Load(string) error
Load(string, string) error
}

View File

@@ -31,10 +31,10 @@ type TOMLLoader struct {
}
// Load load the configuraiton TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml 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)
log.Error("Load config failed", err)
return err
}
@@ -45,24 +45,40 @@ func (c TOMLLoader) Load(pathToToml string) (err error) {
Conf.Default = d
servers := make(map[string]ServerInfo)
if keyPass != "" {
d.KeyPassword = keyPass
}
i := 0
for name, v := range conf.Servers {
s := ServerInfo{ServerName: name}
s.User = v.User
if s.User == "" {
s.User = d.User
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.Password = v.Password
if s.Password == "" {
s.Password = d.Password
s := ServerInfo{ServerName: name}
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 s.Host == "" {
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
@@ -72,10 +88,11 @@ func (c TOMLLoader) Load(pathToToml string) (err error) {
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 == "" {
s.KeyPassword = d.KeyPassword
@@ -86,12 +103,31 @@ func (c TOMLLoader) Load(pathToToml string) (err error) {
s.CpeNames = d.CpeNames
}
s.LogMsgAnsiColor = Colors[i%len(conf.Servers)]
s.Containers = v.Containers
if len(s.Containers) == 0 {
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++
servers[name] = s
}
log.Debug("Config loaded.")
log.Debug("Config loaded")
log.Debugf("%s", pp.Sprintf("%v", servers))
Conf.Servers = servers
return

View File

@@ -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 len(errs) > 0 || 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,20 +134,43 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDet
return
}
func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errChan chan<- error) {
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
var resp *http.Response
f := func() (err error) {
resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
if len(errs) > 0 || resp.StatusCode != 200 {
errChan <- fmt.Errorf("HTTP error. errs: %v, url: %s", errs, url)
// resp, body, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
resp, body, errs = gorequest.New().Get(url).End()
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP GET error: %v, url: %s, resp: %v", errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
log.Warnf("Failed to HTTP GET. retrying in %s seconds. err: %s", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
@@ -196,8 +227,11 @@ type responseGetCveDetailByCpeName struct {
}
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,13 +252,13 @@ 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 error. errs: %v, url: %s", errs, url)
if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
}
return nil
}
notify := func(err error, t time.Duration) {
log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
log.Warnf("Failed to HTTP POST. retrying in %s seconds. err: %s", t, err)
}
err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
if err != nil {
@@ -238,3 +272,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
}

View File

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

117
glide.lock generated Normal file
View File

@@ -0,0 +1,117 @@
hash: 9683c87b3cf998e7fac1b12c4a94bf2bd18cb5422e9108539811546e703a439a
updated: 2016-07-12T16:20:45.462913061+09:00
imports:
- name: github.com/asaskevich/govalidator
version: df81827fdd59d8b4fb93d8910b286ab7a3919520
- name: github.com/aws/aws-sdk-go
version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
subpackages:
- aws
- aws/credentials
- aws/session
- service/s3
- aws/awserr
- aws/client
- aws/corehandlers
- aws/defaults
- aws/request
- private/endpoints
- aws/awsutil
- aws/client/metadata
- aws/signer/v4
- private/protocol
- private/protocol/restxml
- private/waiter
- aws/credentials/ec2rolecreds
- 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: 58a13e378daf3b06e65925397185684b16321111
subpackages:
- storage
- name: github.com/BurntSushi/toml
version: ffaa107fbd880f6d18cd6fec9b511668dcad8639
- name: github.com/cenkalti/backoff
version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
- name: github.com/cheggaaa/pb
version: 04b234c80d661c663dbcebd52fc7218fdacc6d0c
- name: github.com/go-ini/ini
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
- 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: 66487b23f2880ba32e185121d2cd51a338ea069a
- name: github.com/jinzhu/gorm
version: 613c0655691abb7691b70c5fda80a716d9e20b1b
- name: github.com/jinzhu/inflection
version: 8f4d3a0d04ce0b7c0cf3126fb98524246d00d102
- name: github.com/jmespath/go-jmespath
version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
- name: github.com/jroimartin/gocui
version: 2dcda558bf18ec07c7065bf1eaf071b5305f7c0c
- name: github.com/k0kubun/pp
version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
- name: github.com/kotakanbe/go-cve-dictionary
version: 1a336b8ac785badfe89a175ee926d39574901232
subpackages:
- config
- db
- models
- log
- jvn
- nvd
- name: github.com/kotakanbe/go-pingscanner
version: 58e188a3e4f6ab1a6371e33421e4502e26fa1e80
- name: github.com/kotakanbe/logrus-prefixed-formatter
version: f4f7d41649cf1e75e736884da8d05324aa76ea25
- name: github.com/mattn/go-colorable
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
- name: github.com/mattn/go-isatty
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
- name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f
- name: github.com/mattn/go-sqlite3
version: 38ee283dabf11c9cbdb968eebd79b1fa7acbabe6
- name: github.com/mgutz/ansi
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
- name: github.com/moul/http2curl
version: b1479103caacaa39319f75e7f57fc545287fca0d
- name: github.com/nsf/termbox-go
version: c45773466a30b680355d6494cc8826113c93cd0f
- name: github.com/parnurzeal/gorequest
version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
- name: github.com/rifflock/lfshook
version: 05a24e24fa8d3a2eca8c2baf23aa2d5a2c51490c
- name: github.com/Sirupsen/logrus
version: f3cfb454f4c209e6668c95216c4744b8fddb2356
- name: golang.org/x/crypto
version: c2f4947f41766b144bb09066e919466da5eddeae
subpackages:
- ssh
- ssh/agent
- ssh/terminal
- curve25519
- ed25519
- ed25519/internal/edwards25519
- name: golang.org/x/net
version: f841c39de738b1d0df95b5a7187744f0e03d8112
subpackages:
- context
- publicsuffix
- name: golang.org/x/sys
version: a408501be4d17ee978c04a618e7a1b22af058c0e
subpackages:
- unix
- name: gopkg.in/alexcesaro/quotedprintable.v3
version: 2caba252f4dc53eaf6b553000885530023f54623
- name: gopkg.in/gomail.v2
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
devImports: []

39
glide.yaml Normal file
View File

@@ -0,0 +1,39 @@
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
- db
- 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

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 77 KiB

446
img/vuls-scan-flow.graphml Normal file
View File

@@ -0,0 +1,446 @@
<?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">Get 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="271.369140625" x="-1.6845703124999432" y="11.8671875">Get all changelogs by using package manager
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="d9"/>
<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="d9"/>
<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="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">
<y:Point x="743.3698412698412" y="401.8409153761062"/>
</y:Path>
<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

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
img/vuls_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

12
main.go
View File

@@ -19,11 +19,13 @@ package main
import (
"flag"
"fmt"
"os"
"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"
@@ -37,8 +39,18 @@ func main() {
subcommands.Register(&commands.TuiCmd{}, "tui")
subcommands.Register(&commands.ScanCmd{}, "scan")
subcommands.Register(&commands.PrepareCmd{}, "prepare")
subcommands.Register(&commands.HistoryCmd{}, "history")
subcommands.Register(&commands.ConfigtestCmd{}, "configtest")
var v = flag.Bool("v", false, "Show version")
flag.Parse()
if *v {
fmt.Printf("%s %s\n", version.Name, version.Version)
os.Exit(int(subcommands.ExitSuccess))
}
ctx := context.Background()
os.Exit(int(subcommands.Execute(ctx)))
}

View File

@@ -30,16 +30,34 @@ import (
// ScanHistory is the history of Scanning.
type ScanHistory struct {
gorm.Model
ScanResults []ScanResult
ScanResults ScanResults
ScannedAt time.Time
}
// ScanResults is slice of ScanResult.
type ScanResults []ScanResult
// Len implement Sort Interface
func (s ScanResults) Len() int {
return len(s)
}
// Swap implement Sort Interface
func (s ScanResults) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less implement Sort Interface
func (s ScanResults) Less(i, j int) bool {
if s[i].ServerName == s[j].ServerName {
return s[i].Container.ContainerID < s[i].Container.ContainerID
}
return s[i].ServerName < s[j].ServerName
}
// FilterByCvssOver is filter function.
func (results ScanResults) FilterByCvssOver() (filtered ScanResults) {
for _, result := range results {
func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
for _, result := range s {
cveInfos := []CveInfo{}
for _, cveInfo := range result.KnownCves {
if config.Conf.CvssScoreOver < cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
@@ -54,17 +72,69 @@ func (results 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:"-"`
ServerName string // TOML Section key
// Hostname string
Family string
Release string
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 == "" {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"%s / %s (%s%s) on %s",
r.Container.Name,
r.Container.ContainerID,
r.Family,
r.Release,
r.ServerName,
)
}
return hostinfo
}
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
hostinfo := ""
if r.Container.ContainerID == "" {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
r.Family,
r.Release,
)
} else {
hostinfo = fmt.Sprintf(
"|-- %s (%s%s)",
r.Container.Name,
r.Family,
r.Release,
// r.Container.ContainerID,
)
}
return hostinfo
}
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
@@ -84,16 +154,19 @@ func (r ScanResult) CveSummary() string {
unknown++
}
}
if config.Conf.IgnoreUnscoredCves {
return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d)",
high+middle+low, high, middle, low)
}
return fmt.Sprintf("Total: %d (High:%d Middle:%d Low:%d ?:%d)",
high+middle+low+unknown,
high, middle, low, unknown,
)
high+middle+low+unknown, high, middle, low, unknown)
}
// NWLink has network link information.
type NWLink struct {
gorm.Model
ScanResultID uint
gorm.Model `json:"-"`
ScanResultID uint `json:"-"`
IPAddress string
Netmask string
@@ -114,13 +187,16 @@ func (c CveInfos) Swap(i, j int) {
func (c CveInfos) Less(i, j int) bool {
lang := config.Conf.Lang
if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
return c[i].CveDetail.CveID < c[j].CveDetail.CveID
}
return c[i].CveDetail.CvssScore(lang) > c[j].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
@@ -130,8 +206,8 @@ type CveInfo struct {
// CpeName has CPE name
type CpeName struct {
gorm.Model
CveInfoID uint
gorm.Model `json:"-"`
CveInfoID uint `json:"-"`
Name string
}
@@ -196,8 +272,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
@@ -231,14 +307,31 @@ func (p PackageInfo) ToStringNewVersion() string {
return str
}
// DistroAdvisory has Amazon Linux AMI Security Advisory information.
//TODO Rename to DistroAdvisory
// 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
Issued time.Time
Updated time.Time
}
// Container has Container information
type Container struct {
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
View 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 res.Container.ContainerID == "" {
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
}

View File

@@ -20,18 +20,43 @@ package report
import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"github.com/future-architect/vuls/models"
)
// JSONWriter writes report as JSON format
// JSONWriter writes results to file.
type JSONWriter struct{}
func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
var j []byte
if j, err = json.MarshalIndent(scanResults, "", " "); err != nil {
return
path, err := ensureResultDir()
var jsonBytes []byte
if jsonBytes, err = json.Marshal(scanResults); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
all := filepath.Join(path, "all.json")
if err := ioutil.WriteFile(all, jsonBytes, 0644); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", all, err)
}
for _, r := range scanResults {
jsonPath := ""
if r.Container.ContainerID == "" {
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
}

View File

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

View File

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

112
report/s3.go Normal file
View 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 r.Container.ContainerID == "" {
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
}

View File

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

View File

@@ -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 {

63
report/textfile.go Normal file
View File

@@ -0,0 +1,63 @@
/* 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"
"github.com/future-architect/vuls/models"
)
// TextFileWriter writes results to file.
type TextFileWriter struct{}
func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
path, err := ensureResultDir()
all := []string{}
for _, r := range scanResults {
textFilePath := ""
if r.Container.ContainerID == "" {
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
}

View File

@@ -40,9 +40,9 @@ var currentCveInfo int
var currentDetailLimitY int
// RunTui execute main logic
func RunTui() subcommands.ExitStatus {
func RunTui(historyID string) subcommands.ExitStatus {
var err error
scanHistory, err = latestScanHistory()
scanHistory, err = selectScanHistory(historyID)
if err != nil {
log.Fatal(err)
return subcommands.ExitFailure
@@ -70,12 +70,12 @@ func RunTui() subcommands.ExitStatus {
return subcommands.ExitSuccess
}
func latestScanHistory() (latest models.ScanHistory, err error) {
func selectScanHistory(historyID string) (latest models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return latest, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
}
latest, err = db.SelectLatestScanHistory()
latest, err = db.SelectScanHistory(historyID)
return
}
@@ -410,7 +410,7 @@ func changeHost(g *gocui.Gui, v *gocui.View) error {
serverName := strings.TrimSpace(l)
for _, r := range scanHistory.ScanResults {
if serverName == r.ServerName {
if serverName == strings.TrimSpace(r.ServerInfoTui()) {
currentScanResult = r
break
}
@@ -509,14 +509,14 @@ func layout(g *gocui.Gui) error {
func setSideLayout(g *gocui.Gui) error {
_, maxY := g.Size()
if v, err := g.SetView("side", -1, -1, 30, maxY); err != nil {
if v, err := g.SetView("side", -1, -1, 40, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Highlight = true
for _, result := range scanHistory.ScanResults {
fmt.Fprintln(v, result.ServerName)
fmt.Fprintln(v, result.ServerInfoTui())
}
currentScanResult = scanHistory.ScanResults[0]
if err := g.SetCurrentView("side"); err != nil {
@@ -528,7 +528,7 @@ func setSideLayout(g *gocui.Gui) error {
func setSummaryLayout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("summary", 30, -1, maxX, int(float64(maxY)*0.2)); err != nil {
if v, err := g.SetView("summary", 40, -1, maxX, int(float64(maxY)*0.2)); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@@ -564,19 +564,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 +584,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(),
)
}
@@ -602,7 +602,6 @@ func summaryLines(data models.ScanResult) string {
}
stable.AddRow(icols...)
}
// ignore UnknownCves
return fmt.Sprintf("%s", stable)
}
@@ -617,7 +616,7 @@ func setDetailLayout(g *gocui.Gui) error {
_, oy := summaryView.Origin()
currentCveInfo = cy + oy
if v, err := g.SetView("detail", 30, int(float64(maxY)*0.2), maxX, maxY); err != nil {
if v, err := g.SetView("detail", 40, int(float64(maxY)*0.2), maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
@@ -625,7 +624,6 @@ func setDetailLayout(g *gocui.Gui) error {
// currentScanResult.KnownCves[currentCveInfo],
// currentScanResult.Family)
//TODO error handling
text, err := detailLines()
if err != nil {
return err
@@ -654,6 +652,10 @@ type dataForTmpl struct {
}
func detailLines() (string, error) {
if len(currentScanResult.KnownCves) == 0 {
return "No vulnerable packages", nil
}
cveInfo := currentScanResult.KnownCves[currentCveInfo]
cveID := cveInfo.CveDetail.CveID
@@ -668,16 +670,16 @@ 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()
}
links := []string{

View File

@@ -20,26 +20,52 @@ 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() (path string, err error) {
if resultDirPath != "" {
return resultDirPath, nil
}
const timeLayout = "20060102_1504"
timedir := time.Now().Format(timeLayout)
wd, _ := os.Getwd()
dir := filepath.Join(wd, "results", timedir)
if err := os.MkdirAll(dir, 0755); err != nil {
return "", fmt.Errorf("Failed to create dir: %s", err)
}
symlinkPath := filepath.Join(wd, "results", "current")
if _, err := os.Stat(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) {
hostinfo := fmt.Sprintf(
"%s (%s %s)",
scanResult.ServerName,
scanResult.Family,
scanResult.Release,
)
serverInfo := scanResult.ServerInfo()
var buffer bytes.Buffer
for i := 0; i < len(hostinfo); i++ {
for i := 0; i < len(serverInfo); i++ {
buffer.WriteString("=")
}
header := fmt.Sprintf("%s\n%s", hostinfo, buffer.String())
header := fmt.Sprintf("%s\n%s", serverInfo, buffer.String())
if len(scanResult.KnownCves) == 0 && len(scanResult.UnknownCves) == 0 {
return fmt.Sprintf(`
@@ -53,7 +79,12 @@ No unsecure packages.
scoredReport, unscoredReport = toPlainTextDetails(scanResult, scanResult.Family)
scored := strings.Join(scoredReport, "\n\n")
unscored := strings.Join(unscoredReport, "\n\n")
unscored := ""
if !config.Conf.IgnoreUnscoredCves {
unscored = strings.Join(unscoredReport, "\n\n")
}
detail := fmt.Sprintf(`
%s
@@ -72,30 +103,35 @@ func ToPlainTextSummary(r models.ScanResult) string {
stable := uitable.New()
stable.MaxColWidth = 84
stable.Wrap = true
cves := append(r.KnownCves, r.UnknownCves...)
cves := r.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, r.UnknownCves...)
}
for _, d := range cves {
var scols []string
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,
}
@@ -103,7 +139,7 @@ func ToPlainTextSummary(r models.ScanResult) string {
scols = []string{
d.CveDetail.CveID,
"?",
d.CveDetail.Nvd.Summary,
d.CveDetail.Nvd.CveSummary(),
}
}
@@ -116,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 {
@@ -129,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 {
@@ -185,14 +220,14 @@ 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("JVN", jvn.Link())
dtable.AddRow("NVD", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID))
@@ -227,14 +262,14 @@ 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("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))
@@ -306,6 +341,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{}
}

View File

@@ -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

297
scan/base.go Normal file
View File

@@ -0,0 +1,297 @@
/* 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"
"sort"
"strings"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
)
type base struct {
ServerInfo config.ServerInfo
Family string
Release string
Platform models.Platform
osPackages
log *logrus.Entry
errs []error
}
func (l *base) ssh(cmd string, sudo bool) sshResult {
return sshExec(l.ServerInfo, cmd, sudo, l.log)
}
func (l *base) setServerInfo(c config.ServerInfo) {
l.ServerInfo = c
}
func (l base) getServerInfo() config.ServerInfo {
return l.ServerInfo
}
func (l *base) setDistributionInfo(fam, rel string) {
l.Family = fam
l.Release = rel
}
func (l base) getDistributionInfo() string {
return fmt.Sprintf("%s %s", l.Family, l.Release)
}
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}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *base) runningContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
stdout, err := l.dockerPs("--format '{{.ID}} {{.Names}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *base) exitedContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
stdout, err := l.dockerPs("--filter 'status=exited' --format '{{.ID}} {{.Names}}'")
if err != nil {
return containers, err
}
return l.parseDockerPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Container.Type)
}
}
func (l *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 SSH: %s", r)
}
return r.Stdout, nil
}
func (l *base) parseDockerPs(stdout string) (containers []config.Container, err error) {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 0 {
break
}
if len(fields) != 2 {
return containers, fmt.Errorf("Unknown format: %s", line)
}
containers = append(containers, config.Container{
ContainerID: fields[0],
Name: fields[1],
})
}
return
}
func (l *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 id == "not found" {
// status: 0, stdout: "not found" on degitalocean or Azure
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)
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)
}
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 {
unscoredCves = append(unscoredCves, models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
})
continue
}
cpenames := []models.CpeName{}
for _, cpename := range p.CpeNames {
cpenames = append(cpenames,
models.CpeName{Name: cpename})
}
cve := models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
CpeNames: cpenames,
}
scoredCves = append(scoredCves, cve)
}
container := models.Container{
ContainerID: l.ServerInfo.Container.ContainerID,
Name: l.ServerInfo.Container.Name,
}
sort.Sort(scoredCves)
sort.Sort(unscoredCves)
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,
Family: l.Family,
Release: l.Release,
Container: container,
Platform: l.Platform,
KnownCves: scoredCves,
UnknownCves: unscoredCves,
Optional: l.ServerInfo.Optional,
}, nil
}
// scanVulnByCpeName search vulnerabilities that specified in config file.
func (l *base) scanVulnByCpeName() error {
unsecurePacks := CvePacksList{}
serverInfo := l.getServerInfo()
cpeNames := serverInfo.CpeNames
// remove duplicate
set := map[string]CvePacksInfo{}
for _, name := range cpeNames {
details, err := cveapi.CveClient.FetchCveDetailsByCpeName(name)
if err != nil {
return err
}
for _, detail := range details {
if val, ok := set[detail.CveID]; ok {
names := val.CpeNames
names = append(names, name)
val.CpeNames = names
set[detail.CveID] = val
} else {
set[detail.CveID] = CvePacksInfo{
CveID: detail.CveID,
CveDetail: detail,
CpeNames: []string{name},
}
}
}
}
for key := range set {
unsecurePacks = append(unsecurePacks, set[key])
}
unsecurePacks = append(unsecurePacks, l.UnsecurePackages...)
l.setUnsecurePackages(unsecurePacks)
return nil
}
func (l *base) setErrs(errs []error) {
l.errs = errs
}
func (l base) getErrs() []error {
return l.errs
}

58
scan/base_test.go Normal file
View File

@@ -0,0 +1,58 @@
/* 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 (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
)
func TestParseDockerPs(t *testing.T) {
var test = struct {
in string
expected []config.Container
}{
`c7ca0992415a romantic_goldberg
f570ae647edc agitated_lovelace`,
[]config.Container{
{
ContainerID: "c7ca0992415a",
Name: "romantic_goldberg",
},
{
ContainerID: "f570ae647edc",
Name: "agitated_lovelace",
},
},
}
r := newRedhat(config.ServerInfo{})
actual, err := r.parseDockerPs(test.in)
if err != nil {
t.Errorf("Error occurred. in: %s, err: %s", test.in, err)
return
}
for i, e := range test.expected {
if !reflect.DeepEqual(e, actual[i]) {
t.Errorf("expected %v, actual %v", e, actual[i])
}
}
}

View File

@@ -20,7 +20,6 @@ package scan
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"time"
@@ -33,7 +32,7 @@ import (
// inherit OsTypeInterface
type debian struct {
linux
base
}
// NewDebian is constructor
@@ -45,17 +44,20 @@ func newDebian(c config.ServerInfo) *debian {
// 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() {
@@ -70,13 +72,12 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {
if len(result) == 0 {
deb.setDistributionInfo("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]))
}
return true, deb
return true, deb, nil
}
if r := sshExec(c, "cat /etc/lsb-release", noSudo); r.isSuccess() {
@@ -90,39 +91,47 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface) {
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)
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
deb.setDistributionInfo("debian/ubuntu", "unknown")
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistributionInfo(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
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) 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)
}
@@ -131,8 +140,7 @@ func (o *debian) install() error {
// 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)
}
@@ -153,8 +161,7 @@ func (o *debian) install() error {
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)
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -174,7 +181,7 @@ func (o *debian) scanPackages() error {
var unsecurePacks []CvePacksInfo
if unsecurePacks, err = o.scanUnsecurePackages(packs); err != nil {
o.log.Errorf("Failed to scan valnerable packages")
o.log.Errorf("Failed to scan vulnerable packages")
return err
}
o.setUnsecurePackages(unsecurePacks)
@@ -184,9 +191,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 +200,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,7 +214,7 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
return
}
func (o *debian) parseScanedPackagesLine(line string) (name, version string, err error) {
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
re, _ := regexp.Compile(`^([^\t']+)\t(.+)$`)
result := re.FindStringSubmatch(line)
if len(result) == 3 {
@@ -226,8 +231,8 @@ func (o *debian) parseScanedPackagesLine(line string) (name, version string, err
func (o *debian) checkRequiredPackagesInstalled() error {
if o.Family == "debian" {
if r := o.ssh("test -f /usr/bin/aptitude", sudo); !r.isSuccess() {
msg := "aptitude is not installed"
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -238,7 +243,7 @@ func (o *debian) checkRequiredPackagesInstalled() error {
}
if r := o.ssh("type unattended-upgrade", noSudo); !r.isSuccess() {
msg := "unattended-upgrade is not installed"
msg := fmt.Sprintf("unattended-upgrade is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
@@ -250,10 +255,7 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
// cmd := prependProxyEnv(conf.HTTPProxy, "apt-get update | cat; echo 1")
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
@@ -283,7 +285,7 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
unsecurePacks, err = o.fillCandidateVersion(unsecurePacks)
if err != nil {
return nil, err
return nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
}
// Collect CVE information of upgradable packages
@@ -317,12 +319,10 @@ func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.Pack
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
cmd := fmt.Sprintf("apt-cache policy %s", p.Name)
cmd := fmt.Sprintf("LANG=en_US.UTF-8 apt-cache policy %s", p.Name)
r := o.ssh(cmd, sudo)
if !r.isSuccess() {
errChan <- fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
errChan <- fmt.Errorf("Failed to SSH: %s.", r)
return
}
ver, err := o.parseAptCachePolicy(r.Stdout, p.Name)
@@ -336,6 +336,7 @@ func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.Pack
}
}
errs := []error{}
result := []models.PackageInfo{}
for i := 0; i < len(packs); i++ {
select {
@@ -344,11 +345,14 @@ func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.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
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout fillCandidateVersion.")
return nil, fmt.Errorf("Timeout fillCandidateVersion")
}
}
if 0 < len(errs) {
return nil, fmt.Errorf("%v", errs)
}
return result, nil
}
@@ -389,7 +393,7 @@ func (o *debian) GetUnsecurePackNamesUsingUnattendedUpgrades() (packNames []stri
}
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)
@@ -464,7 +468,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 +475,51 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
if cveIds, err := o.scanPackageCveIds(p); err != nil {
if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
errChan <- err
} else {
resChan <- struct {
models.PackageInfo
strarray
}{p, cveIds}
}{p, cveIDs}
}
}(pack)
}
}
}
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,11 +533,10 @@ 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) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
cmd := ""
switch o.Family {
case "ubuntu":
@@ -542,19 +548,12 @@ 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)
if err != nil {
trimUbuntu := strings.Split(pack.Version, "ubuntu")[0]
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, trimUbuntu)
}
return
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, pack.Version)
}
func (o *debian) getCveIDParsingChangelog(changelog string,
@@ -581,7 +580,7 @@ func (o *debian) getCveIDParsingChangelog(changelog string,
}
//TODO report as unable to parse changelog.
o.log.Warn(err)
o.log.Error(err)
return []string{}, nil
}

View File

@@ -25,7 +25,7 @@ import (
"github.com/k0kubun/pp"
)
func TestParseScanedPackagesLineDebian(t *testing.T) {
func TestParseScannedPackagesLineDebian(t *testing.T) {
var packagetests = []struct {
in string
@@ -43,7 +43,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)
}
@@ -199,7 +199,7 @@ 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.")
t.Errorf("Returning error is unexpected")
}
}
}
@@ -504,7 +504,7 @@ Calculating upgrade... Done
for _, tt := range tests {
actual, err := d.parseAptGetUpgrade(tt.in)
if err != nil {
t.Errorf("Returning error is unexpected.")
t.Errorf("Returning error is unexpected")
}
if len(tt.expected) != len(actual) {
t.Errorf("Result length is not as same as expected. expected: %d, actual: %d", len(tt.expected), len(actual))

238
scan/freebsd.go Normal file
View File

@@ -0,0 +1,238 @@
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)
return d
}
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
c.Family = "FreeBSD"
if r := sshExec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(r.Stdout, "FreeBSD") == true {
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
bsd.setDistributionInfo("FreeBSD", strings.TrimSpace(b.Stdout))
bsd.setServerInfo(c)
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
View 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)
}
}
}

View File

@@ -1,129 +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 scan
import (
"sort"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
)
type linux struct {
ServerInfo config.ServerInfo
Family string
Release string
osPackages
log *logrus.Entry
}
func (l *linux) ssh(cmd string, sudo bool) sshResult {
return sshExec(l.ServerInfo, cmd, sudo, l.log)
}
func (l *linux) setServerInfo(c config.ServerInfo) {
l.ServerInfo = c
}
func (l *linux) getServerInfo() config.ServerInfo {
return l.ServerInfo
}
func (l *linux) setDistributionInfo(fam, rel string) {
l.Family = fam
l.Release = rel
}
func (l *linux) convertToModel() (models.ScanResult, error) {
var cves, unknownScoreCves []models.CveInfo
for _, p := range l.UnsecurePackages {
if p.CveDetail.CvssScore(config.Conf.Lang) < 0 {
unknownScoreCves = append(unknownScoreCves, models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
})
continue
}
cpenames := []models.CpeName{}
for _, cpename := range p.CpeNames {
cpenames = append(cpenames,
models.CpeName{Name: cpename})
}
cve := models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
CpeNames: cpenames,
}
cves = append(cves, cve)
}
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,
Family: l.Family,
Release: l.Release,
KnownCves: cves,
UnknownCves: unknownScoreCves,
}, nil
}
// scanVulnByCpeName search vulnerabilities that specified in config file.
func (l *linux) scanVulnByCpeName() error {
unsecurePacks := CvePacksList{}
serverInfo := l.getServerInfo()
cpeNames := serverInfo.CpeNames
// remove duplicate
set := map[string]CvePacksInfo{}
for _, name := range cpeNames {
details, err := cveapi.CveClient.FetchCveDetailsByCpeName(name)
if err != nil {
return err
}
for _, detail := range details {
if val, ok := set[detail.CveID]; ok {
names := val.CpeNames
names = append(names, name)
val.CpeNames = names
set[detail.CveID] = val
} else {
set[detail.CveID] = CvePacksInfo{
CveID: detail.CveID,
CveDetail: detail,
CpeNames: []string{name},
}
}
}
}
for key := range set {
unsecurePacks = append(unsecurePacks, set[key])
}
unsecurePacks = append(unsecurePacks, l.UnsecurePackages...)
sort.Sort(CvePacksList(unsecurePacks))
l.setUnsecurePackages(unsecurePacks)
return nil
}

View File

@@ -1,18 +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 scan

View File

@@ -35,7 +35,7 @@ import (
// inherit OsTypeInterface
type redhat struct {
linux
base
}
// NewRedhat is constructor
@@ -47,16 +47,12 @@ func newRedhat(c config.ServerInfo) *redhat {
// 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{ExecBySudoSh: 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)
Log.Warn("Fedora not tested yet: %s", r)
return true, red
}
@@ -69,9 +65,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
re, _ := regexp.Compile(`(.*) 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
}
@@ -100,10 +94,20 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
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
}
func (o *redhat) checkIfSudoNoPasswd() error {
r := o.ssh("yum --version", 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-plugin-security, yum-changelog
// CentOS 6 ... yum-plugin-security, yum-plugin-changelog
// CentOS 7 ... yum-plugin-security, yum-plugin-changelog
@@ -129,17 +133,15 @@ func (o *redhat) installYumPluginSecurity() error {
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 fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
func (o *redhat) installYumChangelog() error {
o.log.Info("Installing yum-plugin-security...")
if o.Family == "centos" {
var majorVersion int
@@ -160,17 +162,16 @@ func (o *redhat) installYumChangelog() error {
cmd := "rpm -q " + packName
if r := o.ssh(cmd, noSudo); r.isSuccess() {
o.log.Infof("Ignored: %s already installed.", packName)
o.log.Infof("Ignored: %s already installed", packName)
return nil
}
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)
o.log.Infof("Installed: %s", packName)
}
return nil
}
@@ -179,8 +180,8 @@ 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" {
cmd := "rpm -q yum-plugin-security"
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
msg := "yum-plugin-security is not installed"
o.log.Errorf(msg)
@@ -228,7 +229,7 @@ func (o *redhat) scanPackages() error {
var unsecurePacks []CvePacksInfo
if unsecurePacks, err = o.scanUnsecurePackages(); err != nil {
o.log.Errorf("Failed to scan valnerable packages")
o.log.Errorf("Failed to scan vulnerable packages")
return err
}
o.setUnsecurePackages(unsecurePacks)
@@ -245,7 +246,7 @@ func (o *redhat) scanInstalledPackages() (installedPackages models.PackageInfoLi
for _, line := range lines {
if trimed := strings.TrimSpace(line); len(trimed) != 0 {
var packinfo models.PackageInfo
if packinfo, err = o.parseScanedPackagesLine(line); err != nil {
if packinfo, err = o.parseScannedPackagesLine(line); err != nil {
return
}
installedPackages = append(installedPackages, packinfo)
@@ -259,17 +260,17 @@ func (o *redhat) scanInstalledPackages() (installedPackages models.PackageInfoLi
r.ExitStatus, r.Stdout, r.Stderr)
}
func (o *redhat) parseScanedPackagesLine(line string) (pack models.PackageInfo, err error) {
re, _ := regexp.Compile(`^([^\t']+)\t([^\t]+)\t(.+)$`)
result := re.FindStringSubmatch(line)
if len(result) == 4 {
pack.Name = result[1]
pack.Version = result[2]
pack.Release = strings.TrimSpace(result[3])
} else {
err = fmt.Errorf("redhat: Failed to parse package line: %s", line)
func (o *redhat) parseScannedPackagesLine(line string) (models.PackageInfo, error) {
fields := strings.Fields(line)
if len(fields) != 3 {
return models.PackageInfo{},
fmt.Errorf("Failed to parse package line: %s", line)
}
return
return models.PackageInfo{
Name: fields[0],
Version: fields[1],
Release: fields[2],
}, nil
}
func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
@@ -286,13 +287,11 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
//TODO return whether already expired.
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
cmd := "yum check-update"
cmd := "LANG=en_US.UTF-8 yum --color=never check-update"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
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.
@@ -300,20 +299,29 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
if err != nil {
return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err)
}
o.log.Debugf("%s", pp.Sprintf("%s", packInfoList))
o.log.Debugf("%s", pp.Sprintf("%v", packInfoList))
// Collect CVE-IDs in changelog
type PackInfoCveIDs struct {
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. 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)
@@ -394,7 +402,6 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error)
// CvssScore: cinfo.CvssScore(conf.Lang),
})
}
sort.Sort(CvePacksList(cvePacksList))
return cvePacksList, nil
}
@@ -409,16 +416,20 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
continue
}
if needToParse {
if strings.HasPrefix(line, "Obsoleting") {
continue
}
candidate, err := o.parseYumCheckUpdateLine(line)
if err != nil {
return models.PackageInfoList{}, err
return results, err
}
installed, found := o.Packages.FindByName(candidate.Name)
if !found {
return models.PackageInfoList{}, fmt.Errorf(
"Failed to parse yum check update line: %s-%s-%s",
o.log.Warnf("Not found the package in rpm -qa. candidate: %s-%s-%s",
candidate.Name, candidate.Version, candidate.Release)
results = append(results, candidate)
continue
}
installed.NewVersion = candidate.NewVersion
installed.NewRelease = candidate.NewRelease
@@ -445,7 +456,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,
@@ -454,12 +465,127 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
}, nil
}
func (o *redhat) getChangelog(packageNames string) (stdout string, err error) {
command := "echo N | "
func (o *redhat) mkPstring() *string {
str := ""
return &str
}
func (o *redhat) regexpReplace(src string, pat string, rep string) string {
r := regexp.MustCompile(pat)
return r.ReplaceAllString(src, rep)
}
func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo models.PackageInfo) string {
rpm := fmt.Sprintf("%s-%s-%s", packInfo.Name, packInfo.NewVersion, packInfo.NewRelease)
retLine := ""
if rpm2changelog[rpm] != nil {
lines := strings.Split(*rpm2changelog[rpm], "\n")
for _, line := range lines {
match, _ := regexp.MatchString("CVE-[0-9]+-[0-9]+", line)
if match {
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.Release) && o.Family == "centos" {
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
} else {
return nil, fmt.Errorf(
"Not implemented yet. family: %s, release: %s",
o.Family, o.Release)
}
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 i > 0 {
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 {
stop, _ := regexp.MatchString("^Dependencies Resolved", line)
if stop {
return rpm2changelog, nil
}
*writePointer += fmt.Sprintf("%s\n", line)
}
}
return rpm2changelog, nil
}
func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout string, err error) {
packageNames := ""
for _, packInfo := range packInfoList {
packageNames += fmt.Sprintf("%s ", packInfo.Name)
}
command := ""
if o.ServerInfo.User == "root" {
command = "echo N | "
}
if 0 < len(config.Conf.HTTPProxy) {
command += util.ProxyEnv()
}
command += fmt.Sprintf(" yum update --changelog %s | grep CVE", packageNames)
// yum update --changelog doesn't have --color option.
command += fmt.Sprintf(" LANG=en_US.UTF-8 yum update --changelog %s", packageNames)
r := o.ssh(command, sudo)
if !r.isSuccess(0, 1) {
@@ -485,53 +611,39 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
"yum updateinfo is not suppported on CentOS")
}
cmd := "yum repolist"
cmd := "yum --color=never repolist"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
"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 updateinfo list available --security"
cmd = "yum --color=never updateinfo list available --security"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess() {
return nil, fmt.Errorf(
"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 = "yum check-update --security"
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)
}
vulnerablePackInfoList, err := o.parseYumCheckUpdateLines(r.Stdout)
updatable, err := o.parseYumCheckUpdateLines(r.Stdout)
if err != nil {
return nil, fmt.Errorf("Failed to parse %s. err: %s", cmd, err)
}
o.log.Debugf("%s", pp.Sprintf("%s", vulnerablePackInfoList))
for i, packInfo := range vulnerablePackInfoList {
installedPack, found := o.Packages.FindByName(packInfo.Name)
if !found {
return nil, fmt.Errorf(
"Parsed package not found. packInfo: %#v", packInfo)
}
vulnerablePackInfoList[i].Version = installedPack.Version
vulnerablePackInfoList[i].Release = installedPack.Release
}
o.log.Debugf("%s", pp.Sprintf("%v", updatable))
dict := map[string][]models.PackageInfo{}
for _, advIDPackNames := range advIDPackNamesList {
packInfoList := models.PackageInfoList{}
for _, packName := range advIDPackNames.PackNames {
packInfo, found := vulnerablePackInfoList.FindByName(packName)
packInfo, found := updatable.FindByName(packName)
if !found {
return nil, fmt.Errorf(
"PackInfo not found. packInfo: %#v", packName)
@@ -543,12 +655,10 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
}
// get advisoryID(RHSA, ALAS) - CVE IDs
cmd = "yum updateinfo --security update"
r = o.ssh(util.PrependProxyEnv(cmd), noSudo)
cmd = "yum --color=never updateinfo --security update"
r = o.ssh(util.PrependProxyEnv(cmd), 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 {
@@ -711,6 +821,23 @@ func (o *redhat) isHorizontalRule(line string) (bool, error) {
return regexp.MatchString("^=+$", line)
}
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")
ok, err := regexp.MatchString(
`^[^ ]+\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`, s)
if !ok {
return false, err
}
}
return true, nil
}
// see test case
func (o *redhat) parseYumUpdateinfoHeaderCentOS(line string) (packs []models.PackageInfo, err error) {
pkgs := strings.Split(strings.TrimSpace(line), ",")
@@ -859,3 +986,7 @@ func (o *redhat) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacks
}
return result, nil
}
func (o *redhat) clone() osTypeInterface {
return o
}

View File

@@ -48,10 +48,18 @@ func TestParseScanedPackagesLineRedhat(t *testing.T) {
Release: "30.el6.11",
},
},
{
"Percona-Server-shared-56 5.6.19 rel67.0.el6",
models.PackageInfo{
Name: "Percona-Server-shared-56",
Version: "5.6.19",
Release: "rel67.0.el6",
},
},
}
for _, tt := range packagetests {
p, _ := r.parseScanedPackagesLine(tt.in)
p, _ := r.parseScannedPackagesLine(tt.in)
if p.Name != tt.pack.Name {
t.Errorf("name: expected %s, actual %s", tt.pack.Name, p.Name)
}
@@ -294,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 {
@@ -556,7 +612,12 @@ Loading mirror speeds from cached hostfile
audit-libs.x86_64 2.3.7-5.el6 base
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{
{
Name: "audit-libs",
@@ -568,6 +629,21 @@ bash.x86_64 4.1.2-33.el6_7.1 updates
Version: "4.1.1",
Release: "33",
},
{
Name: "python-libs",
Version: "2.6.0",
Release: "1.1-0",
},
{
Name: "python-ordereddict",
Version: "1.0",
Release: "1",
},
{
Name: "bind-utils",
Version: "1.0",
Release: "1",
},
}
var tests = []struct {
in string
@@ -590,6 +666,27 @@ bash.x86_64 4.1.2-33.el6_7.1 updates
NewVersion: "4.1.2",
NewRelease: "33.el6_7.1",
},
{
Name: "python-libs",
Version: "2.6.0",
Release: "1.1-0",
NewVersion: "2.6.6",
NewRelease: "64.el6",
},
{
Name: "python-ordereddict",
Version: "1.0",
Release: "1",
NewVersion: "1.1",
NewRelease: "3.el6ev",
},
{
Name: "bind-utils",
Version: "1.0",
Release: "1",
NewVersion: "9.3.6",
NewRelease: "25.P1.el5_11.8",
},
},
},
}
@@ -612,23 +709,23 @@ bash.x86_64 4.1.2-33.el6_7.1 updates
func TestParseYumCheckUpdateLinesAmazon(t *testing.T) {
r := newRedhat(config.ServerInfo{})
r.Family = "amzon"
r.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",
},
{
@@ -646,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",
},
{
@@ -774,39 +871,6 @@ updateinfo list done`
}
}
func TestParseYumUpdateinfoToGetUpdateID(t *testing.T) {
r := newRedhat(config.ServerInfo{})
var packagetests = []struct {
in string
pack models.PackageInfo
}{
{
"openssl 1.0.1e 30.el6.11",
models.PackageInfo{
Name: "openssl",
Version: "1.0.1e",
Release: "30.el6.11",
},
},
}
for _, tt := range packagetests {
p, _ := r.parseScanedPackagesLine(tt.in)
if p.Name != tt.pack.Name {
t.Errorf("name: expected %s, actual %s", tt.pack.Name, p.Name)
}
if p.Version != tt.pack.Version {
t.Errorf("version: expected %s, actual %s", tt.pack.Version, p.Version)
}
if p.Release != tt.pack.Release {
t.Errorf("release: expected %s, actual %s", tt.pack.Release, p.Release)
}
}
}
func TestExtractPackNameVerRel(t *testing.T) {
r := newRedhat(config.ServerInfo{})
var tests = []struct {
@@ -841,3 +905,304 @@ 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.Family = "centos"
r.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.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)
}
}
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
cve "github.com/kotakanbe/go-cve-dictionary/models"
)
@@ -16,16 +15,30 @@ 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
checkIfSudoNoPasswd() error
detectPlatform() error
getPlatform() models.Platform
checkRequiredPackagesInstalled() error
scanPackages() error
scanVulnByCpeName() error
install() error
convertToModel() (models.ScanResult, error)
runningContainers() ([]config.Container, error)
exitedContainers() ([]config.Container, error)
allContainers() ([]config.Container, error)
getErrs() []error
setErrs([]error)
}
// osPackages included by linux struct
@@ -52,10 +65,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
@@ -91,53 +103,298 @@ 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) {
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
} else if itsMe {
Log.Debugf("Debian like Linux. Host: %s:%s", c.Host, c.Port)
return
}
itsMe, osType = detectRedhat(c)
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
}
// 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)
}
}
fmt.Printf("\n")
}
// InitServers detect the kind of OS distribution of target servers
func InitServers(localLogger *logrus.Entry) (err error) {
func InitServers(localLogger *logrus.Entry) {
Log = localLogger
if servers, err = detectServersOS(); err != nil {
err = fmt.Errorf("Failed to detect OS")
} else {
Log.Debugf("%s", pp.Sprintf("%s", servers))
}
return
servers = detectServerOSes()
containers := detectContainerOSes()
servers = append(servers, containers...)
}
func detectServersOS() (osi []osTypeInterface, err error) {
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) {
osTypeChan <- detectOs(s)
defer func() {
if p := recover(); p != nil {
Log.Debugf("Panic: %s on %s", p, s.ServerName)
}
}()
osTypeChan <- detectOS(s)
}(s)
}
timeout := time.After(60 * time.Second)
var oses []osTypeInterface
timeout := time.After(30 * time.Second)
for i := 0; i < len(config.Conf.Servers); i++ {
select {
case res := <-osTypeChan:
osi = append(osi, res)
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.getDistributionInfo())
}
case <-timeout:
Log.Error("Timeout Occured while detecting OS.")
err = fmt.Errorf("Timeout!")
return
msg := "Timed out while detecting servers"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
for _, o := range oses {
if servername == o.getServerInfo().ServerName {
found = true
break
}
}
if !found {
Log.Errorf("(%d/%d) Timed out: %s",
i+1, len(config.Conf.Servers),
servername)
i++
}
}
}
}
for _, o := range oses {
if len(o.getErrs()) == 0 {
sshAbleOses = append(sshAbleOses, o)
}
}
return
}
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)
}
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
}
oses = append(oses, res...)
Log.Infof("Detected: %s@%s: %s",
sinfo.Container.Name, sinfo.ServerName, osi.getDistributionInfo())
}
case <-timeout:
msg := "Timed out while detecting containers"
Log.Error(msg)
for servername := range config.Conf.Servers {
found := false
for _, o := range oses {
if servername == o.getServerInfo().ServerName {
found = true
break
}
}
if !found {
Log.Errorf("Timed out: %s", servername)
}
}
}
}
for _, o := range oses {
if len(o.getErrs()) == 0 {
actives = append(actives, o)
}
}
return
}
func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeInterface) {
containerHostInfo := containerHost.getServerInfo()
if len(containerHostInfo.Containers) == 0 {
return
}
running, err := containerHost.runningContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get running containers on %s. err: %s",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
if containerHostInfo.Containers[0] == "${running}" {
for _, containerInfo := range running {
copied := containerHostInfo
copied.SetContainer(config.Container{
ContainerID: containerInfo.ContainerID,
Name: containerInfo.Name,
})
os := detectOS(copied)
oses = append(oses, os)
}
return oses
}
exitedContainers, err := containerHost.exitedContainers()
if err != nil {
containerHost.setErrs([]error{fmt.Errorf(
"Failed to get exited containers on %s. err: %s",
containerHost.getServerInfo().ServerName, err)})
return append(oses, containerHost)
}
var exited, unknown []string
for _, container := range containerHostInfo.Containers {
found := false
for _, c := range running {
if c.ContainerID == container || c.Name == container {
copied := containerHostInfo
copied.SetContainer(c)
os := detectOS(copied)
oses = append(oses, os)
found = true
break
}
}
if !found {
foundInExitedContainers := false
for _, c := range exitedContainers {
if c.ContainerID == container || c.Name == container {
exited = append(exited, container)
foundInExitedContainers = true
break
}
}
if !foundInExitedContainers {
unknown = append(unknown, container)
}
}
}
if 0 < len(exited) || 0 < len(unknown) {
containerHost.setErrs([]error{fmt.Errorf(
"Some containers on %s are exited or unknown. exited: %s, unknown: %s",
containerHost.getServerInfo().ServerName, exited, unknown)})
return append(oses, containerHost)
}
return oses
}
// 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 {
@@ -151,7 +408,7 @@ func Prepare() []error {
// Scan scan
func Scan() []error {
if len(servers) == 0 {
return []error{fmt.Errorf("Not initialize yet.")}
return []error{fmt.Errorf("No server defined. Check the configuration")}
}
Log.Info("Check required packages for scanning...")
@@ -160,12 +417,12 @@ func Scan() []error {
return errs
}
Log.Info("Scanning vuluneable OS packages...")
Log.Info("Scanning vulnerable OS packages...")
if errs := scanPackages(); errs != nil {
return errs
}
Log.Info("Scanning vulnerable software specified in CPE...")
Log.Info("Scanning vulnerable software specified in the CPE...")
if errs := scanVulnByCpeName(); errs != nil {
return errs
}
@@ -180,7 +437,7 @@ func checkRequiredPackagesInstalled() []error {
}
func scanPackages() []error {
timeoutSec := 30 * 60
timeoutSec := 120 * 60
return parallelSSHExec(func(o osTypeInterface) error {
return o.scanPackages()
}, timeoutSec)
@@ -201,7 +458,7 @@ func GetScanResults() (results models.ScanResults, err error) {
for _, s := range servers {
r, err := s.convertToModel()
if err != nil {
return results, fmt.Errorf("Failed converting to model: %s.", err)
return results, fmt.Errorf("Failed converting to model: %s", err)
}
results = append(results, r)
}

View File

@@ -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,66 +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)
}
var err error
if sudo && c.User != "root" {
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(cmd, "\n", "", -1))
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
}
@@ -159,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
}
@@ -172,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()
@@ -184,16 +220,103 @@ func sshExec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (re
result.Stdout = stdoutBuf.String()
result.Stderr = stderrBuf.String()
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
logger.Debugf(
"SSH executed. cmd: %s, status: %#v\nstdout: \n%s\nstderr: \n%s",
cmd, err, result.Stdout, result.Stderr)
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)
}
if c.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 agconn, err := net.Dial("unix", sock); err == nil {
@@ -218,18 +341,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("Faild to add keyAuth. err: %s", err)
}
if c.Password != "" {
auths = append(auths, ssh.Password(c.Password))
return nil, err
}
// http://blog.ralch.com/tutorial/golang-ssh-connection/
@@ -237,12 +355,11 @@ func sshConnect(c conf.ServerInfo) (client *ssh.Client, err error) {
User: c.User,
Auth: auths,
}
// log.Debugf("config: %s", pp.Sprintf("%v", config))
notifyFunc := func(e error, t time.Duration) {
logrus.Warnf("Faild to ssh %s@%s:%s. err: %s, Retrying in %s...",
c.User, c.Host, c.Port, e, t)
logrus.Debugf("sshConInfo: %s", pp.Sprintf("%v", c))
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 {
@@ -306,6 +423,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)
}
}

101
setup/docker/README.ja.md Normal file
View 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の過去年分の脆弱性データベースを更新する
```
$ docker exec -t vuls scripts/fetch_nvd_last2y.sh
```
- JVNの過去ヶ月分の脆弱性データベースを更新する
```
$ 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
View 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
```

View File

View 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"

View File

@@ -0,0 +1,89 @@
FROM buildpack-deps:jessie-scm
# golang Install
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc \
libc6-dev \
make \
curl \
&& rm -rf /var/lib/apt/lists/*
ENV GOLANG_VERSION 1.6.2
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
ENV GOLANG_DOWNLOAD_SHA256 e40c36ae71756198478624ed1bb4ce17597b3c19d243f3f0899bb5740d56212a
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
&& tar -C /usr/local -xzf golang.tar.gz \
&& rm golang.tar.gz
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
# glide install
ENV GLIDE_VERSION 0.10.2
ENV GLIDE_DOWNLOAD_URL https://github.com/Masterminds/glide/releases/download/$GLIDE_VERSION/glide-$GLIDE_VERSION-linux-amd64.tar.gz
RUN curl -fsSL "$GLIDE_DOWNLOAD_URL" -o glide.tar.gz \
&& mkdir /usr/local/glide \
&& tar -C /usr/local/glide -xzf glide.tar.gz \
&& ln -s /usr/local/glide/linux-amd64/glide /usr/local/bin/ \
&& rm glide.tar.gz
# nginx Install
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \
&& echo "deb http://nginx.org/packages/mainline/debian/ jessie nginx" >> /etc/apt/sources.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
ca-certificates \
nginx \
nginx-module-xslt \
nginx-module-geoip \
nginx-module-image-filter \
nginx-module-perl \
nginx-module-njs \
gettext-base \
wget \
unzip \
&& rm -rf /var/lib/apt/lists/*
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
COPY nginx.conf /etc/nginx/nginx.conf
#Vuls Install
ENV VULS_ROOT /opt/vuls
RUN mkdir -p /var/log/vuls ${VULS_ROOT}/conf /root/.ssh/
RUN chmod 700 -R /var/log/vuls $VULS_ROOT
# RUN go get github.com/kotakanbe/go-cve-dictionary
# RUN go get github.com/future-architect/vuls
RUN go get -v -d github.com/kotakanbe/go-cve-dictionary \
&& cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary \
&& glide install \
&& go install
RUN go get -v -d github.com/future-architect/vuls \
&& cd $GOPATH/src/github.com/future-architect/vuls \
&& glide install \
&& go install
# Copy custom Scripts
COPY ./scripts/ ${VULS_ROOT}/scripts
RUN chmod 755 ${VULS_ROOT}/scripts/*
#Vulrepo Install
RUN git clone https://github.com/usiusi360/vulsrepo /tmp/vulsrepo
RUN mkdir /usr/share/nginx/html/vulsrepo/
RUN cp -rp /tmp/vulsrepo/src/* /usr/share/nginx/html/vulsrepo
RUN rm -rf /tmp/vulsrepo
#Home
WORKDIR /opt/vuls
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,32 @@
user root;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

View File

@@ -0,0 +1,6 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -entire

View File

@@ -0,0 +1,6 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -month

View File

@@ -0,0 +1,6 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -week

View 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

View File

@@ -0,0 +1,6 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchnvd -last2y

View File

@@ -0,0 +1,7 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
VULS_CONF=${VULS_ROOT}/conf
NGINX_VULSREPO_ROOT=/usr/share/nginx/html/vulsrepo
cd $VULS_ROOT
vuls scan -report-json --cve-dictionary-dbpath=${VULS_ROOT}/cve.sqlite3 -config=${VULS_CONF}/config.toml
ln -sf ${VULS_ROOT}/results/current ${NGINX_VULSREPO_ROOT}/current

View File

@@ -0,0 +1,17 @@
#!/bin/bash
cd $GOPATH/src/github.com/future-architect/vuls
git pull origin master
glide install
go install
cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
git pull origin master
glide install
go install
git clone https://github.com/usiusi360/vulsrepo /tmp/vulsrepo
cp -rp /tmp/vulsrepo/src/* /usr/share/nginx/html/vulsrepo
rm -rf /tmp/vulsrepo

View File

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

View File

@@ -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()
}

View File

@@ -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.
// Name is Vuls
const Name string = "vuls"
// Version.
const Version string = "0.1.0"
// Version of Vuls
const Version string = "0.1.5"