Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00c0354a8e | ||
|
|
a2a6973ba1 | ||
|
|
dd1d3a05fa | ||
|
|
2afe2d2640 | ||
|
|
29678f9b59 | ||
|
|
77edb251bb | ||
|
|
29151fa267 | ||
|
|
b3f13790bd | ||
|
|
38857c3356 | ||
|
|
d75990d9fd | ||
|
|
ed063f6534 | ||
|
|
c8a9bdc517 | ||
|
|
595729cdf8 | ||
|
|
6119f79748 | ||
|
|
d4fb46c9ba | ||
|
|
c41301afca | ||
|
|
50fd80830e | ||
|
|
1c203b4272 | ||
|
|
c545e9045d | ||
|
|
2721dc0647 | ||
|
|
51d13f4234 | ||
|
|
a60a5d6eab | ||
|
|
5959235425 | ||
|
|
d8e6d4e5fc | ||
|
|
7dfc9815b3 | ||
|
|
0c53b187a4 | ||
|
|
42dadfed8f | ||
|
|
a46c603c77 | ||
|
|
ad0020d9a6 | ||
|
|
a224f0bfd4 | ||
|
|
d8dc3650d3 | ||
|
|
30f7527f10 | ||
|
|
b1f5bdd8b2 | ||
|
|
c8e7c8b9fa | ||
|
|
30bf3223f8 | ||
|
|
886710ec30 | ||
|
|
510dc8d828 | ||
|
|
5ff7b2aab4 | ||
|
|
1e33536205 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,6 +3,8 @@ vuls
|
||||
*.txt
|
||||
*.json
|
||||
*.sqlite3
|
||||
*.db
|
||||
tags
|
||||
.gitmodules
|
||||
coverage.out
|
||||
issues/
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@@ -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)
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go')
|
||||
PKGS = ./. ./db ./config ./models ./report ./cveapi ./scan ./util ./commands
|
||||
PKGS = ./. ./config ./models ./report ./cveapi ./scan ./util ./commands ./cache
|
||||
|
||||
all: test
|
||||
|
||||
|
||||
142
README.ja.md
142
README.ja.md
@@ -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
|
||||
|
||||

|
||||
|
||||
## 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をパースする。
|
||||
アップデート対象のパッケージが沢山ある場合、チェンジログの取得に時間がかかるので、初回のスキャンは遅い。
|
||||
ただ、2回目以降はキャッシュしたchangelogを使うので速くなる。
|
||||
|
||||
- CentOS
|
||||
アップデート対象すべてのchangelogを一度で取得しパースする。スキャンスピードは高速、サーバリソース消費量は小さい。
|
||||
アップデート対象すべてのchangelogを一度で取得しパースする。スキャンスピードは速い、サーバリソース消費量は小さい。
|
||||
|
||||
- Amazon, RHEL and FreeBSD
|
||||
高速にスキャンし、スキャン対象サーバのリソース消費量は小さい。
|
||||
|
||||
| Distribution| Scan Speed | Resource Usage On Target Server |
|
||||
|:------------|:-------------------|:-------------|
|
||||
| Ubuntu | Slow | Heavy |
|
||||
| Debian | Slow | Heavy |
|
||||
| CentOS | Fast | Light |
|
||||
| Amazon | Fast | Light |
|
||||
| RHEL | Fast | Light |
|
||||
| FreeBSD | Fast | Light |
|
||||
| Distribution| Scan Speed |
|
||||
|:------------|:-------------------|
|
||||
| Ubuntu | 初回は遅い / 2回目以降速い |
|
||||
| Debian | 初回は遅い / 2回目以降速い |
|
||||
| CentOS | 速い |
|
||||
| Amazon | 速い |
|
||||
| RHEL | 速い |
|
||||
| FreeBSD | 速い |
|
||||
|
||||
----
|
||||
|
||||
@@ -333,7 +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して進捗を確認するようにした方が精神衛生的にも良い。
|
||||
|
||||
|
||||
## スキャン実行
|
||||
|
||||
|
||||
67
README.md
67
README.md
@@ -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
|
||||
|
||||

