Compare commits

...

39 Commits

Author SHA1 Message Date
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
49 changed files with 1575 additions and 1144 deletions

2
.gitignore vendored
View File

@@ -3,6 +3,8 @@ vuls
*.txt
*.json
*.sqlite3
*.db
tags
.gitmodules
coverage.out
issues/

View File

@@ -1,5 +1,44 @@
# Change Log
## [v0.1.6](https://github.com/future-architect/vuls/tree/v0.1.6) (2016-09-12)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.5...v0.1.6)
**Implemented enhancements:**
- High speed scan on Ubuntu/Debian [\#172](https://github.com/future-architect/vuls/pull/172) ([kotakanbe](https://github.com/kotakanbe))
- Support CWE\(Common Weakness Enumeration\) [\#169](https://github.com/future-architect/vuls/pull/169) ([kotakanbe](https://github.com/kotakanbe))
- Enable to scan without sudo on amazon linux [\#167](https://github.com/future-architect/vuls/pull/167) ([kotakanbe](https://github.com/kotakanbe))
- Remove deprecated options -use-unattended-upgrades,-use-yum-plugin-security [\#161](https://github.com/future-architect/vuls/pull/161) ([kotakanbe](https://github.com/kotakanbe))
- delete sqlite3 [\#152](https://github.com/future-architect/vuls/pull/152) ([sadayuki-matsuno](https://github.com/sadayuki-matsuno))
**Fixed bugs:**
- Failed to setup vuls docker [\#170](https://github.com/future-architect/vuls/issues/170)
- yum check-update error occurred when no reboot after kernel updating [\#165](https://github.com/future-architect/vuls/issues/165)
- error thrown from 'docker build .' [\#157](https://github.com/future-architect/vuls/issues/157)
- CVE-ID is truncated to 4 digits [\#153](https://github.com/future-architect/vuls/issues/153)
- 'yum update --changelog' stalled in 'vuls scan'. if ssh user is not 'root'. [\#150](https://github.com/future-architect/vuls/issues/150)
- Panic on packet scan [\#131](https://github.com/future-architect/vuls/issues/131)
- Update glide.lock \#170 [\#171](https://github.com/future-architect/vuls/pull/171) ([kotakanbe](https://github.com/kotakanbe))
- Fix detecting a platform on Azure [\#168](https://github.com/future-architect/vuls/pull/168) ([kotakanbe](https://github.com/kotakanbe))
- Fix parse error for yum check-update \#165 [\#166](https://github.com/future-architect/vuls/pull/166) ([kotakanbe](https://github.com/kotakanbe))
- Fix bug: Vuls on Docker [\#159](https://github.com/future-architect/vuls/pull/159) ([tjinjin](https://github.com/tjinjin))
- Fix CVE-ID is truncated to 4 digits [\#155](https://github.com/future-architect/vuls/pull/155) ([usiusi360](https://github.com/usiusi360))
- Fix yum update --changelog stalled when non-root ssh user on CentOS \#150 [\#151](https://github.com/future-architect/vuls/pull/151) ([kotakanbe](https://github.com/kotakanbe))
**Closed issues:**
- Support su for root privilege escalation [\#44](https://github.com/future-architect/vuls/issues/44)
- Support FreeBSD [\#34](https://github.com/future-architect/vuls/issues/34)
**Merged pull requests:**
- Change scripts for data fetching from jvn [\#164](https://github.com/future-architect/vuls/pull/164) ([kotakanbe](https://github.com/kotakanbe))
- Fix: setup vulsrepo [\#162](https://github.com/future-architect/vuls/pull/162) ([tjinjin](https://github.com/tjinjin))
- Fix-docker-vulsrepo-install [\#160](https://github.com/future-architect/vuls/pull/160) ([usiusi360](https://github.com/usiusi360))
- Reduce regular expression compilation [\#158](https://github.com/future-architect/vuls/pull/158) ([itchyny](https://github.com/itchyny))
- Add testcases for \#153 [\#156](https://github.com/future-architect/vuls/pull/156) ([kotakanbe](https://github.com/kotakanbe))
## [v0.1.5](https://github.com/future-architect/vuls/tree/v0.1.5) (2016-08-16)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.1.4...v0.1.5)

View File

@@ -11,7 +11,7 @@
clean
SRCS = $(shell git ls-files '*.go')
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
PKGS = ./. ./config ./models ./report ./cveapi ./scan ./util ./commands ./cache
all: test

View File

@@ -50,6 +50,7 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり
- CIDRを指定してサーバを自動検出、設定ファイルのテンプレートを生成
- EmailやSlackで通知可能日本語でのレポートも可能
- 付属するTerminal-Based User Interfaceビューアでは、Vim風キーバインドでスキャン結果を参照可能
- Web UI([VulsRepo](https://github.com/usiusi360/vulsrepo))を使えばピボットテーブルのように分析可能
----
@@ -88,6 +89,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
@@ -271,6 +273,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
@@ -294,22 +301,24 @@ $ vuls tui
# 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 +342,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|
@@ -538,16 +547,18 @@ 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権限なしでスキャン可能
----
@@ -559,8 +570,8 @@ Prepareサブコマンドは、Vuls内部で利用する以下のパッケージ
|:------------|-------------------:|:-------------|
| 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 | - |
@@ -579,8 +590,6 @@ prepare
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-use-unattended-upgrades
[Deprecated] For Ubuntu, install unattended-upgrades
```
----
@@ -588,15 +597,15 @@ 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-dbpath=/path/to/cve.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cache-dbpath=/path/to/cache.db]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ssh-external]
@@ -619,7 +628,6 @@ scan:
[SERVER]...
-ask-key-password
Ask ssh privatekey password before scanning
-aws-profile string
@@ -634,6 +642,8 @@ 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")
-cve-dictionary-dbpath string
@@ -642,8 +652,6 @@ scan:
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 +672,10 @@ scan:
Send report via Slack
-report-text
Write report to text files ($PWD/results/current)
-results-dir string
/path/to/results (default "$PWD/results")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
-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
@@ -890,10 +895,10 @@ 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
@@ -974,7 +979,7 @@ fetchnvd:
- Fetch data of the entire period
```
$ go-cve-dictionary fetchnvd -entire
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
```
- Fetch data in the last 2 years
@@ -989,66 +994,71 @@ $ go-cve-dictionary fetchnvd -last2y
- JVNから日本語の脆弱性情報を取得
```
$ go-cve-dictionary fetchjvn -help
$ go-cve-dictionary fetchjvn -h
fetchjvn:
fetchjvn [-dump-path=$PWD/cve] [-dpath=$PWD/vuls.sqlite3] [-week] [-month] [-entire]
fetchjvn
[-latest]
[-last2y]
[-years] 1998 1999 ...
[-dbpath=$PWD/cve.sqlite3]
[-http-proxy=http://192.168.0.1:8080]
[-debug]
[-debug-sql]
-dbpath string
/path/to/sqlite3/DBfile (default "$PWD/cve.sqlite3")
/path/to/sqlite3 (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)
-http-proxy string
http://proxy-url:port (default: empty)
-last2y
Refresh JVN data in the last two years.
-latest
Refresh JVN data for latest.
-years
Refresh JVN data of specific years.
```
- すべての期間の脆弱性情報を取得(1時間以上かかる)
- すべての期間の脆弱性情報を取得(10分未満)
```
$ go-cve-dictionary fetchjvn -entire
$ for i in {1998..2016}; do ./go-cve-dictionary fetchjvn -years $i; done
```
- 直近1ヶ月間に更新された脆弱性情報を取得(1分未満)
- 2年分の情報を取得
```
$ go-cve-dictionary fetchjvn -month
$ go-cve-dictionary fetchjvn -last2y
```
- 直近1週間に更新された脆弱性情報を取得(1分未満)
- 最新情報のみ取得
```
$ go-cve-dictionary fetchjvn -week
$ go-cve-dictionary fetchjvn -latest
```
- 脆弱性情報の自動アップデート
Cronなどのジョブスケジューラを用いて実現可能。
-week オプションを指定して夜間の日次実行を推奨。
-latestオプションを指定して夜間の日次実行を推奨。
- 注意
NVDとJVNの両方の情報を取得する場合、NVDからの情報を取得した後でJVNの情報を取得することを勧める
2016年7月現在で、
## fetchnvd, fetchjvnの実行順序の注意
```
$ go-cve-dictionary fetchnvd -years 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
**fetchjvn -> fetchnvdの順番だとすごく時間がかかる** (2016年9月現在)
**fetchnvd -> fetchjvnの順番で実行すること**
$ go-cve-dictionary fetchjvn -entire
```
```
$ 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分程度環境依存
の順でやった場合、最初のコマンドが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して進捗を確認するようにした方が精神衛生的にも良い。
## スキャン実行

View File

@@ -55,7 +55,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
- Auto generation of configuration file template
- Auto detection of servers set using CIDR, generate configuration file template
- Email and Slack notification is possible (supports Japanese language)
- Scan result is viewable on accessory software, TUI Viewer terminal.
- Scan result is viewable on accessory software, TUI Viewer terminal or Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo)).
----
@@ -96,6 +96,7 @@ This can be done in the following steps.
1. Prepare
1. Scan
1. TUI(Terminal-Based User Interface)
1. Web UI ([VulsRepo](https://github.com/usiusi360/vulsrepo))
## Step1. Launch Amazon Linux
@@ -263,6 +264,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/).
----
@@ -292,25 +297,27 @@ see https://github.com/future-architect/vuls/tree/master/setup/docker
----
# 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|
@@ -538,15 +545,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 +569,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 | - |
@@ -578,8 +589,6 @@ prepare
/path/to/toml (default "$PWD/config.toml")
-debug
debug mode
-use-unattended-upgrades
[Deprecated] For Ubuntu, install unattended-upgrades
```
----
@@ -593,9 +602,10 @@ 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-dbpath=/path/to/cve.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cache-dbpath=/path/to/cache.db]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ssh-external]
@@ -632,6 +642,8 @@ 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")
-cve-dictionary-dbpath string
@@ -640,8 +652,6 @@ scan:
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,13 +672,10 @@ scan:
Send report via Slack
-report-text
Write report to text files ($PWD/results/current)
-results-dir string
/path/to/results (default "$PWD/results")
-ssh-external
Use external ssh command. Default: Use the Go native implementation
-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
@@ -881,10 +888,10 @@ For more details, see [Architecture section](https://github.com/future-architect
```
$ vuls tui -h
tui:
tui [-dbpath=/path/to/vuls.sqlite3]
tui [-results-dir=/path/to/results]
-dbpath string
/path/to/sqlite3 (default "$PWD/vuls.sqlite3")
-results-dir string
/path/to/results (default "$PWD/results")
-debug-sql
debug SQL

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

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

View File

@@ -37,8 +37,6 @@ type PrepareCmd struct {
askSudoPassword bool
askKeyPassword bool
useUnattendedUpgrades bool
}
// Name return subcommand name
@@ -46,7 +44,6 @@ func (*PrepareCmd) Name() string { return "prepare" }
// Synopsis return synopsis
func (*PrepareCmd) Synopsis() string {
// return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
return `Install required packages to scan.
CentOS: yum-plugin-security, yum-plugin-changelog
Amazon: None
@@ -91,13 +88,6 @@ func (p *PrepareCmd) SetFlags(f *flag.FlagSet) {
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASON. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useUnattendedUpgrades,
"use-unattended-upgrades",
false,
"[Deprecated] For Ubuntu, install unattended-upgrades",
)
}
// Execute execute
@@ -143,7 +133,6 @@ func (p *PrepareCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{
}
c.Conf.Debug = p.debug
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
// Set up custom logger
logger := util.NewCustomLogger(c.ServerInfo{})

View File

@@ -24,11 +24,11 @@ import (
"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"
@@ -44,9 +44,10 @@ type ScanCmd struct {
configPath string
dbpath string
jsonBaseDir string
cvedbpath string
cveDictionaryURL string
cacheDBPath string
cvssScoreOver float64
ignoreUnscoredCves bool
@@ -71,9 +72,6 @@ type ScanCmd struct {
azureKey string
azureContainer string
useYumPluginSecurity bool
useUnattendedUpgrades bool
sshExternal bool
}
@@ -89,9 +87,10 @@ func (*ScanCmd) Usage() string {
scan
[-lang=en|ja]
[-config=/path/to/config.toml]
[-dbpath=/path/to/vuls.sqlite3]
[-results-dir=/path/to/results]
[-cve-dictionary-dbpath=/path/to/cve.sqlite3]
[-cve-dictionary-url=http://127.0.0.1:1323]
[-cache-dbpath=/path/to/cache.db]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ssh-external]
@@ -127,8 +126,8 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
defaultConfPath := filepath.Join(wd, "config.toml")
f.StringVar(&p.configPath, "config", defaultConfPath, "/path/to/toml")
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
defaultJSONBaseDir := filepath.Join(wd, "results")
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/path/to/results")
f.StringVar(
&p.cvedbpath,
@@ -143,6 +142,13 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
defaultURL,
"http://CVE.Dictionary")
defaultCacheDBPath := filepath.Join(wd, "cache.db")
f.StringVar(
&p.cacheDBPath,
"cache-dbpath",
defaultCacheDBPath,
"/path/to/cache.db (local cache of changelog for Ubuntu/Debian)")
f.Float64Var(
&p.cvssScoreOver,
"cvss-over",
@@ -212,21 +218,6 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
false,
"[Deprecated] THIS OPTION WAS REMOVED FOR SECURITY REASONS. Define NOPASSWD in /etc/sudoers on tareget servers and use SSH key-based authentication",
)
f.BoolVar(
&p.useYumPluginSecurity,
"use-yum-plugin-security",
false,
"[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
@@ -302,6 +293,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
// logger
Log := util.NewCustomLogger(c.ServerInfo{})
scannedAt := time.Now()
// report
reports := []report.ResultWriter{
@@ -315,10 +307,10 @@ 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.reportS3 {
c.Conf.AwsRegion = p.awsRegion
@@ -333,17 +325,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 +347,14 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
reports = append(reports, report.AzureBlobWriter{})
}
c.Conf.DBPath = p.dbpath
c.Conf.JSONBaseDir = p.jsonBaseDir
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDictionaryURL = p.cveDictionaryURL
c.Conf.CacheDBPath = p.cacheDBPath
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.SSHExternal = p.sshExternal
c.Conf.HTTPProxy = p.httpProxy
c.Conf.UseYumPluginSecurity = p.useYumPluginSecurity
c.Conf.UseUnattendedUpgrades = p.useUnattendedUpgrades
Log.Info("Validating Config...")
if !c.Conf.Validate() {
@@ -402,21 +393,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

@@ -19,11 +19,9 @@ package commands
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
@@ -35,9 +33,9 @@ import (
// TuiCmd is Subcommand of host discovery mode
type TuiCmd struct {
lang string
debugSQL bool
dbpath string
lang string
debugSQL bool
jsonBaseDir string
}
// Name return subcommand name
@@ -49,7 +47,7 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to anayze vulnerabilites
// Usage return usage
func (*TuiCmd) Usage() string {
return `tui:
tui [-dbpath=/path/to/vuls.sqlite3]
tui [-results-dir=/path/to/results]
`
}
@@ -61,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))
defaultJSONBaseDir := filepath.Join(wd, "results")
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/path/to/results")
}
// Execute execute
func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
c.Conf.Lang = "en"
c.Conf.DebugSQL = p.debugSQL
c.Conf.DBPath = p.dbpath
c.Conf.JSONBaseDir = p.jsonBaseDir
historyID := ""
var jsonDirName string
var err error
if 0 < len(f.Args()) {
if _, err := strconv.Atoi(f.Args()[0]); err != nil {
log.Errorf("First Argument have to be scan_histores record ID: %s", err)
var jsonDirs report.JSONDirs
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
return subcommands.ExitFailure
}
for _, d := range jsonDirs {
splitPath := strings.Split(d, string(os.PathSeparator))
if splitPath[len(splitPath)-1] == f.Args()[0] {
jsonDirName = f.Args()[0]
break
}
}
if len(jsonDirName) == 0 {
log.Errorf("First Argument have to be JSON directory name : %s", err)
return subcommands.ExitFailure
}
historyID = f.Args()[0]
} else {
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

@@ -46,9 +46,10 @@ type Config struct {
SSHExternal bool
HTTPProxy string `valid:"url"`
DBPath string
CveDBPath string
HTTPProxy string `valid:"url"`
JSONBaseDir string
CveDBPath string
CacheDBPath string
AwsProfile string
AwsRegion string
@@ -60,25 +61,30 @@ 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.JSONBaseDir) != 0 {
if ok, _ := valid.IsFilePath(c.JSONBaseDir); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB path must be a *Absolute* file path. dbpath: %s", c.DBPath))
"JSON base directory must be a *Absolute* file path. -results-dir: %s", c.JSONBaseDir))
}
}
if len(c.CveDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CveDBPath); !ok {
errs = append(errs, fmt.Errorf(
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. dbpath: %s", c.CveDBPath))
"SQLite3 DB(Cve Dictionary) path must be a *Absolute* file path. -cve-dictionary-dbpath: %s", c.CveDBPath))
}
}
if len(c.CacheDBPath) != 0 {
if ok, _ := valid.IsFilePath(c.CacheDBPath); !ok {
errs = append(errs, fmt.Errorf(
"Cache DB path must be a *Absolute* file path. -cache-dbpath: %s", c.CacheDBPath))
}
}
@@ -232,7 +238,17 @@ type ServerInfo struct {
// used internal
LogMsgAnsiColor string // DebugLog Color
Container Container
Family string
Distro Distro
}
// Distro has distribution info
type Distro struct {
Family string
Release string
}
func (l Distro) String() string {
return fmt.Sprintf("%s %s", l.Family, l.Release)
}
// IsContainer returns whether this ServerInfo is about container

View File

@@ -68,7 +68,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 +82,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 +94,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
}

View File

@@ -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
@@ -186,41 +186,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 +217,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

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
}

56
glide.lock generated
View File

@@ -1,10 +1,10 @@
hash: 9683c87b3cf998e7fac1b12c4a94bf2bd18cb5422e9108539811546e703a439a
updated: 2016-07-12T16:20:45.462913061+09:00
hash: 28d14f88e90c0765c1b660ddde796e51e197239d353bb79bfc5d8f8cf9b5f9ee
updated: 2016-09-08T19:35:45.581570944+09:00
imports:
- name: github.com/asaskevich/govalidator
version: df81827fdd59d8b4fb93d8910b286ab7a3919520
version: 593d64559f7600f29581a3ee42177f5dbded27a9
- name: github.com/aws/aws-sdk-go
version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
version: bc572378d109481c50d45d9dba4490d80386e98e
subpackages:
- aws
- aws/credentials
@@ -13,6 +13,7 @@ imports:
- aws/awserr
- aws/client
- aws/corehandlers
- aws/credentials/stscreds
- aws/defaults
- aws/request
- private/endpoints
@@ -22,24 +23,26 @@ imports:
- private/protocol
- private/protocol/restxml
- private/waiter
- service/sts
- aws/credentials/ec2rolecreds
- aws/credentials/endpointcreds
- aws/ec2metadata
- private/protocol/rest
- private/protocol/query
- private/protocol/xml/xmlutil
- private/protocol/query/queryutil
- name: github.com/Azure/azure-sdk-for-go
version: 58a13e378daf3b06e65925397185684b16321111
version: 34467930a15f0d2872168deb11435b8ac3d863bb
subpackages:
- storage
- name: github.com/BurntSushi/toml
version: ffaa107fbd880f6d18cd6fec9b511668dcad8639
version: 99064174e013895bbd9b025c31100bd1d9b590ca
- name: github.com/cenkalti/backoff
version: cdf48bbc1eb78d1349cbda326a4a037f7ba565c6
version: 8edc80b07f38c27352fb186d971c628a6c32552b
- name: github.com/cheggaaa/pb
version: 04b234c80d661c663dbcebd52fc7218fdacc6d0c
version: ad4efe000aa550bb54918c06ebbadc0ff17687b9
- name: github.com/go-ini/ini
version: cf53f9204df4fbdd7ec4164b57fa6184ba168292
version: 6e4869b434bd001f6983749881c7ead3545887d8
- name: github.com/google/subcommands
version: 1c7173745a6001f67d8d96ab4e178284c77f7759
- name: github.com/gosuri/uitable
@@ -48,52 +51,53 @@ imports:
- util/strutil
- util/wordwrap
- name: github.com/howeyc/gopass
version: 66487b23f2880ba32e185121d2cd51a338ea069a
version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
- name: github.com/jinzhu/gorm
version: 613c0655691abb7691b70c5fda80a716d9e20b1b
version: 02f6ae3c4ed211472b0492cee02ff3ddfdc1830d
- 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: 30f7d65597dc2c421ce452b164c36b7014ef94be
- name: github.com/k0kubun/pp
version: f5dce6ed0ccf6c350f1679964ff6b61f3d6d2033
- name: github.com/kotakanbe/go-cve-dictionary
version: 1a336b8ac785badfe89a175ee926d39574901232
version: f9f68fee57dca8e60fb5d9d6b34d3215d854fc06
subpackages:
- config
- db
- models
- db
- log
- jvn
- nvd
- util
- name: github.com/kotakanbe/go-pingscanner
version: 58e188a3e4f6ab1a6371e33421e4502e26fa1e80
- name: github.com/kotakanbe/logrus-prefixed-formatter
version: f4f7d41649cf1e75e736884da8d05324aa76ea25
- name: github.com/mattn/go-colorable
version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59
version: ed8eb9e318d7a84ce5915b495b7d35e0cfe7b5a8
- name: github.com/mattn/go-isatty
version: 56b76bdf51f7708750eac80fa38b952bb9f32639
version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8
- name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f
- name: github.com/mattn/go-sqlite3
version: 38ee283dabf11c9cbdb968eebd79b1fa7acbabe6
version: 3fb7a0e792edd47bf0cf1e919dfc14e2be412e15
- name: github.com/mgutz/ansi
version: c286dcecd19ff979eeb73ea444e479b903f2cfcb
- name: github.com/moul/http2curl
version: b1479103caacaa39319f75e7f57fc545287fca0d
- name: github.com/nsf/termbox-go
version: c45773466a30b680355d6494cc8826113c93cd0f
version: e8f6d27f72a2f2bb598eb3579afd5ea364ef67f7
- name: github.com/parnurzeal/gorequest
version: 6e8ad4ebdee4bec2934ed5afaaa1c7b877832a17
version: 2aea80ce763523ecc6452e61c3727ae9595a5809
- name: github.com/rifflock/lfshook
version: 05a24e24fa8d3a2eca8c2baf23aa2d5a2c51490c
version: f9d14dda07b109a7aa56f135c31b34062eb14392
- name: github.com/Sirupsen/logrus
version: f3cfb454f4c209e6668c95216c4744b8fddb2356
version: 3ec0642a7fb6488f65b06f9040adc67e3990296a
- name: golang.org/x/crypto
version: c2f4947f41766b144bb09066e919466da5eddeae
version: 9e590154d2353f3f5e1b24da7275686040dcf491
subpackages:
- ssh
- ssh/agent
@@ -102,12 +106,12 @@ imports:
- ed25519
- ed25519/internal/edwards25519
- name: golang.org/x/net
version: f841c39de738b1d0df95b5a7187744f0e03d8112
version: 9313baa13d9262e49d07b20ed57dceafcd7240cc
subpackages:
- context
- publicsuffix
- name: golang.org/x/sys
version: a408501be4d17ee978c04a618e7a1b22af058c0e
version: 30de6d19a3bd89a5f38ae4028e23aaa5582648af
subpackages:
- unix
- name: gopkg.in/alexcesaro/quotedprintable.v3

View File

@@ -22,7 +22,6 @@ import:
- package: github.com/kotakanbe/go-cve-dictionary
subpackages:
- config
- db
- models
- package: github.com/kotakanbe/go-pingscanner
- package: github.com/kotakanbe/logrus-prefixed-formatter

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>
@@ -222,7 +222,7 @@ Reporting<y:LabelModel>
<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>
@@ -397,7 +397,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e12" source="n1" target="n12">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="20.0" tx="0.0" ty="-28.0"/>
@@ -416,7 +415,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e13" source="n12" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="28.0" tx="0.0" ty="-28.0"/>
@@ -427,7 +425,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e14" source="n13" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="134.00000000000006" sy="0.0" tx="0.0" ty="-28.0">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -74,6 +74,7 @@ func (s ScanResults) FilterByCvssOver() (filtered ScanResults) {
type ScanResult struct {
gorm.Model `json:"-"`
ScanHistoryID uint `json:"-"`
ScannedAt time.Time
ServerName string // TOML Section key
// Hostname string
@@ -95,7 +96,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 +119,7 @@ func (r ScanResult) ServerInfo() string {
// ServerInfoTui returns server infromation for TUI sidebar
func (r ScanResult) ServerInfoTui() string {
hostinfo := ""
if r.Container.ContainerID == "" {
if len(r.Container.ContainerID) == 0 {
hostinfo = fmt.Sprintf(
"%s (%s%s)",
r.ServerName,
@@ -190,7 +191,7 @@ 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.

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,17 +21,44 @@ 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 {
@@ -44,7 +71,7 @@ func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
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,
@@ -60,3 +87,77 @@ func (w JSONWriter) Write(scanResults []models.ScanResult) (err error) {
}
return nil
}
// JSONDirPattern is file name pattern of JSON directory
var JSONDirPattern = regexp.MustCompile(`^\d{8}_\d{4}$`)
// GetValidJSONDirs return valid json directory as array
func GetValidJSONDirs() (jsonDirs JSONDirs, err error) {
var dirInfo []os.FileInfo
if dirInfo, err = ioutil.ReadDir(c.Conf.JSONBaseDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", c.Conf.JSONBaseDir, err)
return
}
for _, d := range dirInfo {
if d.IsDir() && JSONDirPattern.MatchString(d.Name()) {
jsonDir := filepath.Join(c.Conf.JSONBaseDir, d.Name())
jsonDirs = append(jsonDirs, jsonDir)
}
}
sort.Sort(jsonDirs)
return
}
// LoadOneScanHistory read JSON data
func LoadOneScanHistory(jsonDir string) (scanHistory models.ScanHistory, err error) {
var scanResults []models.ScanResult
var files []os.FileInfo
if files, err = ioutil.ReadDir(jsonDir); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonDir, err)
return
}
for _, file := range files {
// TODO this "if block" will be deleted in a future release
if file.Name() == "all.json" {
continue
}
if filepath.Ext(file.Name()) != ".json" {
continue
}
var scanResult models.ScanResult
var data []byte
jsonPath := filepath.Join(jsonDir, file.Name())
if data, err = ioutil.ReadFile(jsonPath); err != nil {
err = fmt.Errorf("Failed to read %s: %s", jsonPath, err)
return
}
if json.Unmarshal(data, &scanResult) != nil {
err = fmt.Errorf("Failed to parse %s: %s", jsonPath, err)
return
}
scanResults = append(scanResults, scanResult)
}
if len(scanResults) == 0 {
err = fmt.Errorf("There is no json file under %s", jsonDir)
return
}
var scannedAt time.Time
if scanResults[0].ScannedAt.IsZero() {
splitPath := strings.Split(jsonDir, string(os.PathSeparator))
timeStr := splitPath[len(splitPath)-1]
timeformat := "20060102_1504"
if scannedAt, err = time.Parse(timeformat, timeStr); err != nil {
err = fmt.Errorf("Failed to parse %s: %s", timeStr, err)
return
}
} else {
scannedAt = scanResults[0].ScannedAt
}
scanHistory = models.ScanHistory{
ScanResults: scanResults,
ScannedAt: scannedAt,
}
return
}

View File

@@ -90,7 +90,7 @@ func (w S3Writer) Write(scanResults []models.ScanResult) (err error) {
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

@@ -80,7 +80,7 @@ func (w SlackWriter) Write(scanResults []models.ScanResult) error {
Send(string(jsonBody)).End()
if resp.StatusCode != 200 {
log.Errorf("Resonse body: %s", body)
if len(errs) > 0 {
if 0 < len(errs) {
return errs[0]
}
}
@@ -205,6 +205,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,

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,9 +40,9 @@ var currentCveInfo int
var currentDetailLimitY int
// RunTui execute main logic
func RunTui(historyID string) subcommands.ExitStatus {
func RunTui(jsonDirName string) subcommands.ExitStatus {
var err error
scanHistory, err = selectScanHistory(historyID)
scanHistory, err = selectScanHistory(jsonDirName)
if err != nil {
log.Fatal(err)
return subcommands.ExitFailure
@@ -70,12 +70,20 @@ func RunTui(historyID string) subcommands.ExitStatus {
return subcommands.ExitSuccess
}
func selectScanHistory(historyID string) (latest models.ScanHistory, err error) {
if err := db.OpenDB(); err != nil {
return latest, fmt.Errorf(
"Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
func selectScanHistory(jsonDirName string) (latest models.ScanHistory, err error) {
var jsonDir string
if 0 < len(jsonDirName) {
jsonDir = filepath.Join(config.Conf.JSONBaseDir, jsonDirName)
} else {
var jsonDirs JSONDirs
if jsonDirs, err = GetValidJSONDirs(); err != nil {
return
}
jsonDir = jsonDirs[0]
}
if latest, err = LoadOneScanHistory(jsonDir); err != nil {
return
}
latest, err = db.SelectScanHistory(historyID)
return
}
@@ -332,7 +340,7 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
if v != nil {
ox, oy := v.Origin()
cx, cy := v.Cursor()
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
if err := v.SetCursor(cx, cy-1); err != nil && 0 < oy {
if err := v.SetOrigin(ox, oy-1); err != nil {
return err
}
@@ -643,6 +651,7 @@ type dataForTmpl struct {
CvssVector string
CvssSeverity string
Summary string
CweURL string
VulnSiteLinks []string
References []cve.Reference
Packages []string
@@ -682,6 +691,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 +726,7 @@ func detailLines() (string, error) {
CvssSeverity: cvssSeverity,
CvssVector: cvssVector,
Summary: summary,
CweURL: cweURL,
VulnSiteLinks: links,
References: refs,
Packages: packages,
@@ -746,6 +758,11 @@ Summary
{{.Summary }}
CWE
--------------
{{.CweURL }}
Package/CPE
--------------

View File

@@ -30,13 +30,13 @@ import (
"github.com/gosuri/uitable"
)
func ensureResultDir() (path string, err error) {
func ensureResultDir(scannedAt time.Time) (path string, err error) {
if resultDirPath != "" {
return resultDirPath, nil
}
const timeLayout = "20060102_1504"
timedir := time.Now().Format(timeLayout)
timedir := scannedAt.Format(timeLayout)
wd, _ := os.Getwd()
dir := filepath.Join(wd, "results", timedir)
if err := os.MkdirAll(dir, 0755); err != nil {
@@ -44,7 +44,7 @@ func ensureResultDir() (path string, err error) {
}
symlinkPath := filepath.Join(wd, "results", "current")
if _, err := os.Stat(symlinkPath); err == nil {
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)
@@ -205,13 +205,11 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
}
func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
cveDetail := cveInfo.CveDetail
cveID := cveDetail.CveID
jvn := cveDetail.Jvn
dtable := uitable.New()
//TODO resize
dtable.MaxColWidth = 100
dtable.Wrap = true
dtable.AddRow(cveID)
@@ -228,6 +226,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 +252,6 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
nvd := cveDetail.Nvd
dtable := uitable.New()
//TODO resize
dtable.MaxColWidth = 100
dtable.Wrap = true
dtable.AddRow(cveID)
@@ -270,6 +269,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 +377,12 @@ func addCpeNames(table *uitable.Table, names []models.CpeName) *uitable.Table {
}
return table
}
func cweURL(cweID string) string {
return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html",
strings.TrimPrefix(cweID, "CWE-"))
}
func cweJvnURL(cweID string) string {
return fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID)
}

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,9 +32,8 @@ import (
type base struct {
ServerInfo config.ServerInfo
Distro config.Distro
Family string
Release string
Platform models.Platform
osPackages
@@ -52,13 +53,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) {
@@ -164,9 +172,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 +192,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,6 +211,13 @@ 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
for _, p := range l.UnsecurePackages {
@@ -239,8 +255,9 @@ func (l *base) convertToModel() (models.ScanResult, error) {
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,

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
}
@@ -126,7 +125,6 @@ func (o *debian) checkIfSudoNoPasswd() error {
}
func (o *debian) install() error {
// apt-get update
o.log.Infof("apt-get update...")
cmd := util.PrependProxyEnv("apt-get update")
@@ -136,7 +134,7 @@ func (o *debian) install() error {
return fmt.Errorf(msg)
}
if o.Family == "debian" {
if o.Distro.Family == "debian" {
// install aptitude
cmd = util.PrependProxyEnv("apt-get install --force-yes -y aptitude")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
@@ -146,27 +144,6 @@ func (o *debian) install() error {
}
o.log.Infof("Installed: aptitude")
}
// install unattended-upgrades
if !config.Conf.UseUnattendedUpgrades {
return nil
}
if r := o.ssh("type unattended-upgrade", noSudo); r.isSuccess() {
o.log.Infof(
"Ignored: unattended-upgrade already installed")
return nil
}
cmd = util.PrependProxyEnv(
"apt-get install --force-yes -y unattended-upgrades")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
msg := fmt.Sprintf("Failed to SSH: %s", r)
o.log.Errorf(msg)
return fmt.Errorf(msg)
}
o.log.Infof("Installed: unattended-upgrades")
return nil
}
@@ -214,12 +191,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 +208,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 +241,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().ServerName,
Distro: o.getServerInfo().Distro,
Packs: unsecurePacks,
}
o.log.Debugf("Ensure changelog cache: %s", current.Name)
if err := o.ensureChangelogCache(current); err != nil {
return nil, err
}
// Collect CVE information of upgradable packages
cvePacksInfos, err := o.scanPackageCveInfos(unsecurePacks)
if err != nil {
@@ -297,99 +265,61 @@ 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("LANG=en_US.UTF-8 apt-cache policy %s", strings.Join(names, " "))
r := o.ssh(cmd, sudo)
if r.isSuccess(0, 1) {
packNames = strings.Split(strings.TrimSpace(r.Stdout), " ")
return packNames, nil
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s.", r)
}
return packNames, fmt.Errorf(
"Failed to %s. status: %d, stdout: %s, stderr: %s",
cmd, r.ExitStatus, r.Stdout, r.Stderr)
packChangelog := o.splitAptCachePolicy(r.Stdout)
for k, v := range packChangelog {
ver, err := o.parseAptCachePolicy(v, k)
if err != nil {
return nil, fmt.Errorf("Failed to parse %s", err)
}
p, found := before.FindByName(k)
if !found {
return nil, fmt.Errorf("Not found: %s", k)
}
p.NewVersion = ver.Candidate
filled = append(filled, p)
}
return
}
func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
@@ -404,8 +334,8 @@ func (o *debian) GetUpgradablePackNames() (packNames []string, err error) {
}
func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, err error) {
startRe, _ := regexp.Compile(`The following packages will be upgraded:`)
stopRe, _ := regexp.Compile(`^(\d+) upgraded.*`)
startRe := regexp.MustCompile(`The following packages will be upgraded:`)
stopRe := regexp.MustCompile(`^(\d+) upgraded.*`)
startLineFound, stopLineFound := false, false
lines := strings.Split(stdout, "\n")
@@ -446,9 +376,11 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
}
func (o *debian) scanPackageCveInfos(unsecurePacks []models.PackageInfo) (cvePacksList CvePacksList, err error) {
// { CVE ID: [packageInfo] }
cvePackages := make(map[string][]models.PackageInfo)
meta := cache.Meta{
Name: o.getServerInfo().ServerName,
Distro: o.getServerInfo().Distro,
Packs: unsecurePacks,
}
type strarray []string
resChan := make(chan struct {
@@ -475,6 +407,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 +432,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 +452,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 +460,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 +480,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 chnagelog. bucket: %s, key:%s, err: %s",
meta.Name, pack.Name, err)
return ""
}
if len(changelog) == 0 {
return ""
}
o.log.Debugf("Cache hit: %s, len: %d, %s...",
meta.Name, len(changelog), util.Truncate(changelog, 30))
return changelog
}
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
cmd := ""
switch o.Family {
switch o.Distro.Family {
case "ubuntu":
cmd = fmt.Sprintf(`apt-get changelog %s | grep '\(urgency\|CVE\)'`, pack.Name)
case "debian":
@@ -553,35 +520,38 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
return nil, nil
}
return o.getCveIDParsingChangelog(r.Stdout, pack.Name, pack.Version)
err := cache.DB.PutChangelog(o.getServerInfo().ServerName, 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 +559,8 @@ func (o *debian) getCveIDParsingChangelog(changelog string,
func (o *debian) parseChangelog(changelog string,
packName string, versionOrLater string) (cveIDs []string, err error) {
cveRe, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
stopRe, _ := regexp.Compile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
stopLineFound := false
lines := strings.Split(changelog, "\n")
for _, line := range lines {
@@ -598,7 +568,7 @@ func (o *debian) parseChangelog(changelog string,
o.log.Debugf("Found the stop line. line: %s", line)
stopLineFound = true
break
} else if matches := cveRe.FindAllString(line, -1); len(matches) > 0 {
} else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
for _, m := range matches {
cveIDs = util.AppendIfMissing(cveIDs, m)
}
@@ -614,6 +584,29 @@ func (o *debian) parseChangelog(changelog string,
return
}
func (o *debian) splitAptCachePolicy(stdout string) map[string]string {
// re := regexp.MustCompile(`(?m:^[^ \t]+:$)`)
re := regexp.MustCompile(`(?m:^[^ \t]+:\r\n)`)
ii := re.FindAllStringIndex(stdout, -1)
ri := []int{}
for i := len(ii) - 1; 0 <= i; i-- {
ri = append(ri, ii[i][0])
}
splitted := []string{}
lasti := len(stdout)
for _, i := range ri {
splitted = append(splitted, stdout[i:lasti])
lasti = i
}
packChangelog := map[string]string{}
for _, r := range splitted {
packName := r[:strings.Index(r, ":")]
packChangelog[packName] = r
}
return packChangelog
}
type packCandidateVer struct {
Name string
Installed string

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,18 @@ 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"
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
}
}

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,49 +108,28 @@ 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 {
switch o.Distro.Family {
case "rhel", "amazon":
o.log.Infof("Nothing to do")
return nil
}
if err := o.installYumPluginSecurity(); err != nil {
return err
}
// CentOS
return o.installYumChangelog()
}
func (o *redhat) installYumPluginSecurity() error {
if r := o.ssh("rpm -q yum-plugin-security", noSudo); r.isSuccess() {
o.log.Infof("Ignored: yum-plugin-security already installed")
return nil
}
o.log.Info("Installing yum-plugin-security...")
cmd := util.PrependProxyEnv("yum install -y yum-plugin-security")
if r := o.ssh(cmd, sudo); !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
func (o *redhat) installYumChangelog() error {
if o.Family == "centos" {
if o.Distro.Family == "centos" {
var majorVersion int
if 0 < len(o.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Release, ".")[0])
if 0 < len(o.Distro.Release) {
majorVersion, _ = strconv.Atoi(strings.Split(o.Distro.Release, ".")[0])
} else {
return fmt.Errorf(
"Not implemented yet. family: %s, release: %s",
o.Family, o.Release)
"Not implemented yet: %s", o.Distro)
}
var packName = ""
@@ -177,26 +156,12 @@ func (o *redhat) installYumChangelog() error {
}
func (o *redhat) checkRequiredPackagesInstalled() error {
if config.Conf.UseYumPluginSecurity {
// check if yum-plugin-security is installed.
// Amazon Linux, REHL can execute 'yum updateinfo --security updates' without yum-plugin-security
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)
}
@@ -274,7 +239,7 @@ func (o *redhat) parseScannedPackagesLine(line string) (models.PackageInfo, erro
}
func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
if o.Family != "centos" || config.Conf.UseYumPluginSecurity {
if o.Distro.Family != "centos" {
// Amazon, RHEL has yum updateinfo as default
// yum updateinfo can collenct vendor advisory information.
return o.scanUnsecurePackagesUsingYumPluginSecurity()
@@ -284,9 +249,8 @@ func (o *redhat) scanUnsecurePackages() ([]CvePacksInfo, error) {
return o.scanUnsecurePackagesUsingYumCheckUpdate()
}
//TODO return whether already expired.
// For CentOS
func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (CvePacksList, error) {
cmd := "LANG=en_US.UTF-8 yum --color=never check-update"
r := o.ssh(util.PrependProxyEnv(cmd), sudo)
if !r.isSuccess(0, 100) {
@@ -416,7 +380,9 @@ func (o *redhat) parseYumCheckUpdateLines(stdout string) (results models.Package
continue
}
if needToParse {
if strings.HasPrefix(line, "Obsoleting") {
if strings.HasPrefix(line, "Obsoleting") ||
strings.HasPrefix(line, "Security:") {
// see https://github.com/future-architect/vuls/issues/165
continue
}
candidate, err := o.parseYumCheckUpdateLine(line)
@@ -471,18 +437,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 +459,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 +475,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 +525,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,16 +534,14 @@ 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()
}
@@ -602,9 +564,9 @@ type distroAdvisoryCveIDs struct {
}
// Scaning unsecure packages using yum-plugin-security.
//TODO return whether already expired.
// Amazon, RHEL
func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, error) {
if o.Family == "centos" {
if o.Distro.Family == "centos" {
// CentOS has no security channel.
// So use yum check-update && parse changelog
return CvePacksList{}, fmt.Errorf(
@@ -612,14 +574,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)
}
@@ -628,7 +590,7 @@ 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)
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 +618,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 +668,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (CvePacksList, err
return result, nil
}
var horizontalRulePattern = regexp.MustCompile(`^=+$`)
func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveIDs, err error) {
sectionState := Outside
lines := strings.Split(stdout, "\n")
@@ -723,7 +687,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
line = strings.TrimSpace(line)
// find the new section pattern
if match, _ := o.isHorizontalRule(line); match {
if horizontalRulePattern.MatchString(line) {
// set previous section's result to return-variable
if sectionState == Content {
@@ -750,7 +714,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
switch sectionState {
case Header:
switch o.Family {
switch o.Distro.Family {
case "centos":
// CentOS has no security channel.
// So use yum check-update && parse changelog
@@ -817,9 +781,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 +792,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 +816,10 @@ func (o *redhat) parseYumUpdateinfoHeaderCentOS(line string) (packs []models.Pac
return
}
var yumHeaderPattern = regexp.MustCompile(`(ALAS-.+): (.+) priority package update for (.+)$`)
func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdvisory, names []string, err error) {
re, _ := regexp.Compile(`(ALAS-.+): (.+) priority package update for (.+)$`)
result := re.FindStringSubmatch(line)
result := yumHeaderPattern.FindStringSubmatch(line)
if len(result) == 4 {
a.AdvisoryID = result[1]
a.Severity = result[2]
@@ -869,31 +831,36 @@ func (o *redhat) parseYumUpdateinfoHeaderAmazon(line string) (a models.DistroAdv
return
}
var yumCveIDPattern = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
func (o *redhat) parseYumUpdateinfoLineToGetCveIDs(line string) []string {
re, _ := regexp.Compile(`(CVE-\d{4}-\d{4})`)
return re.FindAllString(line, -1)
return yumCveIDPattern.FindAllString(line, -1)
}
var yumAdvisoryIDPattern = regexp.MustCompile(`^ *Update ID : (.*)$`)
func (o *redhat) parseYumUpdateinfoToGetAdvisoryID(line string) (advisoryID string, found bool) {
re, _ := regexp.Compile(`^ *Update ID : (.*)$`)
result := re.FindStringSubmatch(line)
result := yumAdvisoryIDPattern.FindStringSubmatch(line)
if len(result) != 2 {
return "", false
}
return strings.TrimSpace(result[1]), true
}
var yumIssuedPattern = regexp.MustCompile(`^\s*Issued : (\d{4}-\d{2}-\d{2})`)
func (o *redhat) parseYumUpdateinfoLineToGetIssued(line string) (date time.Time, found bool) {
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Issued : (\d{4}-\d{2}-\d{2})`)
return o.parseYumUpdateinfoLineToGetDate(line, yumIssuedPattern)
}
var yumUpdatedPattern = regexp.MustCompile(`^\s*Updated : (\d{4}-\d{2}-\d{2})`)
func (o *redhat) parseYumUpdateinfoLineToGetUpdated(line string) (date time.Time, found bool) {
return o.parseYumUpdateinfoLineToGetDate(line, `^\s*Updated : (\d{4}-\d{2}-\d{2})`)
return o.parseYumUpdateinfoLineToGetDate(line, yumUpdatedPattern)
}
func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (date time.Time, found bool) {
re, _ := regexp.Compile(regexpFormat)
result := re.FindStringSubmatch(line)
func (o *redhat) parseYumUpdateinfoLineToGetDate(line string, regexpPattern *regexp.Regexp) (date time.Time, found bool) {
result := regexpPattern.FindStringSubmatch(line)
if len(result) != 2 {
return date, false
}
@@ -904,14 +871,16 @@ func (o *redhat) parseYumUpdateinfoLineToGetDate(line, regexpFormat string) (dat
return t, true
}
var yumDescriptionPattern = regexp.MustCompile(`^\s*Description : `)
func (o *redhat) isDescriptionLine(line string) bool {
re, _ := regexp.Compile(`^\s*Description : `)
return re.MatchString(line)
return yumDescriptionPattern.MatchString(line)
}
var yumSeverityPattern = regexp.MustCompile(`^ *Severity : (.*)$`)
func (o *redhat) parseYumUpdateinfoToGetSeverity(line string) (severity string, found bool) {
re, _ := regexp.Compile(`^ *Severity : (.*)$`)
result := re.FindStringSubmatch(line)
result := yumSeverityPattern.FindStringSubmatch(line)
if len(result) != 2 {
return "", false
}
@@ -990,3 +959,12 @@ func (o *redhat) parseYumUpdateinfoListAvailable(stdout string) (advisoryIDPacks
func (o *redhat) clone() osTypeInterface {
return o
}
func (o *redhat) sudo() bool {
switch o.Distro.Family {
case "amazon":
return false
default:
return true
}
}

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
@@ -709,7 +709,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
@@ -1110,8 +1110,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 +1196,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,3 +1,20 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
@@ -5,6 +22,7 @@ import (
"time"
"github.com/Sirupsen/logrus"
"github.com/future-architect/vuls/cache"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
cve "github.com/kotakanbe/go-cve-dictionary/models"
@@ -20,8 +38,9 @@ type osTypeInterface interface {
setServerInfo(config.ServerInfo)
getServerInfo() config.ServerInfo
setDistributionInfo(string, string)
getDistributionInfo() string
setDistro(string, string)
getDistro() config.Distro
// getFamily() string
checkIfSudoNoPasswd() error
detectPlatform() error
@@ -188,7 +207,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"
@@ -248,7 +267,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"
@@ -417,6 +436,13 @@ func Scan() []error {
return errs
}
if err := setupCangelogCache(); err != nil {
return []error{err}
}
if cache.DB != nil {
defer cache.DB.Close()
}
Log.Info("Scanning vulnerable OS packages...")
if errs := scanPackages(); errs != nil {
return errs
@@ -429,6 +455,23 @@ func Scan() []error {
return nil
}
func setupCangelogCache() error {
needToSetupCache := false
for _, s := range servers {
switch s.getDistro().Family {
case "ubuntu", "debian":
needToSetupCache = true
break
}
}
if needToSetupCache {
if err := cache.SetupBolt(config.Conf.CacheDBPath, Log); err != nil {
return err
}
}
return nil
}
func checkRequiredPackagesInstalled() []error {
timeoutSec := 30 * 60
return parallelSSHExec(func(o osTypeInterface) error {

View File

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

@@ -32,28 +32,6 @@ RUN curl -fsSL "$GLIDE_DOWNLOAD_URL" -o 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/
@@ -77,13 +55,19 @@ 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
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
apache2 \
libcgi-pm-perl \
libjson-perl \
&& rm -rf /var/lib/apt/lists/* \
&& cd /var/www/html/ \
&& git clone https://github.com/usiusi360/vulsrepo \
&& mkdir /var/www/html/vulsrepo/results \
&& cp /var/www/html/vulsrepo/dist/cgi/vulsrepo.conf.sample /etc/apache2/conf-enabled/vulsrepo.conf \
&& a2enmod cgid
#Home
WORKDIR /opt/vuls
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT service apache2 start && tail -f /dev/null

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

@@ -2,5 +2,5 @@
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -entire
for i in {2002..2016}; do go-cve-dictionary fetchjvn -years $i; done

View File

@@ -2,5 +2,5 @@
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -month
go-cve-dictionary fetchjvn -last2y

View File

@@ -2,5 +2,4 @@
VULS_ROOT=/opt/vuls
#VULS_CONF=${VULS_ROOT}/conf
cd $VULS_ROOT
go-cve-dictionary fetchjvn -week
go-cve-dictionary fetchjvn -latest

View File

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

View File

@@ -11,7 +11,6 @@ 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
cd /var/www/html/vulsrepo
git pull origin master

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

@@ -21,4 +21,4 @@ package version
const Name string = "vuls"
// Version of Vuls
const Version string = "0.1.5"
const Version string = "0.1.6"