Compare commits

...

23 Commits

Author SHA1 Message Date
kota kanbe
473096d35d Fix .goreleaser.yml 2017-10-19 14:31:35 +09:00
kota kanbe
0eae26e261 Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix a bug of making channels when fill oval information via HTTP (#514)
2017-10-17 13:37:06 +09:00
Kota Kanbe
a32845f652 Fix a bug of making channels when fill oval information via HTTP (#514)
* Fix a bug of making channels when fill oval information via HTTP
2017-10-17 13:36:49 +09:00
kota kanbe
15a0f7eadb Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Fix OVAL detection on Debian and Ubuntu (#509)
2017-10-16 14:13:40 +09:00
Kota Kanbe
5a0a6abf11 Fix OVAL detection on Debian and Ubuntu (#509)
* Add filter options to tui subcommand (#508)

* Capture version of source packages on Debian based linux

* Change makefile, gofmt -s

* Refactoring

* Implement OVAL detection of source packages for Debian, Ubuntu
2017-10-13 17:22:11 +09:00
kota kanbe
032b8d9572 Merge branch 'master' of https://github.com/future-architect/vuls
* 'master' of https://github.com/future-architect/vuls:
  Add filter options to tui subcommand (#508)
2017-09-29 08:41:31 +09:00
Kota Kanbe
5798e3af83 Add filter options to tui subcommand (#508) 2017-09-29 08:37:32 +09:00
Kota Kanbe
8e15b9ce1c Add filter options to tui subcommand (#508) 2017-09-28 18:31:09 +09:00
Kota Kanbe
7a1f132c1f Add -ignore-unfixed option to report subcommand #485 (#507) 2017-09-28 17:29:47 +09:00
Emilien Kenler
a8483b2195 Add goreleaser to distribute binaries (#460)
See https://github.com/future-architect/vuls/issues/459
2017-09-28 15:29:27 +09:00
kota kanbe
83bbbd0cb0 Add goreportcard to README 2017-09-28 15:23:51 +09:00
Kota Kanbe
132432dce6 Support SUSE Enterprise Linux (#487)
* Support SUSE Enterprise Linux

* Implement Reboot Required detection on SLES

* Fix query OVAL because SUSE provides OVAL data each major.minor version

* Update README

* Support SUSE Enterprise 11
2017-09-28 12:23:19 +09:00
Xiuming Chen
e5eb8e42f5 Debian: Use --showformat flag to get status of packages and ignore n(not-inst… (#484)
* Use --showformat flag to get status of packages and ignore n(not-installed) and c(removed, only has config files remaining) packages.

* Ignoring all packages that are not in 'Installed' status.

* Simplify char escaping in the command.

* Fix typo.
2017-09-27 09:43:59 +09:00
Takayuki Ushida
1095ebea24 fix vulsrepo dockerfile (#496) 2017-09-26 18:17:46 +09:00
328
1541a602b2 Update README.ja.md (#498) 2017-09-26 18:17:19 +09:00
~Stack~
03a141c252 Fix typos (#499)
* Update bolt.go

Fix typos

* Update util.go

Fix Typos
2017-09-26 18:16:54 +09:00
Kota Kanbe
5f2183fc8e Check repoquery with sudo nopasswd in deep scan mode on RedHat (#492) 2017-09-14 09:14:20 -07:00
Kota Kanbe
820831fa5d Fix sort order of servers on TUI (#481) 2017-09-05 15:54:13 +09:00
Kota Kanbe
6d2d767c52 Fix a arg of report subcommand (#479) 2017-09-04 14:47:25 +08:00
Kota Kanbe
e0c3a728ae Fix ping option of discover subcommand #471 (#472) 2017-08-30 14:13:53 +08:00
sadayuki-matsuno
ec92f7797f add windows type (#470) 2017-08-28 18:49:34 +08:00
Kota Kanbe
0ba490c6df Merge pull request #469 from usiusi360/use_vulsrepo-server
use_vulsrepo-server
2017-08-25 21:59:52 +09:00
usiusi360
cfd668e11d use_vulsrepo-server 2017-08-25 21:42:33 +09:00
41 changed files with 1429 additions and 449 deletions

24
.goreleaser.yml Normal file
View File

@@ -0,0 +1,24 @@
project_name: vuls
release:
github:
owner: future-architect
name: vuls
builds:
- goos:
- linux
goarch:
- amd64
main: .
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
binary: vuls
archive:
format: tar.gz
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- LICENSE
- NOTICE
- README*
- CHANGELOG.md
snapshot:
name_template: SNAPSHOT-{{ .Commit }}

View File

@@ -3,3 +3,5 @@ language: go
go:
- 1.8
after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

View File

@@ -31,10 +31,10 @@ depup:
go get -u github.com/golang/dep/...
dep ensure -update
build: main.go dep
build: main.go dep pretest
go build -ldflags "$(LDFLAGS)" -o vuls $<
install: main.go dep
install: main.go dep pretest
go install -ldflags "$(LDFLAGS)"
@@ -47,10 +47,10 @@ vet:
echo $(PKGS) | xargs go vet || exit;
fmt:
gofmt -w $(SRCS)
gofmt -s -w $(SRCS)
fmtcheck:
$(foreach file,$(SRCS),gofmt -d $(file);)
$(foreach file,$(SRCS),gofmt -s -d $(file);)
pretest: lint vet fmtcheck

67
Gopkg.lock generated
View File

@@ -4,14 +4,14 @@
[[projects]]
name = "github.com/Azure/azure-sdk-for-go"
packages = ["storage"]
revision = "57db66900881e9fd21fd041a9d013514700ecab3"
version = "v10.3.0-beta"
revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a"
version = "v10.3.1-beta"
[[projects]]
name = "github.com/Azure/go-autorest"
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
revision = "77a52603f06947221c672f10275abc9bf2c7d557"
version = "v8.3.0"
revision = "f6be1abbb5abd0517522f850dd785990d373da7e"
version = "v8.4.0"
[[projects]]
name = "github.com/BurntSushi/toml"
@@ -28,8 +28,8 @@
[[projects]]
name = "github.com/aws/aws-sdk-go"
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/restxml","private/protocol/xml/xmlutil","service/s3","service/sts"]
revision = "264af29009637e0a9e5d4a276d0969c3ed918ffd"
version = "v1.10.29"
revision = "c652f9369083515c3ddf1fbaf6df68da2c101545"
version = "v1.12.1"
[[projects]]
name = "github.com/boltdb/bolt"
@@ -46,8 +46,8 @@
[[projects]]
name = "github.com/cheggaaa/pb"
packages = ["."]
revision = "0d6285554e726cc0620cbecc7e6969c945dcc63b"
version = "v1.0.17"
revision = "657164d0228d6bebe316fdf725c69f131a50fb10"
version = "v1.0.18"
[[projects]]
name = "github.com/dgrijalva/jwt-go"
@@ -64,8 +64,8 @@
[[projects]]
name = "github.com/go-redis/redis"
packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"]
revision = "19c1c2272e00c1aaa903cf574c746cd449f9cd3c"
version = "v6.5.7"
revision = "975882d73d21759d45a4eb49652064083bc23e61"
version = "v6.7.0"
[[projects]]
name = "github.com/go-sql-driver/mysql"
@@ -106,8 +106,7 @@
[[projects]]
name = "github.com/jmespath/go-jmespath"
packages = ["."]
revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9"
version = "0.2.2"
revision = "0b12d6b5"
[[projects]]
name = "github.com/jroimartin/gocui"
@@ -119,7 +118,7 @@
branch = "master"
name = "github.com/k0kubun/pp"
packages = ["."]
revision = "d1532fc5d94ecdf2da29e24d7b99721f3287de4a"
revision = "e057ee7a28277be4d2af303443b6da377768181f"
[[projects]]
branch = "master"
@@ -137,7 +136,7 @@
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
packages = ["config","db","jvn","log","models","nvd","util"]
revision = "c20fa7e1d07f7c700baf12c855f7fcf61525f1b6"
revision = "f5406ffe8226f01f64544723339c6a17b2bd74af"
[[projects]]
name = "github.com/kotakanbe/go-pingscanner"
@@ -149,7 +148,7 @@
branch = "master"
name = "github.com/kotakanbe/goval-dictionary"
packages = ["config","db","db/rdb","log","models"]
revision = "3523cc174e68f285d0572d07c68ffa3a9290799c"
revision = "fd8ff5a6343912117d1b7db16fbd5fa1f4116c3a"
[[projects]]
branch = "master"
@@ -157,17 +156,11 @@
packages = ["."]
revision = "75edb2e85a38873f0318be05a458446681d1022f"
[[projects]]
name = "github.com/labstack/gommon"
packages = ["color","log"]
revision = "779b8a8b9850a97acba6a3fe20feb628c39e17c1"
version = "0.2.2"
[[projects]]
branch = "master"
name = "github.com/lib/pq"
packages = [".","hstore","oid"]
revision = "e42267488fe361b9dc034be7a6bffef5b195bceb"
revision = "b77235e3890a962fe8a6f8c4c7198679ca7814e7"
[[projects]]
name = "github.com/mattn/go-colorable"
@@ -178,8 +171,8 @@
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
version = "v0.0.2"
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
name = "github.com/mattn/go-runewidth"
@@ -203,7 +196,7 @@
branch = "master"
name = "github.com/moul/http2curl"
packages = ["."]
revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8"
revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
[[projects]]
branch = "master"
@@ -239,19 +232,7 @@
branch = "master"
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "84573d5f03ab3740f524c7842c3a9bf617961d32"
[[projects]]
branch = "master"
name = "github.com/valyala/bytebufferpool"
packages = ["."]
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
[[projects]]
branch = "master"
name = "github.com/valyala/fasttemplate"
packages = ["."]
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
revision = "89742aefa4b206dcf400792f3bd35b542998eb3b"
[[projects]]
branch = "master"
@@ -263,25 +244,25 @@
branch = "master"
name = "golang.org/x/crypto"
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
revision = "eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035"
revision = "c84b36c635ad003a10f0c755dff5685ceef18c71"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","idna","publicsuffix"]
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
revision = "0a9397675ba34b2845f758fe3cd68828369c6517"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
revision = "07c182904dbd53199946ba614a412c61d3c548f5"
revision = "314a259e304ff91bd6985da2a7149bbf91237993"
[[projects]]
branch = "master"
name = "golang.org/x/text"
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
revision = "e56139fd9c5bc7244c76116c68e500765bb6db6b"
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
revision = "1cbadb444a806fd9430d14ad08967ed91da4fa0a"
[solve-meta]
analyzer-name = "dep"

View File

@@ -139,7 +139,7 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり
# Main Features
- サーバに存在する脆弱性をスキャン
- FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Raspbianに対応
- FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbianに対応
- クラウド、オンプレミス、Docker
- 高精度なスキャン
- Vulsは複数の脆弱性データベース、複数の検知方法を組み合わせることで高精度なスキャンを実現している
@@ -328,6 +328,7 @@ $ goval-dictionary fetch-redhat 7
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
- [Oracle Linux](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
## Step5. Deploy Vuls
@@ -586,9 +587,10 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
| Oracle | Fast |  No | Supported | No |
| Ubuntu | Fast |  No | Supported | No |
| Debian | Fast |  No | Supported | No |
| Raspbian |1st time: Slow <br> From 2nd time: Fast | Need | No | Need |
| FreeBSD | Fast |  No | No | Need |
| Amazon | Fast |  No | No | Need |
| Raspbian |1st time: Slow <br> From 2nd time: Fast | Need | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No|
----
@@ -604,22 +606,26 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
| Oracle | Slow |  Need | Supported | Need |
| Ubuntu |1st time: Slow <br> From 2nd time: Fast| Need | Supported | Need |
| Debian |1st time: Slow <br> From 2nd time: Fast| Need | Supported | Need |
| Raspbian |1st time: Slow <br> From 2nd time: Fast| Need | No | Need |
| FreeBSD | Fast |  No | No | Need |
| Amazon | Slow |  No | No | Need |
| Raspbian |1st time: Slow <br> From 2nd time: Fast| Need | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No|
- Ubuntu, Debian, Raspbian
- On Ubuntu, Debian and Raspbian
`apt-get changelog`でアップデート対象のパッケージのチェンジログを取得し、含まれるCVE IDをパースする。
アップデート対象のパッケージが沢山ある場合、チェンジログの取得に時間がかかるので、初回のスキャンは遅い。
ただ、回目以降はキャッシュしたchangelogを使うので速くなる。
- CentOS
- On CentOS
`yum changelog`でアップデート対象のパッケージのチェンジログを取得し、含まれるCVE IDをパースする。
- Amazon, RHEL and FreeBSD
- On RHEL, Oracle, Amazon and FreeBSD
`yum changelog`でアップデート対象のパッケージのチェンジログを取得する(パースはしない)。
- On SUSE Enterprise Linux
Same as fast scan mode for now.
----
# Use Cases
@@ -646,6 +652,7 @@ web/app server in the same configuration under the load balancer
| CentOS | 6, 7|
| Amazon Linux| All|
| FreeBSD | 10, 11|
| SUSE Enterprise | 11, 12|
| Raspbian | Jessie, Stretch |
----
@@ -882,6 +889,7 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン
| Amazon | All | - |
| RHEL | 5, 6, 7 | - |
| Oracle Linux | 5, 6, 7 | - |
| SUSE Enterprise| 11, 12 | - |
| FreeBSD | 10, 11 | - |
| Raspbian | Jessie, Stretch | - |
@@ -889,7 +897,7 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン
Deep Scan Modeではスキャン対象サーバ上にいくつかの依存パッケージが必要。
configtestに--deepをつけて実行するとSSH接続に加えて以下もチェックする。
- スキャン対象のサーバ上に依存パッケーがインストールされているか
- スキャン対象のサーバ上に依存パッケーがインストールされているか
- /etc/sudoers
### Dependencies and /etc/sudoers on Target Servers
@@ -899,13 +907,14 @@ Deep Scan Modeでスキャンするためには、下記のパッケージが必
| Distribution | Release | Requirements |
|:-------------|-------------------:|:-------------|
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8, 9| aptitude, reboot-notifier |
| Debian | 7, 8, 9| aptitude, reboot-notifier |
| CentOS | 6, 7| yum-plugin-changelog, yum-utils |
| Amazon | All | yum-plugin-changelog, yum-utils |
| RHEL | 5 | yum-utils, yum-security, yum-changelog |
| RHEL | 6, 7 | yum-utils, yum-plugin-changelog |
| Oracle Linux | 5 | yum-utils, yum-security, yum-changelog |
| Oracle Linux | 6, 7 | yum-utils, yum-plugin-changelog |
| SUSE Enterprise| 11, 12 | - |
| FreeBSD | 10 | - |
| Raspbian | Wheezy, Jessie | - |
@@ -919,13 +928,13 @@ For details, see [-ssh-native-insecure option](#-ssh-native-insecure-option)
- RHEL 5 / Oracle Linux 5
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never info-security
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never info-security, /usr/bin/repoquery
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 6, 7 / Oracle Linux 6, 7
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never --security updateinfo updates
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never --security updateinfo updates, /usr/bin/repoquery
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
@@ -935,7 +944,7 @@ vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- CentOS, Amazon Linux, FreeBSDは今のところRoot権限なしでスキャン可能
- CentOS, Amazon Linux, SUSE Enterprise, FreeBSDは今のところRoot権限なしでスキャン可能
----
@@ -1152,6 +1161,7 @@ report:
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-to-email]
[-to-slack]
[-to-localfile]
@@ -1176,7 +1186,8 @@ report:
[-debug-sql]
[-pipe]
[SERVER]...
[RFC3339 datetime format under results dir]
-aws-profile string
AWS profile to use (default "default")
-aws-region string
@@ -1225,6 +1236,8 @@ report:
http://proxy-url:port (default: empty)
-ignore-unscored-cves
Don't report the unscored CVEs
-ignore-unfixed
Don't report the unfixed CVEs
-lang string
[en|ja] (default "en")
-log-dir string
@@ -1337,7 +1350,7 @@ Confidence 100 / OvalMatch
| Detection Method | Confidence | OS |Description|
|:-----------------------|-------------------:|:---------------------------------|:--|
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian |Detection using OVAL |
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian, SUSE |Detection using OVAL |
| YumUpdateSecurityMatch | 100 | RHEL, Amazon, Oracle |Detection using yum-plugin-security|
| ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version|
| ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version|
@@ -1712,6 +1725,7 @@ $ vuls report -ovaldb-url=http://192.168.0.1:1323
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
- [Oracle](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
----

View File

@@ -4,6 +4,7 @@
[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](http://goo.gl/forms/xm5KFo35tu)
[![License](https://img.shields.io/github/license/future-architect/vuls.svg?style=flat-square)](https://github.com/future-architect/vuls/blob/master/LICENSE.txt)
[![Build Status](https://travis-ci.org/future-architect/vuls.svg?branch=master)](https://travis-ci.org/future-architect/vuls)
[![Go Report Card](https://goreportcard.com/badge/github.com/future-architect/vuls)](https://goreportcard.com/report/github.com/future-architect/vuls)
![Vuls-logo](img/vuls_logo.png)
@@ -144,7 +145,7 @@ Vuls is a tool created to solve the problems listed above. It has the following
# Main Features
- Scan for any vulnerabilities in Linux/FreeBSD Server
- Supports FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux and Raspbian
- Supports FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise Linux and Raspbian
- Cloud, on-premise, Docker
- High quality scan
- Vuls uses Multiple vulnerability databases
@@ -335,6 +336,7 @@ If you want to scan other than CentOS 7, fetch OVAL data according to the OS typ
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
- [Oracle Linux](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
## Step5. Deploy Vuls
@@ -597,6 +599,7 @@ On the aggregation server, you can refer to the scanning result of each scan tar
| Raspbian |1st time: Slow <br> From 2nd time: Fast | Need | No | Need |
| FreeBSD | Fast |  No | No | Need |
| Amazon | Fast |  No | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No|
---------
@@ -614,6 +617,7 @@ On the aggregation server, you can refer to the scanning result of each scan tar
| Raspbian |1st time: Slow <br> From 2nd time: Fast| Need | No | Need |
| FreeBSD | Fast |  No | No | Need |
| Amazon | Slow |  No | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No|
- On Ubuntu, Debian and Raspbian
@@ -624,9 +628,13 @@ From the second time on, the scan speed is fast by using the local cache.
- On CentOS
Vuls issues `yum changelog` to get changelogs of upgradable packages at once and parse the changelog.
- On RHEL, Oracle, Amazon and FreeBSD
Detect CVE IDs by using package manager.
- On SUSE Enterprise Linux
Same as fast scan mode for now.
----
# Use Cases
@@ -658,6 +666,7 @@ If there is a staging environment with the same configuration as the production
| CentOS | 6, 7|
| Amazon Linux | All|
| FreeBSD | 10, 11|
| SUSE Enterprise | 11, 12|
| Raspbian | Jessie, Stretch |
----
@@ -893,6 +902,7 @@ The configtest subcommand checks whether vuls is able to connect via SSH to serv
| Amazon | All | - |
| RHEL | 5, 6, 7 | - |
| Oracle Linux | 5, 6, 7 | - |
| SUSE Enterprise| 11, 12 | - |
| FreeBSD | 10, 11 | - |
| Raspbian | Jessie, Stretch | - |
@@ -915,6 +925,7 @@ In order to scan with deep scan mode, the following dependencies are required, s
| RHEL | 6, 7 | yum-utils, yum-plugin-changelog |
| Oracle Linux | 5 | yum-utils, yum-security, yum-changelog |
| Oracle Linux | 6, 7 | yum-utils, yum-plugin-changelog |
| SUSE Enterprise| 11, 12 | - |
| FreeBSD | 10 | - |
| Raspbian | Wheezy, Jessie | - |
@@ -928,13 +939,13 @@ Example of /etc/sudoers on target servers
- RHEL 5 / Oracle Linux 5
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never info-security
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never list-security --security, /usr/bin/yum --color=never info-security, /usr/bin/repoquery
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- RHEL 6, 7 / Oracle Linux 6, 7
```
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never --security updateinfo updates
vuls ALL=(ALL) NOPASSWD:/usr/bin/yum --color=never repolist, /usr/bin/yum --color=never --security updateinfo list updates, /usr/bin/yum --color=never --security updateinfo updates, /usr/bin/repoquery
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
@@ -944,7 +955,7 @@ vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
- On CentOS, Amazon Linux, FreeBSD, it is possible to scan without root privilege for now.
- On CentOS, Amazon Linux, SUSE Enterprise, FreeBSD, it is possible to scan without root privilege for now.
----
@@ -1163,6 +1174,7 @@ report:
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-to-email]
[-to-slack]
[-to-localfile]
@@ -1187,7 +1199,8 @@ report:
[-debug-sql]
[-pipe]
[SERVER]...
[RFC3339 datetime format under results dir]
-aws-profile string
AWS profile to use (default "default")
-aws-region string
@@ -1236,6 +1249,8 @@ report:
http://proxy-url:port (default: empty)
-ignore-unscored-cves
Don't report the unscored CVEs
-ignore-unfixed
Don't report the unfixed CVEs
-lang string
[en|ja] (default "en")
-log-dir string
@@ -1348,7 +1363,7 @@ Confidence 100 / OvalMatch
| Detection Method | Confidence | OS |Description|
|:-----------------------|-------------------:|:---------------------------------|:--|
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian |Detection using OVAL |
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian, SUSE |Detection using OVAL |
| YumUpdateSecurityMatch | 100 | RHEL, Amazon, Oracle |Detection using yum-plugin-security|
| ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version|
| ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version|
@@ -1599,13 +1614,16 @@ How to integrate Vuls with OWASP Dependency Check
```
tui:
tui
[-refresh-cve]
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
[-cvedb-url=http://127.0.0.1:1323 DB connection string]
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-refresh-cve]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
[-debug]
@@ -1624,6 +1642,12 @@ tui:
DB type for fetching OVAL dictionary (sqlite3 or mysql) (default "sqlite3")
-ovaldb-url string
http://goval-dictionary.com:1324 or mysql connection string
-cvss-over float
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
-ignore-unfixed
Don't report the unfixed CVEs
-ignore-unscored-cves
Don't report the unscored CVEs
-debug
debug mode
-debug-sql
@@ -1711,6 +1735,7 @@ $ vuls report -ovaldb-url=http://192.168.0.1:1323
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
- [Oracle](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
----

4
cache/bolt.go vendored
View File

@@ -163,7 +163,7 @@ func (b Bolt) GetChangelog(servername, packName string) (changelog string, err e
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)
return fmt.Errorf("Failed to get Bucket: %s", servername)
}
v := bkt.Get([]byte(packName))
if v == nil {
@@ -181,7 +181,7 @@ 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)
return fmt.Errorf("Failed to get Bucket: %s", servername)
}
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
return err

View File

@@ -57,6 +57,7 @@ func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
// validate
if len(f.Args()) == 0 {
logrus.Errorf("Usage: " + p.Usage())
return subcommands.ExitUsageError
}
@@ -65,7 +66,6 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
CIDR: cidr,
PingOptions: []string{
"-c1",
"-t1",
},
NumOfConcurrency: 100,
}

View File

@@ -44,7 +44,9 @@ type ReportCmd struct {
cvssScoreOver float64
ignoreUnscoredCves bool
httpProxy string
ignoreUnfixed bool
httpProxy string
cveDBType string
cveDBPath string
@@ -107,6 +109,7 @@ func (*ReportCmd) Usage() string {
[-cvss-over=7]
[-diff]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-to-email]
[-to-slack]
[-to-localfile]
@@ -131,7 +134,7 @@ func (*ReportCmd) Usage() string {
[-debug-sql]
[-pipe]
[SERVER]...
[RFC3339 datetime format under results dir]
`
}
@@ -213,6 +216,12 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
false,
"Don't report the unscored CVEs")
f.BoolVar(
&p.ignoreUnfixed,
"ignore-unfixed",
false,
"Don't report the unfixed CVEs")
f.StringVar(
&p.httpProxy,
"http-proxy",
@@ -312,6 +321,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.OvalDBURL = p.ovalDBURL
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
c.Conf.HTTPProxy = p.httpProxy
c.Conf.FormatXML = p.formatXML

View File

@@ -49,6 +49,10 @@ type TuiCmd struct {
ovalDBPath string
ovalDBURL string
cvssScoreOver float64
ignoreUnscoredCves bool
ignoreUnfixed bool
pipe bool
}
@@ -62,6 +66,7 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabiliti
func (*TuiCmd) Usage() string {
return `tui:
tui
[-refresh-cve]
[-config=/path/to/config.toml]
[-cvedb-type=sqlite3|mysql|postgres]
[-cvedb-path=/path/to/cve.sqlite3]
@@ -69,7 +74,9 @@ func (*TuiCmd) Usage() string {
[-ovaldb-type=sqlite3|mysql]
[-ovaldb-path=/path/to/oval.sqlite3]
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
[-refresh-cve]
[-cvss-over=7]
[-ignore-unscored-cves]
[-ignore-unfixed]
[-results-dir=/path/to/results]
[-log-dir=/path/to/log]
[-debug]
@@ -139,6 +146,24 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
"",
"http://goval-dictionary.example.com:1324 or mysql connection string")
f.Float64Var(
&p.cvssScoreOver,
"cvss-over",
0,
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
f.BoolVar(
&p.ignoreUnscoredCves,
"ignore-unscored-cves",
false,
"Don't report the unscored CVEs")
f.BoolVar(
&p.ignoreUnfixed,
"ignore-unfixed",
false,
"Don't report the unfixed CVEs")
f.BoolVar(
&p.pipe,
"pipe",
@@ -169,6 +194,9 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.OvalDBType = p.ovalDBType
c.Conf.OvalDBPath = p.ovalDBPath
c.Conf.OvalDBURL = p.ovalDBURL
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
log.Info("Validating config...")
if !c.Conf.ValidateOnTui() {

View File

@@ -58,6 +58,24 @@ const (
// Raspbian is
Raspbian = "raspbian"
// Windows is
Windows = "windows"
// OpenSUSE is
OpenSUSE = "opensuse"
// OpenSUSELeap is
OpenSUSELeap = "opensuse.leap"
// SUSEEnterpriseServer is
SUSEEnterpriseServer = "suse.linux.enterprise.server"
// SUSEEnterpriseDesktop is
SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
// SUSEOpenstackCloud is
SUSEOpenstackCloud = "suse.openstack.cloud"
)
//Config is struct of Configuration
@@ -73,6 +91,7 @@ type Config struct {
CvssScoreOver float64
IgnoreUnscoredCves bool
IgnoreUnfixed bool
SSHNative bool
ContainersOnly bool

View File

@@ -229,6 +229,9 @@ const (
// Oracle is Oracle Linux
Oracle CveContentType = "oracle"
// SUSE is SUSE Linux
SUSE CveContentType = "suse"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)

View File

@@ -18,4 +18,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package models
// JSONVersion is JSON Version
const JSONVersion = 2
const JSONVersion = 3

View File

@@ -81,7 +81,7 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
return "", Package{}, false
}
// Package has installed packages.
// Package has installed binary packages.
type Package struct {
Name string
Version string
@@ -116,6 +116,8 @@ func (p Package) FormatVersionFromTo(notFixedYet bool) string {
to := p.FormatNewVer()
if notFixedYet {
to = "Not Fixed Yet"
} else if p.NewVersion == "" {
to = "Unknown"
}
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
}
@@ -151,3 +153,31 @@ type Changelog struct {
Contents string
Method DetectionMethod
}
// SrcPackage has installed source package information.
// Debian based Linux has both of package and source information in dpkg.
// OVAL database often includes a source version (Not a binary version),
// so it is also needed to capture source version for OVAL version comparison.
// https://github.com/future-architect/vuls/issues/504
type SrcPackage struct {
Name string
Version string
BinaryNames []string
}
// AddBinaryName add the name if not exists
func (s *SrcPackage) AddBinaryName(name string) {
found := false
for _, n := range s.BinaryNames {
if n == name {
return
}
}
if !found {
s.BinaryNames = append(s.BinaryNames, name)
}
}
// SrcPackages is Map of SrcPackage
// { "package-name": SrcPackage }
type SrcPackages map[string]SrcPackage

View File

@@ -87,3 +87,49 @@ func TestMerge(t *testing.T) {
t.Errorf("expected %s, actual %s", e, a)
}
}
func TestAddBinaryName(t *testing.T) {
var tests = []struct {
in SrcPackage
name string
expected SrcPackage
}{
{
SrcPackage{Name: "hoge"},
"curl",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
},
{
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
"curl",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
},
{
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl"},
},
"openssh",
SrcPackage{
Name: "hoge",
BinaryNames: []string{"curl", "openssh"},
},
},
}
for _, tt := range tests {
tt.in.AddBinaryName(tt.name)
if !reflect.DeepEqual(tt.in, tt.expected) {
t.Errorf("expected %#v, actual %#v", tt.in, tt.expected)
}
}
}

View File

@@ -46,8 +46,10 @@ type ScanResult struct {
RunningKernel Kernel
Packages Packages
Errors []string
Optional [][]interface{}
SrcPackages SrcPackages
Errors []string
Optional [][]interface{}
Config struct {
Scan config.Config
@@ -76,10 +78,8 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
}
return false
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
r.ScannedCves = filtered
return r
}
// FilterIgnoreCves is filter function.
@@ -92,9 +92,24 @@ func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
}
return true
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
r.ScannedCves = filtered
return r
}
// FilterUnfixed is filter function.
func (r ScanResult) FilterUnfixed() ScanResult {
if !config.Conf.IgnoreUnfixed {
return r
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
NotFixedAll := true
for _, p := range v.AffectedPackages {
NotFixedAll = NotFixedAll && p.NotFixedYet
}
return !NotFixedAll
})
r.ScannedCves = filtered
return r
}
// ReportFileName returns the filename on localhost without extention

View File

@@ -21,6 +21,7 @@ import (
"testing"
"time"
"github.com/future-architect/vuls/config"
"github.com/k0kubun/pp"
)
@@ -255,3 +256,83 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
}
}
}
func TestFilterUnfixed(t *testing.T) {
var tests = []struct {
in ScanResult
out ScanResult
}{
{
in: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
AffectedPackages: PackageStatuses{
{
Name: "a",
NotFixedYet: true,
},
},
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
{
Name: "b",
NotFixedYet: false,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
{
Name: "c",
NotFixedYet: true,
},
{
Name: "d",
NotFixedYet: false,
},
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
AffectedPackages: PackageStatuses{
{
Name: "b",
NotFixedYet: false,
},
},
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
AffectedPackages: PackageStatuses{
{
Name: "c",
NotFixedYet: true,
},
{
Name: "d",
NotFixedYet: false,
},
},
},
},
},
},
}
for i, tt := range tests {
config.Conf.IgnoreUnfixed = true
actual := tt.in.FilterUnfixed()
if !reflect.DeepEqual(tt.out.ScannedCves, actual.ScannedCves) {
o := pp.Sprintf("%v", tt.out.ScannedCves)
a := pp.Sprintf("%v", actual.ScannedCves)
t.Errorf("[%d] expected: %v\n actual: %v\n", i, o, a)
}
}
}

View File

@@ -546,6 +546,8 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
return links
case config.Debian:
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
case config.SUSEEnterpriseServer:
links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
case config.FreeBSD:
for _, advisory := range v.DistroAdvisories {
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)

View File

@@ -60,8 +60,10 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
@@ -107,11 +109,17 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version)
linuxImage := "linux-image-" + r.RunningKernel.Release
// Add linux and set the version of running kernel to search OVAL.
newVer := ""
if p, ok := r.Packages[linuxImage]; ok {
newVer = p.NewVersion
}
if r.Container.ContainerID == "" {
r.Packages["linux"] = models.Package{
Name: "linux",
Version: r.RunningKernel.Version,
Name: "linux",
Version: r.RunningKernel.Version,
NewVersion: newVer,
}
}
@@ -121,7 +129,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
return err
}
}
@@ -129,10 +137,10 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
delete(r.Packages, "linux")
for _, defPacks := range relatedDefs.entries {
// Remove linux added above to search for oval
// Remove "linux" added above for oval search
// linux is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = true
if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
delete(defPacks.actuallyAffectedPackNames, "linux")
for i, p := range defPacks.def.AffectedPacks {
if p.Name == "linux" {
@@ -141,6 +149,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
}
}
}
o.update(r, defPacks)
}
@@ -230,7 +239,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
return err
}
}
@@ -240,7 +249,6 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
}
for _, defPacks := range relatedDefs.entries {
// Remove "linux" added above to search for oval
// "linux" is not a real package name (key of affected packages in OVAL)
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
@@ -253,6 +261,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
}
}
}
o.update(r, defPacks)
}

View File

@@ -36,7 +36,10 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packC"},
},
},
},
},
@@ -55,7 +58,8 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
{Name: "packB", NotFixedYet: true},
{Name: "packC"},
},
},
},

View File

@@ -41,8 +41,7 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(
o.family, r.Release, r.Packages); err != nil {
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
return err
}
}
@@ -98,7 +97,8 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
defPacks.actuallyAffectedPackNames[pack.Name] = true
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages.Sort()
@@ -156,7 +156,7 @@ func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
return score, strings.Join(ss[1:], "/")
}
return 0, ""
}
@@ -170,7 +170,7 @@ func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
return 0, ""
}
return score, strings.Join(ss[1:len(ss)], "/")
return score, strings.Join(ss[1:], "/")
}
return 0, ""
}

