Compare commits

...

145 Commits

Author SHA1 Message Date
Kota Kanbe
a522218c4e Update CHANGELOG.md 2016-11-08 21:15:57 +09:00
Kota Kanbe
820455399c Bump up version 2016-11-08 21:08:03 +09:00
Kota Kanbe
959d612534 Merge pull request #147 from future-architect/enablerepos
Supports yum --enablerepo option (supports only base,updates for now)
2016-11-08 15:56:28 +09:00
kota kanbe
cd81e6eab2 Add enablerepos option 2016-11-08 15:39:30 +09:00
Kota Kanbe
e6ec6920ad Merge pull request #248 from future-architect/skip-broken
Add -skip-broken option [CentOS only] #245
2016-11-07 21:24:33 +09:00
Kota Kanbe
18a92fa1ca Add -skip-broken option [CentOS only] #245 2016-11-07 21:22:38 +09:00
Kota Kanbe
f95af9897b Merge pull request #244 from future-architect/display-unknown-cves-tui
Display unknown CVEs to TUI
2016-11-07 15:03:25 +09:00
Kota Kanbe
b61adcb1fd Display unknown CVEs to TUI 2016-11-07 14:59:50 +09:00
Kota Kanbe
1bbf320755 Merge pull request #243 from yoheimuta/go1.7-context
Moved golang.org/x/net/context to context
2016-11-07 11:16:37 +09:00
Kota Kanbe
159f26171c Merge pull request #240 from gleentea/feature/report-xml
Add the XML output
2016-11-07 10:44:10 +09:00
yoheimuta
8ac00f6c0d Moved golang.org/x/net/context to context 2016-11-04 17:56:42 +09:00
gleentea
ce2daf2493 add xml-report
add struct tag for encoding/xml

update README

update glide.lock
2016-11-04 15:21:32 +09:00
Kota Kanbe
f014f8fd59 Merge pull request #241 from sadayuki-matsuno/fix-docker-readme-cation
fix readme
2016-11-02 13:49:28 +09:00
Kota Kanbe
f50a39a9e2 Merge pull request #242 from future-architect/readme-mysql
Update README #225
2016-11-02 13:46:51 +09:00
Kota Kanbe
e0d8147104 Update README #225 2016-11-02 13:45:37 +09:00
Sadayuki Matsuno
c5cfac62da fix readme 2016-11-01 20:24:37 +09:00
Kota Kanbe
83469ce5cc Update glide.lock 2016-11-01 15:09:53 +09:00
Kota Kanbe
7cd7b4a9a2 Merge pull request #238 from future-architect/debcache
Fix changelog cache bug on Ubuntu and Debian #235
2016-11-01 13:05:18 +09:00
Kota Kanbe
7681b277cf Fix changelog cache bug on Ubuntu and Debian #235 2016-11-01 13:03:44 +09:00
Kota Kanbe
406efa96c0 Merge pull request #237 from future-architect/readme
Fix README #234
2016-11-01 10:57:39 +09:00
Kota Kanbe
9a7a30c0bc Fix README #234 2016-11-01 10:54:59 +09:00
Kota Kanbe
64bdfa0e80 Merge pull request #234 from mykstmhr/master
add '-ssh-external' option to prepare subcommand
2016-10-31 19:26:00 +09:00
Kota Kanbe
067089973c Merge pull request #236 from future-architect/glide
Update glide files
2016-10-31 18:03:43 +09:00
Kota Kanbe
85e6d753c7 Update glide files 2016-10-31 18:02:41 +09:00
Kota Kanbe
4094984642 Merge pull request #225 from oswell/feature/mysql.support
Add support for reading CVE data from MySQL.
2016-10-31 17:07:06 +09:00
Kota Kanbe
85c0009a43 Merge pull request #232 from future-architect/owasp
Integrate OWASP Dependency Check
2016-10-31 15:16:13 +09:00
Tomohiro Miyakoshi
234e312ee2 add '-ssh-external' option to prepare subcommand
modify gofmt

modify gofmt
2016-10-28 19:13:38 +09:00
Kota Kanbe
ce3ca64678 Merge pull request #231 from ymd38/master
Fixed error for the latest version of gocui
2016-10-28 15:54:27 +09:00
Kota Kanbe
b042a600c3 Integrate OWASP Dependency Check 2016-10-27 22:00:53 +09:00
hirokazu yamada
686e9f07a9 Fixed error for the latest version of gocui 2016-10-26 00:51:21 +09:00
Mike Oswell
bb6725372b Add support for reading CVE data from MySQL. 2016-10-24 19:18:11 -07:00
Kota Kanbe
6f012fc9c5 Merge pull request #229 from oswell/feature/fix.tui.errors
Handle the refactored gocui SetCurrentView method.
2016-10-24 11:29:43 +09:00
Mike Oswell
4c82458481 Support recent refactoring of gocui's SetCurrentView method. 2016-10-23 19:16:40 -07:00
Kota Kanbe
a0ac863998 Update README.ja.md 2016-10-19 15:12:04 +09:00
Kota Kanbe
d23ef838f8 Update README.md 2016-10-19 15:08:08 +09:00
Kota Kanbe
f81ac197f5 Merge pull request #226 from usiusi360/fix-README
fix README
2016-10-17 22:55:24 +09:00
Takayuki Ushida
652b37e630 fix README 2016-10-17 22:43:20 +09:00
Kota Kanbe
c57e430393 Merge pull request #223 from sadayuki-matsuno/remove_base_image
remove base docker image
2016-10-17 18:14:52 +09:00
Kota Kanbe
fff6047df9 Merge pull request #222 from future-architect/ignore-cves
Support ignore CveIDs in config
2016-10-17 17:13:34 +09:00
Kota Kanbe
1e2b93d55b Support ignore CveIDs in config 2016-10-17 17:09:44 +09:00
Sadayuki Matsuno
66b27a7795 remove base docker image 2016-10-15 13:59:27 +09:00
Kota Kanbe
63f0a272c4 Update README 2016-10-13 19:30:36 +09:00
Kota Kanbe
8d2180cf5a Update README 2016-10-13 16:14:05 +09:00
Kota Kanbe
1986f7e4dd Merge pull request #219 from future-architect/confirm-before-preparing
Confirm before installing dependencies on prepare
2016-10-13 16:07:32 +09:00
Kota Kanbe
21beb396b4 Confirm before installing dependencies on prepare 2016-10-13 16:06:48 +09:00
Kota Kanbe
cb5a6f38d6 Merge pull request #221 from ymomoi/fix-misspelling
fix some misspelling.
2016-10-13 10:43:28 +09:00
Kota Kanbe
67e4aaede0 Merge pull request #216 from future-architect/makefile
Improve makefile, -version shows git hash, fix README
2016-10-13 10:35:06 +09:00
Yasunari Momoi
b42805d00c fix some misspelling. 2016-10-12 23:57:57 +09:00
Kota Kanbe
95d6888c87 Improve makefile, -version shows git hash, fix README 2016-10-12 20:31:47 +09:00
Kota Kanbe
549b315a65 Merge pull request #218 from future-architect/remove-all-json
Remove all.json
2016-10-12 20:05:48 +09:00
Kota Kanbe
5b80b16684 Remove all.json 2016-10-12 19:57:47 +09:00
Kota Kanbe
0cd0a4bf2b Merge pull request #217 from future-architect/ISSUE_TEMPLATE
Add GitHub issue template
2016-10-12 16:55:22 +09:00
Kota Kanbe
b5cf06cad8 Add GitHub issue template 2016-10-12 16:53:59 +09:00
Kota Kanbe
b964d19d82 Merge pull request #215 from future-architect/lang-to-language
Fix locale env var LANG to LANGUAGE
2016-10-12 09:03:07 +09:00
Kota Kanbe
cf7990d444 Fix locale env var LANG to LANGUAGE 2016-10-12 08:59:05 +09:00
Kota Kanbe
738ccf7dbb Merge pull request #214 from sadayuki-matsuno/fix-docker-readme
fix docker readme
2016-10-11 19:45:47 +09:00
Sadayuki Matsuno
fc2ea48c1d fix docker readme 2016-10-11 19:43:50 +09:00
Kota Kanbe
3af93b93d7 Merge pull request #206 from essentialkaos/master
Fixed bug with parsing update line on CentOS/RHEL
2016-10-11 13:20:53 +09:00
Kota Kanbe
f386c3be92 Merge pull request #213 from shokohara/patch-1
Fix ja document about typo
2016-10-11 13:10:54 +09:00
Sho Kohara
239d910dbe Fix ja document about typo 2016-10-11 13:09:45 +09:00
Kota Kanbe
48929deabd Merge pull request #212 from sadayuki-matsuno/fix-readme-about-mail
fix readme
2016-10-11 12:50:11 +09:00
Sadayuki Matsuno
79523de1db fix readme 2016-10-11 12:37:30 +09:00
Kota Kanbe
fbfc14dfeb Merge pull request #211 from sadayuki-matsuno/fast_mail_package
change e-mail package from gomail to net/smtp
2016-10-11 12:18:13 +09:00
Kota Kanbe
a8dc886f89 Merge pull request #204 from usiusi360/patch-1
fix typo
2016-10-11 11:39:31 +09:00
Sadayuki Matsuno
cfc9e064b9 change e-mail package from gomail to net/smtp 2016-10-11 10:29:18 +09:00
sadayuki-matsuno
e72fa3362a Merge pull request #207 from sadayuki-matsuno/fix-readme
fix README
2016-10-10 10:47:18 +09:00
Sadayuki Matsuno
26364421e8 fix README 2016-10-10 10:46:31 +09:00
Anton Novojilov
4a07974b54 Fixed bug with parsing update line on CentOS/RHEL 2016-10-07 08:26:36 -04:00
Takayuki Ushida
eaddc7f2ba fix typo 2016-10-06 21:05:57 +09:00
Kota Kanbe
85056aaa00 Update README.md 2016-10-01 17:12:58 +09:00
Kota Kanbe
c077c740fa Merge pull request #163 from hikachan/repo01
Improve setup/docker
2016-10-01 17:12:10 +09:00
Sadayuki Matsuno
c2eab87a3f fix docker 2016-10-01 13:21:00 +09:00
Kota Kanbe
ea582d2d2e Merge pull request #201 from future-architect/fix-defer
Fix defer cache.DB.close
2016-10-01 12:43:41 +09:00
Kota Kanbe
2f89a24100 Fix defer cache.DB.close 2016-10-01 12:39:18 +09:00
Kota Kanbe
73ebb94f67 Merge pull request #195 from future-architect/fix-help-msg-azure
Fix a help message of -report-azure-blob option
2016-09-24 20:36:52 +09:00
Kota Kanbe
95bf387ecc Fix a help message of -report-azure-blob option 2016-09-24 20:35:41 +09:00
Kota Kanbe
f17a8452f9 Merge pull request #191 from sadayuki-matsuno/add-gitignore
fix gitignore
2016-09-23 22:00:31 +09:00
Kota Kanbe
920ffe1f33 Merge pull request #193 from future-architect/fix-error-handling-in-tui
Fix error handling in tui
2016-09-23 22:00:07 +09:00
Kota Kanbe
093bcb7477 Fix error handling in tui 2016-09-23 21:59:27 +09:00
Sadayuki Matsuno
c06b3ec9eb fix gitignore 2016-09-21 16:50:30 +09:00
Kota Kanbe
ac6fe6f9fc Merge pull request #190 from future-architect/add-only-containers
Add only-containers option to scan subcommand #122
2016-09-20 21:34:32 +09:00
Kota Kanbe
2dffdaac42 Add only-containers option to scan subcommand #122 2016-09-20 21:32:58 +09:00
Kota Kanbe
cb445c9504 Merge pull request #189 from future-architect/Fix-not-working-changelog-cache-on-docker
Fix not working changelog cache on Container
2016-09-20 20:35:04 +09:00
Kota Kanbe
e3fc3aa9d1 Fix not working changelog cache on Container 2016-09-20 20:29:02 +09:00
Kota Kanbe
97c3f5d642 Update README 2016-09-20 11:51:30 +09:00
Kota Kanbe
0a52fc9a56 Merge pull request #188 from future-architect/update-glide
Update glide.lock
2016-09-20 10:08:41 +09:00
Kota Kanbe
c831339b0d Update glide.lock 2016-09-20 10:07:00 +09:00
Kota Kanbe
058ccf575f Merge pull request #186 from dladuke/master
Fix path in setup/docker/README
2016-09-16 16:37:54 +09:00
dladuke
92be12bc2f Fix config path 2016-09-15 22:29:44 -07:00
dladuke
1aa2f4b5b1 Fixs paths & typos
Fixs paths & typos
2016-09-15 22:27:53 -07:00
Kota Kanbe
bba9431985 Merge pull request #185 from future-architect/fix-results-dir 2016-09-14 21:45:53 +09:00
Kota Kanbe
3c39f1e737 Fix -results-dir option of scan subcommand 2016-09-14 21:45:03 +09:00
Kota Kanbe
e6f4d07a87 Merge pull request #184 from future-architect/fix-release-detection-on-bsd
Fix release version detection on FreeBSD
2016-09-14 20:20:39 +09:00
Kota Kanbe
e43358a0d2 Fix release version detection on FreeBSD 2016-09-14 20:19:32 +09:00
Kota Kanbe
f0644e8a9d Merge pull request #183 from future-architect/fix-defer-close-cache
Fix defer cahce.DB.close()
2016-09-14 18:25:04 +09:00
Kota Kanbe
11b010b281 Fix defer cahce.DB.close() 2016-09-14 18:16:18 +09:00
Kota Kanbe
c751029127 Merge pull request #182 from future-architect/change-output-file-mode
Fix a mode of files/dir (report, log)
2016-09-14 17:50:25 +09:00
Kota Kanbe
fb70d1b2f0 Fix a mode of files/dir (report, log) 2016-09-14 17:47:12 +09:00
Kota Kanbe
3d68783b7f Merge pull request #181 from future-architect/fix-nilpointer-no-json-dir-tui
Fix a error when no json dirs are found under results #180
2016-09-14 12:12:49 +09:00
Kota Kanbe
0d77853912 Fix a error when no json dirs are found under results #180 2016-09-14 12:09:14 +09:00
Kota Kanbe
ea1b5dd8f7 Merge pull request #179 from future-architect/ssh-external-configtest
ssh-external option of configtest is not working #178
2016-09-14 10:53:15 +09:00
Kota Kanbe
2dcb7d5ce1 ssh-external option of configtest is not working #178 2016-09-14 10:46:50 +09:00
Kota Kanbe
99cab34527 Merge pull request #177 from future-architect/erorr-when-no-scannable-servers
Show error when no scannable servers are detected.
2016-09-14 09:39:39 +09:00
Kota Kanbe
f5eeed0bc2 Show error when no scannable servers are detected. 2016-09-14 09:35:15 +09:00
Kota Kanbe
1b85e56961 Merge pull request #176 from future-architect/add_sudo_check_to_prepare
Add sudo check to prepare subcommand
2016-09-14 08:54:55 +09:00
Kota Kanbe
8a8ac5fd22 Add sudo check to prepare subcommand 2016-09-14 08:52:54 +09:00
Kota Kanbe
00c0354a8e Bump up version 2016-09-12 22:03:49 +09:00
Kota Kanbe
a2a6973ba1 Merge pull request #172 from future-architect/ubuntu_bakusoku
High speed scan on Ubuntu/Debian
2016-09-12 21:45:35 +09:00
Kota Kanbe
dd1d3a05fa High speed scan on Ubuntu/Debian 2016-09-12 21:10:21 +09:00
Kota Kanbe
2afe2d2640 Merge pull request #171 from future-architect/update-glide
Update glide.lock #170
2016-09-08 19:41:07 +09:00
Kota Kanbe
29678f9b59 Update glide.lock #170 2016-09-08 19:37:13 +09:00
Kota Kanbe
77edb251bb Merge pull request #169 from future-architect/cwe-support
Support CWE(Common Weakness Enumeration)
2016-09-07 19:45:05 +09:00
Kota Kanbe
29151fa267 Support CWE(Common Weakness Enumeration) 2016-09-07 19:42:46 +09:00
Kota Kanbe
b3f13790bd Merge pull request #168 from future-architect/fix-detect-platform
Fix detecting a platform on Azure
2016-09-07 13:57:21 +09:00
Kota Kanbe
38857c3356 Fix detecting a platform on Azure 2016-09-07 13:56:37 +09:00
Kota Kanbe
d75990d9fd Merge pull request #167 from future-architect/nosudo-amazon
Enable to scan without sudo on amazon linux
2016-09-06 16:28:25 +09:00
Kota Kanbe
ed063f6534 Enable to scan without sudo on amazon linux 2016-09-06 16:26:51 +09:00
Kota Kanbe
c8a9bdc517 Merge pull request #152 from sadayuki-matsuno/delete_sqlite
delete sqlite3
2016-09-06 13:19:07 +09:00
Sadayuki Matsuno
595729cdf8 delete sqlite3 2016-09-06 12:25:47 +09:00
Kota Kanbe
6119f79748 Merge pull request #166 from future-architect/yum-parse-err
Fix parse Error for yum check-update #165
2016-09-06 10:59:46 +09:00
Kota Kanbe
d4fb46c9ba Fix parse Error for yum check-update #165 2016-09-06 10:57:11 +09:00
Kota Kanbe
c41301afca Merge pull request #164 from future-architect/Change_docker_scripts_for_high_speed_jvn_fetch
Change scripts for data fetching from jvn
2016-09-05 10:34:07 +09:00
Kota Kanbe
50fd80830e Change scripts for datafetch from jvn under setup/docker/dockerfile/scripts
see https://github.com/kotakanbe/go-cve-dictionary/pull/21
2016-09-05 10:28:52 +09:00
Kota Kanbe
1c203b4272 Merge pull request #162 from tjinjin/fix_vulsrepo_setup
Fix: setup vulsrepo
2016-08-31 00:43:16 +09:00
tjinjin
c545e9045d Fix: setup vulsrepo 2016-08-31 00:31:35 +09:00
Kota Kanbe
2721dc0647 Merge pull request #160 from usiusi360/Fix-docker-vulsrepo-install
Fix-docker-vulsrepo-install
2016-08-30 14:16:39 +09:00
Kota Kanbe
51d13f4234 Merge pull request #161 from future-architect/remove-deprecated-options
Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security
2016-08-30 12:40:39 +09:00
Kota Kanbe
a60a5d6eab Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security 2016-08-30 12:37:03 +09:00
Kota Kanbe
5959235425 Merge pull request #158 from itchyny/regexp-must-compile
Reduce regular expression compilation
2016-08-29 18:00:13 +09:00
Takayuki Ushida
d8e6d4e5fc Fix-docker-vulsrepo-install 2016-08-27 21:56:09 +09:00
itchyny
7dfc9815b3 Reduce regexp compilation
- use regexp.MustCompile instead of regexp.Compile
- use strings.HasPrefix instead of regular expression when it is enough
2016-08-26 20:39:31 +09:00
Kota Kanbe
0c53b187a4 Merge pull request #159 from tjinjin/fix_vulsrepo_path
Fix bug: Vuls on Docker
2016-08-26 11:50:37 +09:00
tanaka masato
42dadfed8f Fix VulRepo path 2016-08-26 11:18:49 +09:00
Kota Kanbe
a46c603c77 Update README.ja.md 2016-08-24 16:00:22 +09:00
Kota Kanbe
ad0020d9a6 Update README.md 2016-08-24 15:46:26 +09:00
Kota Kanbe
a224f0bfd4 Merge pull request #156 from future-architect/add-testcase-153
Add testcases for #153
2016-08-23 19:31:14 +09:00
Kota Kanbe
d8dc3650d3 Add testcases for #153 2016-08-23 19:26:34 +09:00
Kota Kanbe
30f7527f10 Merge pull request #155 from usiusi360/Fix-CVE-ID-is-truncated-to-4-digits
Fix CVE-ID is truncated to 4 digits
2016-08-23 15:58:50 +09:00
Takayuki Ushida
b1f5bdd8b2 Fix CVE-ID is truncated to 4 digits 2016-08-20 21:23:31 +09:00
Kota Kanbe
c8e7c8b9fa Update README.ja.md 2016-08-18 17:39:46 +09:00
Kota Kanbe
30bf3223f8 Update README.md 2016-08-18 17:39:13 +09:00
Kota Kanbe
886710ec30 Update README.md 2016-08-18 17:19:03 +09:00
Kota Kanbe
510dc8d828 Update README.ja.md 2016-08-18 17:17:26 +09:00
Kota Kanbe
5ff7b2aab4 Merge pull request #151 from future-architect/enable-to-scan-on-centos-non-root
Fix yum update --changelog stalled when non-root ssh user on CentOS #150
2016-08-18 16:25:00 +09:00
kota kanbe
1e33536205 Fix yum update --changelog stalled when non-root ssh user on CentOS #150 2016-08-18 16:20:01 +09:00
73 changed files with 3142 additions and 1963 deletions

5
.gitignore vendored
View File