|
||||
|
||||
## 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
173
cache/bolt.go
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// Bolt holds a pointer of bolt.DB
|
||||
// boltdb is used to store a cache of Changelogs of Ubuntu/Debian
|
||||
type Bolt struct {
|
||||
Path string
|
||||
Log *logrus.Entry
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
// SetupBolt opens a boltdb and creates a meta bucket if not exists.
|
||||
func SetupBolt(path string, l *logrus.Entry) error {
|
||||
l.Infof("Open boltDB: %s", path)
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := Bolt{
|
||||
Path: path,
|
||||
Log: l,
|
||||
db: db,
|
||||
}
|
||||
if err = b.createBucketIfNotExists(metabucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
DB = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close a db.
|
||||
func (b Bolt) Close() error {
|
||||
if b.db == nil {
|
||||
return nil
|
||||
}
|
||||
return b.db.Close()
|
||||
}
|
||||
|
||||
// CreateBucketIfNotExists creates a buket that is specified by arg.
|
||||
func (b *Bolt) createBucketIfNotExists(name string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.CreateBucketIfNotExists([]byte(name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create bucket: %s", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetMeta gets a Meta Information os the servername to boltdb.
|
||||
func (b Bolt) GetMeta(serverName string) (meta Meta, found bool, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(serverName))
|
||||
if len(v) == 0 {
|
||||
found = false
|
||||
return nil
|
||||
}
|
||||
if e := json.Unmarshal(v, &meta); e != nil {
|
||||
return e
|
||||
}
|
||||
found = true
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// EnsureBuckets puts a Meta information and create a buket that holds changelogs.
|
||||
func (b Bolt) EnsureBuckets(meta Meta) error {
|
||||
jsonBytes, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to marshal to JSON: %s", err)
|
||||
}
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
b.Log.Debugf("Put to meta: %s", meta.Name)
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if err := bkt.Put([]byte(meta.Name), jsonBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// re-create a bucket (bucket name: servername)
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
if bkt != nil {
|
||||
b.Log.Debugf("Delete bucket: %s", meta.Name)
|
||||
if err := tx.DeleteBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket deleted: %s", meta.Name)
|
||||
}
|
||||
b.Log.Debugf("Create bucket: %s", meta.Name)
|
||||
if _, err := tx.CreateBucket([]byte(meta.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Log.Debugf("Bucket created: %s", meta.Name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// PrettyPrint is for debuging
|
||||
func (b Bolt) PrettyPrint(meta Meta) error {
|
||||
return b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
v := bkt.Get([]byte(meta.Name))
|
||||
b.Log.Debugf("key:%s, value:%s", meta.Name, v)
|
||||
|
||||
bkt = tx.Bucket([]byte(meta.Name))
|
||||
c := bkt.Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
b.Log.Debugf("key:%s, len: %d, %s...",
|
||||
k, len(v), util.Truncate(string(v), 30))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetChangelog get the changelgo of specified packName from the Bucket
|
||||
func (b Bolt) GetChangelog(servername, packName string) (changelog string, err error) {
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
}
|
||||
v := bkt.Get([]byte(packName))
|
||||
if v == nil {
|
||||
changelog = ""
|
||||
return nil
|
||||
}
|
||||
changelog = string(v)
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// PutChangelog put the changelgo of specified packName into the Bucket
|
||||
func (b Bolt) PutChangelog(servername, packName, changelog string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
}
|
||||
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
134
cache/bolt_test.go
vendored
Normal file
134
cache/bolt_test.go
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
const path = "/tmp/vuls-test-cache-11111111.db"
|
||||
const servername = "server1"
|
||||
|
||||
var meta = Meta{
|
||||
Name: servername,
|
||||
Distro: config.Distro{
|
||||
Family: "ubuntu",
|
||||
Release: "16.04",
|
||||
},
|
||||
Packs: []models.PackageInfo{
|
||||
{
|
||||
Name: "apt",
|
||||
Version: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSetupBolt(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
err := SetupBolt(path, log)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
// check if meta bucket exists
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(metabucket))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestEnsureBuckets(t *testing.T) {
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
m, found, err := DB.GetMeta(servername)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get meta: %s", err)
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Not Found in meta")
|
||||
}
|
||||
if !reflect.DeepEqual(meta, m) {
|
||||
t.Errorf("expected %v, actual %v", meta, m)
|
||||
}
|
||||
if err := DB.Close(); err != nil {
|
||||
t.Errorf("Failed to close bolt: %s", err)
|
||||
}
|
||||
|
||||
db, err := bolt.Open(path, 0600, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open bolt: %s", err)
|
||||
}
|
||||
db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
t.Errorf("Meta bucket nof found")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestPutGetChangelog(t *testing.T) {
|
||||
clog := "changelog-text"
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
if err := DB.PutChangelog(servername, "apt", clog); err != nil {
|
||||
t.Errorf("Failed to put changelog: %s", err)
|
||||
}
|
||||
if actual, err := DB.GetChangelog(servername, "apt"); err != nil {
|
||||
t.Errorf("Failed to get changelog: %s", err)
|
||||
} else {
|
||||
if actual != clog {
|
||||
t.Errorf("changelog is not same. e: %s, a: %s", clog, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
56
cache/db.go
vendored
Normal file
56
cache/db.go
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
// DB has a cache instance
|
||||
var DB Cache
|
||||
|
||||
const metabucket = "changelog-meta"
|
||||
|
||||
// Cache is a interface of cache
|
||||
type Cache interface {
|
||||
Close() error
|
||||
GetMeta(string) (Meta, bool, error)
|
||||
EnsureBuckets(Meta) error
|
||||
PrettyPrint(Meta) error
|
||||
GetChangelog(string, string) (string, error)
|
||||
PutChangelog(string, string, string) error
|
||||
}
|
||||
|
||||
// Meta holds a server name, distro information of the scanned server and
|
||||
// package information that was collected at the last scan.
|
||||
type Meta struct {
|
||||
Name string
|
||||
Distro config.Distro
|
||||
Packs []models.PackageInfo
|
||||
}
|
||||
|
||||
// FindPack search a PackageInfo
|
||||
func (m Meta) FindPack(name string) (pack models.PackageInfo, found bool) {
|
||||
for _, p := range m.Packs {
|
||||
if name == p.Name {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return pack, false
|
||||
}
|
||||
@@ -20,25 +20,22 @@ package commands
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/db"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/report"
|
||||
"github.com/google/subcommands"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// HistoryCmd is Subcommand of list scanned results
|
||||
type HistoryCmd struct {
|
||||
debug bool
|
||||
debugSQL bool
|
||||
|
||||
dbpath string
|
||||
debug bool
|
||||
debugSQL bool
|
||||
jsonBaseDir string
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -53,7 +50,7 @@ func (*HistoryCmd) Synopsis() string {
|
||||
func (*HistoryCmd) Usage() string {
|
||||
return `history:
|
||||
history
|
||||
[-dbpath=/path/to/vuls.sqlite3]
|
||||
[-results-dir=/path/to/results]
|
||||
`
|
||||
}
|
||||
|
||||
@@ -62,47 +59,45 @@ func (p *HistoryCmd) SetFlags(f *flag.FlagSet) {
|
||||
f.BoolVar(&p.debugSQL, "debug-sql", false, "SQL debug mode")
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
defaultDBPath := filepath.Join(wd, "vuls.sqlite3")
|
||||
f.StringVar(&p.dbpath, "dbpath", defaultDBPath, "/path/to/sqlite3")
|
||||
defaultJSONBaseDir := filepath.Join(wd, "results")
|
||||
f.StringVar(&p.jsonBaseDir, "results-dir", defaultJSONBaseDir, "/path/to/results")
|
||||
}
|
||||
|
||||
// Execute execute
|
||||
func (p *HistoryCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
|
||||
c.Conf.DebugSQL = p.debugSQL
|
||||
c.Conf.DBPath = p.dbpath
|
||||
c.Conf.JSONBaseDir = p.jsonBaseDir
|
||||
|
||||
// _, err := scanHistories()
|
||||
histories, err := scanHistories()
|
||||
if err != nil {
|
||||
logrus.Error("Failed to select scan histories: ", err)
|
||||
var err error
|
||||
var jsonDirs report.JSONDirs
|
||||
if jsonDirs, err = report.GetValidJSONDirs(); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
const timeLayout = "2006-01-02 15:04"
|
||||
for _, history := range histories {
|
||||
names := []string{}
|
||||
for _, result := range history.ScanResults {
|
||||
if 0 < len(result.Container.ContainerID) {
|
||||
names = append(names, result.Container.Name)
|
||||
} else {
|
||||
names = append(names, result.ServerName)
|
||||
}
|
||||
for _, d := range jsonDirs {
|
||||
var files []os.FileInfo
|
||||
if files, err = ioutil.ReadDir(d); err != nil {
|
||||
return subcommands.ExitFailure
|
||||
}
|
||||
fmt.Printf("%-3d %s scanned %d servers: %s\n",
|
||||
history.ID,
|
||||
history.ScannedAt.Format(timeLayout),
|
||||
len(history.ScanResults),
|
||||
strings.Join(names, ", "),
|
||||
var hosts []string
|
||||
for _, f := range files {
|
||||
// TODO this "if block" will be deleted in a future release
|
||||
if f.Name() == "all.json" {
|
||||
continue
|
||||
}
|
||||
if filepath.Ext(f.Name()) != ".json" {
|
||||
continue
|
||||
}
|
||||
fileBase := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||
hosts = append(hosts, fileBase)
|
||||
}
|
||||
splitPath := strings.Split(d, string(os.PathSeparator))
|
||||
timeStr := splitPath[len(splitPath)-1]
|
||||
fmt.Printf("%s scanned %d servers: %s\n",
|
||||
timeStr,
|
||||
len(hosts),
|
||||
strings.Join(hosts, ", "),
|
||||
)
|
||||
}
|
||||
return subcommands.ExitSuccess
|
||||
}
|
||||
|
||||
func scanHistories() (histories []models.ScanHistory, err error) {
|
||||
if err := db.OpenDB(); err != nil {
|
||||
return histories, fmt.Errorf(
|
||||
"Failed to open DB. datafile: %s, err: %s", c.Conf.DBPath, err)
|
||||
}
|
||||
histories, err = db.SelectScanHistories()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ type PrepareCmd struct {
|
||||
|
||||
askSudoPassword bool
|
||||
askKeyPassword bool
|
||||
|
||||
useUnattendedUpgrades bool
|
||||
}
|
||||
|
||||
// Name return subcommand name
|
||||
@@ -46,7 +44,6 @@ func (*PrepareCmd) Name() string { return "prepare" }
|
||||
|
||||
// Synopsis return synopsis
|
||||
func (*PrepareCmd) Synopsis() string {
|
||||
// return "Install packages Ubuntu: unattended-upgrade, CentOS: yum-plugin-security)"
|
||||
return `Install required packages to scan.
|
||||
CentOS: yum-plugin-security, yum-plugin-changelog
|
||||
Amazon: None
|
||||
@@ -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{})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
324
db/db.go
@@ -1,324 +0,0 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
m "github.com/future-architect/vuls/models"
|
||||
"github.com/jinzhu/gorm"
|
||||
cvedb "github.com/kotakanbe/go-cve-dictionary/db"
|
||||
cve "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
var db *gorm.DB
|
||||
|
||||
// OpenDB opens Database
|
||||
func OpenDB() (err error) {
|
||||
db, err = gorm.Open("sqlite3", config.Conf.DBPath)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to open DB. datafile: %s, err: %s", config.Conf.DBPath, err)
|
||||
return
|
||||
|
||||
}
|
||||
db.LogMode(config.Conf.DebugSQL)
|
||||
return
|
||||
}
|
||||
|
||||
// MigrateDB migrates Database
|
||||
func MigrateDB() error {
|
||||
if err := db.AutoMigrate(
|
||||
&m.ScanHistory{},
|
||||
&m.ScanResult{},
|
||||
// &m.NWLink{},
|
||||
&m.Container{},
|
||||
&m.CveInfo{},
|
||||
&m.CpeName{},
|
||||
&m.PackageInfo{},
|
||||
&m.DistroAdvisory{},
|
||||
&cve.CveDetail{},
|
||||
&cve.Jvn{},
|
||||
&cve.Nvd{},
|
||||
&cve.Reference{},
|
||||
&cve.Cpe{},
|
||||
).Error; err != nil {
|
||||
return fmt.Errorf("Failed to migrate. err: %s", err)
|
||||
}
|
||||
|
||||
errMsg := "Failed to create index. err: %s"
|
||||
// if err := db.Model(&m.NWLink{}).
|
||||
// AddIndex("idx_n_w_links_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
// return fmt.Errorf(errMsg, err)
|
||||
// }
|
||||
if err := db.Model(&m.Container{}).
|
||||
AddIndex("idx_containers_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.CveInfo{}).
|
||||
AddIndex("idx_cve_infos_scan_result_id", "scan_result_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.CpeName{}).
|
||||
AddIndex("idx_cpe_names_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.PackageInfo{}).
|
||||
AddIndex("idx_package_infos_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&m.DistroAdvisory{}).
|
||||
//TODO check table name
|
||||
AddIndex("idx_distro_advisories_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_details_cve_info_id", "cve_info_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.CveDetail{}).
|
||||
AddIndex("idx_cve_details_cveid", "cve_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Nvd{}).
|
||||
AddIndex("idx_nvds_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Jvn{}).
|
||||
AddIndex("idx_jvns_cve_detail_id", "cve_detail_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_jvn_id", "jvn_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Cpe{}).
|
||||
AddIndex("idx_cpes_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
if err := db.Model(&cve.Reference{}).
|
||||
AddIndex("idx_references_nvd_id", "nvd_id").Error; err != nil {
|
||||
return fmt.Errorf(errMsg, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert inserts scan results into DB
|
||||
func Insert(results []m.ScanResult) error {
|
||||
for _, r := range results {
|
||||
r.KnownCves = resetGormIDs(r.KnownCves)
|
||||
r.UnknownCves = resetGormIDs(r.UnknownCves)
|
||||
}
|
||||
|
||||
history := m.ScanHistory{
|
||||
ScanResults: results,
|
||||
ScannedAt: time.Now(),
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
if err := db.Create(&history).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, scanResult := range history.ScanResults {
|
||||
scanResult.ScanHistoryID = history.ID
|
||||
if err := db.Create(&scanResult).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
scanResult.Container.ScanResultID = scanResult.ID
|
||||
if err := db.Create(&scanResult.Container).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.KnownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := insertCveInfos(scanResult.ID, scanResult.UnknownCves); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func insertCveInfos(scanResultID uint, infos []m.CveInfo) error {
|
||||
for _, cveInfo := range infos {
|
||||
cveInfo.ScanResultID = scanResultID
|
||||
if err := db.Create(&cveInfo).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pack := range cveInfo.Packages {
|
||||
pack.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&pack).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, distroAdvisory := range cveInfo.DistroAdvisories {
|
||||
distroAdvisory.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&distroAdvisory).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cpeName := range cveInfo.CpeNames {
|
||||
cpeName.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cpeName).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
db = db.Set("gorm:save_associations", true)
|
||||
cveDetail := cveInfo.CveDetail
|
||||
cveDetail.CveInfoID = cveInfo.ID
|
||||
if err := db.Create(&cveDetail).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
db = db.Set("gorm:save_associations", false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetGormIDs(infos []m.CveInfo) []m.CveInfo {
|
||||
for i := range infos {
|
||||
infos[i].CveDetail.ID = 0
|
||||
// NVD
|
||||
infos[i].CveDetail.Nvd.ID = 0
|
||||
for j := range infos[i].CveDetail.Nvd.Cpes {
|
||||
infos[i].CveDetail.Nvd.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Nvd.References {
|
||||
infos[i].CveDetail.Nvd.References[j].ID = 0
|
||||
}
|
||||
|
||||
// JVN
|
||||
infos[i].CveDetail.Jvn.ID = 0
|
||||
for j := range infos[i].CveDetail.Jvn.Cpes {
|
||||
infos[i].CveDetail.Jvn.Cpes[j].ID = 0
|
||||
}
|
||||
for j := range infos[i].CveDetail.Jvn.References {
|
||||
infos[i].CveDetail.Jvn.References[j].ID = 0
|
||||
}
|
||||
|
||||
//Packages
|
||||
for j := range infos[i].Packages {
|
||||
infos[i].Packages[j].ID = 0
|
||||
infos[i].Packages[j].CveInfoID = 0
|
||||
}
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// SelectScanHistory select scan history from DB
|
||||
func SelectScanHistory(historyID string) (m.ScanHistory, error) {
|
||||
var err error
|
||||
|
||||
scanHistory := m.ScanHistory{}
|
||||
if historyID == "" {
|
||||
// select latest
|
||||
db.Order("scanned_at desc").First(&scanHistory)
|
||||
} else {
|
||||
var id int
|
||||
if id, err = strconv.Atoi(historyID); err != nil {
|
||||
return m.ScanHistory{},
|
||||
fmt.Errorf("historyID have to be numeric number: %s", err)
|
||||
}
|
||||
db.First(&scanHistory, id)
|
||||
}
|
||||
|
||||
if scanHistory.ID == 0 {
|
||||
return m.ScanHistory{}, fmt.Errorf("No scanHistory records")
|
||||
}
|
||||
|
||||
// results := []m.ScanResult{}
|
||||
results := m.ScanResults{}
|
||||
db.Model(&scanHistory).Related(&results, "ScanResults")
|
||||
scanHistory.ScanResults = results
|
||||
|
||||
for i, r := range results {
|
||||
// nw := []m.NWLink{}
|
||||
// db.Model(&r).Related(&nw, "NWLinks")
|
||||
// scanHistory.ScanResults[i].NWLinks = nw
|
||||
|
||||
di := m.Container{}
|
||||
db.Model(&r).Related(&di, "Container")
|
||||
scanHistory.ScanResults[i].Container = di
|
||||
|
||||
knownCves := selectCveInfos(&r, "KnownCves")
|
||||
sort.Sort(m.CveInfos(knownCves))
|
||||
scanHistory.ScanResults[i].KnownCves = knownCves
|
||||
}
|
||||
|
||||
sort.Sort(scanHistory.ScanResults)
|
||||
return scanHistory, nil
|
||||
}
|
||||
|
||||
func selectCveInfos(result *m.ScanResult, fieldName string) []m.CveInfo {
|
||||
cveInfos := []m.CveInfo{}
|
||||
db.Model(&result).Related(&cveInfos, fieldName)
|
||||
|
||||
for i, cveInfo := range cveInfos {
|
||||
cveDetail := cve.CveDetail{}
|
||||
db.Model(&cveInfo).Related(&cveDetail, "CveDetail")
|
||||
id := cveDetail.CveID
|
||||
filledCveDetail := cvedb.Get(id, db)
|
||||
cveInfos[i].CveDetail = filledCveDetail
|
||||
|
||||
packs := []m.PackageInfo{}
|
||||
db.Model(&cveInfo).Related(&packs, "Packages")
|
||||
cveInfos[i].Packages = packs
|
||||
|
||||
advisories := []m.DistroAdvisory{}
|
||||
db.Model(&cveInfo).Related(&advisories, "DistroAdvisories")
|
||||
cveInfos[i].DistroAdvisories = advisories
|
||||
|
||||
names := []m.CpeName{}
|
||||
db.Model(&cveInfo).Related(&names, "CpeNames")
|
||||
cveInfos[i].CpeNames = names
|
||||
}
|
||||
return cveInfos
|
||||
}
|
||||
|
||||
// SelectScanHistories select latest scan history from DB
|
||||
func SelectScanHistories() ([]m.ScanHistory, error) {
|
||||
scanHistories := []m.ScanHistory{}
|
||||
db.Order("scanned_at desc").Find(&scanHistories)
|
||||
|
||||
if len(scanHistories) == 0 {
|
||||
return []m.ScanHistory{}, fmt.Errorf("No scanHistory records")
|
||||
}
|
||||
|
||||
for i, history := range scanHistories {
|
||||
results := m.ScanResults{}
|
||||
db.Model(&history).Related(&results, "ScanResults")
|
||||
scanHistories[i].ScanResults = results
|
||||
|
||||
for j, r := range results {
|
||||
di := m.Container{}
|
||||
db.Model(&r).Related(&di, "Container")
|
||||
scanHistories[i].ScanResults[j].Container = di
|
||||
}
|
||||
}
|
||||
return scanHistories, nil
|
||||
}
|
||||
56
glide.lock
generated
56
glide.lock
generated
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -222,14 +222,14 @@ ALAS (Amazon)<y:LabelModel>
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="365.54366048177087" width="251.74999999999977" x="641.4999999999995" y="285.984130859375"/>
|
||||
<y:Geometry height="382.650146484375" width="320.0" x="575.0" y="285.984130859375"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">Vuls</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="320.0" x="0.0" y="0.0">Vuls</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="14" bottomF="13.710652669270871" left="25" leftF="25.499999999999773" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
|
||||
<y:BorderInsets bottom="29" bottomF="28.63427734375" left="0" leftF="0.0" right="0" rightF="0.0" top="31" topF="30.57621256510413"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -248,7 +248,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
<node id="n4::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="798.2499999999993" y="566.4311319986981"/>
|
||||
<y:Geometry height="50.0" width="80.0" x="800.0" y="575.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="42.595703125" x="18.7021484375" y="15.93359375">Report<y:LabelModel>
|
||||
@@ -296,6 +296,24 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4::n3">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="590.0" y="575.0"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="63.203125" x="8.3984375" y="8.8671875">Web View
|
||||
(Vulsrepo)<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n5">
|
||||
@@ -319,30 +337,6 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="699.9999999999993" y="453.02174886067706"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="48.4140625" x="6.04296875" y="25.93359375">SQLite3<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:SVGNode>
|
||||
<y:Geometry height="37.0" width="109.57881927490234" x="889.737640380859" y="748.5119222005209"/>
|
||||
@@ -356,7 +350,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:SVGNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.bpmn.Artifact.withShadow">
|
||||
<y:Geometry height="24.0" width="35.0" x="811.8264548778523" y="681.5277913411459"/>
|
||||
@@ -379,20 +373,20 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9" yfiles.foldertype="group">
|
||||
<node id="n8" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="293.63460286458337" width="251.74999999999977" x="645.7499999999998" y="-51.181884765625114"/>
|
||||
<y:Geometry height="293.63460286458337" width="322.9999999999998" x="574.4999999999998" y="-51.181884765625114"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="251.74999999999977" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="322.9999999999998" x="0.0" y="0.0">go-cve-dictionary</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="25" bottomF="24.639889388761645" left="12" leftF="12.249999999999773" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
|
||||
<y:BorderInsets bottom="27" bottomF="27.139889388761617" left="37" leftF="37.249999999999886" right="2" rightF="2.4999999999995453" top="50" topF="49.515869140625114"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
@@ -407,12 +401,12 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n9:">
|
||||
<node id="n9::n0">
|
||||
<graph edgedefault="directed" id="n8:">
|
||||
<node id="n8::n0">
|
||||
<data key="d6">
|
||||
<y:GenericNode configuration="com.yworks.flowchart.dataBase">
|
||||
<y:Geometry height="70.0" width="60.5" x="809.75" y="132.8128287101966"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:Geometry height="70.0" width="60.5" x="779.75" y="130.31282871019664"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="28.25" y="33.0">
|
||||
<y:LabelModel>
|
||||
@@ -432,10 +426,10 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:GenericNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9::n1">
|
||||
<node id="n8::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="101.0" x="672.9999999999995" y="148.4841308593749"/>
|
||||
<y:Geometry height="50.0" width="101.0" x="626.7499999999997" y="142.23413085937491"/>
|
||||
<y:Fill color="#C0C0C0" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="73.943359375" x="13.5283203125" y="15.93359375">HTTP server<y:LabelModel>
|
||||
@@ -449,7 +443,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9::n2">
|
||||
<node id="n8::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="80.0" x="800.0" y="35.0"/>
|
||||
@@ -468,7 +462,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<node id="n9" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
@@ -496,8 +490,8 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n10:">
|
||||
<node id="n10::n0">
|
||||
<graph edgedefault="directed" id="n9:">
|
||||
<node id="n9::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="433.22635904947913"/>
|
||||
@@ -514,7 +508,7 @@ ALAS (Amazon)<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n10::n1">
|
||||
<node id="n9::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="333.3980916341144"/>
|
||||
@@ -534,7 +528,7 @@ Container<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n11" yfiles.foldertype="group">
|
||||
<node id="n10" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
@@ -562,8 +556,8 @@ Container<y:LabelModel>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n11:">
|
||||
<node id="n11::n0">
|
||||
<graph edgedefault="directed" id="n10:">
|
||||
<node id="n10::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1011.6223485469814" y="553.0387573242189"/>
|
||||
@@ -580,7 +574,7 @@ Container<y:LabelModel>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n1">
|
||||
<node id="n10::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="50.0" width="85.0" x="1032.6223485469814" y="566.4311319986981"/>
|
||||
@@ -599,7 +593,89 @@ Container<y:LabelModel>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n9::n2" target="n0">
|
||||
<node id="n11" yfiles.foldertype="group">
|
||||
<data key="d4"/>
|
||||
<data key="d6">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="108.94242662355293" width="124.0" x="649.0" y="434.05757337644707"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="124.0" x="0.0" y="0.0">results dir</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 7</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n11:">
|
||||
<node id="n11::n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="664.0" y="470.72358900144707"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="671.0" y="480.2981186685961"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n11::n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="36.0" width="76.0" x="682.0" y="492.0"/>
|
||||
<y:Fill color="#99CCFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="32.3828125" x="21.80859375" y="8.93359375">JSON<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
|
||||
</y:ModelParameter>
|
||||
</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n8::n2" target="n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -636,17 +712,17 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n4::n2" target="n9::n1">
|
||||
<edge id="e2" source="n4::n2" target="n8::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="34.626953125" x="-70.78547462732604" y="-104.38485166353485">HTTP<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="241.78515625" x="-130.8343747008911" y="-104.32744691064147">HTTP or --cve-dictoianry-dbpath option<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="left" ratio="0.6418953379495804" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.6111993328569665" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
@@ -678,7 +754,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-23.436084747315135" y="96.06095129235564">send<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="31.802734375" x="-24.439868927002635" y="91.6240450195761">send<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -696,7 +772,7 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="39.557346848955035" y="107.80743708610783">Generate<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.201171875" x="41.04640068840672" y="114.35365985463022">Generate<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -714,11 +790,11 @@ Vulnerability data<y:LabelModel>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="109.7265625" x="-125.3179906251471" y="-50.63345557215132">Detail Information<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="140.76953125" x="-148.83947500014722" y="-53.63345557215143">View Detail Information<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="58.40488430810431" distanceToCenter="true" position="left" ratio="0.3174763616620919" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="65.44983715439666" distanceToCenter="true" position="left" ratio="0.3605218238342404" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
@@ -726,7 +802,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n4::n2" target="n11::n0">
|
||||
<edge id="e6" source="n4::n2" target="n10::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="87.9098606499872" sy="24.54915453361525" tx="0.0" ty="0.0"/>
|
||||
@@ -744,7 +820,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n4::n2" target="n10::n0">
|
||||
<edge id="e7" source="n4::n2" target="n9::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -762,7 +838,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n10::e0" source="n10::n0" target="n10::n1">
|
||||
<edge id="n9::e0" source="n9::n0" target="n9::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -780,7 +856,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n10::n1" target="n3">
|
||||
<edge id="e8" source="n9::n1" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -790,7 +866,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e9" source="n11::n0" target="n3">
|
||||
<edge id="e9" source="n10::n0" target="n3">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -800,13 +876,13 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e0" source="n9::n2" target="n9::n0">
|
||||
<edge id="n8::e0" source="n8::n2" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-48.552734375" y="14.840008105098306">Insert<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.10546875" x="-56.200249246840485" y="13.59000810509832">Insert<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -818,44 +894,7 @@ Vulnerability data<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e10" source="n4::n2" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="70.275390625" x="-75.4169220517914" y="5.2924810128525905">Insert
|
||||
Scan Result<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e11" source="n6" target="n4::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="-48.96875000000057" y="19.334788004557254">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e12" source="n4::n0" target="n7">
|
||||
<edge id="e10" source="n4::n0" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -865,7 +904,7 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n7" target="n5">
|
||||
<edge id="e11" source="n6" target="n5">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
@@ -883,18 +922,18 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n4::n2" target="n9::n0">
|
||||
<data key="d9"/>
|
||||
<edge id="e12" source="n4::n2" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="191.283203125" x="39.258457373248575" y="-102.36487019599565">--cve-dictoianry-dbpath option<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="4.0" x="116.345248769779" y="-97.77053302962645">
|
||||
<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.749331808189429" segment="-1"/>
|
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="99.7428628309468" distanceToCenter="true" position="right" ratio="0.7403938917830506" segment="-1"/>
|
||||
</y:ModelParameter>
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
@@ -902,14 +941,13 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n9::e1" source="n9::n1" target="n9::n0">
|
||||
<data key="d9"/>
|
||||
<edge id="n8::e1" source="n8::n1" target="n8::n0">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="0.8643523609480326" y="20.003628886299452">Select<y:LabelModel>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="37.9375" x="7.031249999999773" y="20.56044056577005">Select<y:LabelModel>
|
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
|
||||
</y:LabelModel>
|
||||
<y:ModelParameter>
|
||||
@@ -921,6 +959,48 @@ Scan Result<y:LabelModel>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e13" source="n4::n2" target="n11">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e14" source="n11" target="n4::n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e15" source="n5" target="n4::n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e16" source="n11" target="n4::n3">
|
||||
<data key="d9"/>
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
@@ -72,7 +72,7 @@ FreeBSD: pkg<y:LabelModel>
|
||||
<y:Geometry height="56.0" width="268.0" x="10.0" y="287.8409153761062"/>
|
||||
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Get upgradable packages
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" modelName="custom" textColor="#000000" visible="true" width="260.83984375" x="3.580078125" y="11.8671875">Check upgradable packages
|
||||
Debian/Ubuntu: apt-get upgrade --dry-run<y:LabelModel>
|
||||
<y:SmartNodeLabelModel distance="4.0"/>
|
||||
</y:LabelModel>
|
||||
@@ -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 |
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
107
report/json.go
107
report/json.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
--------------
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
41
scan/base.go
41
scan/base.go
@@ -19,8 +19,10 @@ package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/config"
|
||||
@@ -30,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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
333
scan/debian.go
333
scan/debian.go
@@ -24,6 +24,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/cveapi"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -39,6 +40,7 @@ type debian struct {
|
||||
func newDebian(c config.ServerInfo) *debian {
|
||||
d := &debian{}
|
||||
d.log = util.NewCustomLogger(c)
|
||||
d.setServerInfo(c)
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -46,7 +48,6 @@ func newDebian(c config.ServerInfo) *debian {
|
||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/debian.rb
|
||||
func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err error) {
|
||||
deb = newDebian(c)
|
||||
deb.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/debian_version", noSudo); !r.isSuccess() {
|
||||
if r.Error != nil {
|
||||
@@ -65,17 +66,16 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// root@fa3ec524be43:/# lsb_release -ir
|
||||
// Distributor ID: Ubuntu
|
||||
// Release: 14.04
|
||||
re, _ := regexp.Compile(
|
||||
`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
|
||||
re := regexp.MustCompile(`(?s)^Distributor ID:\s*(.+?)\n*Release:\s*(.+?)$`)
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
|
||||
if len(result) == 0 {
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu version. lsb_release -ir: %s", r)
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
deb.setDistributionInfo(distro, trim(result[2]))
|
||||
deb.setDistro(distro, trim(result[2]))
|
||||
}
|
||||
return true, deb, nil
|
||||
}
|
||||
@@ -86,16 +86,15 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// DISTRIB_RELEASE=14.04
|
||||
// DISTRIB_CODENAME=trusty
|
||||
// DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
|
||||
re, _ := regexp.Compile(
|
||||
`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
|
||||
re := regexp.MustCompile(`(?s)^DISTRIB_ID=(.+?)\n*DISTRIB_RELEASE=(.+?)\n.*$`)
|
||||
result := re.FindStringSubmatch(trim(r.Stdout))
|
||||
if len(result) == 0 {
|
||||
Log.Warnf(
|
||||
"Unknown Debian/Ubuntu. cat /etc/lsb-release: %s", r)
|
||||
deb.setDistributionInfo("debian/ubuntu", "unknown")
|
||||
deb.setDistro("debian/ubuntu", "unknown")
|
||||
} else {
|
||||
distro := strings.ToLower(trim(result[1]))
|
||||
deb.setDistributionInfo(distro, trim(result[2]))
|
||||
deb.setDistro(distro, trim(result[2]))
|
||||
}
|
||||
return true, deb, nil
|
||||
}
|
||||
@@ -103,7 +102,7 @@ func detectDebian(c config.ServerInfo) (itsMe bool, deb osTypeInterface, err err
|
||||
// Debian
|
||||
cmd := "cat /etc/debian_version"
|
||||
if r := sshExec(c, cmd, noSudo); r.isSuccess() {
|
||||
deb.setDistributionInfo("debian", trim(r.Stdout))
|
||||
deb.setDistro("debian", trim(r.Stdout))
|
||||
return true, deb, nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,10 +18,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package scan
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/future-architect/vuls/cache"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
@@ -54,7 +58,7 @@ func TestParseScannedPackagesLineDebian(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestgetCveIDParsingChangelog(t *testing.T) {
|
||||
func TestGetCveIDParsingChangelog(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
in []string
|
||||
@@ -86,12 +90,11 @@ systemd (227-1) unstable; urgency=medium`,
|
||||
"CVE-2015-3210",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// ver
|
||||
[]string{
|
||||
"libpcre3",
|
||||
"2:8.38-1ubuntu1",
|
||||
"2:8.35-7.1ubuntu1",
|
||||
`pcre3 (2:8.38-2) unstable; urgency=low
|
||||
pcre3 (2:8.38-1) unstable; urgency=low
|
||||
pcre3 (2:8.35-8) unstable; urgency=low
|
||||
@@ -110,7 +113,6 @@ pcre3 (2:8.35-7) unstable; urgency=medium`,
|
||||
"CVE-2015-3210",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// ver-ubuntu3
|
||||
[]string{
|
||||
@@ -151,7 +153,7 @@ sysvinit (2.88dsf-57) unstable; urgency=low`,
|
||||
util-linux (2.27.1-3) unstable; urgency=medium
|
||||
CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795)
|
||||
CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285)
|
||||
CVE-2015-3210: heap buffer overflow in pcre_compile2() /
|
||||
CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() /
|
||||
util-linux (2.27.1-2) unstable; urgency=medium
|
||||
util-linux (2.27.1-1ubuntu4) xenial; urgency=medium
|
||||
util-linux (2.27.1-1ubuntu3) xenial; urgency=medium
|
||||
@@ -178,15 +180,17 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
|
||||
"CVE-2015-2325",
|
||||
"CVE-2015-2326",
|
||||
"CVE-2015-3210",
|
||||
"CVE-2016-1000000",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual, _ := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], tt.in[1])
|
||||
actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1])
|
||||
if len(actual) != len(tt.expected) {
|
||||
t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual)
|
||||
t.Errorf(pp.Sprintf("%s", tt.in))
|
||||
continue
|
||||
}
|
||||
for i := range tt.expected {
|
||||
@@ -195,13 +199,6 @@ util-linux (2.26.2-6) unstable; urgency=medium`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
_, err := d.getCveIDParsingChangelog(tt.in[2], tt.in[0], "version number do'nt match case")
|
||||
if err != nil {
|
||||
t.Errorf("Returning error is unexpected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUpdatablePackNames(t *testing.T) {
|
||||
@@ -520,6 +517,95 @@ Calculating upgrade... Done
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChangelogCache(t *testing.T) {
|
||||
const servername = "server1"
|
||||
pack := models.PackageInfo{
|
||||
Name: "apt",
|
||||
Version: "1.0.0",
|
||||
NewVersion: "1.0.1",
|
||||
}
|
||||
var meta = cache.Meta{
|
||||
Name: servername,
|
||||
Distro: config.Distro{
|
||||
Family: "ubuntu",
|
||||
Release: "16.04",
|
||||
},
|
||||
Packs: []models.PackageInfo{pack},
|
||||
}
|
||||
|
||||
const path = "/tmp/vuls-test-cache-11111111.db"
|
||||
log := logrus.NewEntry(&logrus.Logger{})
|
||||
if err := cache.SetupBolt(path, log); err != nil {
|
||||
t.Errorf("Failed to setup bolt: %s", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
|
||||
if err := cache.DB.EnsureBuckets(meta); err != nil {
|
||||
t.Errorf("Failed to ensure buckets: %s", err)
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
actual := d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("Failed to get empty stirng from cache:")
|
||||
}
|
||||
|
||||
clog := "changelog-text"
|
||||
if err := cache.DB.PutChangelog(servername, "apt", clog); err != nil {
|
||||
t.Errorf("Failed to put changelog: %s", err)
|
||||
}
|
||||
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != clog {
|
||||
t.Errorf("Failed to get changelog from cache: %s", actual)
|
||||
}
|
||||
|
||||
// increment a version of the pack
|
||||
pack.NewVersion = "1.0.2"
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("The changelog is not invalidated: %s", actual)
|
||||
}
|
||||
|
||||
// change a name of the pack
|
||||
pack.Name = "bash"
|
||||
actual = d.getChangelogCache(meta, pack)
|
||||
if actual != "" {
|
||||
t.Errorf("The changelog is not invalidated: %s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitAptCachePolicy(t *testing.T) {
|
||||
var tests = []struct {
|
||||
stdout string
|
||||
expected map[string]string
|
||||
}{
|
||||
// This function parse apt-cache policy by using Regexp multi-line mode.
|
||||
// So, test data includes "\r\n"
|
||||
{
|
||||
"apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\napt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\nbase-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
map[string]string{
|
||||
"apt": "apt:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
"apt-utils": "apt-utils:\r\n Installed: 1.2.6\r\n Candidate: 1.2.12~ubuntu16.04.1\r\n Version table:\r\n 1.2.12~ubuntu16.04.1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 1.2.10ubuntu1 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 1.2.6 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
|
||||
"base-files": "base-files:\r\n Installed: 9.4ubuntu3\r\n Candidate: 9.4ubuntu4.2\r\n Version table:\r\n 9.4ubuntu4.2 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages\r\n 9.4ubuntu4 500\r\n 500 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages\r\n *** 9.4ubuntu3 100\r\n 100 /var/lib/dpkg/status\r\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range tests {
|
||||
actual := d.splitAptCachePolicy(tt.stdout)
|
||||
if !reflect.DeepEqual(tt.expected, actual) {
|
||||
e := pp.Sprintf("%v", tt.expected)
|
||||
a := pp.Sprintf("%v", actual)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAptCachePolicy(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
@@ -19,18 +36,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
|
||||
}
|
||||
}
|
||||
|
||||
188
scan/redhat.go
188
scan/redhat.go
@@ -42,16 +42,16 @@ type redhat struct {
|
||||
func newRedhat(c config.ServerInfo) *redhat {
|
||||
r := &redhat{}
|
||||
r.log = util.NewCustomLogger(c)
|
||||
r.setServerInfo(c)
|
||||
return r
|
||||
}
|
||||
|
||||
// https://github.com/serverspec/specinfra/blob/master/lib/specinfra/helper/detect_os/redhat.rb
|
||||
func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
red = newRedhat(c)
|
||||
red.setServerInfo(c)
|
||||
|
||||
if r := sshExec(c, "ls /etc/fedora-release", noSudo); r.isSuccess() {
|
||||
red.setDistributionInfo("fedora", "unknown")
|
||||
red.setDistro("fedora", "unknown")
|
||||
Log.Warn("Fedora not tested yet: %s", r)
|
||||
return true, red
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
// $ cat /etc/redhat-release
|
||||
// CentOS release 6.5 (Final)
|
||||
if r := sshExec(c, "cat /etc/redhat-release", noSudo); r.isSuccess() {
|
||||
re, _ := regexp.Compile(`(.*) release (\d[\d.]*)`)
|
||||
re := regexp.MustCompile(`(.*) release (\d[\d.]*)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 3 {
|
||||
Log.Warn("Failed to parse RedHat/CentOS version: %s", r)
|
||||
@@ -72,9 +72,9 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
release := result[2]
|
||||
switch strings.ToLower(result[1]) {
|
||||
case "centos", "centos linux":
|
||||
red.setDistributionInfo("centos", release)
|
||||
red.setDistro("centos", release)
|
||||
default:
|
||||
red.setDistributionInfo("rhel", release)
|
||||
red.setDistro("rhel", release)
|
||||
}
|
||||
return true, red
|
||||
}
|
||||
@@ -90,7 +90,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
release = fields[4]
|
||||
}
|
||||
}
|
||||
red.setDistributionInfo(family, release)
|
||||
red.setDistro(family, release)
|
||||
return true, red
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func detectRedhat(c config.ServerInfo) (itsMe bool, red osTypeInterface) {
|
||||
}
|
||||
|
||||
func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
r := o.ssh("yum --version", sudo)
|
||||
r := o.ssh("yum --version", o.sudo())
|
||||
if !r.isSuccess() {
|
||||
o.log.Errorf("sudo error on %s", r)
|
||||
return fmt.Errorf("Failed to sudo: %s", r)
|
||||
@@ -108,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
13
util/util.go
13
util/util.go
@@ -111,7 +111,7 @@ func ProxyEnv() string {
|
||||
|
||||
// PrependProxyEnv prepends proxy enviroment variable
|
||||
func PrependProxyEnv(cmd string) string {
|
||||
if config.Conf.HTTPProxy == "" {
|
||||
if len(config.Conf.HTTPProxy) == 0 {
|
||||
return cmd
|
||||
}
|
||||
return fmt.Sprintf("%s %s", ProxyEnv(), cmd)
|
||||
@@ -124,3 +124,14 @@ func PrependProxyEnv(cmd string) string {
|
||||
// }
|
||||
// return time.Unix(i, 0), nil
|
||||
// }
|
||||
|
||||
// Truncate truncates string to the length
|
||||
func Truncate(str string, length int) string {
|
||||
if length < 0 {
|
||||
return str
|
||||
}
|
||||
if length <= len(str) {
|
||||
return str[:length]
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -131,3 +131,43 @@ func TestPrependHTTPProxyEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTruncate(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
length int
|
||||
out string
|
||||
}{
|
||||
{
|
||||
in: "abcde",
|
||||
length: 3,
|
||||
out: "abc",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 5,
|
||||
out: "abcde",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 10,
|
||||
out: "abcdefg",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: 0,
|
||||
out: "",
|
||||
},
|
||||
{
|
||||
in: "abcdefg",
|
||||
length: -1,
|
||||
out: "abcdefg",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
actual := Truncate(tt.in, tt.length)
|
||||
if actual != tt.out {
|
||||
t.Errorf("\nexpected: %s\n actual: %s", tt.out, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user