View File

@@ -102,7 +102,10 @@ func TestPackNamesOfUpdate(t *testing.T) {
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB", NotFixedYet: false},
},
},
},
},
@@ -125,7 +128,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
"CVE-2000-1000": models.VulnInfo{
AffectedPackages: models.PackageStatuses{
{Name: "packA"},
{Name: "packB"},
{Name: "packB", NotFixedYet: true},
},
},
},

120
oval/suse.go Normal file
View File

@@ -0,0 +1,120 @@
/* 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 oval
import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// SUSE is the struct of SUSE Linux
type SUSE struct {
Base
}
// NewSUSE creates OVAL client for SUSE
func NewSUSE() SUSE {
// TODO implement other family
return SUSE{
Base{
family: config.SUSEEnterpriseServer,
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o SUSE) FillWithOval(r *models.ScanResult) (err error) {
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
return err
}
}
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.SUSE]; ok {
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
vuln.CveContents[models.SUSE] = cont
}
}
return nil
}
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[defPacks.def.Title]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
vinfo = models.VulnInfo{
CveID: defPacks.def.Title,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title)
} else {
util.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, pack := range vinfo.AffectedPackages {
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Title] = vinfo
}
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
return &models.CveContent{
CveID: def.Title,
Title: def.Title,
Summary: def.Description,
References: refs,
}
}

View File

@@ -40,114 +40,58 @@ type ovalResult struct {
}
type defPacks struct {
def ovalmodels.Definition
def ovalmodels.Definition
// BinaryPackageName : NotFixedYet
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
switch family {
case config.Ubuntu:
packNotFixedYet := map[string]bool{}
for _, p := range e.def.AffectedPacks {
packNotFixedYet[p.Name] = p.NotFixedYet
}
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
NotFixedYet: packNotFixedYet[k],
})
}
case config.CentOS, config.Debian:
// There are many packages that has been fixed in RedHat, but not been fixed in CentOS
for name := range e.actuallyAffectedPackNames {
pack, ok := packs[name]
if !ok {
util.Log.Warnf("Faild to find in Package list: %s", name)
return
}
ovalPackVer := ""
for _, p := range e.def.AffectedPacks {
if p.Name == name {
ovalPackVer = p.Version
break
}
}
if ovalPackVer == "" {
util.Log.Warnf("Faild to find in Oval Package list: %s", name)
return
}
if pack.NewVersion == "" {
// compare version: installed vs oval
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
} else {
// compare version: newVer vs oval
packNewVer := fmt.Sprintf("%s-%s", pack.NewVersion, pack.NewRelease)
vera := rpmver.NewVersion(packNewVer)
verb := rpmver.NewVersion(ovalPackVer)
notFixedYet := false
if vera.LessThan(verb) {
notFixedYet = true
}
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
}
}
default:
for k := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: k,
})
}
for name, notFixedYet := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: name,
NotFixedYet: notFixedYet,
})
}
return
}
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = true
e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
return true
}
}
e.entries = append(e.entries, defPacks{
def: def,
actuallyAffectedPackNames: map[string]bool{packName: true},
actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
})
return false
}
type request struct {
pack models.Package
packName string
versionRelease string
NewVersionRelease string
binaryPackNames []string
isSrcPack bool
}
type response struct {
pack *models.Package
defs []ovalmodels.Definition
request request
defs []ovalmodels.Definition
}
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
relatedDefs ovalResult, err error) {
reqChan := make(chan request, len(r.Packages))
resChan := make(chan response, len(r.Packages))
errChan := make(chan error, len(r.Packages))
nReq := len(r.Packages) + len(r.SrcPackages)
reqChan := make(chan request, nReq)
resChan := make(chan response, nReq)
errChan := make(chan error, nReq)
defer close(reqChan)
defer close(resChan)
defer close(errChan)
@@ -155,14 +99,26 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
go func() {
for _, pack := range r.Packages {
reqChan <- request{
pack: pack,
packName: pack.Name,
versionRelease: pack.FormatVer(),
NewVersionRelease: pack.FormatVer(),
isSrcPack: false,
}
for _, pack := range r.SrcPackages {
reqChan <- request{
packName: pack.Name,
binaryPackNames: pack.BinaryNames,
versionRelease: pack.Version,
isSrcPack: true,
}
}
}
}()
concurrency := 10
tasks := util.GenWorkers(concurrency)
for range r.Packages {
for i := 0; i < nReq; i++ {
tasks <- func() {
select {
case req := <-reqChan:
@@ -171,13 +127,13 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
"packs",
r.Family,
r.Release,
req.pack.Name,
req.packName,
)
if err != nil {
errChan <- err
} else {
util.Log.Debugf("HTTP Request to %s", url)
httpGet(url, &req.pack, resChan, errChan)
httpGet(url, req, resChan, errChan)
}
}
}
@@ -185,26 +141,21 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
timeout := time.After(2 * 60 * time.Second)
var errs []error
for range r.Packages {
for i := 0; i < nReq; i++ {
select {
case res := <-resChan:
for _, def := range res.defs {
for _, p := range def.AffectedPacks {
if res.pack.Name != p.Name {
continue
}
affected, notFixedYet := isOvalDefAffected(def, r.Family, res.request)
if !affected {
continue
}
if p.NotFixedYet {
relatedDefs.upsert(def, p.Name)
continue
}
if less, err := lessThan(r.Family, *res.pack, p); err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", *res.pack, p)
} else if less {
relatedDefs.upsert(def, p.Name)
if res.request.isSrcPack {
for _, n := range res.request.binaryPackNames {
relatedDefs.upsert(def, n, false)
}
} else {
relatedDefs.upsert(def, res.request.packName, notFixedYet)
}
}
case err := <-errChan:
@@ -219,7 +170,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
return
}
func httpGet(url string, pack *models.Package, resChan chan<- response, errChan chan<- error) {
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
var body string
var errs []error
var resp *http.Response
@@ -257,14 +208,12 @@ func httpGet(url string, pack *models.Package, resChan chan<- response, errChan
return
}
resChan <- response{
pack: pack,
defs: defs,
request: req,
defs: defs,
}
}
func getDefsByPackNameFromOvalDB(family, osRelease string,
installedPacks models.Packages) (relatedDefs ovalResult, err error) {
func getDefsByPackNameFromOvalDB(r *models.ScanResult) (relatedDefs ovalResult, err error) {
ovallog.Initialize(config.Conf.LogDir)
path := config.Conf.OvalDBURL
if config.Conf.OvalDBType == "sqlite3" {
@@ -273,48 +222,96 @@ func getDefsByPackNameFromOvalDB(family, osRelease string,
util.Log.Debugf("Open oval-dictionary db (%s): %s", config.Conf.OvalDBType, path)
var ovaldb db.DB
if ovaldb, err = db.NewDB(
family,
config.Conf.OvalDBType,
path,
config.Conf.DebugSQL,
); err != nil {
if ovaldb, err = db.NewDB(r.Family, config.Conf.OvalDBType,
path, config.Conf.DebugSQL); err != nil {
return
}
defer ovaldb.CloseDB()
for _, installedPack := range installedPacks {
definitions, err := ovaldb.GetByPackName(osRelease, installedPack.Name)
requests := []request{}
for _, pack := range r.Packages {
requests = append(requests, request{
packName: pack.Name,
versionRelease: pack.FormatVer(),
NewVersionRelease: pack.FormatNewVer(),
isSrcPack: false,
})
}
for _, pack := range r.SrcPackages {
requests = append(requests, request{
packName: pack.Name,
binaryPackNames: pack.BinaryNames,
versionRelease: pack.Version,
isSrcPack: true,
})
}
for _, req := range requests {
definitions, err := ovaldb.GetByPackName(r.Release, req.packName)
if err != nil {
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", r.Family, err)
}
for _, def := range definitions {
for _, ovalPack := range def.AffectedPacks {
if installedPack.Name != ovalPack.Name {
continue
}
affected, notFixedYet := isOvalDefAffected(def, r.Family, req)
if !affected {
continue
}
if ovalPack.NotFixedYet {
relatedDefs.upsert(def, installedPack.Name)
continue
}
less, err := lessThan(family, installedPack, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s", err)
util.Log.Debugf("%#v\n%#v", installedPack, ovalPack)
} else if less {
relatedDefs.upsert(def, installedPack.Name)
if req.isSrcPack {
for _, n := range req.binaryPackNames {
relatedDefs.upsert(def, n, false)
}
} else {
relatedDefs.upsert(def, req.packName, notFixedYet)
}
}
}
return
}
func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bool, error) {
func isOvalDefAffected(def ovalmodels.Definition, family string, req request) (affected, notFixedYet bool) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
}
if ovalPack.NotFixedYet {
return true, true
}
less, err := lessThan(family, req.versionRelease, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
err, req.versionRelease, ovalPack, def.DefinitionID)
return false, false
}
if less {
if req.isSrcPack {
// Unable to judge whether fixed or not fixed of src package(Ubuntu, Debian)
return true, false
}
if req.NewVersionRelease == "" {
return true, true
}
// compare version: newVer vs oval
less, err := lessThan(family, req.NewVersionRelease, ovalPack)
if err != nil {
util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
err, req.NewVersionRelease, ovalPack, def.DefinitionID)
return false, false
}
return true, less
}
}
return false, false
}
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
switch family {
case config.Debian, config.Ubuntu:
vera, err := debver.NewVersion(packA.Version)
vera, err := debver.NewVersion(versionRelease)
if err != nil {
return false, err
}
@@ -323,10 +320,12 @@ func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bo
return false, err
}
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS, config.Oracle:
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", packA.Version, packA.Release))
case config.RedHat, config.CentOS, config.Oracle, config.SUSEEnterpriseServer:
vera := rpmver.NewVersion(versionRelease)
verb := rpmver.NewVersion(packB.Version)
return vera.LessThan(verb), nil
default:
util.Log.Errorf("Not implemented yet: %s", family)
}
return false, fmt.Errorf("Package version comparison not supported: %s", family)
}

View File

@@ -11,11 +11,12 @@ import (
func TestUpsert(t *testing.T) {
var tests = []struct {
res ovalResult
def ovalmodels.Definition
packName string
upserted bool
out ovalResult
res ovalResult
def ovalmodels.Definition
packName string
notFixedYet bool
upserted bool
out ovalResult
}{
//insert
{
@@ -23,8 +24,9 @@ func TestUpsert(t *testing.T) {
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack1",
upserted: false,
packName: "pack1",
notFixedYet: true,
upserted: false,
out: ovalResult{
[]defPacks{
{
@@ -63,8 +65,9 @@ func TestUpsert(t *testing.T) {
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack2",
upserted: true,
packName: "pack2",
notFixedYet: false,
upserted: true,
out: ovalResult{
[]defPacks{
{
@@ -73,7 +76,7 @@ func TestUpsert(t *testing.T) {
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
"pack2": true,
"pack2": false,
},
},
{
@@ -89,7 +92,7 @@ func TestUpsert(t *testing.T) {
},
}
for i, tt := range tests {
upserted := tt.res.upsert(tt.def, tt.packName)
upserted := tt.res.upsert(tt.def, tt.packName, tt.notFixedYet)
if tt.upserted != upserted {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
}
@@ -127,90 +130,6 @@ func TestDefpacksToPackStatuses(t *testing.T) {
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
},
},
},
out: models.PackageStatuses{
{
Name: "a",
NotFixedYet: true,
},
{
Name: "b",
NotFixedYet: false,
},
},
},
// RedHat, Amazon, Debian
{
in: in{
family: "redhat",
packs: models.Packages{},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
},
{
Name: "b",
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
},
},
},
out: models.PackageStatuses{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
},
},
},
// CentOS
{
in: in{
family: "centos",
packs: models.Packages{
"a": {Version: "1.0.0"},
"b": {
Version: "1.0.0",
NewVersion: "2.0.0",
},
"c": {
Version: "1.0.0",
NewVersion: "1.5.0",
},
},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
Version: "1.0.1",
},
{
Name: "b",
Version: "1.5.0",
},
{
Name: "c",
Version: "2.0.0",
},
},
},
actuallyAffectedPackNames: map[string]bool{
"a": true,
"b": true,
@@ -225,7 +144,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
},
{
Name: "b",
NotFixedYet: false,
NotFixedYet: true,
},
{
Name: "c",
@@ -244,3 +163,171 @@ func TestDefpacksToPackStatuses(t *testing.T) {
}
}
}
func TestIsOvalDefAffected(t *testing.T) {
type in struct {
def ovalmodels.Definition
family string
req request
}
var tests = []struct {
in in
affected bool
notFixedYet bool
}{
// 0. Ubuntu ovalpack.NotFixedYet == true
{
in: in{
family: "ubuntu",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: true,
},
{
Name: "b",
NotFixedYet: true,
},
},
},
req: request{
packName: "b",
},
},
affected: true,
notFixedYet: true,
},
// 1. Ubuntu
// ovalpack.NotFixedYet == false
// req.isSrcPack == true
// Version comparison
// oval vs installed
{
in: in{
family: "ubuntu",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "1.0.0-1",
},
},
},
req: request{
packName: "b",
isSrcPack: true,
versionRelease: "1.0.0-0",
},
},
affected: true,
notFixedYet: false,
},
// 2. Ubuntu
// ovalpack.NotFixedYet == false
// Version comparison not hit
// oval vs installed
{
in: in{
family: "ubuntu",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "1.0.0-1",
},
},
},
req: request{
packName: "b",
versionRelease: "1.0.0-2",
},
},
affected: false,
notFixedYet: false,
},
// 3. Ubuntu
// ovalpack.NotFixedYet == false
// req.isSrcPack == false
// Version comparison
// oval vs NewVersion
// oval.version < installed.newVersion
{
in: in{
family: "ubuntu",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "1.0.0-3",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "1.0.0-0",
NewVersionRelease: "1.0.0-2",
},
},
affected: true,
notFixedYet: true,
},
// 4. Ubuntu
// ovalpack.NotFixedYet == false
// req.isSrcPack == false
// Version comparison
// oval vs NewVersion
// oval.version < installed.newVersion
{
in: in{
family: "ubuntu",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "1.0.0-2",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "1.0.0-0",
NewVersionRelease: "1.0.0-3",
},
},
affected: true,
notFixedYet: false,
},
}
for i, tt := range tests {
affected, notFixedYet := isOvalDefAffected(tt.in.def, tt.in.family, tt.in.req)
if tt.affected != affected {
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
}
if tt.notFixedYet != notFixedYet {
t.Errorf("[%d] notfixedyet\nexpected: %v\n actual: %v\n", i, tt.notFixedYet, notFixedYet)
}
}
}

View File

@@ -81,6 +81,10 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro
for _, r := range filled {
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
r = r.FilterIgnoreCves(c.Conf.Servers[r.ServerName].IgnoreCves)
r = r.FilterUnfixed()
if c.Conf.IgnoreUnscoredCves {
r.ScannedCves = r.ScannedCves.FindScoredVulns()
}
filtered = append(filtered, r)
}
return filtered, nil
@@ -173,7 +177,11 @@ func FillWithOval(r *models.ScanResult) (err error) {
case c.Oracle:
ovalClient = oval.NewOracle()
ovalFamily = c.Oracle
case c.Amazon, c.Raspbian, c.FreeBSD:
case c.SUSEEnterpriseServer:
// TODO other suse family
ovalClient = oval.NewSUSE()
ovalFamily = c.SUSEEnterpriseServer
case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows:
return nil
default:
return fmt.Errorf("OVAL for %s is not implemented yet", r.Family)

View File

@@ -165,13 +165,7 @@ func msgText(r models.ScanResult) string {
}
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
var vinfos []models.VulnInfo
if config.Conf.IgnoreUnscoredCves {
vinfos = r.ScannedCves.FindScoredVulns().ToSortedSlice()
} else {
vinfos = r.ScannedCves.ToSortedSlice()
}
vinfos := r.ScannedCves.ToSortedSlice()
for _, vinfo := range vinfos {
curent := []string{}
for _, affected := range vinfo.AffectedPackages {

View File

@@ -45,6 +45,12 @@ var currentChangelogLimitY int
// RunTui execute main logic
func RunTui(results models.ScanResults) subcommands.ExitStatus {
scanResults = results
sort.Slice(scanResults, func(i, j int) bool {
if scanResults[i].ServerName == scanResults[j].ServerName {
return scanResults[i].Container.Name < scanResults[j].Container.Name
}
return scanResults[i].ServerName < scanResults[j].ServerName
})
// g, err := gocui.NewGui(gocui.OutputNormal)
g := gocui.NewGui()

View File

@@ -93,12 +93,7 @@ func formatShortPlainText(r models.ScanResult) string {
header, r.Errors)
}
vulns := r.ScannedCves
if config.Conf.IgnoreUnscoredCves {
vulns = vulns.FindScoredVulns()
}
if len(vulns) == 0 {
if len(r.ScannedCves) == 0 {
return fmt.Sprintf(`
%s
No CVE-IDs are found in updatable packages.
@@ -109,7 +104,7 @@ No CVE-IDs are found in updatable packages.
stable := uitable.New()
stable.MaxColWidth = maxColWidth
stable.Wrap = true
for _, vuln := range vulns.ToSortedSlice() {
for _, vuln := range r.ScannedCves.ToSortedSlice() {
summaries := vuln.Summaries(config.Conf.Lang, r.Family)
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
@@ -167,12 +162,7 @@ func formatFullPlainText(r models.ScanResult) string {
header, r.Errors)
}
vulns := r.ScannedCves
if config.Conf.IgnoreUnscoredCves {
vulns = vulns.FindScoredVulns()
}
if len(vulns) == 0 {
if len(r.ScannedCves) == 0 {
return fmt.Sprintf(`
%s
No CVE-IDs are found in updatable packages.
@@ -183,7 +173,7 @@ No CVE-IDs are found in updatable packages.
table := uitable.New()
table.MaxColWidth = maxColWidth
table.Wrap = true
for _, vuln := range vulns.ToSortedSlice() {
for _, vuln := range r.ScannedCves.ToSortedSlice() {
table.AddRow(vuln.CveID)
table.AddRow("----------------")
table.AddRow("Max Score", vuln.FormatMaxCvssScore())

View File

@@ -313,6 +313,7 @@ func (l *base) convertToModel() models.ScanResult {
ScannedCves: l.VulnInfos,
RunningKernel: l.Kernel,
Packages: l.Packages,
SrcPackages: l.SrcPackages,
Optional: l.ServerInfo.Optional,
Errors: errs,
}

View File

@@ -176,7 +176,6 @@ func (o *debian) checkDependencies() error {
}
for _, name := range packNames {
//TODO --show-format
cmd := "dpkg-query -W " + name
if r := o.exec(cmd, noSudo); !r.isSuccess() {
msg := fmt.Sprintf("%s is not installed", name)
@@ -206,12 +205,13 @@ func (o *debian) scanPackages() error {
RebootRequired: rebootRequired,
}
installed, updatable, err := o.scanInstalledPackages()
installed, updatable, srcPacks, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
o.Packages = installed
o.SrcPackages = srcPacks
if config.Conf.Deep || o.Distro.Family == config.Raspbian {
unsecures, err := o.scanUnsecurePackages(updatable)
@@ -238,34 +238,72 @@ func (o *debian) rebootRequired() (bool, error) {
}
}
func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, error) {
installed, updatable := models.Packages{}, models.Packages{}
r := o.exec("dpkg-query -W", noSudo)
func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, models.SrcPackages, error) {
installed, updatable, srcPacks := models.Packages{}, models.Packages{}, models.SrcPackages{}
r := o.exec(`dpkg-query -W -f='${binary:Package},${db:Status-Abbrev},${Version},${Source},${source:Version}\n'`, noSudo)
if !r.isSuccess() {
return nil, nil, fmt.Errorf("Failed to SSH: %s", r)
return nil, nil, nil, fmt.Errorf("Failed to SSH: %s", r)
}
// e.g.
// curl 7.19.7-40.el6_6.4
// openldap 2.4.39-8.el6
// e.g.
// curl,ii ,7.38.0-4+deb8u2,,7.38.0-4+deb8u2
// openssh-server,ii ,1:6.7p1-5+deb8u3,openssh,1:6.7p1-5+deb8u3
// tar,ii ,1.27.1-2+b1,tar (1.27.1-2),1.27.1-2
lines := strings.Split(r.Stdout, "\n")
for _, line := range lines {
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
name, version, err := o.parseScannedPackagesLine(trimmed)
name, status, version, srcName, srcVersion, err := o.parseScannedPackagesLine(trimmed)
if err != nil {
return nil, nil, fmt.Errorf(
return nil, nil, nil, fmt.Errorf(
"Debian: Failed to parse package line: %s", line)
}
packageStatus := status[1]
// Package status:
// n = Not-installed
// c = Config-files
// H = Half-installed
// U = Unpacked
// F = Half-configured
// W = Triggers-awaiting
// t = Triggers-pending
// i = Installed
if packageStatus != 'i' {
o.log.Debugf("%s package status is '%c', ignoring", name, packageStatus)
continue
}
installed[name] = models.Package{
Name: name,
Version: version,
}
if srcName != "" && srcName != name {
if pack, ok := srcPacks[srcName]; ok {
pack.AddBinaryName(name)
srcPacks[srcName] = pack
} else {
srcPacks[srcName] = models.SrcPackage{
Name: srcName,
Version: srcVersion,
BinaryNames: []string{name},
}
}
}
}
}
// Remove "linux"
// kernel-related packages are showed "linux" as source package name
// If "linux" is left, oval detection will cause trouble, so delete.
delete(srcPacks, "linux")
// Remove duplicate
for name := range installed {
delete(srcPacks, name)
}
updatableNames, err := o.getUpdatablePackNames()
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
for _, name := range updatableNames {
for _, pack := range installed {
@@ -279,28 +317,30 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, erro
// Fill the candidate versions of upgradable packages
err = o.fillCandidateVersion(updatable)
if err != nil {
return nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
return nil, nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
}
installed.MergeNewVersion(updatable)
return installed, updatable, nil
return installed, updatable, srcPacks, nil
}
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
result := packageLinePattern.FindStringSubmatch(line)
if len(result) == 3 {
func (o *debian) parseScannedPackagesLine(line string) (name, status, version, srcName, srcVersion string, err error) {
ss := strings.Split(line, ",")
if len(ss) == 5 {
// remove :amd64, i386...
name = result[1]
name = ss[0]
if i := strings.IndexRune(name, ':'); i >= 0 {
name = name[:i]
}
version = result[2]
status = ss[1]
version = ss[2]
// remove version. ex: tar (1.27.1-2)
srcName = strings.Split(ss[3], " ")[0]
srcVersion = ss[4]
return
}
return "", "", fmt.Errorf("Unknown format: %s", line)
return "", "", "", "", "", fmt.Errorf("Unknown format: %s", line)
}
func (o *debian) aptGetUpdate() error {

View File

@@ -29,35 +29,6 @@ import (
"github.com/sirupsen/logrus"
)
func TestParseScannedPackagesLineDebian(t *testing.T) {
var packagetests = []struct {
in string
name string
version string
}{
{"base-passwd 3.5.33", "base-passwd", "3.5.33"},
{"bzip2 1.0.6-5", "bzip2", "1.0.6-5"},
{"adduser 3.113+nmu3ubuntu3", "adduser", "3.113+nmu3ubuntu3"},
{"bash 4.3-7ubuntu1.5", "bash", "4.3-7ubuntu1.5"},
{"bsdutils 1:2.20.1-5.1ubuntu20.4", "bsdutils", "1:2.20.1-5.1ubuntu20.4"},
{"ca-certificates 20141019ubuntu0.14.04.1", "ca-certificates", "20141019ubuntu0.14.04.1"},
{"apt 1.0.1ubuntu2.8", "apt", "1.0.1ubuntu2.8"},
}
d := newDebian(config.ServerInfo{})
for _, tt := range packagetests {
n, v, _ := d.parseScannedPackagesLine(tt.in)
if n != tt.name {
t.Errorf("name: expected %s, actual %s", tt.name, n)
}
if v != tt.version {
t.Errorf("version: expected %s, actual %s", tt.version, v)
}
}
}
func TestGetCveIDsFromChangelog(t *testing.T) {
var tests = []struct {

View File

@@ -153,6 +153,10 @@ func (o *redhat) checkIfSudoNoPasswd() error {
{"yum --color=never --security updateinfo updates", zero},
}
}
if o.Distro.Family == config.RedHat {
cmds = append(cmds, cmd{"repoquery -h", zero})
}
}
for _, c := range cmds {
@@ -276,14 +280,7 @@ func (o *redhat) scanInstalledPackages() (models.Packages, error) {
}
installed := models.Packages{}
var cmd string
majorVersion, _ := o.Distro.MajorVersion()
if majorVersion < 6 {
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n'"
} else {
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
}
r := o.exec(cmd, noSudo)
r := o.exec(rpmQa(o.Distro), noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Scan packages failed: %s", r)
}
@@ -300,14 +297,13 @@ func (o *redhat) scanInstalledPackages() (models.Packages, error) {
// Kernel package may be isntalled multiple versions.
// From the viewpoint of vulnerability detection,
// pay attention only to the running kernel
if pack.Name == "kernel" {
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
if o.Kernel.Release != ver {
o.log.Debugf("Not a running kernel: %s, uname: %s", ver, release)
isKernel, running := isRunningKernel(pack, o.Distro.Family, o.Kernel)
if isKernel {
if !running {
o.log.Debugf("Not a running kernel. pack: %#v, kernel: %#v", pack, o.Kernel)
continue
} else {
o.log.Debugf("Running kernel: %s, uname: %s", ver, release)
}
o.log.Debugf("Found a running kernel. pack: %#v, kernel: %#v", pack, o.Kernel)
}
installed[pack.Name] = pack
}
@@ -389,7 +385,7 @@ func (o *redhat) parseUpdatablePacksLine(line string) (models.Package, error) {
ver = fmt.Sprintf("%s:%s", epoch, fields[2])
}
repos := strings.Join(fields[4:len(fields)], " ")
repos := strings.Join(fields[4:], " ")
p := models.Package{
Name: fields[0],
@@ -820,7 +816,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
inDesctiption, inCves = true, false
ss := strings.Split(line, " : ")
advisory.Description += fmt.Sprintf("%s\n",
strings.Join(ss[1:len(ss)], " : "))
strings.Join(ss[1:], " : "))
continue
}
@@ -834,7 +830,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
if inDesctiption {
if ss := strings.Split(line, ": "); 1 < len(ss) {
advisory.Description += fmt.Sprintf("%s\n",
strings.Join(ss[1:len(ss)], ": "))
strings.Join(ss[1:], ": "))
}
continue
}
@@ -842,7 +838,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
if found := o.isCvesHeaderLine(line); found {
inCves = true
ss := strings.Split(line, "CVEs : ")
line = strings.Join(ss[1:len(ss)], " ")
line = strings.Join(ss[1:], " ")
cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
for _, cveID := range cveIDs {
cveIDsSetInThisSection[cveID] = true

View File

@@ -61,6 +61,9 @@ type osPackages struct {
// installed packages
Packages models.Packages
// installed source packages (Debian based only)
SrcPackages models.SrcPackages
// unsecure packages
VulnInfos models.VulnInfos
@@ -89,6 +92,11 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
return
}
if itsMe, osType = detectSUSE(c); itsMe {
util.Log.Debugf("SUSE Linux. Host: %s:%s", c.Host, c.Port)
return
}
if itsMe, osType = detectFreebsd(c); itsMe {
util.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
return

185
scan/suse.go Normal file
View File

@@ -0,0 +1,185 @@
package scan
import (
"bufio"
"fmt"
"regexp"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// inherit OsTypeInterface
type suse struct {
redhat
}
// NewRedhat is constructor
func newSUSE(c config.ServerInfo) *suse {
r := &suse{
redhat: redhat{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
},
}
r.log = util.NewCustomLogger(c)
r.setServerInfo(c)
return r
}
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/suse.rb
func detectSUSE(c config.ServerInfo) (itsMe bool, suse osTypeInterface) {
suse = newSUSE(c)
if r := exec(c, "ls /etc/os-release", noSudo); r.isSuccess() {
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
name := ""
if strings.Contains(r.Stdout, "ID=opensuse") {
//TODO check opensuse or opensuse.leap
name = config.OpenSUSE
} else if strings.Contains(r.Stdout, `NAME="SLES"`) {
name = config.SUSEEnterpriseServer
} else {
util.Log.Warn("Failed to parse SUSE edition: %s", r)
return true, suse
}
re := regexp.MustCompile(`VERSION_ID=\"(\d+\.\d+|\d+)\"`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) != 2 {
util.Log.Warn("Failed to parse SUSE Linux version: %s", r)
return true, suse
}
suse.setDistro(name, result[1])
return true, suse
}
}
} else if r := exec(c, "ls /etc/SuSE-release", noSudo); r.isSuccess() {
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
if r := exec(c, "cat /etc/SuSE-release", noSudo); r.isSuccess() {
re := regexp.MustCompile(`openSUSE (\d+\.\d+|\d+)`)
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
//TODO check opensuse or opensuse.leap
suse.setDistro(config.OpenSUSE, result[1])
return true, suse
}
re = regexp.MustCompile(`VERSION = (\d+)`)
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
version := result[1]
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
if len(result) == 2 {
suse.setDistro(config.SUSEEnterpriseServer,
fmt.Sprintf("%s.%s", version, result[1]))
return true, suse
}
}
util.Log.Warn("Failed to parse SUSE Linux version: %s", r)
return true, suse
}
}
}
util.Log.Debugf("Not SUSE Linux. servername: %s", c.ServerName)
return false, suse
}
func (o *suse) checkDependencies() error {
o.log.Infof("Dependencies... No need")
return nil
}
func (o *suse) checkIfSudoNoPasswd() error {
// SUSE doesn't need root privilege
o.log.Infof("sudo ... No need")
return nil
}
func (o *suse) scanPackages() error {
installed, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
rebootRequired, err := o.rebootRequired()
if err != nil {
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
return err
}
o.Kernel.RebootRequired = rebootRequired
updatable, err := o.scanUpdatablePackages()
if err != nil {
o.log.Errorf("Failed to scan updatable packages: %s", err)
return err
}
installed.MergeNewVersion(updatable)
o.Packages = installed
return nil
}
func (o *suse) rebootRequired() (bool, error) {
r := o.exec("rpm -q --last kernel-default | head -n1", noSudo)
if !r.isSuccess() {
return false, fmt.Errorf("Failed to detect the last installed kernel : %v", r)
}
stdout := strings.Fields(r.Stdout)[0]
return !strings.Contains(stdout, strings.TrimSuffix(o.Kernel.Release, "-default")), nil
}
func (o *suse) scanUpdatablePackages() (models.Packages, error) {
cmd := ""
if v, _ := o.Distro.MajorVersion(); v < 12 {
cmd = "zypper -q lu"
} else {
cmd = "zypper --no-color -q lu"
}
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to scan updatable packages: %v", r)
}
return o.parseZypperLULines(r.Stdout)
}
func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
updatables := models.Packages{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "S | Repository") ||
strings.HasPrefix(line, "--+----------------") {
continue
}
pack, err := o.parseZypperLUOneLine(line)
if err != nil {
return nil, err
}
updatables[pack.Name] = *pack
}
return updatables, nil
}
func (o *suse) parseZypperLUOneLine(line string) (*models.Package, error) {
fs := strings.Fields(line)
if len(fs) != 11 {
return nil, fmt.Errorf("zypper -q lu Unknown format: %s", line)
}
available := strings.Split(fs[8], "-")
return &models.Package{
Name: fs[4],
NewVersion: available[0],
NewRelease: available[1],
Arch: fs[10],
}, nil
}

106
scan/suse_test.go Normal file
View File

@@ -0,0 +1,106 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/k0kubun/pp"
)
func TestScanUpdatablePackages(t *testing.T) {
r := newSUSE(config.ServerInfo{})
r.Distro = config.Distro{Family: "sles"}
stdout := `S | Repository | Name | Current Version | Available Version | Arch
--+---------------------------------------------+-------------------------------+-----------------------------+-----------------------------+-------
v | SLES12-SP2-Updates | SUSEConnect | 0.3.0-19.8.1 | 0.3.1-19.11.2 | x86_64
v | SLES12-SP2-Updates | SuSEfirewall2 | 3.6.312-2.3.1 | 3.6.312-2.10.1 | noarch`
var tests = []struct {
in string
out models.Packages
}{
{
stdout,
models.NewPackages(
models.Package{
Name: "SUSEConnect",
NewVersion: "0.3.1",
NewRelease: "19.11.2",
Arch: "x86_64",
},
models.Package{
Name: "SuSEfirewall2",
NewVersion: "3.6.312",
NewRelease: "2.10.1",
Arch: "noarch",
},
),
},
}
for _, tt := range tests {
packages, err := r.parseZypperLULines(tt.in)
if err != nil {
t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in)
return
}
for name, ePack := range tt.out {
if !reflect.DeepEqual(ePack, packages[name]) {
e := pp.Sprintf("%v", ePack)
a := pp.Sprintf("%v", packages[name])
t.Errorf("expected %s, actual %s", e, a)
}
}
}
}
func TestScanUpdatablePackage(t *testing.T) {
r := newSUSE(config.ServerInfo{})
r.Distro = config.Distro{Family: "sles"}
stdout := `v | SLES12-SP2-Updates | SUSEConnect | 0.3.0-19.8.1 | 0.3.1-19.11.2 | x86_64`
var tests = []struct {
in string
out models.Package
}{
{
stdout,
models.Package{
Name: "SUSEConnect",
NewVersion: "0.3.1",
NewRelease: "19.11.2",
Arch: "x86_64",
},
},
}
for _, tt := range tests {
pack, err := r.parseZypperLUOneLine(tt.in)
if err != nil {
t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in)
return
}
if !reflect.DeepEqual(*pack, tt.out) {
e := pp.Sprintf("%v", tt.out)
a := pp.Sprintf("%v", pack)
t.Errorf("expected %s, actual %s", e, a)
}
}
}

69
scan/utils.go Normal file
View File

@@ -0,0 +1,69 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
"fmt"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (isKernel, running bool) {
switch family {
case config.SUSEEnterpriseServer:
if pack.Name == "kernel-default" {
// Remove the last period and later because uname don't show that.
ss := strings.Split(pack.Release, ".")
rel := strings.Join(ss[0:len(ss)-1], ".")
ver := fmt.Sprintf("%s-%s-default", pack.Version, rel)
return true, kernel.Release == ver
}
return false, false
case config.RedHat, config.Oracle, config.CentOS, config.Amazon:
if pack.Name == "kernel" {
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
return true, kernel.Release == ver
}
return false, false
default:
util.Log.Warnf("Reboot required is not implemented yet: %s, %s", family, kernel)
}
return false, false
}
func rpmQa(distro config.Distro) string {
const old = "rpm -qa --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n'"
const new = "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
switch distro.Family {
case config.SUSEEnterpriseServer:
if v, _ := distro.MajorVersion(); v < 12 {
return old
}
return new
default:
if v, _ := distro.MajorVersion(); v < 6 {
return old
}
return new
}
}

117
scan/utils_test.go Normal file
View File

@@ -0,0 +1,117 @@
/* 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 (
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
func TestIsRunningKernelSUSE(t *testing.T) {
r := newSUSE(config.ServerInfo{})
r.Distro = config.Distro{Family: config.SUSEEnterpriseServer}
kernel := models.Kernel{
Release: "4.4.74-92.35-default",
Version: "",
}
var tests = []struct {
pack models.Package
family string
kernel models.Kernel
expected bool
}{
{
pack: models.Package{
Name: "kernel-default",
Version: "4.4.74",
Release: "92.35.1",
Arch: "x86_64",
},
family: config.SUSEEnterpriseServer,
kernel: kernel,
expected: true,
},
{
pack: models.Package{
Name: "kernel-default",
Version: "4.4.59",
Release: "92.20.2",
Arch: "x86_64",
},
family: config.SUSEEnterpriseServer,
kernel: kernel,
expected: false,
},
}
for i, tt := range tests {
_, actual := isRunningKernel(tt.pack, tt.family, tt.kernel)
if tt.expected != actual {
t.Errorf("[%d] expected %t, actual %t", i, tt.expected, actual)
}
}
}
func TestIsRunningKernelRedHatLikeLinux(t *testing.T) {
r := newRedhat(config.ServerInfo{})
r.Distro = config.Distro{Family: config.Amazon}
kernel := models.Kernel{
Release: "4.9.43-17.38.amzn1.x86_64",
Version: "",
}
var tests = []struct {
pack models.Package
family string
kernel models.Kernel
expected bool
}{
{
pack: models.Package{
Name: "kernel",
Version: "4.9.43",
Release: "17.38.amzn1",
Arch: "x86_64",
},
family: config.Amazon,
kernel: kernel,
expected: true,
},
{
pack: models.Package{
Name: "kernel",
Version: "4.9.38",
Release: "16.35.amzn1",
Arch: "x86_64",
},
family: config.Amazon,
kernel: kernel,
expected: false,
},
}
for i, tt := range tests {
_, actual := isRunningKernel(tt.pack, tt.family, tt.kernel)
if tt.expected != actual {
t.Errorf("[%d] expected %t, actual %t", i, tt.expected, actual)
}
}
}

View File

@@ -201,7 +201,7 @@ $ docker run --rm -it \
```console
$docker run -dt \
-v $PWD:/vuls \
-p 80:80 \
-p 5111:5111 \
vuls/vulsrepo
```

View File

@@ -1,31 +1,18 @@
FROM httpd:2.4
FROM alpine:3.6
MAINTAINER hikachan sadayuki-matsuno
# install packages
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
vim \
git \
libcgi-pm-perl \
libjson-perl \
&& rm -r /var/lib/apt/lists/*
# env
ENV HTTPD_PREFIX /usr/local/apache2
MAINTAINER hikachan sadayuki-matsuno usiusi360
VOLUME /vuls
WORKDIR ${HTTPD_PREFIX}/htdocs
RUN git clone https://github.com/usiusi360/vulsrepo.git \
&& echo "LoadModule cgid_module modules/mod_cgid.so" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo "<Directory \"$HTTPD_PREFIX/htdocs/vulsrepo/dist/cgi\">" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo " Options +ExecCGI +FollowSymLinks" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo " AddHandler cgi-script cgi" >> $HTTPD_PREFIX/conf/httpd.conf \
&& echo "</Directory>" >> $HTTPD_PREFIX/conf/httpd.conf \
&& sed -i -e 's/User daemon/#User/g' $HTTPD_PREFIX/conf/httpd.conf \
&& sed -i -e 's/Group daemon/#Group/g' $HTTPD_PREFIX/conf/httpd.conf \
&& ln -snf /vuls/results /usr/local/apache2/htdocs/vulsrepo/results
RUN apk --no-cache add git \
&& git clone https://github.com/usiusi360/vulsrepo.git
EXPOSE 80
CMD ["httpd-foreground"]
RUN cd /vulsrepo/server \
&& cp vulsrepo-config.toml.sample vulsrepo-config.toml \
&& sed -i -e 's/\/home\/vuls-user//g' vulsrepo-config.toml \
&& sed -i -e 's/\/opt//g' vulsrepo-config.toml \
&& mkdir /lib64 \
&& ln -s /lib/ld-musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
EXPOSE 5111
CMD ["/vulsrepo/server/vulsrepo-server"]

View File

@@ -23,7 +23,7 @@ VulsRepo is visualized based on the json report output in [vuls](https://github.
```console
$docker run -dt \
-v $PWD:/vuls \
-p 80:80 \
-p 5111:5111 \
vuls/vulsrepo
```