@@ -2,7 +2,9 @@ vuls
.vscode
*.txt
*.json
*.sqlite3
*.sqlite3*
*.db
tags
.gitmodules
coverage.out
issues/
@@ -10,3 +12,4 @@ vendor/
log/
results/
*config.toml
!setup/docker/*

View File

@@ -1,5 +1,122 @@
# Change Log
## [v0.1.7](https://github.com/future-architect/vuls/tree/v0.1.7) (2016-11-08)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.6...v0.1.7)
**Implemented enhancements:**
- Enable to scan only docker container, without docker host [\#122](https://github.com/future-architect/vuls/issues/122)
- Add -skip-broken option \[CentOS only\] \#245 [\#248](https://github.com/future-architect/vuls/pull/248) ([kotakanbe](https://github.com/kotakanbe))
- Display unknown CVEs to TUI [\#244](https://github.com/future-architect/vuls/pull/244) ([kotakanbe](https://github.com/kotakanbe))
- Add the XML output [\#240](https://github.com/future-architect/vuls/pull/240) ([gleentea](https://github.com/gleentea))
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
- Integrate OWASP Dependency Check [\#232](https://github.com/future-architect/vuls/pull/232) ([kotakanbe](https://github.com/kotakanbe))
- Add support for reading CVE data from MySQL. [\#225](https://github.com/future-architect/vuls/pull/225) ([oswell](https://github.com/oswell))
- Remove base docker image, -v shows commit hash [\#223](https://github.com/future-architect/vuls/pull/223) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- Support ignore CveIDs in config [\#222](https://github.com/future-architect/vuls/pull/222) ([kotakanbe](https://github.com/kotakanbe))
- Confirm before installing dependencies on prepare [\#219](https://github.com/future-architect/vuls/pull/219) ([kotakanbe](https://github.com/kotakanbe))
- Remove all.json [\#218](https://github.com/future-architect/vuls/pull/218) ([kotakanbe](https://github.com/kotakanbe))
- Add GitHub issue template [\#217](https://github.com/future-architect/vuls/pull/217) ([kotakanbe](https://github.com/kotakanbe))
- Improve makefile, -version shows git hash, fix README [\#216](https://github.com/future-architect/vuls/pull/216) ([kotakanbe](https://github.com/kotakanbe))
- change e-mail package from gomail to net/smtp [\#211](https://github.com/future-architect/vuls/pull/211) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- Add only-containers option to scan subcommand \#122 [\#190](https://github.com/future-architect/vuls/pull/190) ([kotakanbe](https://github.com/kotakanbe))
- Fix -results-dir option of scan subcommand [\#185](https://github.com/future-architect/vuls/pull/185) ([kotakanbe](https://github.com/kotakanbe))
- Show error when no scannable servers are detected. [\#177](https://github.com/future-architect/vuls/pull/177) ([kotakanbe](https://github.com/kotakanbe))
- Add sudo check to prepare subcommand [\#176](https://github.com/future-architect/vuls/pull/176) ([kotakanbe](https://github.com/kotakanbe))
- Supports yum --enablerepo option \(supports only base,updates for now\) [\#147](https://github.com/future-architect/vuls/pull/147) ([kotakanbe](https://github.com/kotakanbe))
**Fixed bugs:**
- Debian 8.6 \(jessie\) scan does not show vulnerable packages [\#235](https://github.com/future-architect/vuls/issues/235)
- panic: runtime error: index out of range - ubuntu 16.04 + vuls history [\#180](https://github.com/future-architect/vuls/issues/180)
- Moved golang.org/x/net/context to context [\#243](https://github.com/future-architect/vuls/pull/243) ([yoheimuta](https://github.com/yoheimuta))
- Fix changelog cache bug on Ubuntu and Debian \#235 [\#238](https://github.com/future-architect/vuls/pull/238) ([kotakanbe](https://github.com/kotakanbe))
- add '-ssh-external' option to prepare subcommand [\#234](https://github.com/future-architect/vuls/pull/234) ([mykstmhr](https://github.com/mykstmhr))
- Fixed error for the latest version of gocui [\#231](https://github.com/future-architect/vuls/pull/231) ([ymd38](https://github.com/ymd38))
- Handle the refactored gocui SetCurrentView method. [\#229](https://github.com/future-architect/vuls/pull/229) ([oswell](https://github.com/oswell))
- Fix locale env var LANG to LANGUAGE [\#215](https://github.com/future-architect/vuls/pull/215) ([kotakanbe](https://github.com/kotakanbe))
- Fixed bug with parsing update line on CentOS/RHEL [\#206](https://github.com/future-architect/vuls/pull/206) ([andyone](https://github.com/andyone))
- Fix defer cache.DB.close [\#201](https://github.com/future-architect/vuls/pull/201) ([kotakanbe](https://github.com/kotakanbe))
- Fix a help message of -report-azure-blob option [\#195](https://github.com/future-architect/vuls/pull/195) ([kotakanbe](https://github.com/kotakanbe))
- Fix error handling in tui [\#193](https://github.com/future-architect/vuls/pull/193) ([kotakanbe](https://github.com/kotakanbe))
- Fix not working changelog cache on Container [\#189](https://github.com/future-architect/vuls/pull/189) ([kotakanbe](https://github.com/kotakanbe))
- Fix release version detection on FreeBSD [\#184](https://github.com/future-architect/vuls/pull/184) ([kotakanbe](https://github.com/kotakanbe))
- Fix defer cahce.DB.close\(\) [\#183](https://github.com/future-architect/vuls/pull/183) ([kotakanbe](https://github.com/kotakanbe))
- Fix a mode of files/dir \(report, log\) [\#182](https://github.com/future-architect/vuls/pull/182) ([kotakanbe](https://github.com/kotakanbe))
- Fix a error when no json dirs are found under results \#180 [\#181](https://github.com/future-architect/vuls/pull/181) ([kotakanbe](https://github.com/kotakanbe))
- ssh-external option of configtest is not working \#178 [\#179](https://github.com/future-architect/vuls/pull/179) ([kotakanbe](https://github.com/kotakanbe))
**Closed issues:**
- Recent changes to gobui cause build failures [\#228](https://github.com/future-architect/vuls/issues/228)
- https://hub.docker.com/r/vuls/go-cve-dictionary/ is empty [\#208](https://github.com/future-architect/vuls/issues/208)
- Not able to install gomail fails [\#202](https://github.com/future-architect/vuls/issues/202)
- No results file created - vuls tui failed [\#199](https://github.com/future-architect/vuls/issues/199)
- Wrong file permissions for results/\*.json in official Docker container [\#197](https://github.com/future-architect/vuls/issues/197)
- Failed: Unknown OS Type [\#196](https://github.com/future-architect/vuls/issues/196)
- Segmentation fault with configtest [\#192](https://github.com/future-architect/vuls/issues/192)
- Failed to scan. err: No server defined. Check the configuration [\#187](https://github.com/future-architect/vuls/issues/187)
- vuls configtest -ssh-external doesnt work [\#178](https://github.com/future-architect/vuls/issues/178)
- apt-get update: time out [\#175](https://github.com/future-architect/vuls/issues/175)
- scanning on Centos6, but vuls recognizes debian. [\#174](https://github.com/future-architect/vuls/issues/174)
**Merged pull requests:**
- Update README \#225 [\#242](https://github.com/future-architect/vuls/pull/242) ([kotakanbe](https://github.com/kotakanbe))
- fix readme [\#241](https://github.com/future-architect/vuls/pull/241) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- Fix README \#234 [\#237](https://github.com/future-architect/vuls/pull/237) ([kotakanbe](https://github.com/kotakanbe))
- Update glide files [\#236](https://github.com/future-architect/vuls/pull/236) ([kotakanbe](https://github.com/kotakanbe))
- fix README [\#226](https://github.com/future-architect/vuls/pull/226) ([usiusi360](https://github.com/usiusi360))
- fix some misspelling. [\#221](https://github.com/future-architect/vuls/pull/221) ([ymomoi](https://github.com/ymomoi))
- fix docker readme [\#214](https://github.com/future-architect/vuls/pull/214) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- Fix ja document about typo [\#213](https://github.com/future-architect/vuls/pull/213) ([shokohara](https://github.com/shokohara))
- fix readme [\#212](https://github.com/future-architect/vuls/pull/212) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- fix README [\#207](https://github.com/future-architect/vuls/pull/207) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- fix typo [\#204](https://github.com/future-architect/vuls/pull/204) ([usiusi360](https://github.com/usiusi360))
- fix gitignore [\#191](https://github.com/future-architect/vuls/pull/191) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
- Update glide.lock [\#188](https://github.com/future-architect/vuls/pull/188) ([kotakanbe](https://github.com/kotakanbe))
- Fix path in setup/docker/README [\#186](https://github.com/future-architect/vuls/pull/186) ([dladuke](https://github.com/dladuke))
- Vuls and vulsrepo are now separated [\#163](https://github.com/future-architect/vuls/pull/163) ([hikachan](https://github.com/hikachan))
## [v0.1.6](https://github.com/future-architect/vuls/tree/v0.1.6) (2016-09-12)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.5...v0.1.6)
**Implemented enhancements:**
- High speed scan on Ubuntu/Debian [\#172](https://github.com/future-architect/vuls/pull/172) ([kotakanbe](https://github.com/kotakanbe))
- Support CWE\(Common Weakness Enumeration\) [\#169](https://github.com/future-architect/vuls/pull/169) ([kotakanbe](https://github.com/kotakanbe))
- Enable to scan without sudo on amazon linux [\#167](https://github.com/future-architect/vuls/pull/167) ([kotakanbe](https://github.com/kotakanbe))
- Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security [\#161](https://github.com/future-architect/vuls/pull/161) ([kotakanbe](https://github.com/kotakanbe))
- delete sqlite3 [\#152](https://github.com/future-architect/vuls/pull/152) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
**Fixed bugs:**
- Failed to setup vuls docker [\#170](https://github.com/future-architect/vuls/issues/170)
- yum check-update error occurred when no reboot after kernel updating [\#165](https://github.com/future-architect/vuls/issues/165)
- error thrown from 'docker build .' [\#157](https://github.com/future-architect/vuls/issues/157)
- CVE-ID is truncated to 4 digits [\#153](https://github.com/future-architect/vuls/issues/153)
- 'yum update --changelog' stalled in 'vuls scan'. if ssh user is not 'root'. [\#150](https://github.com/future-architect/vuls/issues/150)
- Panic on packet scan [\#131](https://github.com/future-architect/vuls/issues/131)
- Update glide.lock \#170 [\#171](https://github.com/future-architect/vuls/pull/171) ([kotakanbe](https://github.com/kotakanbe))
- Fix detecting a platform on Azure [\#168](https://github.com/future-architect/vuls/pull/168) ([kotakanbe](https://github.com/kotakanbe))
- Fix parse error for yum check-update \#165 [\#166](https://github.com/future-architect/vuls/pull/166) ([kotakanbe](https://github.com/kotakanbe))
- Fix bug: Vuls on Docker [\#159](https://github.com/future-architect/vuls/pull/159) ([tjinjin](https://github.com/tjinjin))
- Fix CVE-ID is truncated to 4 digits [\#155](https://github.com/future-architect/vuls/pull/155) ([usiusi360](https://github.com/usiusi360))
- Fix yum update --changelog stalled when non-root ssh user on CentOS \#150 [\#151](https://github.com/future-architect/vuls/pull/151) ([kotakanbe](https://github.com/kotakanbe))
**Closed issues:**
- Support su for root privilege escalation [\#44](https://github.com/future-architect/vuls/issues/44)
- Support FreeBSD [\#34](https://github.com/future-architect/vuls/issues/34)
**Merged pull requests:**
- Change scripts for data fetching from jvn [\#164](https://github.com/future-architect/vuls/pull/164) ([kotakanbe](https://github.com/kotakanbe))
- Fix: setup vulsrepo [\#162](https://github.com/future-architect/vuls/pull/162) ([tjinjin](https://github.com/tjinjin))
- Fix-docker-vulsrepo-install [\#160](https://github.com/future-architect/vuls/pull/160) ([usiusi360](https://github.com/usiusi360))
- Reduce regular expression compilation [\#158](https://github.com/future-architect/vuls/pull/158) ([itchyny](https://github.com/itchyny))
- Add testcases for \#153 [\#156](https://github.com/future-architect/vuls/pull/156) ([kotakanbe](https://github.com/kotakanbe))
## [v0.1.5](https://github.com/future-architect/vuls/tree/v0.1.5) (2016-08-16)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.4...v0.1.5)

36
ISSUE_TEMPLATE Normal file
View File

@@ -0,0 +1,36 @@
# Environment
## Vuls
Hash : ____
To check the commit hash of HEAD
$ vuls -v
or
$ cd $GOPATH/src/github.com/future-architect/vuls
$ git rev-parse --short HEAD
## OS
- Target Server: Write here
- Vuls Server: Write here
## Go
- Go version: here
# Current Output
Please re-run the command using ```-debug``` and provide the output below.
# Addition Details
Can you also please fill in each of the remaining sections.
## Expected Behavior
## Actual Behavior
## Steps to reproduce the behaviour

View File

@@ -1,4 +1,9 @@
.PHONY: \
glide \
deps \
update \
build \
install \
all \
vendor \
lint \
@@ -11,14 +16,29 @@
clean
SRCS = $(shell git ls-files '*.go')
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
PKGS = ./. ./config ./models ./report ./cveapi ./scan ./util ./commands ./cache
VERSION := $(shell git describe --tags --abbrev=0)
REVISION := $(shell git rev-parse --short HEAD)
LDFLAGS := -X 'main.version=$(VERSION)' \
-X 'main.revision=$(REVISION)'
glide:
go get github.com/Masterminds/glide
deps: glide
glide install
update: glide
glide update
build: main.go deps
go build -ldflags "$(LDFLAGS)" -o vuls $<
install: main.go deps
go install -ldflags "$(LDFLAGS)"
all: test
# vendor:
# @ go get -v github.com/mjibson/party
# party -d external -c -u
lint:
@ go get -v github.com/golang/lint/golint
$(foreach file,$(SRCS),golint $(file) || exit;)

View File

@@ -107,14 +107,14 @@ Vuls requiert l'installation des paquets suivants :
- sqlite
- git
- gcc
- go v1.6
- go v1.7.1 or later
- https://golang.org/doc/install
```bash
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
$ sudo yum -y install sqlite git gcc
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
$ mkdir $HOME/go
```
Ajoutez les lignes suivantes dans /etc/profile.d/goenv.sh
@@ -201,7 +201,7 @@ Summary Unspecified vulnerability in the Java SE and Java SE Embedded co
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1

View File

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

322
README.md
View File

@@ -52,10 +52,12 @@ Vuls is a tool created to solve the problems listed above. It has the following
- Support software registered in CPE
- Agentless architecture
- User is required to only setup one machine that is connected to other target servers via SSH
- Nondestructive testing
- Pre-authorization is not necessary before scanning on AWS
- Auto generation of configuration file template
- Auto detection of servers set using CIDR, generate configuration file template
- Email and Slack notification is possible (supports Japanese language)
- Scan result is viewable on accessory software, TUI Viewer terminal.
- Scan result is viewable on accessory software, TUI Viewer terminal or Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo)).
----
@@ -96,6 +98,7 @@ This can be done in the following steps.
1. Prepare
1. Scan
1. TUI(Terminal-Based User Interface)
1. Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo))
## Step1. Launch Amazon Linux
@@ -127,17 +130,17 @@ And also, SUDO with password is not supported for security reasons. So you have
Vuls requires the following packages.
- SQLite3
- git v2
- SQLite3 or MySQL
- git
- gcc
- go v1.6
- go v1.7.1 or later
- https://golang.org/doc/install
```bash
$ ssh ec2-user@52.100.100.100 -i ~/.ssh/private.pem
$ sudo yum -y install sqlite git gcc
$ wget https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
$ wget https://storage.googleapis.com/golang/go1.7.1.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.7.1.linux-amd64.tar.gz
$ mkdir $HOME/go
```
Add these lines into /etc/profile.d/goenv.sh
@@ -155,18 +158,18 @@ $ source /etc/profile.d/goenv.sh
## Step4. Deploy [go-cve-dictionary](https://github.com/kotakanbe/go-cve-dictionary)
go get
```bash
$ sudo mkdir /var/log/vuls
$ sudo chown ec2-user /var/log/vuls
$ sudo chmod 700 /var/log/vuls
$ go get github.com/kotakanbe/go-cve-dictionary
$
$ mkdir -p $GOPATH/src/github.com/kotakanbe
$ cd $GOPATH/src/github.com/kotakanbe
$ git clone https://github.com/kotakanbe/go-cve-dictionary.git
$ cd go-cve-dictionary
$ make install
```
If an error occurred while go get, check the following points.
- Update Git
- try [deploying with glide](https://github.com/future-architect/vuls/blob/master/README.md#deploy-with-glide).
The binary was built under `$GOPARH/bin`
Fetch vulnerability data from NVD.
It takes about 10 minutes (on AWS).
@@ -182,14 +185,14 @@ $ ls -alh cve.sqlite3
Launch a new terminal and SSH to the ec2 instance.
go get
```
$ go get github.com/future-architect/vuls
$ mkdir -p $GOPATH/src/github.com/future-architect
$ cd $GOPATH/src/github.com/future-architect
$ git clone https://github.com/future-architect/vuls.git
$ cd vuls
$ make install
```
If an error occurred while go get, check the following points.
- Update Git
- try [deploying with glide](https://github.com/future-architect/vuls/blob/master/README.md#deploy-with-glide).
The binary was built under `$GOPARH/bin`
## Step6. Config
@@ -219,7 +222,7 @@ see [Usage: Prepare](https://github.com/future-architect/vuls#usage-prepare)
## Step8. Start Scanning
```
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3
$ vuls scan -cve-dictionary-dbpath=$PWD/cve.sqlite3 -report-json
INFO[0000] Start scanning (config: /home/ec2-user/config.toml)
INFO[0000] Start scanning
INFO[0000] config: /home/ec2-user/config.toml
@@ -246,7 +249,7 @@ Summary Unspecified vulnerability in the Java SE and Java SE Embedded co
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-0494
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-0494
CVE Details http://www.cvedetails.com/cve/CVE-2016-0494
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-0494&vector=(AV:N/AC:L/Au:N/C:C/I:C/A:C)
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-0494
ALAS-2016-643 https://alas.aws.amazon.com/ALAS-2016-643.html
Package/CPE java-1.7.0-openjdk-1.7.0.91-2.6.2.2.63.amzn1 -> java-1.7.0-openjdk-1:1.7.0.95-2.6.4.0.65.amzn1
@@ -263,6 +266,10 @@ $ vuls tui
![Vuls-TUI](img/hello-vuls-tui.png)
## Step10. Web UI
[VulsRepo](https://github.com/usiusi360/vulsrepo) is a awesome Web UI for Vuls.
Check it out the [Online Demo](http://usiusi360.github.io/vulsrepo/).
----
@@ -277,40 +284,40 @@ see https://github.com/future-architect/vuls/tree/master/setup/docker
![Vuls-Architecture](img/vuls-architecture.png)
## [go-cve-dictinary](https://github.com/kotakanbe/go-cve-dictionary)
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3.
- Fetch vulnerability information from NVD and JVN(Japanese), then insert into SQLite3 or MySQL.
## Vuls
## Scanning Flow
![Vuls-Scan-Flow](img/vuls-scan-flow.png)
- 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)
- Write scan results to JSON file to show the latest report on your terminal
----
# Performance Considerations
- on Ubuntu and Debian
- On Ubuntu and Debian
Vuls issues `apt-get changelog` for each upgradable packages and parse the changelog.
`apt-get changelog` is slow and resource usage is heavy when there are many updatable packages on target server.
`apt-get changelog` is slow and resource usage is heavy when there are many updatable packages on target server.
Vuls stores these changelogs to KVS([boltdb](https://github.com/boltdb/bolt)).
From the second time on, the scan speed is fast by using the local cache.
- on CentOS
- On CentOS
Vuls issues `yum update --changelog` to get changelogs of upgradable packages at once and parse the changelog.
Scan speed is fast and resource usage is light.
- On Amazon, RHEL and FreeBSD
High speed scan and resource usage is light because Vuls can get CVE IDs by using package manager(no need to parse a changelog).
| Distribution| Scan Speed | Resource Usage On Target Server |
| Distribution| Scan Speed |
|:------------|:-------------------|:-------------|
| Ubuntu | Slow | Heavy |
| Debian | Slow | Heavy |
| CentOS | Fast | Light |
| Amazon | Fast | Light |
| RHEL | Fast | Light |
| FreeBSD | Fast | Light |
| Ubuntu | First time: Slow / From the second time: Fast |
| Debian | First time: Slow / From the second time: Fast |
| CentOS | Fast |
| Amazon | Fast |
| RHEL | Fast |
| FreeBSD | Fast |
----
@@ -334,7 +341,7 @@ web/app server in the same configuration under the load balancer
|:------------|-------------------:|
| Ubuntu | 12, 14, 16|
| Debian | 7, 8|
| RHEL | 4, 5, 6, 7|
| RHEL | 6, 7|
| CentOS | 5, 6, 7|
| Amazon Linux| All|
| FreeBSD | 10|
@@ -368,7 +375,7 @@ notifyUsers = ["@username"]
[mail]
smtpAddr = "smtp.gmail.com"
smtpPort = "465"
smtpPort = "587"
user = "username"
password = "password"
from = "from@address.com"
@@ -384,6 +391,7 @@ subjectPrefix = "[vuls]"
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6313"]
#optional = [
# ["key", "value"],
#]
@@ -399,6 +407,7 @@ host = "172.31.4.82"
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6313"]
#optional = [
# ["key", "value"],
#]
@@ -421,7 +430,7 @@ You can customize your configuration using this template.
notifyUsers = ["@username"]
```
- hookURL : Incomming webhook's URL
- hookURL : Incoming webhook's URL
- channel : channel name.
If you set `${servername}` to channel, the report will be sent to each channel.
In the following example, the report will be sent to the `#server1` and `#server2`.
@@ -452,7 +461,7 @@ You can customize your configuration using this template.
```
[mail]
smtpAddr = "smtp.gmail.com"
smtpPort = "465"
smtpPort = "587"
user = "username"
password = "password"
from = "from@address.com"
@@ -471,6 +480,7 @@ You can customize your configuration using this template.
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6313"]
#optional = [
# ["key", "value"],
#]
@@ -490,6 +500,7 @@ You can customize your configuration using this template.
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#containers = ["${running}"]
#ignoreCves = ["CVE-2016-6314"]
#optional = [
# ["key", "value"],
#]
@@ -503,6 +514,7 @@ You can customize your configuration using this template.
- keyPath: SSH private key path
- cpeNames: see [Usage: Scan vulnerability of non-OS package](https://github.com/future-architect/vuls#usage-scan-vulnerability-of-non-os-package)
- containers: see [Usage: Scan Docker containers](https://github.com/future-architect/vuls#usage-scan-docker-containers)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: Add additional information to JSON report.
Vuls supports two types of SSH. One is native go implementation. The other is external SSH command. For details, see [-ssh-external option](https://github.com/future-architect/vuls#-ssh-external-option)
@@ -538,15 +550,19 @@ configtest:
```
And also, configtest subcommand checks sudo settings on target servers whether Vuls is able to SUDO with nopassword via SSH.
Example of /etc/sudoers on target servers
- CentOS, Amazon Linux, RedHat Enterprise Linux
- CentOS, RHEL
```
vuls ALL=(root) NOPASSWD: /usr/bin/yum
vuls ALL=(root) NOPASSWD: /usr/bin/yum, /bin/echo
```
- Ubuntu, Debian
```
vuls ALL=(root) NOPASSWD: /usr/bin/apt-get, /usr/bin/apt-cache
```
- It is possible to scan without root privilege for Amazon Linux, FreeBSD.
----
@@ -558,8 +574,8 @@ Prepare subcommand installs required packages on each server.
|:------------|-------------------:|:-------------|
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8| aptitude |
| CentOS | 5| yum-plugin-security, yum-changelog |
| CentOS | 6, 7| yum-plugin-security, yum-plugin-changelog |
| CentOS | 5| yum-changelog |
| CentOS | 6, 7| yum-plugin-changelog |
| Amazon | All | - |
| RHEL | 4, 5, 6, 7 | - |
| FreeBSD | 10 | - |
@@ -567,19 +583,22 @@ Prepare subcommand installs required packages on each server.
```
$ vuls prepare -help
prepare
[-config=/path/to/config.toml] [-debug]
prepare:
prepare
[-config=/path/to/config.toml]
[-ask-key-password]
[SERVER]...
[-debug]
[-ssh-external]
[SERVER]...
-ask-key-password
Ask ssh privatekey password before scanning
-config string
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-use-unattended-upgrades
[Deprecated] For Ubuntu, install unattended-upgrades
-ssh-external
Use external ssh command. Default: Use the Go native implementation
```
----
@@ -593,18 +612,23 @@ scan:
scan
[-lang=en|ja]
[-config=/path/to/config.toml]
[-dbpath=/path/to/vuls.sqlite3]
[--cve-dictionary-dbpath=/path/to/cve.sqlite3]
[-results-dir=/path/to/results]
[-cve-dictionary-dbtype=sqlite3|mysql]
[-cve-dictionary-dbpath=/path/to/cve.sqlite3 or mysql connection string]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cache-dbpath=/path/to/cache.db]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ssh-external]
[-containers-only]
[-skip-broken]
[-report-azure-blob]
[-report-json]
[-report-mail]
[-report-s3]
[-report-slack]
[-report-text]
[-report-xml]
[-http-proxy=http://192.168.0.1:8080]
[-ask-key-password]
[-debug]
@@ -632,16 +656,20 @@ scan:
Azure storage container name
-azure-key string
Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified
-cache-dbpath string
/path/to/cache.db (local cache of changelog for Ubuntu/Debian) (default "$PWD/cache.db")
-config string
/path/to/toml (default "$PWD/config.toml")
-containers-only
Scan concontainers Only. Default: Scan both of hosts and containers
-cve-dictionary-dbpath string
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
/path/to/sqlite3 (For get cve detail from cve.sqlite3)
-cve-dictionary-dbtype string
DB type for fetching CVE dictionary (sqlite3 or mysql) (default "sqlite3")
-cve-dictionary-url string
http://CVE.Dictionary (default "http://127.0.0.1:1323")
-cvss-over float
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
-dbpath string
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
-debug
debug mode
-debug-sql
@@ -662,24 +690,25 @@ scan:
Send report via Slack
-report-text
Write report to text files ($PWD/results/current)
-report-xml
Write report to XML files ($PWDresults/current)
-results-dir string
/path/to/results (default "$PWD/results")
-skip-broken
[For CentOS] yum update changelog with --skip-broken option
-ssh-external
Use external ssh command. Default: Use the Go native implementation
-use-unattended-upgrades
[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)
-use-yum-plugin-security
[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)
```
## -ssh-external option
Vuls supports different types of SSH.
By Defaut, using a native Go implementation from crypto/ssh.
By Default, using a native Go implementation from crypto/ssh.
This is useful in situations where you may not have access to traditional UNIX tools.
To use external ssh command, specify this option.
This is useful If you want to use ProxyCommand or chiper algorithm of SSH that is not supported by native go implementation.
This is useful If you want to use ProxyCommand or cipher algorithm of SSH that is not supported by native go implementation.
Don't forget to add below line to /etc/sudoers on the target servers. (username: vuls)
```
Defaults:vuls !requiretty
@@ -693,10 +722,10 @@ Defaults:vuls !requiretty
| empty password | - | |
| with password | required | or use ssh-agent |
## -report-json , -report-text option
## -report-json , -report-text , -report-xml option
At the end of the scan, scan results will be available in the `$PWD/result/current/` directory.
`all.(json|txt)` includes the scan results of all servres and `servername.(json|txt)` includes the scan result of the server.
`servername.(json|txt|xml)` includes the scan result of the server.
## Example: Scan all servers defined in config file
```
@@ -708,7 +737,7 @@ $ vuls scan \
-cve-dictionary-dbpath=$PWD/cve.sqlite3
```
With this sample command, it will ..
- Ask SSH key passsword before scanning
- Ask SSH key password before scanning
- Scan all servers defined in config file
- Send scan results to slack and email
- Only Report CVEs that CVSS score is over 7
@@ -772,6 +801,43 @@ $ vuls scan \
-azure-container=vuls
```
## Example: IgnoreCves
Define ignoreCves in config if you don't want to report(slack, mail, text...) specific CVE IDs. But these ignoreCves will be output to JSON file like below.
- config.toml
```toml
[default]
ignoreCves = ["CVE-2016-6313"]
[servers.bsd]
host = "192.168.11.11"
user = "kanbe"
ignoreCves = ["CVE-2016-6314"]
```
- bsd.json
```json
[
{
"ServerName": "bsd",
"Family": "FreeBSD",
"Release": "10.3-RELEASE",
"IgnoredCves" : [
"CveDetail" : {
"CVE-2016-6313",
...
},
"CveDetail" : {
"CVE-2016-6314",
...
}
]
}
]
```
## Example: Add optional key-value pairs to JSON
Optional key-value can be outputted to JSON.
@@ -812,9 +878,17 @@ optional = [
]
```
## Example: Use MySQL as a DB storage back-end
```
$ vuls scan \
-cve-dictionary-dbtype=mysql \
-cve-dictionary-dbpath="user:pass@tcp(localhost:3306)/dbname?parseTime=true"
```
----
# Usage: Scan vulnerability of non-OS package
# Usage: Scan vulnerabilites of non-OS packages
It is possible to detect vulnerabilities in non-OS packages, such as something you compiled by yourself, language libraries and frameworks, that have been registered in the [CPE](https://nvd.nist.gov/cpe.cfm).
@@ -823,10 +897,10 @@ It is possible to detect vulnerabilities in non-OS packages, such as something y
**Check CPE Naming Format: 2.2**
- [go-cpe-dictionary](https://github.com/kotakanbe/go-cpe-dictionary) is a good choice for geeks.
You can search a CPE name by the application name incremenally.
You can search a CPE name by the application name incrementally.
- Configuration
To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
To detect the vulnerability of Ruby on Rails v4.2.1, cpeNames needs to be set in the servers section.
```
[servers]
@@ -838,10 +912,34 @@ To detect the vulnerbility of Ruby on Rails v4.2.1, cpeNames needs to be set in
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
]
```
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities.
Benefit of integrating Vuls And OWASP Dependency Check is below.
- Automatic Update of Vuls config when the libraries are updated.
- Reporting by Email or Slack by using Vuls.
- Reporting in Japanese
- OWASP Dependency Check supports only English
How to integrate Vuls with OWASP Dependency Check
- Execute OWASP Dependency Check with --format=XML option.
- Define the xml file path of dependency check in config.toml.
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"
dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
```
# Usage: Scan Docker containers
It is common that keep Docker containers runnning without SSHd daemon.
It is common that keep Docker containers running without SSHd daemon.
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
Vuls scans Docker containers via `docker exec` instead of SSH.
@@ -873,6 +971,9 @@ For more details, see [Architecture section](https://github.com/future-architect
keyPath = "/home/username/.ssh/id_rsa"
containers = ["container_name_a", "4aa37a8b63b9"]
```
- To scan containers only
- --containers-only option is available.
# Usage: TUI
@@ -881,23 +982,23 @@ For more details, see [Architecture section](https://github.com/future-architect
```
$ vuls tui -h
tui:
tui [-dbpath=/path/to/vuls.sqlite3]
tui [-results-dir=/path/to/results]
-dbpath string
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
-results-dir string
/path/to/results (default "$PWD/results")
-debug-sql
debug SQL
```
Key binding is bellow.
Key binding is below.
| key | |
|:-----------------|:-------|:------|
| TAB | move cursor among the panes |
| Arrow up/down | move cursor to up/down |
| Ctrl+j, Ctrl+k | move cursor to up/donw |
| Ctrl+u, Ctrl+d | page up/donw |
| Ctrl+j, Ctrl+k | move cursor to up/down |
| Ctrl+u, Ctrl+d | page up/down |
For details, see https://github.com/future-architect/vuls/blob/master/report/tui.go
@@ -943,80 +1044,30 @@ $ vuls scan -cve-dictionary-url=http://192.168.0.1:1323
# Usage: Update NVD Data
```
$ go-cve-dictionary fetchnvd -h
fetchnvd:
fetchnvd
[-last2y]
[-dbpath=/path/to/cve.sqlite3]
[-debug]
[-debug-sql]
see [go-cve-dictionary#usage-fetch-nvd-data](https://github.com/kotakanbe/go-cve-dictionary#usage-fetch-nvd-data)
-dbpath string
/path/to/sqlite3 (default "$PWD/cve.sqlite3")
-debug
debug mode
-debug-sql
SQL debug mode
-last2y
Refresh NVD data in the last two years.
```
- Fetch data of the entire period
```
$ go-cve-dictionary fetchnvd -entire
```
- Fetch data in the last 2 years
```
$ go-cve-dictionary fetchnvd -last2y
```
----
# Deploy With Glide
If an error occurred while go get, try deploying with glide.
- Install [Glide](https://github.com/Masterminds/glide)
- Deploy go-cve-dictionary
```
$ go get -d github.com/kotakanbe/go-cve-dictionary
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
$ glide install
$ go install
```
- Deploy vuls
```
$ go get -d github.com/future-architect/vuls
$ cd $GOPATH/src/github.com/future-architect/vuls
$ glide install
$ go install
```
- The binaries are created under $GOPARH/bin
----
# Update Vuls With Glide
- Update go-cve-dictionary
- Update go-cve-dictionary
If the DB schema was changed, please specify new SQLite3 or MySQL DB file.
```
$ cd $GOPATH/src/github.com/kotakanbe/go-cve-dictionary
$ git pull
$ glide install
$ go install
$ mv vendor /tmp/foo
$ make install
```
- Update vuls
```
$ cd $GOPATH/src/github.com/future-architect/vuls
$ git pull
$ glide install
$ go install
$ mv vendor /tmp/bar
$ make install
```
- The binaries are created under $GOPARH/bin
- If the DB schema was changed, please specify new SQLite3 DB file.
Binary file was built under $GOPARH/bin
---
@@ -1032,7 +1083,7 @@ If your system is behind HTTP proxy, you have to specify --http-proxy option.
- How to Daemonize go-cve-dictionary
Use Systemd, Upstart or supervisord, daemontools...
- How to Enable Automatic-Update of Vunerability Data.
- How to Enable Automatic-Update of Vulnerability Data.
Use job scheduler like Cron (with -last2y option).
- How to Enable Automatic-Scan.
@@ -1107,7 +1158,6 @@ Please see [CHANGELOG](https://github.com/future-architect/vuls/blob/master/CHAN
----
# Licence
# License
Please see [LICENSE](https://github.com/future-architect/vuls/blob/master/LICENSE).

173
cache/bolt.go vendored Normal file
View File

@@ -0,0 +1,173 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cache
import (
"encoding/json"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/boltdb/bolt"
"github.com/future-architect/vuls/util"
)
// Bolt holds a pointer of bolt.DB
// boltdb is used to store a cache of Changelogs of Ubuntu/Debian
type Bolt struct {
Path string
Log *logrus.Entry
db *bolt.DB
}
// SetupBolt opens a boltdb and creates a meta bucket if not exists.
func SetupBolt(path string, l *logrus.Entry) error {
l.Infof("Open boltDB: %s", path)
db, err := bolt.Open(path, 0600, nil)
if err != nil {
return err
}
b := Bolt{
Path: path,
Log: l,
db: db,
}
if err = b.createBucketIfNotExists(metabucket); err != nil {
return err
}
DB = b
return nil
}
// Close a db.
func (b Bolt) Close() error {
if b.db == nil {
return nil
}
return b.db.Close()
}
// CreateBucketIfNotExists creates a buket that is specified by arg.
func (b *Bolt) createBucketIfNotExists(name string) error {
return b.db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(name))
if err != nil {
return fmt.Errorf("Failed to create bucket: %s", err)
}
return nil
})
}
// GetMeta gets a Meta Information os the servername to boltdb.
func (b Bolt) GetMeta(serverName string) (meta Meta, found bool, err error) {
err = b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(metabucket))
v := bkt.Get([]byte(serverName))
if len(v) == 0 {
found = false
return nil
}
if e := json.Unmarshal(v, &meta); e != nil {
return e
}
found = true
return nil
})
return
}
// EnsureBuckets puts a Meta information and create a buket that holds changelogs.
func (b Bolt) EnsureBuckets(meta Meta) error {
jsonBytes, err := json.Marshal(meta)
if err != nil {
return fmt.Errorf("Failed to marshal to JSON: %s", err)
}
return b.db.Update(func(tx *bolt.Tx) error {
b.Log.Debugf("Put to meta: %s", meta.Name)
bkt := tx.Bucket([]byte(metabucket))
if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
return err
}
// re-create a bucket (bucket name: servername)
bkt = tx.Bucket([]byte(meta.Name))
if bkt != nil {
b.Log.Debugf("Delete bucket: %s", meta.Name)
if err := tx.DeleteBucket([]byte(meta.Name)); err != nil {
return err
}
b.Log.Debugf("Bucket deleted: %s", meta.Name)
}
b.Log.Debugf("Create bucket: %s", meta.Name)
if _, err := tx.CreateBucket([]byte(meta.Name)); err != nil {
return err
}
b.Log.Debugf("Bucket created: %s", meta.Name)
return nil
})
}
// PrettyPrint is for debuging
func (b Bolt) PrettyPrint(meta Meta) error {
return b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(metabucket))
v := bkt.Get([]byte(meta.Name))
b.Log.Debugf("key:%s, value:%s", meta.Name, v)
bkt = tx.Bucket([]byte(meta.Name))
c := bkt.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
b.Log.Debugf("key:%s, len: %d, %s...",
k, len(v), util.Truncate(string(v), 30))
}
return nil
})
}
// GetChangelog get the changelgo of specified packName from the Bucket
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
err = b.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Faild to get Bucket: %s", servername)
}
v := bkt.Get([]byte(packName))
if v == nil {
changelog = ""
return nil
}
changelog = string(v)
return nil
})
return
}
// PutChangelog put the changelgo of specified packName into the Bucket
func (b Bolt) PutChangelog(servername, packName, changelog string) error {
return b.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
return fmt.Errorf("Faild to get Bucket: %s", servername)
}
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
return err
}
return nil
})
}

134
cache/bolt_test.go vendored Normal file
View File

@@ -0,0 +1,134 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cache
import (
"os"
"reflect"
"testing"
"github.com/Sirupsen/logrus"
"github.com/boltdb/bolt"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
const path = "/tmp/vuls-test-cache-11111111.db"
const servername = "server1"
var meta = Meta{
Name: servername,
Distro: config.Distro{
Family: "ubuntu",
Release: "16.04",
},
Packs: []models.PackageInfo{
{
Name: "apt",
Version: "1",
},
},
}
func TestSetupBolt(t *testing.T) {
log := logrus.NewEntry(&logrus.Logger{})
err := SetupBolt(path, log)
if err != nil {
t.Errorf("Failed to setup bolt: %s", err)
}
defer os.Remove(path)
if err := DB.Close(); err != nil {
t.Errorf("Failed to close bolt: %s", err)
}
// check if meta bucket exists
db, err := bolt.Open(path, 0600, nil)
if err != nil {
t.Errorf("Failed to open bolt: %s", err)
}
db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(metabucket))
if bkt == nil {
t.Errorf("Meta bucket nof found")
}
return nil
})
}
func TestEnsureBuckets(t *testing.T) {
log := logrus.NewEntry(&logrus.Logger{})
if err := SetupBolt(path, log); err != nil {
t.Errorf("Failed to setup bolt: %s", err)
}
if err := DB.EnsureBuckets(meta); err != nil {
t.Errorf("Failed to ensure buckets: %s", err)
}
defer os.Remove(path)
m, found, err := DB.GetMeta(servername)
if err != nil {
t.Errorf("Failed to get meta: %s", err)
}
if !found {
t.Errorf("Not Found in meta")
}
if !reflect.DeepEqual(meta, m) {
t.Errorf("expected %v, actual %v", meta, m)
}
if err := DB.Close(); err != nil {
t.Errorf("Failed to close bolt: %s", err)
}
db, err := bolt.Open(path, 0600, nil)
if err != nil {
t.Errorf("Failed to open bolt: %s", err)
}
db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket([]byte(servername))
if bkt == nil {
t.Errorf("Meta bucket nof found")
}
return nil
})
}
func TestPutGetChangelog(t *testing.T) {
clog := "changelog-text"
log := logrus.NewEntry(&logrus.Logger{})
if err := SetupBolt(path, log); err != nil {
t.Errorf("Failed to setup bolt: %s", err)
}
defer os.Remove(path)
if err := DB.EnsureBuckets(meta); err != nil {
t.Errorf("Failed to ensure buckets: %s", err)
}
if err := DB.PutChangelog(servername, "apt", clog); err != nil {
t.Errorf("Failed to put changelog: %s", err)
}
if actual, err := DB.GetChangelog(servername, "apt"); err != nil {
t.Errorf("Failed to get changelog: %s", err)
} else {
if actual != clog {
t.Errorf("changelog is not same. e: %s, a: %s", clog, actual)
}
}
}

56
cache/db.go vendored Normal file
View File

@@ -0,0 +1,56 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cache
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// DB has a cache instance
var DB Cache
const metabucket = "changelog-meta"
// Cache is a interface of cache
type Cache interface {
Close() error
GetMeta(string) (Meta, bool, error)
EnsureBuckets(Meta) error
PrettyPrint(Meta) error
GetChangelog(string, string) (string, error)
PutChangelog(string, string, string) error
}
// Meta holds a server name, distro information of the scanned server and
// package information that was collected at the last scan.
type Meta struct {
Name string
Distro config.Distro
Packs []models.PackageInfo
}
// FindPack search a PackageInfo
func (m Meta) FindPack(name string) (pack models.PackageInfo, found bool) {
for _, p := range m.Packs {
if name == p.Name {
return p, true
}
}
return pack, false
}

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"io/ioutil"
"os"
@@ -26,7 +27,6 @@ import (
"github.com/Sirupsen/logrus"
"github.com/google/subcommands"
"golang.org/x/net/context"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/scan"
@@ -97,6 +97,7 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
}
c.Conf.Debug = p.debug
c.Conf.SSHExternal = p.sshExternal
err = c.Load(p.configPath, keyPass)
if err != nil {
@@ -150,7 +151,10 @@ func (p *ConfigtestCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interfa
}
Log.Info("Detecting Server/Contianer OS... ")
scan.InitServers(Log)
if err := scan.InitServers(Log); err != nil {
Log.Errorf("Failed to init servers: %s", err)
return subcommands.ExitFailure
}
Log.Info("Checking sudo configuration... ")
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"fmt"
"os"
@@ -25,7 +26,6 @@ import (
"text/template"
"github.com/google/subcommands"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
ps "github.com/kotakanbe/go-pingscanner"
@@ -100,7 +100,7 @@ notifyUsers = ["@username"]
[mail]
smtpAddr = "smtp.gmail.com"
smtpPort = "465"
smtpPort = "587"
user = "username"
password = "password"
from = "from@address.com"
@@ -115,7 +115,9 @@ subjectPrefix = "[vuls]"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#containers = ["${running}"]
#ignoreCves = ["CVE-2014-6271"]
#optional = [
# ["key", "value"],
#]
@@ -131,7 +133,9 @@ host = "{{$ip}}"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#dependencyCheckXMLPath = "/tmp/dependency-check-report.xml"
#containers = ["${running}"]
#ignoreCves = ["CVE-2014-0160"]
#optional = [
# ["key", "value"],
#]

View File

@@ -18,27 +18,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/db"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/report"
"github.com/google/subcommands"
)
// HistoryCmd is Subcommand of list scanned results
type HistoryCmd struct {
debug bool
debugSQL bool
dbpath string
debug bool
debugSQL bool
resultsDir string
}
// Name return subcommand name
@@ -53,7 +50,7 @@ func (*HistoryCmd) Synopsis() string {
func (*HistoryCmd) Usage() string {
return `history:
history
[-dbpath=/path/to/vuls.sqlite3]
[-results-dir=/path/to/results]
`
}
@@ -62,47 +59,41 @@ func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
wd, _ := os.Getwd()
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
}
// Execute execute
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
c.Conf.ResultsDir = p.resultsDir
// _, err := scanHistories()
histories, err := scanHistories()
if err != nil {
logrus.Error("Failed to select scan histories: ", err)
var err error
var jsonDirs report.JSONDirs
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
return subcommands.ExitFailure
}
const timeLayout = "2006-01-02 15:04"
for _, history := range histories {
names := []string{}
for _, result := range history.ScanResults {
if 0 < len(result.Container.ContainerID) {
names = append(names, result.Container.Name)
} else {
names = append(names, result.ServerName)
}
for _, d := range jsonDirs {
var files []os.FileInfo
if files, err = ioutil.ReadDir(d); err != nil {
return subcommands.ExitFailure
}
fmt.Printf("%-3d %s scanned %d servers: %s\n",
history.ID,
history.ScannedAt.Format(timeLayout),
len(history.ScanResults),
strings.Join(names, ", "),
var hosts []string
for _, f := range files {
if filepath.Ext(f.Name()) != ".json" {
continue
}
fileBase := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
hosts = append(hosts, fileBase)
}
splitPath := strings.Split(d, string(os.PathSeparator))
timeStr := splitPath[len(splitPath)-1]
fmt.Printf("%s scanned %d servers: %s\n",
timeStr,
len(hosts),
strings.Join(hosts, ", "),
)
}
return subcommands.ExitSuccess
}
func scanHistories() (histories []models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return histories, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
}
histories, err = db.SelectScanHistories()
return
}

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"os"
"path/filepath"
@@ -27,7 +28,6 @@ import (
"github.com/future-architect/vuls/scan"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
"golang.org/x/net/context"
)
// PrepareCmd is Subcommand of host discovery mode
@@ -38,7 +38,7 @@ type PrepareCmd struct {
askSudoPassword bool
askKeyPassword bool
useUnattendedUpgrades bool
sshExternal bool
}
// Name return subcommand name
@@ -46,7 +46,6 @@ func (*PrepareCmd) Name() string { return "prepare" }
// Synopsis return synopsis
func (*PrepareCmd) Synopsis() string {
// return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
return `Install required packages to scan.
CentOS: yum-plugin-security, yum-plugin-changelog
Amazon: None
@@ -63,8 +62,9 @@ func (*PrepareCmd) Usage() string {
[-config=/path/to/config.toml]
[-ask-key-password]
[-debug]
[-ssh-external]
[SERVER]...
[SERVER]...
`
}
@@ -89,15 +89,15 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
&p.askSudoPassword,
"ask-sudo-password",
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
&p.sshExternal,
"ssh-external",
false,
"[Deprecated] For Ubuntu, install unattended-upgrades",
)
"Use external ssh command. Default: Use the Go native implementation")
}
// Execute execute
@@ -112,7 +112,7 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
}
}
if p.askSudoPassword {
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
return subcommands.ExitFailure
}
@@ -143,22 +143,29 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
}
c.Conf.Debug = p.debug
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
c.Conf.SSHExternal = p.sshExternal
// Set up custom logger
logger := util.NewCustomLogger(c.ServerInfo{})
logger.Info("Detecting OS... ")
scan.InitServers(logger)
if err := scan.InitServers(logger); err != nil {
logger.Errorf("Failed to init servers: %s", err)
return subcommands.ExitFailure
}
logger.Info("Checking sudo configuration... ")
if err := scan.CheckIfSudoNoPasswd(logger); err != nil {
logger.Errorf("Failed to sudo with nopassword via SSH. Define NOPASSWD in /etc/sudoers on target servers")
return subcommands.ExitFailure
}
logger.Info("Installing...")
if errs := scan.Prepare(); 0 < len(errs) {
for _, e := range errs {
logger.Errorf("Failed: %s", e)
logger.Errorf("Failed to prepare: %s", e)
}
return subcommands.ExitFailure
}
logger.Info("Success")
return subcommands.ExitSuccess
}

View File

@@ -18,22 +18,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/db"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/scan"
"github.com/future-architect/vuls/util"
"github.com/google/subcommands"
"golang.org/x/net/context"
"github.com/k0kubun/pp"
)
// ScanCmd is Subcommand of host discovery mode
@@ -44,9 +45,11 @@ type ScanCmd struct {
configPath string
dbpath string
resultsDir string
cvedbtype string
cvedbpath string
cveDictionaryURL string
cacheDBPath string
cvssScoreOver float64
ignoreUnscoredCves bool
@@ -55,6 +58,9 @@ type ScanCmd struct {
askSudoPassword bool
askKeyPassword bool
containersOnly bool
skipBroken bool
// reporting
reportSlack bool
reportMail bool
@@ -62,6 +68,7 @@ type ScanCmd struct {
reportText bool
reportS3 bool
reportAzureBlob bool
reportXML bool
awsProfile string
awsS3Bucket string
@@ -71,9 +78,6 @@ type ScanCmd struct {
azureKey string
azureContainer string
useYumPluginSecurity bool
useUnattendedUpgrades bool
sshExternal bool
}
@@ -89,18 +93,23 @@ func (*ScanCmd) Usage() string {
scan
[-lang=en|ja]
[-config=/path/to/config.toml]
[-dbpath=/path/to/vuls.sqlite3]
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
[-results-dir=/path/to/results]
[-cve-dictionary-dbtype=sqlite3|mysql]
[-cve-dictionary-dbpath=/path/to/cve.sqlite3 or mysql connection string]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cache-dbpath=/path/to/cache.db]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ssh-external]
[-containers-only]
[-skip-broken]
[-report-azure-blob]
[-report-json]
[-report-mail]
[-report-s3]
[-report-slack]
[-report-text]
[-report-xml]
[-http-proxy=http://192.168.0.1:8080]
[-ask-key-password]
[-debug]
@@ -127,8 +136,14 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
f.StringVar(
&p.cvedbtype,
"cve-dictionary-dbtype",
"sqlite3",
"DB type for fetching CVE dictionary (sqlite3 or mysql)")
f.StringVar(
&p.cvedbpath,
@@ -143,6 +158,13 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
defaultURL,
"http://CVE.Dictionary")
defaultCacheDBPath := filepath.Join(wd, "cache.db")
f.StringVar(
&p.cacheDBPath,
"cache-dbpath",
defaultCacheDBPath,
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
f.Float64Var(
&p.cvssScoreOver,
"cvss-over",
@@ -161,6 +183,18 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
false,
"Use external ssh command. Default: Use the Go native implementation")
f.BoolVar(
&p.containersOnly,
"containers-only",
false,
"Scan containers only. Default: Scan both of hosts and containers")
f.BoolVar(
&p.skipBroken,
"skip-broken",
false,
"[For CentOS] yum update changelog with --skip-broken option")
f.StringVar(
&p.httpProxy,
"http-proxy",
@@ -180,6 +214,11 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
false,
fmt.Sprintf("Write report to text files (%s/results/current)", wd),
)
f.BoolVar(&p.reportXML,
"report-xml",
false,
fmt.Sprintf("Write report to XML files (%s/results/current)", wd),
)
f.BoolVar(&p.reportS3,
"report-s3",
@@ -193,7 +232,7 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
f.BoolVar(&p.reportAzureBlob,
"report-azure-blob",
false,
"Write report to S3 (container/yyyyMMdd_HHmm/servername.json)",
"Write report to Azure Storage blob (container/yyyyMMdd_HHmm/servername.json)",
)
f.StringVar(&p.azureAccount, "azure-account", "", "Azure account name to use. AZURE_STORAGE_ACCOUNT environment variable is used if not specified")
f.StringVar(&p.azureKey, "azure-key", "", "Azure account key to use. AZURE_STORAGE_ACCESS_KEY environment variable is used if not specified")
@@ -210,23 +249,8 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
&p.askSudoPassword,
"ask-sudo-password",
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useYumPluginSecurity,
"use-yum-plugin-security",
false,
"[Deprecated] For CentOS 5. Scan by yum-plugin-security or not (use yum check-update by default)",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Deprecated] For Ubuntu. Scan by unattended-upgrades or not (use apt-get upgrade --dry-run by default)",
)
}
// Execute execute
@@ -241,10 +265,11 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
}
if p.askSudoPassword {
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication")
logrus.Errorf("[Deprecated] -ask-sudo-password WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on target servers and use SSH key-based authentication")
return subcommands.ExitFailure
}
c.Conf.Debug = p.debug
err = c.Load(p.configPath, keyPass)
if err != nil {
logrus.Errorf("Error loading %s, %s", p.configPath, err)
@@ -254,7 +279,9 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
logrus.Info("Start scanning")
logrus.Infof("config: %s", p.configPath)
if p.cvedbpath != "" {
logrus.Infof("cve-dictionary: %s", p.cvedbpath)
if p.cvedbtype == "sqlite3" {
logrus.Infof("cve-dictionary: %s", p.cvedbpath)
}
} else {
logrus.Infof("cve-dictionary: %s", p.cveDictionaryURL)
}
@@ -295,13 +322,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
if 0 < len(servernames) {
c.Conf.Servers = target
}
logrus.Debugf("%s", pp.Sprintf("%v", target))
c.Conf.Lang = p.lang
c.Conf.Debug = p.debug
c.Conf.DebugSQL = p.debugSQL
// logger
Log := util.NewCustomLogger(c.ServerInfo{})
scannedAt := time.Now()
// report
reports := []report.ResultWriter{
@@ -315,10 +343,13 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
reports = append(reports, report.MailWriter{})
}
if p.reportJSON {
reports = append(reports, report.JSONWriter{})
reports = append(reports, report.JSONWriter{ScannedAt: scannedAt})
}
if p.reportText {
reports = append(reports, report.TextFileWriter{})
reports = append(reports, report.TextFileWriter{ScannedAt: scannedAt})
}
if p.reportXML {
reports = append(reports, report.XMLWriter{ScannedAt: scannedAt})
}
if p.reportS3 {
c.Conf.AwsRegion = p.awsRegion
@@ -333,17 +364,17 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
if p.reportAzureBlob {
c.Conf.AzureAccount = p.azureAccount
if c.Conf.AzureAccount == "" {
if len(c.Conf.AzureAccount) == 0 {
c.Conf.AzureAccount = os.Getenv("AZURE_STORAGE_ACCOUNT")
}
c.Conf.AzureKey = p.azureKey
if c.Conf.AzureKey == "" {
if len(c.Conf.AzureKey) == 0 {
c.Conf.AzureKey = os.Getenv("AZURE_STORAGE_ACCESS_KEY")
}
c.Conf.AzureContainer = p.azureContainer
if c.Conf.AzureContainer == "" {
if len(c.Conf.AzureContainer) == 0 {
Log.Error("Azure storage container name is requied with --azure-container option")
return subcommands.ExitUsageError
}
@@ -355,15 +386,17 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
reports = append(reports, report.AzureBlobWriter{})
}
c.Conf.DBPath = p.dbpath
c.Conf.ResultsDir = p.resultsDir
c.Conf.CveDBType = p.cvedbtype
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDictionaryURL = p.cveDictionaryURL
c.Conf.CacheDBPath = p.cacheDBPath
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.SSHExternal = p.sshExternal
c.Conf.HTTPProxy = p.httpProxy
c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
c.Conf.ContainersOnly = p.containersOnly
c.Conf.SkipBroken = p.skipBroken
Log.Info("Validating Config...")
if !c.Conf.Validate() {
@@ -377,7 +410,10 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
}
Log.Info("Detecting Server/Contianer OS... ")
scan.InitServers(Log)
if err := scan.InitServers(Log); err != nil {
Log.Errorf("Failed to init servers: %s", err)
return subcommands.ExitFailure
}
Log.Info("Checking sudo configuration... ")
if err := scan.CheckIfSudoNoPasswd(Log); err != nil {
@@ -402,21 +438,6 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
return subcommands.ExitFailure
}
Log.Info("Insert to DB...")
if err := db.OpenDB(); err != nil {
Log.Errorf("Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
return subcommands.ExitFailure
}
if err := db.MigrateDB(); err != nil {
Log.Errorf("Failed to migrate. err: %s", err)
return subcommands.ExitFailure
}
if err := db.Insert(scanResults); err != nil {
Log.Fatalf("Failed to insert. dbpath: %s, err: %s", c.Conf.DBPath, err)
return subcommands.ExitFailure
}
Log.Info("Reporting...")
filtered := scanResults.FilterByCvssOver()
for _, w := range reports {

View File

@@ -18,26 +18,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package commands
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/report"
"github.com/google/subcommands"
"golang.org/x/net/context"
)
// TuiCmd is Subcommand of host discovery mode
type TuiCmd struct {
lang string
debugSQL bool
dbpath string
lang string
debugSQL bool
resultsDir string
}
// Name return subcommand name
@@ -49,7 +47,7 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites
// Usage return usage
func (*TuiCmd) Usage() string {
return `tui:
tui [-dbpath=/path/to/vuls.sqlite3]
tui [-results-dir=/path/to/results]
`
}
@@ -61,24 +59,34 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
wd, _ := os.Getwd()
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath,
fmt.Sprintf("/path/to/sqlite3 (default: %s)", defaultDBPath))
defaultResultsDir := filepath.Join(wd, "results")
f.StringVar(&p.resultsDir, "results-dir", defaultResultsDir, "/path/to/results")
}
// Execute execute
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.Lang = "en"
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
c.Conf.ResultsDir = p.resultsDir
historyID := ""
var jsonDirName string
var err error
if 0 < len(f.Args()) {
if _, err := strconv.Atoi(f.Args()[0]); err != nil {
log.Errorf("First Argument have to be scan_histores record ID: %s", err)
var jsonDirs report.JSONDirs
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
return subcommands.ExitFailure
}
for _, d := range jsonDirs {
splitPath := strings.Split(d, string(os.PathSeparator))
if splitPath[len(splitPath)-1] == f.Args()[0] {
jsonDirName = f.Args()[0]
break
}
}
if len(jsonDirName) == 0 {
log.Errorf("First Argument have to be JSON directory name : %s", err)
return subcommands.ExitFailure
}
historyID = f.Args()[0]
} else {
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
@@ -89,9 +97,9 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
}
fields := strings.Fields(string(bytes))
if 0 < len(fields) {
historyID = fields[0]
jsonDirName = fields[0]
}
}
}
return report.RunTui(historyID)
return report.RunTui(jsonDirName)
}

View File

@@ -44,11 +44,15 @@ type Config struct {
CvssScoreOver float64
IgnoreUnscoredCves bool
SSHExternal bool
SSHExternal bool
ContainersOnly bool
SkipBroken bool
HTTPProxy string `valid:"url"`
DBPath string
CveDBPath string
HTTPProxy string `valid:"url"`
ResultsDir string
CveDBType string
CveDBPath string
CacheDBPath string
AwsProfile string
AwsRegion string
@@ -60,25 +64,42 @@ type Config struct {
// CpeNames []string
// SummaryMode bool
UseYumPluginSecurity bool
UseUnattendedUpgrades bool
}
// Validate configuration
func (c Config) Validate() bool {
errs := []error{}
if len(c.DBPath) != 0 {
if ok, _ := valid.IsFilePath(c.DBPath); !ok {
if len(c.ResultsDir) != 0 {
if ok, _ := valid.IsFilePath(c.ResultsDir); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB path must be a *Absolute* file path. dbpath: %s", c.DBPath))
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.ResultsDir))
}
}
if len(c.CveDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
// If no valid DB type is set, default to sqlite3
if c.CveDBType == "" {
c.CveDBType = "sqlite3"
}
if c.CveDBType != "sqlite3" && c.CveDBType != "mysql" {
errs = append(errs, fmt.Errorf(
"CVE DB type must be either 'sqlite3' or 'mysql'. -cve-dictionary-dbtype: %s", c.CveDBType))
}
if c.CveDBType == "sqlite3" {
if len(c.CveDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. -cve-dictionary-dbpath: %s", c.CveDBPath))
}
}
}
if len(c.CacheDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. dbpath: %s", c.CveDBPath))
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", c.CacheDBPath))
}
}
@@ -181,7 +202,6 @@ type SlackConf struct {
// Validate validates configuration
func (c *SlackConf) Validate() (errs []error) {
if !c.UseThisTime {
return
}
@@ -221,18 +241,43 @@ type ServerInfo struct {
KeyPath string
KeyPassword string
CpeNames []string
CpeNames []string
DependencyCheckXMLPath string
// Container Names or IDs
Containers []string
IgnoreCves []string
// Optional key-value set that will be outputted to JSON
Optional [][]interface{}
// For CentOS, RHEL, Amazon
Enablerepo string
// used internal
LogMsgAnsiColor string // DebugLog Color
Container Container
Family string
Distro Distro
}
// GetServerName returns ServerName if this serverInfo is about host.
// If this serverInfo is abount a container, returns containerID@ServerName
func (s ServerInfo) GetServerName() string {
if len(s.Container.ContainerID) == 0 {
return s.ServerName
}
return fmt.Sprintf("%s@%s", s.Container.ContainerID, s.ServerName)
}
// Distro has distribution info
type Distro struct {
Family string
Release string
}
func (l Distro) String() string {
return fmt.Sprintf("%s %s", l.Family, l.Release)
}
// IsContainer returns whether this ServerInfo is about container

View File

@@ -20,10 +20,11 @@ package config
import (
"fmt"
"os"
"strings"
"github.com/BurntSushi/toml"
log "github.com/Sirupsen/logrus"
"github.com/k0kubun/pp"
"github.com/future-architect/vuls/contrib/owasp-dependency-check/parser"
)
// TOMLLoader loads config
@@ -31,7 +32,11 @@ type TOMLLoader struct {
}
// Load load the configuraiton TOML file specified by path arg.
func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
func (c TOMLLoader) Load(pathToToml, keyPass string) error {
if Conf.Debug {
log.SetLevel(log.DebugLevel)
}
var conf Config
if _, err := toml.DecodeFile(pathToToml, &conf); err != nil {
log.Error("Load config failed", err)
@@ -51,7 +56,6 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
i := 0
for name, v := range conf.Servers {
if 0 < len(v.KeyPassword) {
log.Warn("[Deprecated] KEYPASSWORD IN CONFIG FILE ARE UNSECURE. REMOVE THEM IMMEDIATELY FOR A SECURITY REASONS. THEY WILL BE REMOVED IN A FUTURE RELEASE.")
}
@@ -68,7 +72,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
}
s.Host = v.Host
if s.Host == "" {
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", name)
}
@@ -82,7 +86,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
}
s.KeyPath = v.KeyPath
if s.KeyPath == "" {
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
if s.KeyPath != "" {
@@ -94,7 +98,7 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
// s.KeyPassword = keyPass
s.KeyPassword = v.KeyPassword
if s.KeyPassword == "" {
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
}
@@ -103,11 +107,42 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
s.CpeNames = d.CpeNames
}
s.DependencyCheckXMLPath = v.DependencyCheckXMLPath
if len(s.DependencyCheckXMLPath) == 0 {
s.DependencyCheckXMLPath = d.DependencyCheckXMLPath
}
// Load CPEs from OWASP Dependency Check XML
if len(s.DependencyCheckXMLPath) != 0 {
cpes, err := parser.Parse(s.DependencyCheckXMLPath)
if err != nil {
return fmt.Errorf(
"Failed to read OWASP Dependency Check XML: %s", err)
}
log.Infof("Loaded from OWASP Dependency Check XML: %s",
s.ServerName)
s.CpeNames = append(s.CpeNames, cpes...)
}
s.Containers = v.Containers
if len(s.Containers) == 0 {
s.Containers = d.Containers
}
s.IgnoreCves = v.IgnoreCves
for _, cve := range d.IgnoreCves {
found := false
for _, c := range s.IgnoreCves {
if cve == c {
found = true
break
}
}
if !found {
s.IgnoreCves = append(s.IgnoreCves, cve)
}
}
s.Optional = v.Optional
for _, dkv := range d.Optional {
found := false
@@ -122,13 +157,28 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) (err error) {
}
}
s.Enablerepo = v.Enablerepo
if len(s.Enablerepo) == 0 {
s.Enablerepo = d.Enablerepo
}
if len(s.Enablerepo) != 0 {
for _, repo := range strings.Split(s.Enablerepo, ",") {
switch repo {
case "base", "updates":
// nop
default:
return fmt.Errorf(
"For now, enablerepo have to be base or updates: %s, servername: %s",
s.Enablerepo, name)
}
}
}
s.LogMsgAnsiColor = Colors[i%len(Colors)]
i++
servers[name] = s
}
log.Debug("Config loaded")
log.Debugf("%s", pp.Sprintf("%v", servers))
Conf.Servers = servers
return
return nil
}

View File

@@ -0,0 +1,64 @@
package parser
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
)
type analysis struct {
Dependencies []dependency `xml:"dependencies>dependency"`
}
type dependency struct {
Identifiers []identifier `xml:"identifiers>identifier"`
}
type identifier struct {
Name string `xml:"name"`
Type string `xml:"type,attr"`
}
func appendIfMissing(slice []string, str string) []string {
for _, s := range slice {
if s == str {
return slice
}
}
return append(slice, str)
}
// Parse parses XML and collect list of cpe
func Parse(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return []string{}, fmt.Errorf("Failed to open: %s", err)
}
defer file.Close()
b, err := ioutil.ReadAll(file)
if err != nil {
return []string{}, fmt.Errorf("Failed to read: %s", err)
}
var anal analysis
if err := xml.Unmarshal(b, &anal); err != nil {
fmt.Errorf("Failed to unmarshal: %s", err)
}
cpes := []string{}
for _, d := range anal.Dependencies {
for _, ident := range d.Identifiers {
if ident.Type == "cpe" {
name := strings.TrimPrefix(ident.Name, "(")
name = strings.TrimSuffix(name, ")")
cpes = appendIfMissing(cpes, name)
}
}
}
sort.Strings(cpes)
return cpes, nil
}

View File

@@ -49,7 +49,7 @@ func (api *cvedictClient) initialize() {
func (api cvedictClient) CheckHealth() (ok bool, err error) {
if config.Conf.CveDBPath != "" {
log.Debugf("get cve-dictionary from sqlite3")
log.Debugf("get cve-dictionary from %s", config.Conf.CveDBType)
return true, nil
}
@@ -59,7 +59,7 @@ func (api cvedictClient) CheckHealth() (ok bool, err error) {
var resp *http.Response
resp, _, errs = gorequest.New().SetDebug(config.Conf.Debug).Get(url).End()
// resp, _, errs = gorequest.New().Proxy(api.httpProxy).Get(url).End()
if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return false, fmt.Errorf("Failed to request to CVE server. url: %s, errs: %v", url, errs)
}
return true, nil
@@ -135,8 +135,10 @@ func (api cvedictClient) FetchCveDetails(cveIDs []string) (cveDetails cve.CveDet
}
func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails cve.CveDetails, err error) {
log.Debugf("open cve-dictionary db")
log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
cveconfig.Conf.DBPath = config.Conf.CveDBPath
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
if err := cvedb.OpenDB(); err != nil {
return []cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
@@ -186,41 +188,6 @@ func (api cvedictClient) httpGet(key, url string, resChan chan<- response, errCh
}
}
// func (api cvedictClient) httpGet(key, url string, query map[string]string, resChan chan<- response, errChan chan<- error) {
// var body string
// var errs []error
// var resp *http.Response
// f := func() (err error) {
// req := gorequest.New().SetDebug(true).Proxy(api.httpProxy).Get(url)
// for key := range query {
// req = req.Query(fmt.Sprintf("%s=%s", key, query[key])).Set("Content-Type", "application/x-www-form-urlencoded")
// }
// pp.Println(req)
// resp, body, errs = req.End()
// if len(errs) > 0 || resp.StatusCode != 200 {
// errChan <- fmt.Errorf("HTTP error. errs: %v, url: %s", errs, url)
// }
// return nil
// }
// notify := func(err error, t time.Duration) {
// log.Warnf("Failed to get. retrying in %s seconds. err: %s", t, err)
// }
// err := backoff.RetryNotify(f, backoff.NewExponentialBackOff(), notify)
// if err != nil {
// errChan <- fmt.Errorf("HTTP Error %s", err)
// }
// // resChan <- body
// cveDetail := cve.CveDetail{}
// if err := json.Unmarshal([]byte(body), &cveDetail); err != nil {
// errChan <- fmt.Errorf("Failed to Unmarshall. body: %s, err: %s", body, err)
// }
// resChan <- response{
// key,
// cveDetail,
// }
// }
type responseGetCveDetailByCpeName struct {
CpeName string
CveDetails []cve.CveDetail
@@ -252,7 +219,7 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
req = req.Send(fmt.Sprintf("%s=%s", key, query[key])).Type("json")
}
resp, body, errs = req.End()
if len(errs) > 0 || resp == nil || resp.StatusCode != 200 {
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
return fmt.Errorf("HTTP POST error: %v, url: %s, resp: %v", errs, url, resp)
}
return nil
@@ -274,8 +241,11 @@ func (api cvedictClient) httpPost(key, url string, query map[string]string) ([]c
}
func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) ([]cve.CveDetail, error) {
log.Debugf("open cve-dictionary db")
log.Debugf("open cve-dictionary db (%s)", config.Conf.CveDBType)
cveconfig.Conf.DBType = config.Conf.CveDBType
cveconfig.Conf.DBPath = config.Conf.CveDBPath
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
if err := cvedb.OpenDB(); err != nil {
return []cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)

324
db/db.go
View File

@@ -1,324 +0,0 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db
import (
"fmt"
"sort"
"strconv"
"time"
"github.com/future-architect/vuls/config"
m "github.com/future-architect/vuls/models"
"github.com/jinzhu/gorm"
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
cve "github.com/kotakanbe/go-cve-dictionary/models"
)
var db *gorm.DB
// OpenDB opens Database
func OpenDB() (err error) {
db, err = gorm.Open("sqlite3", config.Conf.DBPath)
if err != nil {
err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
return
}
db.LogMode(config.Conf.DebugSQL)
return
}
// MigrateDB migrates Database
func MigrateDB() error {
if err := db.AutoMigrate(
&m.ScanHistory{},
&m.ScanResult{},
// &m.NWLink{},
&m.Container{},
&m.CveInfo{},
&m.CpeName{},
&m.PackageInfo{},
&m.DistroAdvisory{},
&cve.CveDetail{},
&cve.Jvn{},
&cve.Nvd{},
&cve.Reference{},
&cve.Cpe{},
).Error; err != nil {
return fmt.Errorf("Failed to migrate. err: %s", err)
}
errMsg := "Failed to create index. err: %s"
// if err := db.Model(&m.NWLink{}).
// AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
// return fmt.Errorf(errMsg, err)
// }
if err := db.Model(&m.Container{}).
AddIndex("idx_containers_scan_result_id", "scan_result_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&m.CveInfo{}).
AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&m.CpeName{}).
AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&m.PackageInfo{}).
AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&m.DistroAdvisory{}).
//TODO check table name
AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.CveDetail{}).
AddIndex("idx_cve_details_cve_info_id", "cve_info_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.CveDetail{}).
AddIndex("idx_cve_details_cveid", "cve_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Nvd{}).
AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Jvn{}).
AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Cpe{}).
AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Reference{}).
AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Cpe{}).
AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
if err := db.Model(&cve.Reference{}).
AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
return fmt.Errorf(errMsg, err)
}
return nil
}
// Insert inserts scan results into DB
func Insert(results []m.ScanResult) error {
for _, r := range results {
r.KnownCves = resetGormIDs(r.KnownCves)
r.UnknownCves = resetGormIDs(r.UnknownCves)
}
history := m.ScanHistory{
ScanResults: results,
ScannedAt: time.Now(),
}
db = db.Set("gorm:save_associations", false)
if err := db.Create(&history).Error; err != nil {
return err
}
for _, scanResult := range history.ScanResults {
scanResult.ScanHistoryID = history.ID
if err := db.Create(&scanResult).Error; err != nil {
return err
}
scanResult.Container.ScanResultID = scanResult.ID
if err := db.Create(&scanResult.Container).Error; err != nil {
return err
}
if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
return err
}
if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
return err
}
}
return nil
}
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
for _, cveInfo := range infos {
cveInfo.ScanResultID = scanResultID
if err := db.Create(&cveInfo).Error; err != nil {
return err
}
for _, pack := range cveInfo.Packages {
pack.CveInfoID = cveInfo.ID
if err := db.Create(&pack).Error; err != nil {
return err
}
}
for _, distroAdvisory := range cveInfo.DistroAdvisories {
distroAdvisory.CveInfoID = cveInfo.ID
if err := db.Create(&distroAdvisory).Error; err != nil {
return err
}
}
for _, cpeName := range cveInfo.CpeNames {
cpeName.CveInfoID = cveInfo.ID
if err := db.Create(&cpeName).Error; err != nil {
return err
}
}
db = db.Set("gorm:save_associations", true)
cveDetail := cveInfo.CveDetail
cveDetail.CveInfoID = cveInfo.ID
if err := db.Create(&cveDetail).Error; err != nil {
return err
}
db = db.Set("gorm:save_associations", false)
}
return nil
}
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
for i := range infos {
infos[i].CveDetail.ID = 0
// NVD
infos[i].CveDetail.Nvd.ID = 0
for j := range infos[i].CveDetail.Nvd.Cpes {
infos[i].CveDetail.Nvd.Cpes[j].ID = 0
}
for j := range infos[i].CveDetail.Nvd.References {
infos[i].CveDetail.Nvd.References[j].ID = 0
}
// JVN
infos[i].CveDetail.Jvn.ID = 0
for j := range infos[i].CveDetail.Jvn.Cpes {
infos[i].CveDetail.Jvn.Cpes[j].ID = 0
}
for j := range infos[i].CveDetail.Jvn.References {
infos[i].CveDetail.Jvn.References[j].ID = 0
}
//Packages
for j := range infos[i].Packages {
infos[i].Packages[j].ID = 0
infos[i].Packages[j].CveInfoID = 0
}
}
return infos
}
// SelectScanHistory select scan history from DB
func SelectScanHistory(historyID string) (m.ScanHistory, error) {
var err error
scanHistory := m.ScanHistory{}
if historyID == "" {
// select latest
db.Order("scanned_at desc").First(&scanHistory)
} else {
var id int
if id, err = strconv.Atoi(historyID); err != nil {
return m.ScanHistory{},
fmt.Errorf("historyID have to be numeric number: %s", err)
}
db.First(&scanHistory, id)
}
if scanHistory.ID == 0 {
return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
}
// results := []m.ScanResult{}
results := m.ScanResults{}
db.Model(&scanHistory).Related(&results, "ScanResults")
scanHistory.ScanResults = results
for i, r := range results {
// nw := []m.NWLink{}
// db.Model(&r).Related(&nw, "NWLinks")
// scanHistory.ScanResults[i].NWLinks = nw
di := m.Container{}
db.Model(&r).Related(&di, "Container")
scanHistory.ScanResults[i].Container = di
knownCves := selectCveInfos(&r, "KnownCves")
sort.Sort(m.CveInfos(knownCves))
scanHistory.ScanResults[i].KnownCves = knownCves
}
sort.Sort(scanHistory.ScanResults)
return scanHistory, nil
}
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
cveInfos := []m.CveInfo{}
db.Model(&result).Related(&cveInfos, fieldName)
for i, cveInfo := range cveInfos {
cveDetail := cve.CveDetail{}
db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
id := cveDetail.CveID
filledCveDetail := cvedb.Get(id, db)
cveInfos[i].CveDetail = filledCveDetail
packs := []m.PackageInfo{}
db.Model(&cveInfo).Related(&packs, "Packages")
cveInfos[i].Packages = packs
advisories := []m.DistroAdvisory{}
db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
cveInfos[i].DistroAdvisories = advisories
names := []m.CpeName{}
db.Model(&cveInfo).Related(&names, "CpeNames")
cveInfos[i].CpeNames = names
}
return cveInfos
}
// SelectScanHistories select latest scan history from DB
func SelectScanHistories() ([]m.ScanHistory, error) {
scanHistories := []m.ScanHistory{}
db.Order("scanned_at desc").Find(&scanHistories)
if len(scanHistories) == 0 {
return []m.ScanHistory{}, fmt.Errorf("No scanHistory records")
}
for i, history := range scanHistories {
results := m.ScanResults{}
db.Model(&history).Related(&results, "ScanResults")
scanHistories[i].ScanResults = results
for j, r := range results {
di := m.Container{}
db.Model(&r).Related(&di, "Container")
scanHistories[i].ScanResults[j].Container = di
}
}
return scanHistories, nil
}

110
glide.lock generated
View File

@@ -1,117 +1,123 @@
hash: 9683c87b3cf998e7fac1b12c4a94bf2bd18cb5422e9108539811546e703a439a
updated: 2016-07-12T16:20:45.462913061+09:00
hash: ca64aef6e9e94c7be91f79b88edb847363c8a5bd48da4ad27784e9342c8db6e2
updated: 2016-11-01T15:05:15.23083077+09:00
imports:
- name: github.com/asaskevich/govalidator
version: df81827fdd59d8b4fb93d8910b286ab7a3919520
version: 7b3beb6df3c42abd3509abfc3bcacc0fbfb7c877
- name: github.com/aws/aws-sdk-go
version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
version: 9e5bedb97b1cd85e53fd99209f93fd1a8a9f1df7
subpackages:
- aws
- aws/credentials
- aws/session
- service/s3
- aws/awserr
- aws/client
- aws/corehandlers
- aws/defaults
- aws/request
- private/endpoints
- aws/awsutil
- aws/client
- aws/client/metadata
- aws/signer/v4
- private/protocol
- private/protocol/restxml
- private/waiter
- aws/corehandlers
- aws/credentials
- aws/credentials/ec2rolecreds
- aws/credentials/endpointcreds
- aws/credentials/stscreds
- aws/defaults
- aws/ec2metadata
- private/protocol/rest
- aws/request
- aws/session
- aws/signer/v4
- private/endpoints
- private/protocol
- private/protocol/query
- private/protocol/xml/xmlutil
- private/protocol/query/queryutil
- private/protocol/rest
- private/protocol/restxml
- private/protocol/xml/xmlutil
- private/waiter
- service/s3
- service/sts
- name: github.com/Azure/azure-sdk-for-go
version: 58a13e378daf3b06e65925397185684b16321111
version: 9016164015faa51e549605e7b4b117f7de2aa6f9
subpackages:
- storage
- name: github.com/boltdb/bolt
version: 074dffcc83e9f421e261526d297cd93f22a34080
- name: github.com/BurntSushi/toml
version: ffaa107fbd880f6d18cd6fec9b511668dcad8639
version: 99064174e013895bbd9b025c31100bd1d9b590ca
- name: github.com/cenkalti/backoff
version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
version: b02f2bbce11d7ea6b97f282ef1771b0fe2f65ef3
- name: github.com/cheggaaa/pb
version: 04b234c80d661c663dbcebd52fc7218fdacc6d0c
version: ad4efe000aa550bb54918c06ebbadc0ff17687b9
- name: github.com/go-ini/ini
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
version: 6e4869b434bd001f6983749881c7ead3545887d8
- name: github.com/go-sql-driver/mysql
version: 2a6c6079c7eff49a7e9d641e109d922f124a3e4c
- name: github.com/google/subcommands
version: 1c7173745a6001f67d8d96ab4e178284c77f7759
version: a71b91e238406bd68766ee52db63bebedce0e9f6
- name: github.com/gosuri/uitable
version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
subpackages:
- util/strutil
- util/wordwrap
- name: github.com/howeyc/gopass
version: 66487b23f2880ba32e185121d2cd51a338ea069a
version: f5387c492211eb133053880d23dfae62aa14123d
- name: github.com/jinzhu/gorm
version: 613c0655691abb7691b70c5fda80a716d9e20b1b
version: c1b9cf186e4bcd8e5d566ef43f2ae2dfe22dc34e
subpackages:
- dialects/mysql
- name: github.com/jinzhu/inflection
version: 8f4d3a0d04ce0b7c0cf3126fb98524246d00d102
version: 74387dc39a75e970e7a3ae6a3386b5bd2e5c5cff
- name: github.com/jmespath/go-jmespath
version: 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
version: bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d
- name: github.com/jroimartin/gocui
version: 2dcda558bf18ec07c7065bf1eaf071b5305f7c0c
version: 357a541add9e311f7b67dfbaf92e28c71680a6b7
- name: github.com/k0kubun/pp
version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
- name: github.com/kotakanbe/go-cve-dictionary
version: 1a336b8ac785badfe89a175ee926d39574901232
version: 70989b6709c3102924ad8c8483e9bdc99bcb598b
subpackages:
- config
- db
- models
- log
- jvn
- log
- models
- nvd
- util
- name: github.com/kotakanbe/go-pingscanner
version: 58e188a3e4f6ab1a6371e33421e4502e26fa1e80
- name: github.com/kotakanbe/logrus-prefixed-formatter
version: f4f7d41649cf1e75e736884da8d05324aa76ea25
- name: github.com/mattn/go-colorable
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
version: 6c903ff4aa50920ca86087a280590b36b3152b9c
- name: github.com/mattn/go-isatty
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
- name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f
version: 737072b4e32b7a5018b4a7125da8d12de90e8045
- name: github.com/mattn/go-sqlite3
version: 38ee283dabf11c9cbdb968eebd79b1fa7acbabe6
version: e5a3c16c5c1d80b24f633e68aecd6b0702786d3d
- name: github.com/mgutz/ansi
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
- name: github.com/moul/http2curl
version: b1479103caacaa39319f75e7f57fc545287fca0d
version: c984a4ec331f8ef0e5cd782975a97c92bd8ab40c
- name: github.com/nsf/termbox-go
version: c45773466a30b680355d6494cc8826113c93cd0f
version: b6acae516ace002cb8105a89024544a1480655a5
- name: github.com/parnurzeal/gorequest
version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
version: e37b9d1efacf7c94820b29b75dd7d0c2996b3fb1
- name: github.com/rifflock/lfshook
version: 05a24e24fa8d3a2eca8c2baf23aa2d5a2c51490c
version: 3f9d976bd7402de39b46357069fb6325a974572e
- name: github.com/Sirupsen/logrus
version: f3cfb454f4c209e6668c95216c4744b8fddb2356
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
- name: golang.org/x/crypto
version: c2f4947f41766b144bb09066e919466da5eddeae
version: 1150b8bd09e53aea1d415621adae9bad665061a1
subpackages:
- ssh
- ssh/agent
- ssh/terminal
- curve25519
- ed25519
- ed25519/internal/edwards25519
- ssh
- ssh/agent
- ssh/terminal
- name: golang.org/x/net
version: f841c39de738b1d0df95b5a7187744f0e03d8112
version: 65dfc08770ce66f74becfdff5f8ab01caef4e946
subpackages:
- context
- publicsuffix
- name: golang.org/x/sys
version: a408501be4d17ee978c04a618e7a1b22af058c0e
version: c200b10b5d5e122be351b67af224adc6128af5bf
subpackages:
- unix
- name: gopkg.in/alexcesaro/quotedprintable.v3
version: 2caba252f4dc53eaf6b553000885530023f54623
- name: gopkg.in/gomail.v2
version: 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1
devImports: []
testImports: []

View File

@@ -12,6 +12,7 @@ import:
- aws/credentials
- aws/session
- service/s3
- package: github.com/boltdb/bolt
- package: github.com/cenkalti/backoff
- package: github.com/google/subcommands
- package: github.com/gosuri/uitable
@@ -36,4 +37,3 @@ import:
- package: golang.org/x/net
subpackages:
- context
- package: gopkg.in/gomail.v2

View File

@@ -222,14 +222,14 @@ ALAS (Amazon)<y:LabelModel>
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="365.54366048177087" width="251.74999999999977" x="641.4999999999995" y="285.984130859375"/>
<y:Geometry height="382.650146484375" width="320.0" x="575.0" y="285.984130859375"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">Vuls</y:NodeLabel>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="320.0" x="0.0" y="0.0">Vuls</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="14" bottomF="13.710652669270871" left="25" leftF="25.499999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
<y:BorderInsets bottom="29" bottomF="28.63427734375" left="0" leftF="0.0" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -248,7 +248,7 @@ ALAS (Amazon)<y:LabelModel>
<node id="n4::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="798.2499999999993" y="566.4311319986981"/>
<y:Geometry height="50.0" width="80.0" x="800.0" y="575.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.595703125" x="18.7021484375" y="15.93359375">Report<y:LabelModel>
@@ -296,6 +296,24 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n4::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="590.0" y="575.0"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.203125" x="8.3984375" y="8.8671875">Web View
(Vulsrepo)<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n5">
@@ -319,30 +337,6 @@ ALAS (Amazon)<y:LabelModel>
</data>
</node>
<node id="n6">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="70.0" width="60.5" x="699.9999999999993" y="453.02174886067706"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n7">
<data key="d6">
<y:SVGNode>
<y:Geometry height="37.0" width="109.57881927490234" x="889.737640380859" y="748.5119222005209"/>
@@ -356,7 +350,7 @@ ALAS (Amazon)<y:LabelModel>
</y:SVGNode>
</data>
</node>
<node id="n8">
<node id="n7">
<data key="d6">
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
<y:Geometry height="24.0" width="35.0" x="811.8264548778523" y="681.5277913411459"/>
@@ -379,20 +373,20 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n9" yfiles.foldertype="group">
<node id="n8" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="293.63460286458337" width="251.74999999999977" x="645.7499999999998" y="-51.181884765625114"/>
<y:Geometry height="293.63460286458337" width="322.9999999999998" x="574.4999999999998" y="-51.181884765625114"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="322.9999999999998" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="25" bottomF="24.639889388761645" left="12" leftF="12.249999999999773" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
<y:BorderInsets bottom="27" bottomF="27.139889388761617" left="37" leftF="37.249999999999886" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
@@ -407,12 +401,12 @@ ALAS (Amazon)<y:LabelModel>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n9:">
<node id="n9::n0">
<graph edgedefault="directed" id="n8:">
<node id="n8::n0">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="70.0" width="60.5" x="809.75" y="132.8128287101966"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:Geometry height="70.0" width="60.5" x="779.75" y="130.31282871019664"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
<y:LabelModel>
@@ -432,10 +426,10 @@ ALAS (Amazon)<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n9::n1">
<node id="n8::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="101.0" x="672.9999999999995" y="148.4841308593749"/>
<y:Geometry height="50.0" width="101.0" x="626.7499999999997" y="142.23413085937491"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
@@ -449,7 +443,7 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n9::n2">
<node id="n8::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="80.0" x="800.0" y="35.0"/>
@@ -468,7 +462,7 @@ ALAS (Amazon)<y:LabelModel>
</node>
</graph>
</node>
<node id="n10" yfiles.foldertype="group">
<node id="n9" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
@@ -496,8 +490,8 @@ ALAS (Amazon)<y:LabelModel>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n10:">
<node id="n10::n0">
<graph edgedefault="directed" id="n9:">
<node id="n9::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="433.22635904947913"/>
@@ -514,7 +508,7 @@ ALAS (Amazon)<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n10::n1">
<node id="n9::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="333.3980916341144"/>
@@ -534,7 +528,7 @@ Container<y:LabelModel>
</node>
</graph>
</node>
<node id="n11" yfiles.foldertype="group">
<node id="n10" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
@@ -562,8 +556,8 @@ Container<y:LabelModel>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n11:">
<node id="n11::n0">
<graph edgedefault="directed" id="n10:">
<node id="n10::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="553.0387573242189"/>
@@ -580,7 +574,7 @@ Container<y:LabelModel>
</y:ShapeNode>
</data>
</node>
<node id="n11::n1">
<node id="n10::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="85.0" x="1032.6223485469814" y="566.4311319986981"/>
@@ -599,7 +593,89 @@ Container<y:LabelModel>
</node>
</graph>
</node>
<edge id="e0" source="n9::n2" target="n0">
<node id="n11" yfiles.foldertype="group">
<data key="d4"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="108.94242662355293" width="124.0" x="649.0" y="434.05757337644707"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="124.0" x="0.0" y="0.0">results dir</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n11:">
<node id="n11::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="36.0" width="76.0" x="664.0" y="470.72358900144707"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n11::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="36.0" width="76.0" x="671.0" y="480.2981186685961"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
<node id="n11::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="36.0" width="76.0" x="682.0" y="492.0"/>
<y:Fill color="#99CCFF" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
<y:Shape type="roundrectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<edge id="e0" source="n8::n2" target="n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -636,17 +712,17 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n4::n2" target="n9::n1">
<edge id="e2" source="n4::n2" target="n8::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-70.78547462732604" y="-104.38485166353485">HTTP<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="241.78515625" x="-130.8343747008911" y="-104.32744691064147">HTTP or --cve-dictoianry-dbpath option<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6418953379495804" segment="-1"/>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.6111993328569665" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -678,7 +754,7 @@ Vulnerability data<y:LabelModel>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-23.436084747315135" y="96.06095129235564">send<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-24.439868927002635" y="91.6240450195761">send<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -696,7 +772,7 @@ Vulnerability data<y:LabelModel>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="39.557346848955035" y="107.80743708610783">Generate<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="41.04640068840672" y="114.35365985463022">Generate<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -714,11 +790,11 @@ Vulnerability data<y:LabelModel>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="109.7265625" x="-125.3179906251471" y="-50.63345557215132">Detail Information<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="140.76953125" x="-148.83947500014722" y="-53.63345557215143">View Detail Information<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="58.40488430810431" distanceToCenter="true" position="left" ratio="0.3174763616620919" segment="-1"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="65.44983715439666" distanceToCenter="true" position="left" ratio="0.3605218238342404" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -726,7 +802,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e6" source="n4::n2" target="n11::n0">
<edge id="e6" source="n4::n2" target="n10::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="87.9098606499872" sy="24.54915453361525" tx="0.0" ty="0.0"/>
@@ -744,7 +820,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n4::n2" target="n10::n0">
<edge id="e7" source="n4::n2" target="n9::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -762,7 +838,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n10::e0" source="n10::n0" target="n10::n1">
<edge id="n9::e0" source="n9::n0" target="n9::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -780,7 +856,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n10::n1" target="n3">
<edge id="e8" source="n9::n1" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -790,7 +866,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n11::n0" target="n3">
<edge id="e9" source="n10::n0" target="n3">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -800,13 +876,13 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n9::e0" source="n9::n2" target="n9::n0">
<edge id="n8::e0" source="n8::n2" target="n8::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-48.552734375" y="14.840008105098306">Insert<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-56.200249246840485" y="13.59000810509832">Insert<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -818,44 +894,7 @@ Vulnerability data<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n4::n2" target="n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="70.275390625" x="-75.4169220517914" y="5.2924810128525905">Insert
Scan Result<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n6" target="n4::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-48.96875000000057" y="19.334788004557254">Select<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n4::n0" target="n7">
<edge id="e10" source="n4::n0" target="n6">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -865,7 +904,7 @@ Scan Result<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n7" target="n5">
<edge id="e11" source="n6" target="n5">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -883,18 +922,18 @@ Scan Result<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n4::n2" target="n9::n0">
<data key="d9"/>
<edge id="e12" source="n4::n2" target="n8::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="191.283203125" x="39.258457373248575" y="-102.36487019599565">--cve-dictoianry-dbpath option<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="4.0" x="116.345248769779" y="-97.77053302962645">
<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.749331808189429" segment="-1"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.7403938917830506" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
@@ -902,14 +941,13 @@ Scan Result<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="n9::e1" source="n9::n1" target="n9::n0">
<data key="d9"/>
<edge id="n8::e1" source="n8::n1" target="n8::n0">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="0.8643523609480326" y="20.003628886299452">Select<y:LabelModel>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -921,6 +959,48 @@ Scan Result<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n4::n2" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n11" target="n4::n1">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n5" target="n4::n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e16" source="n11" target="n4::n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -72,7 +72,7 @@ FreeBSD: pkg<y:LabelModel>
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Get upgradable packages
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -168,22 +168,6 @@ FreeBSD: pkg audit<y:LabelModel>
</data>
</node>
<node id="n9">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="64.1719342604298" width="111.96965865992411" x="687.3850119398792" y="807.0697396491782"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.56640625" x="31.701626204962054" y="23.019560880214726">Vuls DB<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="-8.881784197001252E-16" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n10">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
<y:Geometry height="65.22882427307195" width="136.83944374209864" x="411.5802781289507" y="687.385587863464"/>
@@ -199,13 +183,13 @@ FreeBSD: pkg audit<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n11">
<node id="n10">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="609.3698412698412" y="716.4553275126422"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="126.396484375" x="70.8017578125" y="11.8671875">Insert results into DB
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="152.634765625" x="57.6826171875" y="11.8671875">Write results to JSON files
Reporting<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -216,13 +200,13 @@ Reporting<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n12">
<node id="n11">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="287.8409153761062"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="271.369140625" x="-1.6845703124999432" y="11.8671875">Get all changelogs by using package manager
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="293.06640625" x="-12.533203124999943" y="11.8671875">Get all changelogs of updatable packages at once
CentOS: yum update --changelog<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -233,7 +217,7 @@ CentOS: yum update --changelog<y:LabelModel>
</y:GenericNode>
</data>
</node>
<node id="n13">
<node id="n12">
<data key="d6">
<y:GenericNode configuration="com.yworks.flowchart.process">
<y:Geometry height="56.0" width="268.0" x="309.6849206349206" y="373.8409153761062"/>
@@ -364,7 +348,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n7" target="n11">
<edge id="e9" source="n7" target="n10">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
@@ -374,7 +358,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n7" target="n10">
<edge id="e10" source="n7" target="n9">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-134.01566143419018" sy="6.159084623893818" tx="0.0" ty="-29.333162136535975">
@@ -386,18 +370,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n11" target="n9">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.86721713021484"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n1" target="n12">
<data key="d9"/>
<edge id="e11" source="n1" target="n11">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
@@ -415,8 +388,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n12" target="n13">
<data key="d9"/>
<edge id="e12" source="n11" target="n12">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
@@ -426,8 +398,7 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n13" target="n7">
<data key="d9"/>
<edge id="e13" source="n12" target="n7">
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 81 KiB

11
main.go
View File

@@ -22,15 +22,20 @@ import (
"fmt"
"os"
"golang.org/x/net/context"
"context"
"github.com/future-architect/vuls/commands"
"github.com/future-architect/vuls/version"
"github.com/google/subcommands"
_ "github.com/mattn/go-sqlite3"
)
// Version of Vuls
var version = "0.1.7"
// Revision of Git
var revision string
func main() {
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.Register(subcommands.FlagsCommand(), "")
@@ -47,7 +52,7 @@ func main() {
flag.Parse()
if *v {
fmt.Printf("%s %s\n", version.Name, version.Version)
fmt.Printf("vuls %s %s\n", version, revision)
os.Exit(int(subcommands.ExitSuccess))
}

View File

@@ -72,8 +72,9 @@ func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
// ScanResult has the result of scanned CVE information.
type ScanResult struct {
gorm.Model `json:"-"`
ScanHistoryID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
ScanHistoryID uint `json:"-" xml:"-"`
ScannedAt time.Time
ServerName string // TOML Section key
// Hostname string
@@ -88,6 +89,7 @@ type ScanResult struct {
// NWLinks []NWLink
KnownCves []CveInfo
UnknownCves []CveInfo
IgnoredCves []CveInfo
Optional [][]interface{} `gorm:"-"`
}
@@ -95,7 +97,7 @@ type ScanResult struct {
// ServerInfo returns server name one line
func (r ScanResult) ServerInfo() string {
hostinfo := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
@@ -118,7 +120,7 @@ func (r ScanResult) ServerInfo() string {
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
hostinfo := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
@@ -163,10 +165,15 @@ func (r ScanResult) CveSummary() string {
high+middle+low+unknown, high, middle, low, unknown)
}
// AllCves returns Known and Unknown CVEs
func (r ScanResult) AllCves() []CveInfo {
return append(r.KnownCves, r.UnknownCves...)
}
// NWLink has network link information.
type NWLink struct {
gorm.Model `json:"-"`
ScanResultID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
ScanResultID uint `json:"-" xml:"-"`
IPAddress string
Netmask string
@@ -190,13 +197,13 @@ func (c CveInfos) Less(i, j int) bool {
if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
return c[i].CveDetail.CveID < c[j].CveDetail.CveID
}
return c[i].CveDetail.CvssScore(lang) > c[j].CveDetail.CvssScore(lang)
return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
}
// CveInfo has Cve Information.
type CveInfo struct {
gorm.Model `json:"-"`
ScanResultID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
ScanResultID uint `json:"-" xml:"-"`
CveDetail cve.CveDetail
Packages []PackageInfo
@@ -206,8 +213,8 @@ type CveInfo struct {
// CpeName has CPE name
type CpeName struct {
gorm.Model `json:"-"`
CveInfoID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
CveInfoID uint `json:"-" xml:"-"`
Name string
}
@@ -272,15 +279,15 @@ func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found boo
// PackageInfo has installed packages.
type PackageInfo struct {
gorm.Model `json:"-"`
CveInfoID uint `json:"-"`
Name string
Version string
Release string
gorm.Model `json:"-" xml:"-"`
CveInfoID uint `json:"-" xml:"-"`
Name string
Version string
Release string
NewVersion string
NewRelease string
Repository string
}
// ToStringCurrentVersion returns package name-version-release
@@ -309,8 +316,8 @@ func (p PackageInfo) ToStringNewVersion() string {
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
type DistroAdvisory struct {
gorm.Model `json:"-"`
CveInfoID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
CveInfoID uint `json:"-" xml:"-"`
AdvisoryID string
Severity string
@@ -320,8 +327,8 @@ type DistroAdvisory struct {
// Container has Container information
type Container struct {
gorm.Model `json:"-"`
ScanResultID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
ScanResultID uint `json:"-" xml:"-"`
ContainerID string
Name string
@@ -329,8 +336,8 @@ type Container struct {
// Platform has platform information
type Platform struct {
gorm.Model `json:"-"`
ScanResultID uint `json:"-"`
gorm.Model `json:"-" xml:"-"`
ScanResultID uint `json:"-" xml:"-"`
Name string // aws or azure or gcp or other...
InstanceID string

View File

@@ -115,7 +115,7 @@ func (w AzureBlobWriter) upload(res models.ScanResult) (err error) {
}
timestr := time.Now().Format("20060102_1504")
name := ""
if res.Container.ContainerID == "" {
if len(res.Container.ContainerID) == 0 {
name = fmt.Sprintf("%s/%s.json", timestr, res.ServerName)
} else {
name = fmt.Sprintf("%s/%s_%s.json", timestr, res.ServerName, res.Container.Name)

View File

@@ -21,30 +21,49 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
// JSONDirs array of json files path.
type JSONDirs []string
func (d JSONDirs) Len() int {
return len(d)
}
func (d JSONDirs) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
func (d JSONDirs) Less(i, j int) bool {
return d[j] < d[i]
}
// JSONWriter writes results to file.
type JSONWriter struct{}
type JSONWriter struct {
ScannedAt time.Time
}
func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
var path string
if path, err = ensureResultDir(w.ScannedAt); err != nil {
return fmt.Errorf("Failed to make direcotory/symlink : %s", err)
}
path, err := ensureResultDir()
for _, scanResult := range scanResults {
scanResult.ScannedAt = w.ScannedAt
}
var jsonBytes []byte
if jsonBytes, err = json.Marshal(scanResults); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
all := filepath.Join(path, "all.json")
if err := ioutil.WriteFile(all, jsonBytes, 0644); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", all, err)
}
for _, r := range scanResults {
jsonPath := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
jsonPath = filepath.Join(path, fmt.Sprintf("%s.json", r.ServerName))
} else {
jsonPath = filepath.Join(path,
@@ -54,9 +73,79 @@ func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
if jsonBytes, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
if err := ioutil.WriteFile(jsonPath, jsonBytes, 0644); err != nil {
if err := ioutil.WriteFile(jsonPath, jsonBytes, 0600); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", jsonPath, err)
}
}
return nil
}
// JSONDirPattern is file name pattern of JSON directory
var JSONDirPattern = regexp.MustCompile(`^\d{8}_\d{4}$`)
// GetValidJSONDirs return valid json directory as array
func GetValidJSONDirs() (jsonDirs JSONDirs, err error) {
var dirInfo []os.FileInfo
if dirInfo, err = ioutil.ReadDir(c.Conf.ResultsDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", c.Conf.ResultsDir, err)
return
}
for _, d := range dirInfo {
if d.IsDir() && JSONDirPattern.MatchString(d.Name()) {
jsonDir := filepath.Join(c.Conf.ResultsDir, d.Name())
jsonDirs = append(jsonDirs, jsonDir)
}
}
sort.Sort(jsonDirs)
return
}
// LoadOneScanHistory read JSON data
func LoadOneScanHistory(jsonDir string) (scanHistory models.ScanHistory, err error) {
var scanResults []models.ScanResult
var files []os.FileInfo
if files, err = ioutil.ReadDir(jsonDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonDir, err)
return
}
for _, file := range files {
if filepath.Ext(file.Name()) != ".json" {
continue
}
var scanResult models.ScanResult
var data []byte
jsonPath := filepath.Join(jsonDir, file.Name())
if data, err = ioutil.ReadFile(jsonPath); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonPath, err)
return
}
if json.Unmarshal(data, &scanResult) != nil {
err = fmt.Errorf("Failed to parse %s: %s", jsonPath, err)
return
}
scanResults = append(scanResults, scanResult)
}
if len(scanResults) == 0 {
err = fmt.Errorf("There is no json file under %s", jsonDir)
return
}
var scannedAt time.Time
if scanResults[0].ScannedAt.IsZero() {
splitPath := strings.Split(jsonDir, string(os.PathSeparator))
timeStr := splitPath[len(splitPath)-1]
timeformat := "20060102_1504"
if scannedAt, err = time.Parse(timeformat, timeStr); err != nil {
err = fmt.Errorf("Failed to parse %s: %s", timeStr, err)
return
}
} else {
scannedAt = scanResults[0].ScannedAt
}
scanHistory = models.ScanHistory{
ScanResults: scanResults,
ScannedAt: scannedAt,
}
return
}

View File

@@ -36,7 +36,7 @@ func (w LogrusWriter) Write(scanResults []models.ScanResult) error {
if runtime.GOOS == "windows" {
path = filepath.Join(os.Getenv("APPDATA"), "vuls", "report.log")
}
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
return err
}

View File

@@ -18,13 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package report
import (
"crypto/tls"
"fmt"
"strconv"
"net"
"net/mail"
"net/smtp"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"gopkg.in/gomail.v2"
)
// MailWriter send mail
@@ -33,37 +34,53 @@ type MailWriter struct{}
func (w MailWriter) Write(scanResults []models.ScanResult) (err error) {
conf := config.Conf
for _, s := range scanResults {
m := gomail.NewMessage()
m.SetHeader("From", conf.Mail.From)
m.SetHeader("To", conf.Mail.To...)
m.SetHeader("Cc", conf.Mail.Cc...)
to := strings.Join(conf.Mail.To[:], ", ")
cc := strings.Join(conf.Mail.Cc[:], ", ")
mailAddresses := append(conf.Mail.To, conf.Mail.Cc...)
if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
return fmt.Errorf("Failed to parse email addresses: %s", err)
}
subject := fmt.Sprintf("%s%s %s",
conf.Mail.SubjectPrefix,
s.ServerInfo(),
s.CveSummary(),
)
m.SetHeader("Subject", subject)
headers := make(map[string]string)
headers["From"] = conf.Mail.From
headers["To"] = to
headers["Cc"] = cc
headers["Subject"] = subject
var message string
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
var body string
if body, err = toPlainText(s); err != nil {
return err
}
m.SetBody("text/plain", body)
port, _ := strconv.Atoi(conf.Mail.SMTPPort)
d := gomail.NewPlainDialer(
conf.Mail.SMTPAddr,
port,
conf.Mail.User,
conf.Mail.Password,
message += "\r\n" + body
smtpServer := net.JoinHostPort(conf.Mail.SMTPAddr, conf.Mail.SMTPPort)
err := smtp.SendMail(
smtpServer,
smtp.PlainAuth(
"",
conf.Mail.User,
conf.Mail.Password,
conf.Mail.SMTPAddr,
),
conf.Mail.From,
conf.Mail.To,
[]byte(message),
)
d.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
if err := d.DialAndSend(m); err != nil {
panic(err)
if err != nil {
return fmt.Errorf("Failed to send emails: %s", err)
}
}
return nil

View File

@@ -78,19 +78,9 @@ func (w S3Writer) Write(scanResults []models.ScanResult) (err error) {
// http://docs.aws.amazon.com/sdk-for-go/latest/v1/developerguide/common-examples.title.html
svc := getS3()
timestr := time.Now().Format("20060102_1504")
key := fmt.Sprintf("%s/%s", timestr, "all.json")
_, err = svc.PutObject(&s3.PutObjectInput{
Bucket: &c.Conf.S3Bucket,
Key: &key,
Body: bytes.NewReader(jsonBytes),
})
if err != nil {
return fmt.Errorf("Failed to upload data to %s/%s, %s", c.Conf.S3Bucket, key, err)
}
for _, r := range scanResults {
key := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
key = fmt.Sprintf("%s/%s.json", timestr, r.ServerName)
} else {
key = fmt.Sprintf("%s/%s_%s.json", timestr, r.ServerName, r.Container.Name)

View File

@@ -59,7 +59,6 @@ type SlackWriter struct{}
func (w SlackWriter) Write(scanResults []models.ScanResult) error {
conf := config.Conf.Slack
for _, s := range scanResults {
channel := conf.Channel
if channel == "${servername}" {
channel = fmt.Sprintf("#%s", s.ServerName)
@@ -80,7 +79,7 @@ func (w SlackWriter) Write(scanResults []models.ScanResult) error {
Send(string(jsonBody)).End()
if resp.StatusCode != 200 {
log.Errorf("Resonse body: %s", body)
if len(errs) > 0 {
if 0 < len(errs) {
return errs[0]
}
}
@@ -97,7 +96,6 @@ func (w SlackWriter) Write(scanResults []models.ScanResult) error {
}
func msgText(r models.ScanResult) string {
notifyUsers := ""
if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) {
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
@@ -108,7 +106,6 @@ func msgText(r models.ScanResult) string {
}
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
cves := scanResult.KnownCves
if !config.Conf.IgnoreUnscoredCves {
cves = append(cves, scanResult.UnknownCves...)
@@ -205,6 +202,17 @@ func attachmentText(cveInfo models.CveInfo, osFamily string) string {
func links(cveInfo models.CveInfo, osFamily string) string {
links := []string{}
cweID := cveInfo.CveDetail.CweID()
if 0 < len(cweID) {
links = append(links, fmt.Sprintf("<%s|%s>",
cweURL(cweID), cweID))
if config.Conf.Lang == "ja" {
links = append(links, fmt.Sprintf("<%s|%s(JVN)>",
cweJvnURL(cweID), cweID))
}
}
cveID := cveInfo.CveDetail.CveID
if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) {
jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link())

View File

@@ -22,21 +22,22 @@ import (
"io/ioutil"
"path/filepath"
"strings"
"time"
"github.com/future-architect/vuls/models"
)
// TextFileWriter writes results to file.
type TextFileWriter struct{}
type TextFileWriter struct {
ScannedAt time.Time
}
func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
path, err := ensureResultDir()
path, err := ensureResultDir(w.ScannedAt)
all := []string{}
for _, r := range scanResults {
textFilePath := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
textFilePath = filepath.Join(path, fmt.Sprintf("%s.txt", r.ServerName))
} else {
textFilePath = filepath.Join(path,
@@ -48,7 +49,7 @@ func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
}
all = append(all, text)
b := []byte(text)
if err := ioutil.WriteFile(textFilePath, b, 0644); err != nil {
if err := ioutil.WriteFile(textFilePath, b, 0600); err != nil {
return fmt.Errorf("Failed to write text files. path: %s, err: %s", textFilePath, err)
}
}
@@ -56,7 +57,7 @@ func (w TextFileWriter) Write(scanResults []models.ScanResult) (err error) {
text := strings.Join(all, "\n\n")
b := []byte(text)
allPath := filepath.Join(path, "all.txt")
if err := ioutil.WriteFile(allPath, b, 0644); err != nil {
if err := ioutil.WriteFile(allPath, b, 0600); err != nil {
return fmt.Errorf("Failed to write text files. path: %s, err: %s", allPath, err)
}
return nil

View File

@@ -20,13 +20,13 @@ package report
import (
"bytes"
"fmt"
"path/filepath"
"strings"
"text/template"
"time"
log "github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/db"
"github.com/future-architect/vuls/models"
"github.com/google/subcommands"
"github.com/gosuri/uitable"
@@ -40,42 +40,55 @@ var currentCveInfo int
var currentDetailLimitY int
// RunTui execute main logic
func RunTui(historyID string) subcommands.ExitStatus {
func RunTui(jsonDirName string) subcommands.ExitStatus {
var err error
scanHistory, err = selectScanHistory(historyID)
scanHistory, err = selectScanHistory(jsonDirName)
if err != nil {
log.Fatal(err)
log.Errorf("%s", err)
return subcommands.ExitFailure
}
g := gocui.NewGui()
if err := g.Init(); err != nil {
log.Panicln(err)
g, err := gocui.NewGui()
if err != nil {
log.Errorf("%s", err)
return subcommands.ExitFailure
}
defer g.Close()
g.SetLayout(layout)
g.SetManagerFunc(layout)
if err := keybindings(g); err != nil {
log.Panicln(err)
log.Errorf("%s", err)
return subcommands.ExitFailure
}
g.SelBgColor = gocui.ColorGreen
g.SelFgColor = gocui.ColorBlack
g.Cursor = true
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln(err)
log.Errorf("%s", err)
return subcommands.ExitFailure
}
return subcommands.ExitSuccess
}
func selectScanHistory(historyID string) (latest models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return latest, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
func selectScanHistory(jsonDirName string) (latest models.ScanHistory, err error) {
var jsonDir string
if 0 < len(jsonDirName) {
jsonDir = filepath.Join(config.Conf.ResultsDir, jsonDirName)
} else {
var jsonDirs JSONDirs
if jsonDirs, err = GetValidJSONDirs(); err != nil {
return
}
if len(jsonDirs) == 0 {
return latest, fmt.Errorf("No scan results are found in %s", config.Conf.ResultsDir)
}
jsonDir = jsonDirs[0]
}
if latest, err = LoadOneScanHistory(jsonDir); err != nil {
return
}
latest, err = db.SelectScanHistory(historyID)
return
}
@@ -163,35 +176,41 @@ func keybindings(g *gocui.Gui) (err error) {
}
func nextView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
}
switch v.Name() {
case "side":
return g.SetCurrentView("summary")
_, err = g.SetCurrentView("summary")
case "summary":
return g.SetCurrentView("detail")
_, err = g.SetCurrentView("detail")
case "detail":
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
default:
return g.SetCurrentView("summary")
_, err = g.SetCurrentView("summary")
}
return err
}
func previousView(g *gocui.Gui, v *gocui.View) error {
var err error
if v == nil {
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
}
switch v.Name() {
case "side":
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
case "summary":
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
case "detail":
return g.SetCurrentView("summary")
_, err = g.SetCurrentView("summary")
default:
return g.SetCurrentView("side")
_, err = g.SetCurrentView("side")
}
return err
}
func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
@@ -203,7 +222,7 @@ func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
}
return true, yLimit
case "summary":
yLimit = len(currentScanResult.KnownCves) - 1
yLimit = len(currentScanResult.AllCves()) - 1
if yLimit < nextY {
return false, yLimit
}
@@ -332,7 +351,7 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
if v != nil {
ox, oy := v.Origin()
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
if err := v.SetCursor(cx, cy-1); err != nil && 0 < oy {
if err := v.SetOrigin(ox, oy-1); err != nil {
return err
}
@@ -360,7 +379,7 @@ func cursorPageUp(g *gocui.Gui, v *gocui.View) error {
func previousSummary(g *gocui.Gui, v *gocui.View) error {
if v != nil {
// cursor to summary
if err := g.SetCurrentView("summary"); err != nil {
if _, err := g.SetCurrentView("summary"); err != nil {
return err
}
// move next line
@@ -368,7 +387,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
return err
}
// cursor to detail
if err := g.SetCurrentView("detail"); err != nil {
if _, err := g.SetCurrentView("detail"); err != nil {
return err
}
}
@@ -378,7 +397,7 @@ func previousSummary(g *gocui.Gui, v *gocui.View) error {
func nextSummary(g *gocui.Gui, v *gocui.View) error {
if v != nil {
// cursor to summary
if err := g.SetCurrentView("summary"); err != nil {
if _, err := g.SetCurrentView("summary"); err != nil {
return err
}
// move next line
@@ -386,7 +405,7 @@ func nextSummary(g *gocui.Gui, v *gocui.View) error {
return err
}
// cursor to detail
if err := g.SetCurrentView("detail"); err != nil {
if _, err := g.SetCurrentView("detail"); err != nil {
return err
}
}
@@ -451,7 +470,7 @@ func getLine(g *gocui.Gui, v *gocui.View) error {
return err
}
fmt.Fprintln(v, l)
if err := g.SetCurrentView("msg"); err != nil {
if _, err := g.SetCurrentView("msg"); err != nil {
return err
}
}
@@ -473,7 +492,7 @@ func showMsg(g *gocui.Gui, v *gocui.View) error {
return err
}
fmt.Fprintln(v, l)
if err := g.SetCurrentView("msg"); err != nil {
if _, err := g.SetCurrentView("msg"); err != nil {
return err
}
}
@@ -484,7 +503,7 @@ func delMsg(g *gocui.Gui, v *gocui.View) error {
if err := g.DeleteView("msg"); err != nil {
return err
}
if err := g.SetCurrentView("summary"); err != nil {
if _, err := g.SetCurrentView("summary"); err != nil {
return err
}
return nil
@@ -519,7 +538,7 @@ func setSideLayout(g *gocui.Gui) error {
fmt.Fprintln(v, result.ServerInfoTui())
}
currentScanResult = scanHistory.ScanResults[0]
if err := g.SetCurrentView("side"); err != nil {
if _, err := g.SetCurrentView("side"); err != nil {
return err
}
}
@@ -533,7 +552,7 @@ func setSummaryLayout(g *gocui.Gui) error {
return err
}
lines := summaryLines(currentScanResult)
lines := summaryLines()
fmt.Fprintf(v, lines)
v.Highlight = true
@@ -543,21 +562,21 @@ func setSummaryLayout(g *gocui.Gui) error {
return nil
}
func summaryLines(data models.ScanResult) string {
func summaryLines() string {
stable := uitable.New()
stable.MaxColWidth = 1000
stable.Wrap = false
indexFormat := ""
if len(data.KnownCves) < 10 {
if len(currentScanResult.AllCves()) < 10 {
indexFormat = "[%1d]"
} else if len(data.KnownCves) < 100 {
} else if len(currentScanResult.AllCves()) < 100 {
indexFormat = "[%2d]"
} else {
indexFormat = "[%3d]"
}
for i, d := range data.KnownCves {
for i, d := range currentScanResult.AllCves() {
var cols []string
// packs := []string{}
// for _, pack := range d.Packages {
@@ -643,6 +662,7 @@ type dataForTmpl struct {
CvssVector string
CvssSeverity string
Summary string
CweURL string
VulnSiteLinks []string
References []cve.Reference
Packages []string
@@ -652,11 +672,11 @@ type dataForTmpl struct {
}
func detailLines() (string, error) {
if len(currentScanResult.KnownCves) == 0 {
if len(currentScanResult.AllCves()) == 0 {
return "No vulnerable packages", nil
}
cveInfo := currentScanResult.KnownCves[currentCveInfo]
cveInfo := currentScanResult.AllCves()[currentCveInfo]
cveID := cveInfo.CveDetail.CveID
tmpl, err := template.New("detail").Parse(detailTemplate())
@@ -682,6 +702,8 @@ func detailLines() (string, error) {
refs = nvd.VulnSiteReferences()
}
cweURL := cweURL(cveInfo.CveDetail.CweID())
links := []string{
fmt.Sprintf("[NVD]( %s )", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID)),
fmt.Sprintf("[MITRE]( %s )", fmt.Sprintf("%s%s", mitreBaseURL, cveID)),
@@ -715,6 +737,7 @@ func detailLines() (string, error) {
CvssSeverity: cvssSeverity,
CvssVector: cvssVector,
Summary: summary,
CweURL: cweURL,
VulnSiteLinks: links,
References: refs,
Packages: packages,
@@ -746,6 +769,11 @@ Summary
{{.Summary }}
CWE
--------------
{{.CweURL }}
Package/CPE
--------------

View File

@@ -30,32 +30,34 @@ import (
"github.com/gosuri/uitable"
)
func ensureResultDir() (path string, err error) {
if resultDirPath != "" {
return resultDirPath, nil
}
func ensureResultDir(scannedAt time.Time) (path string, err error) {
const timeLayout = "20060102_1504"
timedir := time.Now().Format(timeLayout)
wd, _ := os.Getwd()
dir := filepath.Join(wd, "results", timedir)
if err := os.MkdirAll(dir, 0755); err != nil {
jsonDirName := scannedAt.Format(timeLayout)
resultsDir := config.Conf.ResultsDir
if len(resultsDir) == 0 {
wd, _ := os.Getwd()
resultsDir = filepath.Join(wd, "results")
}
jsonDir := filepath.Join(resultsDir, jsonDirName)
if err := os.MkdirAll(jsonDir, 0700); err != nil {
return "", fmt.Errorf("Failed to create dir: %s", err)
}
symlinkPath := filepath.Join(wd, "results", "current")
if _, err := os.Stat(symlinkPath); err == nil {
symlinkPath := filepath.Join(resultsDir, "current")
if _, err := os.Lstat(symlinkPath); err == nil {
if err := os.Remove(symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
}
}
if err := os.Symlink(dir, symlinkPath); err != nil {
if err := os.Symlink(jsonDir, symlinkPath); err != nil {
return "", fmt.Errorf(
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
}
return dir, nil
return jsonDir, nil
}
func toPlainText(scanResult models.ScanResult) (string, error) {
@@ -205,13 +207,11 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
}
func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
cveDetail := cveInfo.CveDetail
cveID := cveDetail.CveID
jvn := cveDetail.Jvn
dtable := uitable.New()
//TODO resize
dtable.MaxColWidth = 100
dtable.Wrap = true
dtable.AddRow(cveID)
@@ -228,6 +228,8 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
dtable.AddRow("Vector", jvn.CvssVector())
dtable.AddRow("Title", jvn.CveTitle())
dtable.AddRow("Description", jvn.CveSummary())
dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID()))
dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID()))
dtable.AddRow("JVN", jvn.Link())
dtable.AddRow("NVD", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID))
@@ -252,7 +254,6 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
nvd := cveDetail.Nvd
dtable := uitable.New()
//TODO resize
dtable.MaxColWidth = 100
dtable.Wrap = true
dtable.AddRow(cveID)
@@ -270,6 +271,8 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
dtable.AddRow("Vector", nvd.CvssVector())
dtable.AddRow("Summary", nvd.CveSummary())
dtable.AddRow("CWE", cweURL(cveDetail.CweID()))
dtable.AddRow("NVD", fmt.Sprintf("%s?vulnId=%s", nvdBaseURL, cveID))
dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID))
dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID))
@@ -376,3 +379,12 @@ func addCpeNames(table *uitable.Table, names []models.CpeName) *uitable.Table {
}
return table
}
func cweURL(cweID string) string {
return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
strings.TrimPrefix(cweID, "CWE-"))
}
func cweJvnURL(cweID string) string {
return fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
}

View File

@@ -39,5 +39,3 @@ const (
type ResultWriter interface {
Write([]models.ScanResult) error
}
var resultDirPath string

54
report/xml.go Normal file
View File

@@ -0,0 +1,54 @@
package report
import (
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"path/filepath"
"time"
"github.com/future-architect/vuls/models"
)
const (
vulsOpenTag = "<vulsreport>"
vulsCloseTag = "</vulsreport>"
)
// XMLWriter writes results to file.
type XMLWriter struct {
ScannedAt time.Time
}
func (w XMLWriter) Write(scanResults []models.ScanResult) (err error) {
var path string
if path, err = ensureResultDir(w.ScannedAt); err != nil {
return fmt.Errorf("Failed to make direcotory/symlink : %s", err)
}
for _, scanResult := range scanResults {
scanResult.ScannedAt = w.ScannedAt
}
var xmlBytes []byte
for _, r := range scanResults {
xmlPath := ""
if len(r.Container.ContainerID) == 0 {
xmlPath = filepath.Join(path, fmt.Sprintf("%s.xml", r.ServerName))
} else {
xmlPath = filepath.Join(path,
fmt.Sprintf("%s_%s.xml", r.ServerName, r.Container.Name))
}
if xmlBytes, err = xml.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to XML: %s", err)
}
allBytes := bytes.Join([][]byte{[]byte(xml.Header + vulsOpenTag), xmlBytes, []byte(vulsCloseTag)}, []byte{})
if err := ioutil.WriteFile(xmlPath, allBytes, 0600); err != nil {
return fmt.Errorf("Failed to write XML. path: %s, err: %s", xmlPath, err)
}
}
return nil
}

View File

@@ -19,8 +19,10 @@ package scan
import (
"fmt"
"regexp"
"sort"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/config"
@@ -30,10 +32,10 @@ import (
type base struct {
ServerInfo config.ServerInfo
Distro config.Distro
Platform models.Platform
Family string
Release string
Platform models.Platform
lackDependencies []string
osPackages
log *logrus.Entry
@@ -52,13 +54,20 @@ func (l base) getServerInfo() config.ServerInfo {
return l.ServerInfo
}
func (l *base) setDistributionInfo(fam, rel string) {
l.Family = fam
l.Release = rel
func (l *base) setDistro(fam, rel string) {
d := config.Distro{
Family: fam,
Release: rel,
}
l.Distro = d
s := l.getServerInfo()
s.Distro = d
l.setServerInfo(s)
}
func (l base) getDistributionInfo() string {
return fmt.Sprintf("%s %s", l.Family, l.Release)
func (l base) getDistro() config.Distro {
return l.Distro
}
func (l *base) setPlatform(p models.Platform) {
@@ -69,6 +78,10 @@ func (l base) getPlatform() models.Platform {
return l.Platform
}
func (l base) getLackDependencies() []string {
return l.lackDependencies
}
func (l base) allContainers() (containers []config.Container, err error) {
switch l.ServerInfo.Container.Type {
case "", "docker":
@@ -164,9 +177,7 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
r := l.ssh(cmd, noSudo)
if r.isSuccess() {
id := strings.TrimSpace(r.Stdout)
if id == "not found" {
// status: 0, stdout: "not found" on degitalocean or Azure
if !l.isAwsInstanceID(id) {
return false, "", nil
}
return true, id, nil
@@ -186,6 +197,9 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
r := l.ssh(cmd, noSudo)
if r.isSuccess() {
id := strings.TrimSpace(r.Stdout)
if !l.isAwsInstanceID(id) {
return false, "", nil
}
return true, id, nil
}
@@ -202,14 +216,39 @@ func (l base) detectRunningOnAws() (ok bool, instanceID string, err error) {
l.ServerInfo.ServerName, l.ServerInfo.Container.Name)
}
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/resource-ids.html
var awsInstanceIDPattern = regexp.MustCompile(`^i-[0-9a-f]+$`)
func (l base) isAwsInstanceID(str string) bool {
return awsInstanceIDPattern.MatchString(str)
}
func (l *base) convertToModel() (models.ScanResult, error) {
var scoredCves, unscoredCves models.CveInfos
var scoredCves, unscoredCves, ignoredCves models.CveInfos
for _, p := range l.UnsecurePackages {
// ignoreCves
found := false
for _, icve := range l.getServerInfo().IgnoreCves {
if icve == p.CveDetail.CveID {
ignoredCves = append(ignoredCves, models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories,
})
found = true
break
}
}
if found {
continue
}
// unscoredCves
if p.CveDetail.CvssScore(config.Conf.Lang) <= 0 {
unscoredCves = append(unscoredCves, models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
DistroAdvisories: p.DistroAdvisories,
})
continue
}
@@ -220,10 +259,11 @@ func (l *base) convertToModel() (models.ScanResult, error) {
models.CpeName{Name: cpename})
}
// scoredCves
cve := models.CveInfo{
CveDetail: p.CveDetail,
Packages: p.Packs,
DistroAdvisories: p.DistroAdvisories, // only Amazon Linux
DistroAdvisories: p.DistroAdvisories,
CpeNames: cpenames,
}
scoredCves = append(scoredCves, cve)
@@ -236,15 +276,18 @@ func (l *base) convertToModel() (models.ScanResult, error) {
sort.Sort(scoredCves)
sort.Sort(unscoredCves)
sort.Sort(ignoredCves)
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,
Family: l.Family,
Release: l.Release,
ScannedAt: time.Now(),
Family: l.Distro.Family,
Release: l.Distro.Release,
Container: container,
Platform: l.Platform,
KnownCves: scoredCves,
UnknownCves: unscoredCves,
IgnoredCves: ignoredCves,
Optional: l.ServerInfo.Optional,
}, nil
}

View File

@@ -56,3 +56,25 @@ f570ae647edc agitated_lovelace`,
}
}
}
func TestIsAwsInstanceID(t *testing.T) {
var tests = []struct {
in string
expected bool
}{
{"i-1234567a", true},
{"i-1234567890abcdef0", true},
{"i-1234567890abcdef0000000", true},
{"e-1234567890abcdef0", false},
{"i-1234567890abcdef0 foo bar", false},
{"no data", false},
}
r := newRedhat(config.ServerInfo{})
for _, tt := range tests {
actual := r.isAwsInstanceID(tt.in)
if tt.expected != actual {
t.Errorf("expected %t, actual %t, str: %s", tt.expected, actual, tt.in)
}
}
}

View File

@@ -24,6 +24,7 @@ import (
"strings"
"time"
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
@@ -39,6 +40,7 @@ type debian struct {
func newDebian(c config.ServerInfo) *debian {
d := &debian{}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
}
@@ -46,7 +48,6 @@ func newDebian(c config.ServerInfo) *debian {
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
deb = newDebian(c)
deb.setServerInfo(c)
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
if r.Error != nil {
@@ -65,17 +66,16 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// root@fa3ec524be43:/# lsb_release -ir
// Distributor ID: Ubuntu
// Release: 14.04
re, _ := regexp.Compile(
`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
re := regexp.MustCompile(`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
result := re.FindStringSubmatch(trim(r.Stdout))
if len(result) == 0 {
deb.setDistributionInfo("debian/ubuntu", "unknown")
deb.setDistro("debian/ubuntu", "unknown")
Log.Warnf(
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistributionInfo(distro, trim(result[2]))
deb.setDistro(distro, trim(result[2]))
}
return true, deb, nil
}
@@ -86,16 +86,15 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// DISTRIB_RELEASE=14.04
// DISTRIB_CODENAME=trusty
// DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
re, _ := regexp.Compile(
`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
result := re.FindStringSubmatch(trim(r.Stdout))
if len(result) == 0 {
Log.Warnf(
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
deb.setDistributionInfo("debian/ubuntu", "unknown")
deb.setDistro("debian/ubuntu", "unknown")
} else {
distro := strings.ToLower(trim(result[1]))
deb.setDistributionInfo(distro, trim(result[2]))
deb.setDistro(distro, trim(result[2]))
}
return true, deb, nil
}
@@ -103,7 +102,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
// Debian
cmd := "cat /etc/debian_version"
if r := sshExec(c, cmd, noSudo); r.isSuccess() {
deb.setDistributionInfo("debian", trim(r.Stdout))
deb.setDistro("debian", trim(r.Stdout))
return true, deb, nil
}
@@ -125,7 +124,30 @@ func (o *debian) checkIfSudoNoPasswd() error {
return nil
}
func (o *debian) checkDependencies() error {
switch o.Distro.Family {
case "ubuntu":
return nil
case "debian":
// Debian needs aptitude to get changelogs.
// Because unable to get changelogs via apt-get changelog on Debian.
name := "aptitude"
cmd := name + " -h"
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
o.lackDependencies = []string{name}
}
return nil
default:
return fmt.Errorf("Not implemented yet: %s", o.Distro)
}
}
func (o *debian) install() error {
if len(o.lackDependencies) == 0 {
return nil
}
// apt-get update
o.log.Infof("apt-get update...")
@@ -136,37 +158,15 @@ func (o *debian) install() error {
return fmt.Errorf(msg)
}
if o.Family == "debian" {
// install aptitude
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
for _, name := range o.lackDependencies {
cmd = util.PrependProxyEnv("apt-get install " + name)
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Installed: aptitude")
o.log.Infof("Installed: " + name)
}
// install unattended-upgrades
if !config.Conf.UseUnattendedUpgrades {
return nil
}
if r := o.ssh("type unattended-upgrade", noSudo); r.isSuccess() {
o.log.Infof(
"Ignored: unattended-upgrade already installed")
return nil
}
cmd = util.PrependProxyEnv(
"apt-get install --force-yes -y unattended-upgrades")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Installed: unattended-upgrades")
return nil
}
@@ -214,12 +214,16 @@ func (o *debian) scanInstalledPackages() (packs []models.PackageInfo, err error)
return
}
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
re, _ := regexp.Compile(`^([^\t']+)\t(.+)$`)
result := re.FindStringSubmatch(line)
result := packageLinePattern.FindStringSubmatch(line)
if len(result) == 3 {
// remove :amd64, i386...
name = regexp.MustCompile(":.+").ReplaceAllString(result[1], "")
name = result[1]
if i := strings.IndexRune(name, ':'); i >= 0 {
name = name[:i]
}
version = result[2]
return
}
@@ -227,49 +231,27 @@ func (o *debian) parseScannedPackagesLine(line string) (name, version string, er
return "", "", fmt.Errorf("Unknown format: %s", line)
}
// unattended-upgrade command need to check security upgrades).
func (o *debian) checkRequiredPackagesInstalled() error {
if o.Family == "debian" {
if o.Distro.Family == "debian" {
if r := o.ssh("test -f /usr/bin/aptitude", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("aptitude is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
}
if !config.Conf.UseUnattendedUpgrades {
return nil
}
if r := o.ssh("type unattended-upgrade", noSudo); !r.isSuccess() {
msg := fmt.Sprintf("unattended-upgrade is not installed: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
return nil
}
//TODO return whether already expired.
func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInfo, error) {
// cmd := prependProxyEnv(conf.HTTPProxy, "apt-get update | cat; echo 1")
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
var upgradablePackNames []string
var err error
if config.Conf.UseUnattendedUpgrades {
upgradablePackNames, err = o.GetUnsecurePackNamesUsingUnattendedUpgrades()
if err != nil {
return []CvePacksInfo{}, err
}
} else {
upgradablePackNames, err = o.GetUpgradablePackNames()
if err != nil {
return []CvePacksInfo{}, err
}
upgradablePackNames, err := o.GetUpgradablePackNames()
if err != nil {
return []CvePacksInfo{}, err
}
// Convert package name to PackageInfo struct
@@ -282,12 +264,21 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
}
}
}
unsecurePacks, err = o.fillCandidateVersion(unsecurePacks)
if err != nil {
return nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
}
current := cache.Meta{
Name: o.getServerInfo().GetServerName(),
Distro: o.getServerInfo().Distro,
Packs: unsecurePacks,
}
o.log.Debugf("Ensure changelog cache: %s", current.Name)
if err := o.ensureChangelogCache(current); err != nil {
return nil, err
}
// Collect CVE information of upgradable packages
cvePacksInfos, err := o.scanPackageCveInfos(unsecurePacks)
if err != nil {
@@ -297,103 +288,65 @@ func (o *debian) scanUnsecurePackages(packs []models.PackageInfo) ([]CvePacksInf
return cvePacksInfos, nil
}
func (o *debian) fillCandidateVersion(packs []models.PackageInfo) ([]models.PackageInfo, error) {
reqChan := make(chan models.PackageInfo, len(packs))
resChan := make(chan models.PackageInfo, len(packs))
errChan := make(chan error, len(packs))
defer close(resChan)
defer close(errChan)
defer close(reqChan)
go func() {
for _, pack := range packs {
reqChan <- pack
func (o *debian) ensureChangelogCache(current cache.Meta) error {
// Search from cache
old, found, err := cache.DB.GetMeta(current.Name)
if err != nil {
return fmt.Errorf("Failed to get meta. err: %s", err)
}
if !found {
o.log.Debugf("Not found in meta: %s", current.Name)
err = cache.DB.EnsureBuckets(current)
if err != nil {
return fmt.Errorf("Failed to ensure buckets. err: %s", err)
}
}()
timeout := time.After(5 * 60 * time.Second)
concurrency := 5
tasks := util.GenWorkers(concurrency)
for range packs {
tasks <- func() {
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
cmd := fmt.Sprintf("LANG=en_US.UTF-8 apt-cache policy %s", p.Name)
r := o.ssh(cmd, sudo)
if !r.isSuccess() {
errChan <- fmt.Errorf("Failed to SSH: %s.", r)
return
}
ver, err := o.parseAptCachePolicy(r.Stdout, p.Name)
if err != nil {
errChan <- fmt.Errorf("Failed to parse %s", err)
}
p.NewVersion = ver.Candidate
resChan <- p
}(pack)
} else {
if current.Distro.Family != old.Distro.Family ||
current.Distro.Release != old.Distro.Release {
o.log.Debugf("Need to refesh meta: %s", current.Name)
err = cache.DB.EnsureBuckets(current)
if err != nil {
return fmt.Errorf("Failed to ensure buckets. err: %s", err)
}
} else {
o.log.Debugf("Reuse meta: %s", current.Name)
}
}
errs := []error{}
result := []models.PackageInfo{}
for i := 0; i < len(packs); i++ {
select {
case pack := <-resChan:
result = append(result, pack)
o.log.Infof("(%d/%d) Upgradable: %s-%s -> %s",
i+1, len(packs), pack.Name, pack.Version, pack.NewVersion)
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout fillCandidateVersion")
}
if config.Conf.Debug {
cache.DB.PrettyPrint(current)
}
if 0 < len(errs) {
return nil, fmt.Errorf("%v", errs)
}
return result, nil
return nil
}
func (o *debian) GetUnsecurePackNamesUsingUnattendedUpgrades() (packNames []string, err error) {
cmd := util.PrependProxyEnv("unattended-upgrades --dry-run -d 2>&1 ")
release, err := strconv.ParseFloat(o.Release, 64)
if err != nil {
return packNames, fmt.Errorf(
"OS Release Version is invalid, %s, %s", o.Family, o.Release)
func (o *debian) fillCandidateVersion(before models.PackageInfoList) (filled []models.PackageInfo, err error) {
names := []string{}
for _, p := range before {
names = append(names, p.Name)
}
switch {
case release < 12:
return packNames, fmt.Errorf(
"Support expired. %s, %s", o.Family, o.Release)
case 12 < release && release < 14:
cmd += `| grep 'pkgs that look like they should be upgraded:' |
sed -e 's/pkgs that look like they should be upgraded://g'`
case 14 < release:
cmd += `| grep 'Packages that will be upgraded:' |
sed -e 's/Packages that will be upgraded://g'`
default:
return packNames, fmt.Errorf(
"Not supported yet. %s, %s", o.Family, o.Release)
}
cmd := fmt.Sprintf("LANGUAGE=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
r := o.ssh(cmd, sudo)
if r.isSuccess(0, 1) {
packNames = strings.Split(strings.TrimSpace(r.Stdout), " ")
return packNames, nil
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s.", r)
}
return packNames, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
packChangelog := o.splitAptCachePolicy(r.Stdout)
for k, v := range packChangelog {
ver, err := o.parseAptCachePolicy(v, k)
if err != nil {
return nil, fmt.Errorf("Failed to parse %s", err)
}
p, found := before.FindByName(k)
if !found {
return nil, fmt.Errorf("Not found: %s", k)
}
p.NewVersion = ver.Candidate
filled = append(filled, p)
}
return
}
func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
cmd := util.PrependProxyEnv("LANG=en_US.UTF-8 apt-get upgrade --dry-run")
cmd := util.PrependProxyEnv("LANGUAGE=en_US.UTF-8 apt-get upgrade --dry-run")
r := o.ssh(cmd, sudo)
if r.isSuccess(0, 1) {
return o.parseAptGetUpgrade(r.Stdout)
@@ -404,8 +357,8 @@ func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
}
func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, err error) {
startRe, _ := regexp.Compile(`The following packages will be upgraded:`)
stopRe, _ := regexp.Compile(`^(\d+) upgraded.*`)
startRe := regexp.MustCompile(`The following packages will be upgraded:`)
stopRe := regexp.MustCompile(`^(\d+) upgraded.*`)
startLineFound, stopLineFound := false, false
lines := strings.Split(stdout, "\n")
@@ -446,9 +399,11 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
}
func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePacksList CvePacksList, err error) {
// { CVE ID: [packageInfo] }
cvePackages := make(map[string][]models.PackageInfo)
meta := cache.Meta{
Name: o.getServerInfo().GetServerName(),
Distro: o.getServerInfo().Distro,
Packs: unsecurePacks,
}
type strarray []string
resChan := make(chan struct {
@@ -475,6 +430,18 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
select {
case pack := <-reqChan:
func(p models.PackageInfo) {
changelog := o.getChangelogCache(meta, p)
if 0 < len(changelog) {
cveIDs := o.getCveIDFromChangelog(changelog, p.Name, p.Version)
resChan <- struct {
models.PackageInfo
strarray
}{p, cveIDs}
return
}
// if the changelog is not in cache or failed to get from local cache,
// get the changelog of the package via internet.
if cveIDs, err := o.scanPackageCveIDs(p); err != nil {
errChan <- err
} else {
@@ -488,6 +455,8 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
}
}
// { CVE ID: [packageInfo] }
cvePackages := make(map[string][]models.PackageInfo)
errs := []error{}
for i := 0; i < len(unsecurePacks); i++ {
select {
@@ -506,7 +475,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
return nil, fmt.Errorf("Timeout scanPackageCveIDs")
}
}
if 0 < len(errs) {
return nil, fmt.Errorf("%v", errs)
}
@@ -515,7 +483,6 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
for k := range cvePackages {
cveIDs = append(cveIDs, k)
}
o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
o.log.Info("Fetching CVE details...")
@@ -536,9 +503,32 @@ func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePac
return
}
func (o *debian) getChangelogCache(meta cache.Meta, pack models.PackageInfo) string {
cachedPack, found := meta.FindPack(pack.Name)
if !found {
return ""
}
if cachedPack.NewVersion != pack.NewVersion {
return ""
}
changelog, err := cache.DB.GetChangelog(meta.Name, pack.Name)
if err != nil {
o.log.Warnf("Failed to get changelog. bucket: %s, key:%s, err: %s",
meta.Name, pack.Name, err)
return ""
}
if len(changelog) == 0 {
return ""
}
o.log.Debugf("Cache hit: %s, len: %d, %s...",
meta.Name, len(changelog), util.Truncate(changelog, 30))
return changelog
}
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
cmd := ""
switch o.Family {
switch o.Distro.Family {
case "ubuntu":
cmd = fmt.Sprintf(`apt-get changelog %s | grep '\(urgency\|CVE\)'`, pack.Name)
case "debian":
@@ -551,37 +541,42 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
o.log.Warnf("Failed to SSH: %s", r)
// Ignore this Error.
return nil, nil
}
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, pack.Version)
if 0 < len(strings.TrimSpace(r.Stdout)) {
err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, r.Stdout)
if err != nil {
return nil, fmt.Errorf("Failed to put changelog into cache")
}
}
// No error will be returned. Only logging.
return o.getCveIDFromChangelog(r.Stdout, pack.Name, pack.Version), nil
}
func (o *debian) getCveIDParsingChangelog(changelog string,
packName string, versionOrLater string) (cveIDs []string, err error) {
func (o *debian) getCveIDFromChangelog(changelog string,
packName string, versionOrLater string) []string {
cveIDs, err = o.parseChangelog(changelog, packName, versionOrLater)
if err == nil {
return
if cveIDs, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil {
return cveIDs
}
ver := strings.Split(versionOrLater, "ubuntu")[0]
cveIDs, err = o.parseChangelog(changelog, packName, ver)
if err == nil {
return
if cveIDs, err := o.parseChangelog(changelog, packName, ver); err == nil {
return cveIDs
}
splittedByColon := strings.Split(versionOrLater, ":")
if 1 < len(splittedByColon) {
ver = splittedByColon[1]
}
cveIDs, err = o.parseChangelog(changelog, packName, ver)
cveIDs, err := o.parseChangelog(changelog, packName, ver)
if err == nil {
return
return cveIDs
}
//TODO report as unable to parse changelog.
// Only logging the error.
o.log.Error(err)
return []string{}, nil
return []string{}
}
// Collect CVE-IDs included in the changelog.
@@ -589,8 +584,8 @@ func (o *debian) getCveIDParsingChangelog(changelog string,
func (o *debian) parseChangelog(changelog string,
packName string, versionOrLater string) (cveIDs []string, err error) {
cveRe, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
stopRe, _ := regexp.Compile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
stopLineFound := false
lines := strings.Split(changelog, "\n")
for _, line := range lines {
@@ -598,7 +593,7 @@ func (o *debian) parseChangelog(changelog string,
o.log.Debugf("Found the stop line. line: %s", line)
stopLineFound = true
break
} else if matches := cveRe.FindAllString(line, -1); len(matches) > 0 {
} else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
for _, m := range matches {
cveIDs = util.AppendIfMissing(cveIDs, m)
}
@@ -614,6 +609,29 @@ func (o *debian) parseChangelog(changelog string,
return
}
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
// re := regexp.MustCompile(`(?m:^[^ \t]+:$)`)
re := regexp.MustCompile(`(?m:^[^ \t]+:\r\n)`)
ii := re.FindAllStringIndex(stdout, -1)
ri := []int{}
for i := len(ii) - 1; 0 <= i; i-- {
ri = append(ri, ii[i][0])
}
splitted := []string{}
lasti := len(stdout)
for _, i := range ri {
splitted = append(splitted, stdout[i:lasti])
lasti = i
}
packChangelog := map[string]string{}
for _, r := range splitted {
packName := r[:strings.Index(r, ":")]
packChangelog[packName] = r
}
return packChangelog
}
type packCandidateVer struct {
Name string
Installed string

View File

@@ -18,10 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package scan
import (
"os"
"reflect"
"testing"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
)
@@ -54,7 +58,7 @@ func TestParseScannedPackagesLineDebian(t *testing.T) {
}
func TestgetCveIDParsingChangelog(t *testing.T) {
func TestGetCveIDParsingChangelog(t *testing.T) {
var tests = []struct {
in []string
@@ -86,12 +90,11 @@ systemd (227-1) unstable; urgency=medium`,
"CVE-2015-3210",
},
},
{
// ver
[]string{
"libpcre3",
"2:8.38-1ubuntu1",
"2:8.35-7.1ubuntu1",
`pcre3 (2:8.38-2) unstable; urgency=low
pcre3 (2:8.38-1) unstable; urgency=low
pcre3 (2:8.35-8) unstable; urgency=low
@@ -110,7 +113,6 @@ pcre3 (2:8.35-7) unstable; urgency=medium`,
"CVE-2015-3210",
},
},
{
// ver-ubuntu3
[]string{
@@ -151,7 +153,7 @@ sysvinit (2.88dsf-57) unstable; urgency=low`,
util-linux (2.27.1-3) unstable; urgency=medium
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
util-linux (2.27.1-2) unstable; urgency=medium
util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
util-linux (2.27.1-1ubuntu3) xenial; urgency=medium
@@ -178,15 +180,17 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
"CVE-2015-2325",
"CVE-2015-2326",
"CVE-2015-3210",
"CVE-2016-1000000",
},
},
}
d := newDebian(config.ServerInfo{})
for _, tt := range tests {
actual, _ := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], tt.in[1])
actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1])
if len(actual) != len(tt.expected) {
t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual)
t.Errorf(pp.Sprintf("%s", tt.in))
continue
}
for i := range tt.expected {
@@ -195,13 +199,6 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
}
}
}
for _, tt := range tests {
_, err := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], "version number do'nt match case")
if err != nil {
t.Errorf("Returning error is unexpected")
}
}
}
func TestGetUpdatablePackNames(t *testing.T) {
@@ -520,6 +517,95 @@ Calculating upgrade... Done
}
}
func TestGetChangelogCache(t *testing.T) {
const servername = "server1"
pack := models.PackageInfo{
Name: "apt",
Version: "1.0.0",
NewVersion: "1.0.1",
}
var meta = cache.Meta{
Name: servername,
Distro: config.Distro{
Family: "ubuntu",
Release: "16.04",
},
Packs: []models.PackageInfo{pack},
}
const path = "/tmp/vuls-test-cache-11111111.db"
log := logrus.NewEntry(&logrus.Logger{})
if err := cache.SetupBolt(path, log); err != nil {
t.Errorf("Failed to setup bolt: %s", err)
}
defer os.Remove(path)
if err := cache.DB.EnsureBuckets(meta); err != nil {
t.Errorf("Failed to ensure buckets: %s", err)
}
d := newDebian(config.ServerInfo{})
actual := d.getChangelogCache(meta, pack)
if actual != "" {
t.Errorf("Failed to get empty stirng from cache:")
}
clog := "changelog-text"
if err := cache.DB.PutChangelog(servername, "apt", clog); err != nil {
t.Errorf("Failed to put changelog: %s", err)
}
actual = d.getChangelogCache(meta, pack)
if actual != clog {
t.Errorf("Failed to get changelog from cache: %s", actual)
}
// increment a version of the pack
pack.NewVersion = "1.0.2"
actual = d.getChangelogCache(meta, pack)
if actual != "" {
t.Errorf("The changelog is not invalidated: %s", actual)
}
// change a name of the pack
pack.Name = "bash"
actual = d.getChangelogCache(meta, pack)
if actual != "" {
t.Errorf("The changelog is not invalidated: %s", actual)
}
}
func TestSplitAptCachePolicy(t *testing.T) {
var tests = []struct {
stdout string
expected map[string]string
}{
// This function parse apt-cache policy by using Regexp multi-line mode.
// So, test data includes "\r\n"
{
"apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\napt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\nbase-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
map[string]string{
"apt": "apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
"apt-utils": "apt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
"base-files": "base-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
},
},
}
d := newDebian(config.ServerInfo{})
for _, tt := range tests {
actual := d.splitAptCachePolicy(tt.stdout)
if !reflect.DeepEqual(tt.expected, actual) {
e := pp.Sprintf("%v", tt.expected)
a := pp.Sprintf("%v", actual)
t.Errorf("expected %s, actual %s", e, a)
}
}
}
func TestParseAptCachePolicy(t *testing.T) {
var tests = []struct {

View File

@@ -1,3 +1,20 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
@@ -19,18 +36,22 @@ type bsd struct {
func newBsd(c config.ServerInfo) *bsd {
d := &bsd{}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
}
//https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/freebsd.rb
func detectFreebsd(c config.ServerInfo) (itsMe bool, bsd osTypeInterface) {
bsd = newBsd(c)
c.Family = "FreeBSD"
// Prevent from adding `set -o pipefail` option
c.Distro = config.Distro{Family: "FreeBSD"}
if r := sshExec(c, "uname", noSudo); r.isSuccess() {
if strings.Contains(r.Stdout, "FreeBSD") == true {
if b := sshExec(c, "uname -r", noSudo); b.isSuccess() {
bsd.setDistributionInfo("FreeBSD", strings.TrimSpace(b.Stdout))
bsd.setServerInfo(c)
rel := strings.TrimSpace(b.Stdout)
bsd.setDistro("FreeBSD", rel)
return true, bsd
}
}
@@ -45,6 +66,10 @@ func (o *bsd) checkIfSudoNoPasswd() error {
return nil
}
func (o *bsd) checkDependencies() error {
return nil
}
func (o *bsd) install() error {
return nil
}

View File

@@ -139,17 +139,17 @@ WWW: https://vuxml.FreeBSD.org/freebsd/ab3e98d9-8175-11e4-907d-d050992ecde8.html
d := newBsd(config.ServerInfo{})
for _, tt := range tests {
aName, aCveIDs, aVunlnID := d.parseBlock(tt.in)
aName, aCveIDs, aVulnID := d.parseBlock(tt.in)
if tt.name != aName {
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVunlnID)
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVulnID)
}
for i := range tt.cveIDs {
if tt.cveIDs[i] != aCveIDs[i] {
t.Errorf("expected cveID: %s, actual %s", tt.cveIDs[i], aCveIDs[i])
}
}
if tt.vulnID != aVunlnID {
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVunlnID)
if tt.vulnID != aVulnID {
t.Errorf("expected vulnID: %s, actual %s", tt.vulnID, aVulnID)
}
}
}

View File

@@ -42,16 +42,16 @@ type redhat struct {
func newRedhat(c config.ServerInfo) *redhat {
r := &redhat{}
r.log = util.NewCustomLogger(c)
r.setServerInfo(c)
return r
}
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb
func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
red = newRedhat(c)
red.setServerInfo(c)
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
red.setDistributionInfo("fedora", "unknown")
red.setDistro("fedora", "unknown")
Log.Warn("Fedora not tested yet: %s", r)
return true, red
}
@@ -62,7 +62,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
// $ cat /etc/redhat-release
// CentOS release 6.5 (Final)
if r := sshExec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
re, _ := regexp.Compile(`(.*) release (\d[\d.]*)`)
re := regexp.MustCompile(`(.*) release (\d[\d.]*)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 3 {
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
@@ -72,9 +72,9 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
release := result[2]
switch strings.ToLower(result[1]) {
case "centos", "centos linux":
red.setDistributionInfo("centos", release)
red.setDistro("centos", release)
default:
red.setDistributionInfo("rhel", release)
red.setDistro("rhel", release)
}
return true, red
}
@@ -90,7 +90,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
release = fields[4]
}
}
red.setDistributionInfo(family, release)
red.setDistro(family, release)
return true, red
}
@@ -99,7 +99,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
}
func (o *redhat) checkIfSudoNoPasswd() error {
r := o.ssh("yum --version", sudo)
r := o.ssh("yum --version", o.sudo())
if !r.isSuccess() {
o.log.Errorf("sudo error on %s", r)
return fmt.Errorf("Failed to sudo: %s", r)
@@ -108,104 +108,66 @@ func (o *redhat) checkIfSudoNoPasswd() error {
return nil
}
// CentOS 5 ... yum-plugin-security, yum-changelog
// CentOS 6 ... yum-plugin-security, yum-plugin-changelog
// CentOS 7 ... yum-plugin-security, yum-plugin-changelog
// CentOS 5 ... yum-changelog
// CentOS 6 ... yum-plugin-changelog
// CentOS 7 ... yum-plugin-changelog
// RHEL, Amazon ... no additinal packages needed
func (o *redhat) install() error {
switch o.Family {
func (o *redhat) checkDependencies() error {
switch o.Distro.Family {
case "rhel", "amazon":
o.log.Infof("Nothing to do")
// o.log.Infof("Nothing to do")
return nil
}
if err := o.installYumPluginSecurity(); err != nil {
return err
}
return o.installYumChangelog()
}
func (o *redhat) installYumPluginSecurity() error {
if r := o.ssh("rpm -q yum-plugin-security", noSudo); r.isSuccess() {
o.log.Infof("Ignored: yum-plugin-security already installed")
return nil
}
o.log.Info("Installing yum-plugin-security...")
cmd := util.PrependProxyEnv("yum install -y yum-plugin-security")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
func (o *redhat) installYumChangelog() error {
if o.Family == "centos" {
case "centos":
var majorVersion int
if 0 < len(o.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
if 0 < len(o.Distro.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
} else {
return fmt.Errorf(
"Not implemented yet. family: %s, release: %s",
o.Family, o.Release)
return fmt.Errorf("Not implemented yet: %s", o.Distro)
}
var packName = ""
var name = "yum-plugin-changelog"
if majorVersion < 6 {
packName = "yum-changelog"
} else {
packName = "yum-plugin-changelog"
name = "yum-changelog"
}
cmd := "rpm -q " + packName
cmd := "rpm -q " + name
if r := o.ssh(cmd, noSudo); r.isSuccess() {
o.log.Infof("Ignored: %s already installed", packName)
return nil
}
o.lackDependencies = []string{name}
return nil
o.log.Infof("Installing %s...", packName)
cmd = util.PrependProxyEnv("yum install -y " + packName)
default:
return fmt.Errorf("Not implemented yet: %s", o.Distro)
}
}
func (o *redhat) install() error {
for _, name := range o.lackDependencies {
cmd := util.PrependProxyEnv("yum install -y " + name)
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
}
o.log.Infof("Installed: %s", packName)
o.log.Infof("Installed: %s", name)
}
return nil
}
func (o *redhat) checkRequiredPackagesInstalled() error {
if config.Conf.UseYumPluginSecurity {
// check if yum-plugin-security is installed.
// Amazon Linux, REHL can execute 'yum updateinfo --security updates' without yum-plugin-security
if o.Family == "centos" {
cmd := "rpm -q yum-plugin-security"
if r := o.ssh(cmd, noSudo); !r.isSuccess() {
msg := "yum-plugin-security is not installed"
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
}
return nil
}
if o.Family == "centos" {
if o.Distro.Family == "centos" {
var majorVersion int
if 0 < len(o.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
if 0 < len(o.Distro.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
} else {
msg := fmt.Sprintf("Not implemented yet. family: %s, release: %s", o.Family, o.Release)
msg := fmt.Sprintf("Not implemented yet: %s", o.Distro)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
var packName = ""
var packName = "yum-plugin-changelog"
if majorVersion < 6 {
packName = "yum-changelog"
} else {
packName = "yum-plugin-changelog"
}
cmd := "rpm -q " + packName
@@ -274,7 +236,7 @@ func (o *redhat) parseScannedPackagesLine(line string) (models.PackageInfo, erro
}
func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
if o.Family != "centos" || config.Conf.UseYumPluginSecurity {
if o.Distro.Family != "centos" {
// Amazon, RHEL has yum updateinfo as default
// yum updateinfo can collenct vendor advisory information.
return o.scanUnsecurePackagesUsingYumPluginSecurity()
@@ -284,10 +246,15 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
return o.scanUnsecurePackagesUsingYumCheckUpdate()
}
//TODO return whether already expired.
// For CentOS
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
cmd := "LANGUAGE=en_US.UTF-8 yum --color=never %s check-update"
if o.getServerInfo().Enablerepo != "" {
cmd = fmt.Sprintf(cmd, "--enablerepo="+o.getServerInfo().Enablerepo)
} else {
cmd = fmt.Sprintf(cmd, "")
}
cmd := "LANG=en_US.UTF-8 yum --color=never check-update"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
@@ -416,7 +383,9 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
continue
}
if needToParse {
if strings.HasPrefix(line, "Obsoleting") {
if strings.HasPrefix(line, "Obsoleting") ||
strings.HasPrefix(line, "Security:") {
// see https://github.com/future-architect/vuls/issues/165
continue
}
candidate, err := o.parseYumCheckUpdateLine(line)
@@ -433,6 +402,7 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
}
installed.NewVersion = candidate.NewVersion
installed.NewRelease = candidate.NewRelease
installed.Repository = candidate.Repository
results = append(results, installed)
}
}
@@ -441,7 +411,7 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error) {
fields := strings.Fields(line)
if len(fields) != 3 {
if len(fields) < 3 {
return models.PackageInfo{}, fmt.Errorf("Unknown format: %s", line)
}
splitted := strings.Split(fields[0], ".")
@@ -452,16 +422,19 @@ func (o *redhat) parseYumCheckUpdateLine(line string) (models.PackageInfo, error
packName = strings.Join(strings.Split(fields[0], ".")[0:(len(splitted)-1)], ".")
}
fields = strings.Split(fields[1], "-")
if len(fields) != 2 {
verfields := strings.Split(fields[1], "-")
if len(verfields) != 2 {
return models.PackageInfo{}, fmt.Errorf("Unknown format: %s", line)
}
version := o.regexpReplace(fields[0], `^[0-9]+:`, "")
release := fields[1]
version := o.regexpReplace(verfields[0], `^[0-9]+:`, "")
release := verfields[1]
repos := strings.Join(fields[2:len(fields)], " ")
return models.PackageInfo{
Name: packName,
NewVersion: version,
NewRelease: release,
Repository: repos,
}, nil
}
@@ -471,18 +444,19 @@ func (o *redhat) mkPstring() *string {
}
func (o *redhat) regexpReplace(src string, pat string, rep string) string {
r := regexp.MustCompile(pat)
return r.ReplaceAllString(src, rep)
re := regexp.MustCompile(pat)
return re.ReplaceAllString(src, rep)
}
var changeLogCVEPattern = regexp.MustCompile(`CVE-[0-9]+-[0-9]+`)
func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo models.PackageInfo) string {
rpm := fmt.Sprintf("%s-%s-%s", packInfo.Name, packInfo.NewVersion, packInfo.NewRelease)
retLine := ""
if rpm2changelog[rpm] != nil {
lines := strings.Split(*rpm2changelog[rpm], "\n")
for _, line := range lines {
match, _ := regexp.MatchString("CVE-[0-9]+-[0-9]+", line)
if match {
if changeLogCVEPattern.MatchString(line) {
retLine += fmt.Sprintf("%s\n", line)
}
}
@@ -492,12 +466,10 @@ func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo
func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, error) {
var majorVersion int
if 0 < len(o.Release) && o.Family == "centos" {
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
if 0 < len(o.Distro.Release) && o.Distro.Family == "centos" {
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
} else {
return nil, fmt.Errorf(
"Not implemented yet. family: %s, release: %s",
o.Family, o.Release)
return nil, fmt.Errorf("Not implemented yet: %s", o.getDistro())
}
orglines := strings.Split(allChangelog, "\n")
@@ -510,7 +482,7 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
/* for CentOS5 (yum-util < 1.1.20) */
prev = false
now = false
if i > 0 {
if 0 < i {
prev, err = o.isRpmPackageNameLine(orglines[i-1])
if err != nil {
return nil, err
@@ -560,8 +532,7 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
rpm2changelog[rpm] = pNewString
}
} else {
stop, _ := regexp.MatchString("^Dependencies Resolved", line)
if stop {
if strings.HasPrefix(line, "Dependencies Resolved") {
return rpm2changelog, nil
}
*writePointer += fmt.Sprintf("%s\n", line)
@@ -570,22 +541,27 @@ func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, err
return rpm2changelog, nil
}
// CentOS
func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout string, err error) {
packageNames := ""
for _, packInfo := range packInfoList {
packageNames += fmt.Sprintf("%s ", packInfo.Name)
}
command := ""
if o.ServerInfo.User == "root" {
command = "echo N | "
}
command := "echo N | "
if 0 < len(config.Conf.HTTPProxy) {
command += util.ProxyEnv()
}
yumopts := ""
if o.getServerInfo().Enablerepo != "" {
yumopts = " --enablerepo=" + o.getServerInfo().Enablerepo
}
if config.Conf.SkipBroken {
yumopts += " --skip-broken"
}
// yum update --changelog doesn't have --color option.
command += fmt.Sprintf(" LANG=en_US.UTF-8 yum update --changelog %s", packageNames)
command += fmt.Sprintf(" LANGUAGE=en_US.UTF-8 yum %s --changelog update ", yumopts) + packageNames
r := o.ssh(command, sudo)
if !r.isSuccess(0, 1) {
@@ -602,9 +578,9 @@ type distroAdvisoryCveIDs struct {
}
// Scaning unsecure packages using yum-plugin-security.
//TODO return whether already expired.
// Amazon, RHEL
func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, error) {
if o.Family == "centos" {
if o.Distro.Family == "centos" {
// CentOS has no security channel.
// So use yum check-update && parse changelog
return CvePacksList{}, fmt.Errorf(
@@ -612,14 +588,14 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
}
cmd := "yum --color=never repolist"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
r := o.ssh(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
// get advisoryID(RHSA, ALAS) - package name,version
cmd = "yum --color=never updateinfo list available --security"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -627,8 +603,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
// get package name, version, rel to be upgrade.
// cmd = "yum check-update --security"
cmd = "LANG=en_US.UTF-8 yum --color=never check-update"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
cmd = "LANGUAGE=en_US.UTF-8 yum --color=never check-update"
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess(0, 100) {
//returns an exit code of 100 if there are available updates.
return nil, fmt.Errorf("Failed to SSH: %s", r)
@@ -656,7 +632,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
// get advisoryID(RHSA, ALAS) - CVE IDs
cmd = "yum --color=never updateinfo --security update"
r = o.ssh(util.PrependProxyEnv(cmd), sudo)
r = o.ssh(util.PrependProxyEnv(cmd), o.sudo())
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
@@ -706,6 +682,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
return result, nil
}
var horizontalRulePattern = regexp.MustCompile(`^=+$`)
func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveIDs, err error) {
sectionState := Outside
lines := strings.Split(stdout, "\n")
@@ -723,7 +701,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
line = strings.TrimSpace(line)
// find the new section pattern
if match, _ := o.isHorizontalRule(line); match {
if horizontalRulePattern.MatchString(line) {
// set previous section's result to return-variable
if sectionState == Content {
@@ -750,7 +728,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
switch sectionState {
case Header:
switch o.Family {
switch o.Distro.Family {
case "centos":
// CentOS has no security channel.
// So use yum check-update && parse changelog
@@ -817,9 +795,8 @@ func (o *redhat) changeSectionState(state int) (newState int) {
return newState
}
func (o *redhat) isHorizontalRule(line string) (bool, error) {
return regexp.MatchString("^=+$", line)
}
var rpmPackageArchPattern = regexp.MustCompile(
`^[^ ]+\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`)
func (o *redhat) isRpmPackageNameLine(line string) (bool, error) {
s := strings.TrimPrefix(line, "ChangeLog for: ")
@@ -829,10 +806,8 @@ func (o *redhat) isRpmPackageNameLine(line string) (bool, error) {
}
for _, s := range ss {
s = strings.TrimRight(s, " \r\n")
ok, err := regexp.MatchString(
`^[^ ]+\.(i386|i486|i586|i686|k6|athlon|x86_64|noarch|ppc|alpha|sparc)$`, s)
if !ok {
return false, err
if !rpmPackageArchPattern.MatchString(s) {
return false, nil
}
}
return true, nil
@@ -855,9 +830,10 @@ func (o *redhat) parseYumUpdateinfoHeaderCentOS(line string) (packs []models.Pac
return
}
var yumHeaderPattern = regexp.MustCompile(`(ALAS-.+): (.+) priority package update for (.+)$`)
func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdvisory, names []string, err error) {
re, _ := regexp.Compile(`(ALAS-.+): (.+) priority package update for (.+)$`)
result := re.FindStringSubmatch(line)
result := yumHeaderPattern.FindStringSubmatch(line)
if len(result) == 4 {
a.AdvisoryID = result[1]
a.Severity = result[2]
@@ -869,31 +845,36 @@ func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdv
return
}
var yumCveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
func (o *redhat) parseYumUpdateinfoLineToGetCveIDs(line string) []string {
re, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
return re.FindAllString(line, -1)
return yumCveIDPattern.FindAllString(line, -1)
}
var yumAdvisoryIDPattern = regexp.MustCompile(`^ *Update ID : (.*)$`)
func (o *redhat) parseYumUpdateinfoToGetAdvisoryID(line string) (advisoryID string, found bool) {
re, _ := regexp.Compile(`^ *Update ID : (.*)$`)
result := re.FindStringSubmatch(line)
result := yumAdvisoryIDPattern.FindStringSubmatch(line)
if len(result) != 2 {
return "", false
}
return strings.TrimSpace(result[1]), true
}
var yumIssuedPattern = regexp.MustCompile(`^\s*Issued : (\d{4}-\d{2}-\d{2})`)
func (o *redhat) parseYumUpdateinfoLineToGetIssued(line string) (date time.Time, found bool) {
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Issued : (\d{4}-\d{2}-\d{2})`)
return o.parseYumUpdateinfoLineToGetDate(line, yumIssuedPattern)
}
var yumUpdatedPattern = regexp.MustCompile(`^\s*Updated : (\d{4}-\d{2}-\d{2})`)
func (o *redhat) parseYumUpdateinfoLineToGetUpdated(line string) (date time.Time, found bool) {
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Updated : (\d{4}-\d{2}-\d{2})`)
return o.parseYumUpdateinfoLineToGetDate(line, yumUpdatedPattern)
}
func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (date time.Time, found bool) {
re, _ := regexp.Compile(regexpFormat)
result := re.FindStringSubmatch(line)
func (o *redhat) parseYumUpdateinfoLineToGetDate(line string, regexpPattern *regexp.Regexp) (date time.Time, found bool) {
result := regexpPattern.FindStringSubmatch(line)
if len(result) != 2 {
return date, false
}
@@ -904,14 +885,16 @@ func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (dat
return t, true
}
var yumDescriptionPattern = regexp.MustCompile(`^\s*Description : `)
func (o *redhat) isDescriptionLine(line string) bool {
re, _ := regexp.Compile(`^\s*Description : `)
return re.MatchString(line)
return yumDescriptionPattern.MatchString(line)
}
var yumSeverityPattern = regexp.MustCompile(`^ *Severity : (.*)$`)
func (o *redhat) parseYumUpdateinfoToGetSeverity(line string) (severity string, found bool) {
re, _ := regexp.Compile(`^ *Severity : (.*)$`)
result := re.FindStringSubmatch(line)
result := yumSeverityPattern.FindStringSubmatch(line)
if len(result) != 2 {
return "", false
}
@@ -990,3 +973,12 @@ func (o *redhat) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacks
func (o *redhat) clone() osTypeInterface {
return o
}
func (o *redhat) sudo() bool {
switch o.Distro.Family {
case "amazon":
return false
default:
return true
}
}

View File

@@ -143,11 +143,11 @@ func TestParseYumUpdateinfoLineToGetCveIDs(t *testing.T) {
[]string{"CVE-2015-0278"},
},
{
": 1195457 - nodejs-0.10.35 causes undefined symbolsCVE-2015-0278, CVE-2015-0278, CVE-2015-0277",
": 1195457 - nodejs-0.10.35 causes undefined symbolsCVE-2015-0278, CVE-2015-0278, CVE-2015-02770000000 ",
[]string{
"CVE-2015-0278",
"CVE-2015-0278",
"CVE-2015-0277",
"CVE-2015-02770000000",
},
},
}
@@ -451,7 +451,7 @@ Description : The Berkeley Internet Name Domain (BIND) is an implementation of
updated, _ := time.Parse("2006-01-02", "2015-09-04")
r := newRedhat(config.ServerInfo{})
r.Family = "redhat"
r.Distro = config.Distro{Family: "redhat"}
var tests = []struct {
in string
@@ -511,7 +511,7 @@ Description : The Berkeley Internet Name Domain (BIND) is an implementation of
func TestParseYumUpdateinfoAmazon(t *testing.T) {
r := newRedhat(config.ServerInfo{})
r.Family = "amazon"
r.Distro = config.Distro{Family: "redhat"}
issued, _ := time.Parse("2006-01-02", "2015-12-15")
updated, _ := time.Parse("2006-01-02", "2015-12-16")
@@ -601,7 +601,7 @@ Description : Package updates are available for Amazon Linux AMI that fix the
func TestParseYumCheckUpdateLines(t *testing.T) {
r := newRedhat(config.ServerInfo{})
r.Family = "centos"
r.Distro = config.Distro{Family: "centos"}
stdout := `Loaded plugins: changelog, fastestmirror, keys, protect-packages, protectbase, security
Loading mirror speeds from cached hostfile
* base: mirror.fairway.ne.jp
@@ -616,6 +616,7 @@ Obsoleting Packages
python-libs.i686 2.6.6-64.el6 rhui-REGION-rhel-server-releases
python-ordereddict.noarch 1.1-3.el6ev installed
bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
pytalloc.x86_64 2.0.7-2.el6 @CentOS 6.5/6.5
`
r.Packages = []models.PackageInfo{
@@ -644,6 +645,11 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Version: "1.0",
Release: "1",
},
{
Name: "pytalloc",
Version: "2.0.1",
Release: "0",
},
}
var tests = []struct {
in string
@@ -658,6 +664,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Release: "4.el6",
NewVersion: "2.3.7",
NewRelease: "5.el6",
Repository: "base",
},
{
Name: "bash",
@@ -665,6 +672,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Release: "33",
NewVersion: "4.1.2",
NewRelease: "33.el6_7.1",
Repository: "updates",
},
{
Name: "python-libs",
@@ -672,6 +680,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Release: "1.1-0",
NewVersion: "2.6.6",
NewRelease: "64.el6",
Repository: "rhui-REGION-rhel-server-releases",
},
{
Name: "python-ordereddict",
@@ -679,6 +688,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Release: "1",
NewVersion: "1.1",
NewRelease: "3.el6ev",
Repository: "installed",
},
{
Name: "bind-utils",
@@ -686,6 +696,15 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
Release: "1",
NewVersion: "9.3.6",
NewRelease: "25.P1.el5_11.8",
Repository: "updates",
},
{
Name: "pytalloc",
Version: "2.0.1",
Release: "0",
NewVersion: "2.0.7",
NewRelease: "2.el6",
Repository: "@CentOS 6.5/6.5",
},
},
},
@@ -709,7 +728,7 @@ bind-utils.x86_64 30:9.3.6-25.P1.el5_11.8 updates
func TestParseYumCheckUpdateLinesAmazon(t *testing.T) {
r := newRedhat(config.ServerInfo{})
r.Family = "amazon"
r.Distro = config.Distro{Family: "amazon"}
stdout := `Loaded plugins: priorities, update-motd, upgrade-helper
34 package(s) needed for security, out of 71 available
@@ -747,6 +766,7 @@ if-not-architecture 100-200 amzn-main
Release: "0.33.rc1.45.amzn1",
NewVersion: "9.8.2",
NewRelease: "0.37.rc1.45.amzn1",
Repository: "amzn-main",
},
{
Name: "java-1.7.0-openjdk",
@@ -754,6 +774,7 @@ if-not-architecture 100-200 amzn-main
Release: "2.6.4.0.0.amzn1",
NewVersion: "1.7.0.95",
NewRelease: "2.6.4.0.65.amzn1",
Repository: "amzn-main",
},
{
Name: "if-not-architecture",
@@ -761,6 +782,7 @@ if-not-architecture 100-200 amzn-main
Release: "20",
NewVersion: "100",
NewRelease: "200",
Repository: "amzn-main",
},
},
},
@@ -1110,8 +1132,10 @@ func TestGetChangelogCVELines(t *testing.T) {
}
r := newRedhat(config.ServerInfo{})
r.Family = "centos"
r.Release = "6.7"
r.Distro = config.Distro{
Family: "centos",
Release: "6.7",
}
for _, tt := range testsCentos6 {
rpm2changelog, err := r.parseAllChangelog(stdoutCentos6)
if err != nil {
@@ -1194,7 +1218,10 @@ func TestGetChangelogCVELines(t *testing.T) {
},
}
r.Release = "5.6"
r.Distro = config.Distro{
Family: "centos",
Release: "5.6",
}
for _, tt := range testsCentos5 {
rpm2changelog, err := r.parseAllChangelog(stdoutCentos5)
if err != nil {

View File

@@ -1,10 +1,31 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
"bufio"
"fmt"
"os"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
cve "github.com/kotakanbe/go-cve-dictionary/models"
@@ -20,8 +41,12 @@ type osTypeInterface interface {
setServerInfo(config.ServerInfo)
getServerInfo() config.ServerInfo
setDistributionInfo(string, string)
getDistributionInfo() string
setDistro(string, string)
getDistro() config.Distro
// checkDependencies checks if dependencies are installed on the target server.
checkDependencies() error
getLackDependencies() []string
checkIfSudoNoPasswd() error
detectPlatform() error
@@ -41,7 +66,7 @@ type osTypeInterface interface {
setErrs([]error)
}
// osPackages included by linux struct
// osPackages is included by base struct
type osPackages struct {
// installed packages
Packages models.PackageInfoList
@@ -151,11 +176,20 @@ func PrintSSHableServerNames() {
}
// InitServers detect the kind of OS distribution of target servers
func InitServers(localLogger *logrus.Entry) {
func InitServers(localLogger *logrus.Entry) error {
Log = localLogger
servers = detectServerOSes()
if len(servers) == 0 {
return fmt.Errorf("No scannable servers")
}
containers := detectContainerOSes()
servers = append(servers, containers...)
if config.Conf.ContainersOnly {
servers = containers
} else {
servers = append(servers, containers...)
}
return nil
}
func detectServerOSes() (sshAbleOses []osTypeInterface) {
@@ -188,7 +222,7 @@ func detectServerOSes() (sshAbleOses []osTypeInterface) {
Log.Infof("(%d/%d) Detected: %s: %s",
i+1, len(config.Conf.Servers),
res.getServerInfo().ServerName,
res.getDistributionInfo())
res.getDistro())
}
case <-timeout:
msg := "Timed out while detecting servers"
@@ -228,7 +262,7 @@ func detectContainerOSes() (actives []osTypeInterface) {
defer func() {
if p := recover(); p != nil {
Log.Debugf("Panic: %s on %s",
p, s.getServerInfo().ServerName)
p, s.getServerInfo().GetServerName())
}
}()
osTypesChan <- detectContainerOSesOnServer(s)
@@ -248,7 +282,7 @@ func detectContainerOSes() (actives []osTypeInterface) {
}
oses = append(oses, res...)
Log.Infof("Detected: %s@%s: %s",
sinfo.Container.Name, sinfo.ServerName, osi.getDistributionInfo())
sinfo.Container.Name, sinfo.ServerName, osi.getDistro())
}
case <-timeout:
msg := "Timed out while detecting containers"
@@ -350,7 +384,7 @@ func detectContainerOSesOnServer(containerHost osTypeInterface) (oses []osTypeIn
// CheckIfSudoNoPasswd checks whether vuls can sudo with nopassword via SSH
func CheckIfSudoNoPasswd(localLogger *logrus.Entry) error {
timeoutSec := 1 * 15
timeoutSec := 15
errs := parallelSSHExec(func(o osTypeInterface) error {
return o.checkIfSudoNoPasswd()
}, timeoutSec)
@@ -397,12 +431,65 @@ func detectPlatforms() []error {
// Prepare installs requred packages to scan vulnerabilities.
func Prepare() []error {
return parallelSSHExec(func(o osTypeInterface) error {
errs := parallelSSHExec(func(o osTypeInterface) error {
if err := o.checkDependencies(); err != nil {
return err
}
return nil
})
if len(errs) != 0 {
return errs
}
var targets []osTypeInterface
for _, s := range servers {
deps := s.getLackDependencies()
if len(deps) != 0 {
targets = append(targets, s)
}
}
if len(targets) == 0 {
Log.Info("No need to install dependencies")
return nil
}
Log.Info("Below servers are needed to install dependencies")
for _, s := range targets {
for _, d := range s.getLackDependencies() {
Log.Infof(" - %s on %s", d, s.getServerInfo().GetServerName())
}
}
Log.Info("Is this ok to install dependencies on the servers? [y/N]")
reader := bufio.NewReader(os.Stdin)
for {
text, err := reader.ReadString('\n')
if err != nil {
return []error{err}
}
switch strings.TrimSpace(text) {
case "", "N", "n":
return nil
case "y", "Y":
goto yes
default:
Log.Info("Please enter y or N")
}
}
yes:
servers = targets
errs = parallelSSHExec(func(o osTypeInterface) error {
if err := o.install(); err != nil {
return err
}
return nil
})
if len(errs) != 0 {
return errs
}
Log.Info("All dependencies were installed correctly")
return nil
}
// Scan scan
@@ -417,6 +504,16 @@ func Scan() []error {
return errs
}
if err := setupCangelogCache(); err != nil {
return []error{err}
}
defer func() {
if cache.DB != nil {
cache.DB.Close()
}
}()
Log.Info("Scanning vulnerable OS packages...")
if errs := scanPackages(); errs != nil {
return errs
@@ -429,6 +526,23 @@ func Scan() []error {
return nil
}
func setupCangelogCache() error {
needToSetupCache := false
for _, s := range servers {
switch s.getDistro().Family {
case "ubuntu", "debian":
needToSetupCache = true
break
}
}
if needToSetupCache {
if err := cache.SetupBolt(config.Conf.CacheDBPath, Log); err != nil {
return err
}
}
return nil
}
func checkRequiredPackagesInstalled() []error {
timeoutSec := 30 * 60
return parallelSSHExec(func(o osTypeInterface) error {

View File

@@ -89,7 +89,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
defer func() {
if p := recover(); p != nil {
logrus.Debugf("Panic: %s on %s",
p, s.getServerInfo().ServerName)
p, s.getServerInfo().GetServerName())
}
}()
if err := fn(s); err != nil {
@@ -100,7 +100,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
err,
)
} else {
resChan <- s.getServerInfo().ServerName
resChan <- s.getServerInfo().GetServerName()
}
}(s)
}
@@ -129,7 +129,7 @@ func parallelSSHExec(fn func(osTypeInterface) error, timeoutSec ...int) (errs []
var timedoutSnames []string
if isTimedout {
for _, s := range servers {
name := s.getServerInfo().ServerName
name := s.getServerInfo().GetServerName()
found := false
for _, t := range snames {
if name == t {
@@ -299,9 +299,10 @@ func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
if sudo && c.User != "root" && !c.IsContainer() {
cmd = fmt.Sprintf("sudo -S %s", cmd)
cmd = strings.Replace(cmd, "|", "| sudo ", -1)
}
if c.Family != "FreeBSD" {
if c.Distro.Family != "FreeBSD" {
// set pipefail option. Bash only
// http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another
cmd = fmt.Sprintf("set -o pipefail; %s", cmd)
@@ -318,7 +319,7 @@ func decolateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
}
func getAgentAuth() (auth ssh.AuthMethod, ok bool) {
if sock := os.Getenv("SSH_AUTH_SOCK"); len(sock) > 0 {
if sock := os.Getenv("SSH_AUTH_SOCK"); 0 < len(sock) {
if agconn, err := net.Dial("unix", sock); err == nil {
ag := agent.NewClient(agconn)
auth = ssh.PublicKeysCallback(ag.Signers)

View File

@@ -1,101 +0,0 @@
# Vuls on Docker
## What's Vuls-On-Docker
- 数個のコマンドを実行するだけでVulsとvulsrepoのセットアップが出来るスクリプト
- Dockerコンテナ上にVulsと[vulsrepo](https://github.com/usiusi360/vulsrepo)をセットアップ可能
- スキャン結果をvulsrepoでブラウザで分析可能
- 脆弱性データベースの更新が可能
- モジュールのアップデートが可能
## Setting up your machine
1. [Install Docker](https://docs.docker.com/engine/installation/)
2. [Install Docker-Compose](https://docs.docker.com/compose/install/)
3. 実行前に以下のコマンドが実行可能なことを確認する
```
$ docker version
$ docker-compose version
```
4. Vulsをgit clone
```
mkdir work
cd work
git clone https://github.com/future-architect/vuls.git
cd vuls/setup/docker
```
## Start A Vuls Container
- 以下のコマンドを実行してコンテナをビルドする
```
$ docker-compose up -d
```
## Setting up Vuls
1. スキャン対象サーバのSSH秘密鍵を保存(vuls/setup/docker/conf/)する
2. config.toml(vuls/docker/conf/config.toml) を環境に合わせて作成する
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "conf/id_rsa"
```
## Fetch Vulnerability database
- NVDから脆弱性データベースを取得する
```
$ docker exec -t vuls scripts/fetch_nvd_all.sh
```
- レポートを日本語化する場合は、JVNから脆弱性データを取得する
```
$ docker exec -t vuls scripts/fetch_jvn_all.sh
```
## Scan servers with Vuls-On-Docker
- スキャンを実行する
```
$ docker exec -t vuls vuls prepare -config=conf/config.toml
$ docker exec -t vuls scripts/scan_for_vulsrepo.sh
```
## See the results in a browser
```
http://${Vuls_Host}/vulsrepo/
```
# Update modules
- vuls, go-cve-dictionary, vulsrepoのモジュールをアップデートする
```
$ docker exec -t vuls scripts/update_modules.sh
```
# Update Vulnerability database
- NVDの過去年分の脆弱性データベースを更新する
```
$ 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
```

View File

@@ -1,87 +1,183 @@
# Vuls on Docker
# Vuls Docker components
## What's Vuls-On-Docker
This is the Git repo of the official Docker image for vuls.
- This is a dockernized-Vuls with vulsrepo UI in it.
- It's designed to reduce the cost of installation and the dependencies that vuls requires.
- You can run install and run Vuls on your machine with only a few commands.
- The result can be viewed with a browser
# Supported tags and respective `Dockerfile` links
## Setting up your machine
1. [Install Docker](https://docs.docker.com/engine/installation/)
2. [Install Docker-Compose](https://docs.docker.com/compose/install/)
3. Make sure that you can run the following commands before you move on.
- go-cve-dictionary
- [`latest` (*go-cve-dictionary:latest Dockerfile*)]()
- vuls
- [`latest` (*vuls:latest Dockerfile*)]()
- vulsrepo
- [`latest` (*vulsrepo:latest Dockerfile*)]()
```
$ docker version
$ docker-compose version
```
4. git clone vuls
```
mkdir work
cd work
git clone https://github.com/future-architect/vuls.git
cd vuls/setup/docker
```
This image version is same as the github repository version.
# Caution
This image is built per commit.
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
## Start A Vuls Container
1. Confirm your vuls version
- Execute the following command to build and run a Vuls Container
- go-cve-dictionary
```
$ docker-compose up -d
```
```console
$ docker run --rm vuls/go-cve-dictionary -v
## Setting up Vuls
1. Locate ssh-keys of targer servers in (vuls/docker/conf/)
2. Create and ajust config.toml(vuls/docker/conf/config.toml) to your environment
```
[servers]
[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "conf/id_rsa"
```
## Fetch Vulnerability database
- Fetch Vulnerability database from NVD
```
$ docker exec -t vuls scripts/fetch_nvd_all.sh
```
## Scan servers with Vuls-On-Docker
- Use the embedded script to scan servers for vulsrepo(or run whatever with docker exec)
```
$ docker exec -t vuls vuls prepare -config=conf/config.toml
$ docker exec -t vuls scripts/scan_for_vulsrepo.sh
```
## See the results in a browser
```
http://${Vuls_Host}/vulsrepo/
go-cve-dictionary v0.0.xxx xxxx
```
# Update modules
- vuls
- update vuls, go-cve-dictionary, vulsrepo
```
$ docker exec -t vuls scripts/update_modules.sh
```
```console
$ docker run --rm vuls/vuls -v
# Update Vulnerability database
vuls v0.0.xxx xxxx
```
- Fetch Vulnerability database from NVD
```
$ docker exec -t vuls scripts/fetch_nvd_last2y.sh
```
2. Remove your old docker images
- go-cve-dictionary
```
$ docker rmi vuls/go-cve-dictionary
```
```
$ docker rmi vuls/vuls
```
- vuls
```
$ docker rmi vuls/vuls
```
3. Pull new vuls docker images
- go-cve-dictionary
```
$ docker pull vuls/go-cve-dictionary
```
- vuls
```
$ docker pull vuls/vuls
```
4. Confirm your vuls version
```console
$ docker run --rm vuls/go-cve-dictionary -v
go-cve-dictionary v0.1.xxx xxxx
```
- vuls
```console
$ docker run --rm vuls/vuls -v
vuls v0.1.xxx xxxx
```
# How to use this image
1. fetch nvd (vuls/go-cve-dictionary)
1. configuration (vuls/vuls)
1. prepare (vuls/vuls)
1. scan (vuls/vuls)
1. vulsrepo (vuls/vulsrepo)
## Step1. Fetch NVD
```console
$ for i in {2002..2016}; do \
docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
vuls/go-cve-dictionary fetchnvd -years $i; \
done
```
## Step2. Configuration
Create config.toml referring to [this](https://github.com/future-architect/vuls#configuration).
```toml
[servers]
[servers.amazon]
host = "54.249.93.16"
port = "22"
user = "vuls-user"
keyPath = "/root/.ssh/id_rsa" # path to ssh private key in docker
```
 
```console
$ docker run --rm \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
vuls/vuls configtest \
-config=./config.toml # path to config.toml in docker
```
## Step3. Prepare
```console
$ docker run --rm \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
vuls/vuls prepare \
-config=./config.toml # path to config.toml in docker
```
## Step4. Scan
```console
$ docker run --rm -it \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
-v /etc/localtime:/etc/localtime:ro \
-e "TZ=Asia/Tokyo" \
vuls/vuls scan \
-cve-dictionary-dbpath=/vuls/cve.sqlite3 \
-report-json \
-config=./config.toml # path to config.toml in docker
```
## Step5. vulsrepo
```console
$docker run -dt \
-v $PWD:/vuls \
-p 80:80 \
vuls/vulsrepo
```
# User Feedback
## Documentation
Documentation for this image is stored in the [`docker/` directory]() of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
## Contributing
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
1. get original code: go get github.com/future-architect/vuls
1. work on original code
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
1. push your changes: git push myfork
1. create a new Pull Request

View File

@@ -1,11 +0,0 @@
version: '2'
services:
vuls:
container_name: vuls
build: ./dockerfile
image: vuls-docker:0.1
volumes:
- ./conf:/opt/vuls/conf
ports:
- "80:80"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
#!/bin/bash
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,89 @@
# go-cve-dictionary-Docker
This is the Git repo of the official Docker image for go-cve-dictionary.
See the [Hub page](https://hub.docker.com/r/vuls/go-cve-dictionary/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
# Supported tags and respective `Dockerfile` links
- [`latest` (*go-cve-dictionary:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/go-cve-dictionary/latest/Dockerfile)
# Caution
This image is built per commit.
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
- Remove old docker image
```
$ docker rmi vuls/go-cve-dictionary
```
- Pull new docker image
```
$ docker pull vuls/go-cve-dictionary
```
# What is go-cve-dictionary?
This is tool to build a local copy of the NVD (National Vulnerabilities Database) [1] and the Japanese JVN [2], which contain security vulnerabilities according to their CVE identifiers [3] including exhaustive information and a risk score. The local copy is generated in sqlite format, and the tool has a server mode for easy querying.
[1] https://en.wikipedia.org/wiki/National_Vulnerability_Database
[2] https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures
[3] http://jvndb.jvn.jp/apis/termsofuse.html
# How to use this image
## check vuls version
```
$ docker run --rm vuls/go-cve-dictionary -v
```
## fetchnvd
```console
$ for i in {2002..2016}; do \
docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
vuls/go-cve-dictionary fetchnvd -years $i; \
done
```
## server
```console
$ docker run -dt \
--name go-cve-dictionary \
-v $PWD:/vuls \
-v $PWD/go-cve-dictionary-log:/var/log/vuls \
--expose 1323 \
-p 1323:1323 \
vuls/go-cve-dictionary server --bind=0.0.0.0
```
Prease refer to [this](https://hub.docker.com/r/vuls/go-cve-dictionary).
## vuls
Please refer to [this](https://hub.docker.com/r/vuls/vuls/).
# User Feedback
## Documentation
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
## Contributing
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
1. get original code: go get github.com/future-architect/vuls
1. work on original code
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
1. push your changes: git push myfork
1. create a new Pull Request

View File

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

View File

@@ -0,0 +1,121 @@
# Vuls-Docker
This is the Git repo of the official Docker image for vuls.
See the [Hub page](https://hub.docker.com/r/vuls/vuls/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
# Supported tags and respective `Dockerfile` links
- [`latest` (*vuls:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/vuls/latest/Dockerfile)
# Caution
This image is built per commit.
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
- Remove old docker image
```
$ docker rmi vuls/vuls
```
- Pull new docker image
```
$ docker pull vuls/vuls
```
# What is Vuls?
Vuls is the Vulnerability scanner for Linux/FreeBSD, agentless, written in golang.
Please see the [Documentation](https://github.com/future-architect/vuls)
![logo](https://github.com/future-architect/vuls/blob/master/img/vuls_logo.png?raw=true)
# How to use this image
## check vuls version
```
$ docker run --rm vuls/vuls -v
```
## configtest
Create config.toml referring to [this](https://github.com/future-architect/vuls#configuration).
```toml
[servers]
[servers.amazon]
host = "54.249.93.16"
port = "22"
user = "vuls-user"
keyPath = "/root/.ssh/id_rsa" # path to ssh private key in docker
```
 
```console
$ docker run --rm \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
vuls/vuls configtest
```
## prepare
```console
$ docker run --rm \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
vuls/vuls prepare \
-config=./config.toml # path to config.toml in docker
```
## scan
```console
$ docker run --rm -it \
-v ~/.ssh:/root/.ssh:ro \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
-v /etc/localtime:/etc/localtime:ro \
vuls/vuls scan \
-cve-dictionary-dbpath=/vuls/cve.sqlite3 \
-config=./config.toml \ # path to config.toml in docker
-report-json
```
## tui
```console
$ docker run --rm -it \
-v $PWD:/vuls \
-v $PWD/vuls-log:/var/log/vuls \
vuls/vuls tui
```
## vulsrepo
Prease refer to [this](https://hub.docker.com/r/vuls/vulsrepo/).
# User Feedback
## Documentation
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
## Contributing
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
1. get original code: go get github.com/future-architect/vuls
1. work on original code
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
1. push your changes: git push myfork
1. create a new Pull Request

View File

@@ -0,0 +1,31 @@
FROM httpd:2.4
MAINTAINER hikachan sadayuki-matsuno
# install packages
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
vim \
git \
libcgi-pm-perl \
libjson-perl \
&& rm -r /var/lib/apt/lists/*
# env
ENV HTTPD_PREFIX /usr/local/apache2
VOLUME /vuls
WORKDIR ${HTTPD_PREFIX}/htdocs
RUN git clone https://github.com/usiusi360/vulsrepo.git \
&& echo "LoadModule cgid_module modules/mod_cgid.so" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo "<Directory \"$HTTPD_PREFIX/htdocs/vulsrepo/dist/cgi\">" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo " Options +ExecCGI +FollowSymLinks" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo " AddHandler cgi-script cgi" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo "</Directory>" >> $HTTPD_PREFIX/conf/httpd.conf \
&& sed -i -e 's/User daemon/#User/g' $HTTPD_PREFIX/conf/httpd.conf \
&& sed -i -e 's/Group daemon/#Group/g' $HTTPD_PREFIX/conf/httpd.conf \
&& ln -snf /vuls/results /usr/local/apache2/htdocs/vulsrepo/results
EXPOSE 80
CMD ["httpd-foreground"]

View File

@@ -0,0 +1,47 @@
# VulsRepo-Docker
This is the Git repo of the official Docker image for vulsrepo.
See the [Hub page](https://hub.docker.com/r/vuls/vulsrepo/) for the full readme on how to use the Docker image and for information regarding contributing and issues.
# Supported tags and respective `Dockerfile` links
- [`latest` (*vulsrepo:latest Dockerfile*)](https://github.com/future-architect/vuls/blob/master/setup/docker/vulsrepo/latest/Dockerfile)
# Caution
This image is built per commit.
If you want to use the latest docker image, you should remove the existing image, and pull it once again.
# What is vulsrepo?
VulsRepo is visualized based on the json report output in [vuls](https://github.com/future-architect/vuls).
# How to use this image
## vulsrepo
```console
$docker run -dt \
-v $PWD:/vuls \
-p 80:80 \
vuls/vulsrepo
```
# User Feedback
## Documentation
Documentation for this image is stored in the [`docker/` directory](https://github.com/future-architect/vuls/tree/master/setup/docker) of the [`future-architect/vuls` GitHub repo](https://github.com/future-architect/vuls).
## Issues
If you have any problems with or questions about this image, please contact us through a [GitHub issue](https://github.com/future-architect/vuls/issues).
## Contributing
1. fork a repository: github.com/future-architect/vuls to github.com/you/repo
1. get original code: go get github.com/future-architect/vuls
1. work on original code
1. add remote to your repo: git remote add myfork https://github.com/you/repo.git
1. push your changes: git push myfork
1. create a new Pull Request

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package util
import (
"fmt"
"os"
"path/filepath"
"runtime"
@@ -53,12 +52,7 @@ func NewCustomLogger(c config.ServerInfo) *logrus.Entry {
whereami := "localhost"
if 0 < len(c.ServerName) {
if 0 < len(c.Container.ContainerID) {
whereami = fmt.Sprintf(
"%s_%s", c.ServerName, c.Container.Name)
} else {
whereami = fmt.Sprintf("%s", c.ServerName)
}
whereami = c.GetServerName()
}
if _, err := os.Stat(logDir); err == nil {

View File

@@ -111,7 +111,7 @@ func ProxyEnv() string {
// PrependProxyEnv prepends proxy enviroment variable
func PrependProxyEnv(cmd string) string {
if config.Conf.HTTPProxy == "" {
if len(config.Conf.HTTPProxy) == 0 {
return cmd
}
return fmt.Sprintf("%s %s", ProxyEnv(), cmd)
@@ -124,3 +124,14 @@ func PrependProxyEnv(cmd string) string {
// }
// return time.Unix(i, 0), nil
// }
// Truncate truncates string to the length
func Truncate(str string, length int) string {
if length < 0 {
return str
}
if length <= len(str) {
return str[:length]
}
return str
}

View File

@@ -131,3 +131,43 @@ func TestPrependHTTPProxyEnv(t *testing.T) {
}
}
func TestTruncate(t *testing.T) {
var tests = []struct {
in string
length int
out string
}{
{
in: "abcde",
length: 3,
out: "abc",
},
{
in: "abcdefg",
length: 5,
out: "abcde",
},
{
in: "abcdefg",
length: 10,
out: "abcdefg",
},
{
in: "abcdefg",
length: 0,
out: "",
},
{
in: "abcdefg",
length: -1,
out: "abcdefg",
},
}
for _, tt := range tests {
actual := Truncate(tt.in, tt.length)
if actual != tt.out {
t.Errorf("\nexpected: %s\n actual: %s", tt.out, actual)
}
}
}

View File

@@ -1,24 +0,0 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package version
// Name is Vuls
const Name string = "vuls"
// Version of Vuls
const Version string = "0.1.5"