Compare commits

...

20 Commits

Author SHA1 Message Date
Kota Kanbe
e788e6a5ad Support Alpine Linux #194 (#545)
* Support Alpine Linux #194

* Fix testcase

* Fix README

* Fix dep files

* Fix changelog

* Bump up version
2017-12-01 23:17:28 +09:00
Flaviu
d00e912934 Replace strings.HasPrefix with strings.Index for SuSE scanner (#546) 2017-11-21 11:37:43 +09:00
Kota Kanbe
8ebb663368 Fix yum changelog option (#543) 2017-11-15 17:32:17 +09:00
nnao45
445ffc4123 Update README.md (#542) 2017-11-14 17:05:12 +09:00
Kota Kanbe
6af49f4d55 Fix false positive: ignore oval info when kernel major version is different. (#541) 2017-11-10 23:33:43 +09:00
Mai MISHIRO
1de9e8c086 Fix: Misdetection of OvalMatch for CentOS and Scientific in oval/util.go (#536)
* Fix: Misdecection of OvalMatch for CentOS in oval/util.go

* Remediation: Misdetection of OvalMatch for Scientific (currently treated as RHEL) oval/util.go

* The regular expression was changed because the release number of CentOS and Scientific's unchanged package is different from upstream.

* OvalMatch test of RedHat and CentOS has been added.
2017-11-09 11:20:23 +09:00
Mai MISHIRO
59b0812adf Fix: "Reboot Required" detection process in scan/redhat.go (#534) 2017-11-08 17:16:59 +09:00
kota kanbe
719785c1ed Remove README.fr.md because unable to maintenance.. 2017-11-08 16:11:03 +09:00
nakacya
8e5f627e59 README Typo Update (#538)
* Update README.ja.md

Typo Update

* Update README.md

Typo Update
2017-11-08 15:57:18 +09:00
Kota Kanbe
5ced3c72b8 Insert sudo only at the beginning of command in deep scan #495 (#539)
* Insert `sudo` only at the beginning of command in deep scan #495

* Fix testcase
2017-11-08 15:48:43 +09:00
Kota Kanbe
c002f0168c Fix config.toml validation (#537) 2017-11-06 09:56:18 +09:00
Kota Kanbe
00c690f516 Add pseudo server type for non-ssh scanning (only cpe scan) #512 (#531)
* Add pseudo server type for non-ssh scanning (only cpe scan) #512

* Don't check hostname for pseudo type

* Update README.md
2017-11-02 17:02:06 +09:00
nakacya
ab68ad5cc5 README Update (#530)
* README.ja.md Update

Add Update steps

* Update README.ja.md

* Update README.ja.md

* README.md update

Add Update steps
2017-10-30 13:24:46 +09:00
kota kanbe
5c84ebefab Update README 2017-10-26 14:54:15 +09:00
sadayuki-matsuno
eb2acaff22 send slack msg by api (#525) 2017-10-26 13:30:01 +09:00
shimojomasatsugummm
84d0655c52 fix typo Privious -> Previous (#523) 2017-10-25 18:51:29 +09:00
nashiox
e137ebb9c2 Fix package query fails on debian based container (#519) (#522)
* Fix package query fails on debian based container (#519)

* Fix executil test (#519)
2017-10-25 18:49:47 +09:00
atsu
10d690d929 fix typo from "enviroment" to "environment" (#518) 2017-10-21 18:28:53 +09:00
yuu26
14611d2fd9 Fix typo in config/jsonloader.go (#517) 2017-10-20 14:34:48 +09:00
x-blood
0665bfe15f Modified Spell Miss of "README.md". (#516)
* Modified spell miss of README.md. 1305:Calculator

* Revert "Modified spell miss of README.md. 1305:Calculator"

This reverts commit 0e0db1be8d.

* Modified spell miss of README.md. line:1305"Calculator"
2017-10-20 14:02:16 +09:00
37 changed files with 1561 additions and 456 deletions

View File

@@ -1,5 +1,7 @@
# Change Log
## v0.4.1 and later, see [GitHub release](https://github.com/future-architect/vuls/releases)
## [v0.4.0](https://github.com/future-architect/vuls/tree/v0.4.0) (2017-08-25)
[Full Changelog](https://github.com/future-architect/vuls/compare/v0.3.0...v0.4.0)
@@ -509,4 +511,4 @@
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

56
Gopkg.lock generated
View File

@@ -4,14 +4,14 @@
[[projects]]
name = "github.com/Azure/azure-sdk-for-go"
packages = ["storage"]
revision = "df4dd90d076ebbf6e87d08d3f00bfac8ff4bde1a"
version = "v10.3.1-beta"
revision = "7692b0cef22674113fcf71cc17ac3ccc1a7fef48"
version = "v11.2.2-beta"
[[projects]]
name = "github.com/Azure/go-autorest"
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
revision = "f6be1abbb5abd0517522f850dd785990d373da7e"
version = "v8.4.0"
revision = "c67b24a8e30d876542a85022ebbdecf0e5a935e8"
version = "v9.4.1"
[[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 = "c652f9369083515c3ddf1fbaf6df68da2c101545"
version = "v1.12.1"
revision = "e4f7e38b704e3ed0acc4a7f8196b777696f6f1f3"
version = "v1.12.30"
[[projects]]
name = "github.com/boltdb/bolt"
@@ -52,20 +52,20 @@
[[projects]]
name = "github.com/dgrijalva/jwt-go"
packages = ["."]
revision = "d2709f9f1f31ebcda9651b03077758c1f3a0018c"
version = "v3.0.0"
revision = "dbeaa9332f19a944acb5736b4456cfcc02140e29"
version = "v3.1.0"
[[projects]]
name = "github.com/go-ini/ini"
packages = ["."]
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
version = "v1.28.2"
revision = "32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a"
version = "v1.32.0"
[[projects]]
name = "github.com/go-redis/redis"
packages = [".","internal","internal/consistenthash","internal/hashtag","internal/pool","internal/proto"]
revision = "975882d73d21759d45a4eb49652064083bc23e61"
version = "v6.7.0"
revision = "e5e021257bfc6d5fbf0beac33e30194311fb189f"
version = "v6.7.4"
[[projects]]
name = "github.com/go-sql-driver/mysql"
@@ -136,7 +136,7 @@
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
packages = ["config","db","jvn","log","models","nvd","util"]
revision = "f5406ffe8226f01f64544723339c6a17b2bd74af"
revision = "a64c5fc25cd9669b213986e1a02831c71a5c601d"
[[projects]]
name = "github.com/kotakanbe/go-pingscanner"
@@ -148,7 +148,7 @@
branch = "master"
name = "github.com/kotakanbe/goval-dictionary"
packages = ["config","db","db/rdb","log","models"]
revision = "fd8ff5a6343912117d1b7db16fbd5fa1f4116c3a"
revision = "dca4f21940cbf2245a50817177fed7eb6793e72c"
[[projects]]
branch = "master"
@@ -160,7 +160,7 @@
branch = "master"
name = "github.com/lib/pq"
packages = [".","hstore","oid"]
revision = "b77235e3890a962fe8a6f8c4c7198679ca7814e7"
revision = "8c6ee72f3e6bcb1542298dd5f76cb74af9742cec"
[[projects]]
name = "github.com/mattn/go-colorable"
@@ -183,8 +183,8 @@
[[projects]]
name = "github.com/mattn/go-sqlite3"
packages = ["."]
revision = "ca5e3819723d8eeaf170ad510e7da1d6d2e94a08"
version = "v1.2.0"
revision = "ed69081a91fd053f17672236b0dd52ba7485e1a3"
version = "v1.4.0"
[[projects]]
branch = "master"
@@ -198,11 +198,17 @@
packages = ["."]
revision = "9ac6cf4d929b2fa8fd2d2e6dec5bb0feb4f4911d"
[[projects]]
name = "github.com/nlopes/slack"
packages = ["."]
revision = "c86337c0ef2486a15edd804355d9c73d2f2caed1"
version = "v0.1.0"
[[projects]]
branch = "master"
name = "github.com/nsf/termbox-go"
packages = ["."]
revision = "4ed959e0540971545eddb8c75514973d670cf739"
revision = "aa4a75b1c20a2b03751b1a9f7e41d58bd6f71c43"
[[projects]]
name = "github.com/parnurzeal/gorequest"
@@ -232,7 +238,7 @@
branch = "master"
name = "github.com/sirupsen/logrus"
packages = ["."]
revision = "89742aefa4b206dcf400792f3bd35b542998eb3b"
revision = "95cd2b9c79aa5e72ab0bc69b7ccc2be15bf850f6"
[[projects]]
branch = "master"
@@ -244,29 +250,29 @@
branch = "master"
name = "golang.org/x/crypto"
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
revision = "c84b36c635ad003a10f0c755dff5685ceef18c71"
revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","idna","publicsuffix"]
revision = "0a9397675ba34b2845f758fe3cd68828369c6517"
packages = ["context","idna","publicsuffix","websocket"]
revision = "9dfe39835686865bff950a07b394c12a98ddc811"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
revision = "314a259e304ff91bd6985da2a7149bbf91237993"
revision = "0ac51a24ef1c37380f98ba8b98f56e3bffd59850"
[[projects]]
branch = "master"
name = "golang.org/x/text"
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"
revision = "88f656faf3f37f690df1a32515b479415e1a6769"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "36d700add80d36c56484ed310b9a7e622b3e308ab22eb42bdfb02fd8f5c90407"
inputs-digest = "58ae46498625e705c582d70591148e07dbac30bc4f75cadef3d2fda514cd5099"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -88,3 +88,7 @@
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/go-cve-dictionary"
[[constraint]]
branch = "master"
name = "github.com/kotakanbe/goval-dictionary"

View File

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

View File

@@ -91,7 +91,7 @@ Table of Contents
* [Example: Use MySQL as a DB storage back-end](#example-use-mysql-as-a-db-storage-back-end)
* [Example: Use PostgreSQL as a DB storage back-end](#example-use-postgresql-as-a-db-storage-back-end)
* [Example: Use Redis as a DB storage back-end](#example-use-redis-as-a-db-storage-back-end)
* [Usage: Scan vulnerability of non-OS package](#usage-scan-vulnerability-of-non-os-package)
* [Usage: Scan vulnerability of non-OS package](#usage-scan-vulnerabilites-of-non-os-packages)
* [Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)](#usage-integrate-with-owasp-dependency-check-to-automatic-update-when-the-libraries-are-updated-experimental)
* [Usage: TUI](#usage-tui)
* [Display the latest scan results](#display-the-latest-scan-results)
@@ -139,7 +139,7 @@ Vulsは上に挙げた手動運用での課題を解決するツールであり
# Main Features
- サーバに存在する脆弱性をスキャン
- FreeBSD, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbianに対応
- Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbian, FreeBSD に対応
- クラウド、オンプレミス、Docker
- 高精度なスキャン
- Vulsは複数の脆弱性データベース、複数の検知方法を組み合わせることで高精度なスキャンを実現している
@@ -324,6 +324,7 @@ $ goval-dictionary fetch-redhat 7
今回はスキャン対象がCentOS 7なので、RedHat 7のOVALを取得している。
他の種類のOSをスキャンする場合は以下を参照し、スキャン対象用のOVALを取得しておくこと
- [Alpine](https://github.com/kotakanbe/goval-dictionary#usage-fetch-alpine-secdb-as-oval-data-type)
- [RedHat, CentOS](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat)
- [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)
@@ -340,9 +341,20 @@ $ git clone https://github.com/future-architect/vuls.git
$ cd vuls
$ make install
```
The binary was built under `$GOPATH/bin`
もしもインストールプロセスが途中で止まる場合は、Out of memory errorが発生している可能性があるので、インスタンスタイプを大きくして再実行してみてください。
もし、あなたが以前にvulsをインストールしていて update をする場合は以下を実施してください。
```
$ rm -rf $GOPATH/pkg/linux_amd64/github.com/future-architect/vuls/
$ rm -rf $GOPATH/src/github.com/future-architect/vuls/
$ cd $GOPATH/src/github.com/future-architect
$ git clone https://github.com/future-architect/vuls.git
$ cd vuls
$ make install
```
## Step6. Config
Vulsの設定ファイルを作成するTOMLフォーマット
@@ -582,15 +594,16 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
| Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access <br>on scan tareget|
|:------------|:--------------------------------------:|:-------------------:|:----------:|:---------------------------------------:|
| CentOS | Fast |  No | Supported | No |
| Alpine | Fast |  No | Supported | No |
| CentOS | Fast |  No | Supported | No |
| RHEL | Fast |  No | Supported | No |
| 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 |
| SUSE Enterprise | Fast |  No | Supported | No|
| Amazon | Fast |  No | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No |
----
@@ -601,7 +614,8 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
| Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access <br>on scan tareget|
|:------------|:-------------------------------------:|:-------------------------:|:---------:|:---------------------------------------:|
| CentOS | Slow |  No | Supported | Need |
| Alpine | Fast |  No | Supported | No |
| CentOS | Slow |  No | Supported | Need |
| RHEL | Slow |  Need | Supported | Need |
| Oracle | Slow |  Need | Supported | Need |
| Ubuntu |1st time: Slow <br> From 2nd time: Fast| Need | Supported | Need |
@@ -609,7 +623,7 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
| 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|
| SUSE Enterprise | Fast |  No | Supported | No |
- On Ubuntu, Debian and Raspbian
@@ -623,7 +637,7 @@ Vulsをスキャン対象サーバにデプロイする。Vulsはローカルホ
- On RHEL, Oracle, Amazon and FreeBSD
`yum changelog`でアップデート対象のパッケージのチェンジログを取得する(パースはしない)。
- On SUSE Enterprise Linux
- On SUSE Enterprise Linux and Alpine Linux
Same as fast scan mode for now.
----
@@ -646,6 +660,7 @@ web/app server in the same configuration under the load balancer
| Distribution| Release |
|:------------|-------------------:|
| Alpine | 3.2 and later |
| Ubuntu | 12, 14, 16|
| Debian | 7, 8, 9|
| RHEL | 5, 6, 7|
@@ -676,6 +691,7 @@ $ vuls discover 172.31.4.0/24
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
#legacyToken = "xoxp-11111111111-222222222222-3333333333"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
@@ -710,6 +726,7 @@ host = "172.31.4.82"
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#type = "pseudo"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
@@ -732,6 +749,7 @@ host = "172.31.4.82"
```
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
#legacyToken = "xoxp-11111111111-222222222222-3333333333"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
@@ -739,10 +757,21 @@ host = "172.31.4.82"
notifyUsers = ["@username"]
```
- hookURL : Incoming webhook's URL
- hookURL or legacyToken
どちらか一方を指定する。
もし脆弱性が沢山有る場合はlegacyTokenの利用をおすすめする。legacyTokenはSlackのスレッド形式でポストされる。
スキャンサーバ単位で集約されるのでSlack通知が氾濫しない。
- hookURL : Incoming webhook's URL (legacyTokenが設定されている場合、hookURLは無視される。)
![Vuls-slack](img/vuls-slack-en.png)
- legacyToken : slack legacy token (https://api.slack.com/custom-integrations/legacy-tokens)
![Vuls-slack-thread](https://user-images.githubusercontent.com/8997330/31842418-02b703f2-b629-11e7-8ec3-beda5d3a397e.png)
- channel : channel name.
channelに`${servername}`を指定すると、結果レポートをサーバごとに別チャネルにすることが出来る。
以下のサンプルでは、`#server1`チャネルと`#server2`チャネルに送信される。スキャン前にチャネルを作成する必要がある。
**legacyTokenが設定されている場合、channelは実在するchannelでなければならない。**
```
[slack]
channel = "${servername}"
@@ -808,6 +837,7 @@ host = "172.31.4.82"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
#type = "pseudo"
#ignoreCves = ["CVE-2016-6314"]
#optional = [
# ["key", "value"],
@@ -824,6 +854,7 @@ host = "172.31.4.82"
- port: SSH Port number
- user: SSH username
- keyPath: SSH private key path
- type: "pseudo" for non-ssh scanning. see [#531](https://github.com/future-architect/vuls/pull/531)
- cpeNames: see [Usage: Scan vulnerability of non-OS package](#usage-scan-vulnerability-of-non-os-package)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: JSONレポートに含めたい追加情報
@@ -883,6 +914,7 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン
| Distribution | Release | Requirements |
|:-------------|-------------------:|:-------------|
| Alpine | 3.2 and later | - |
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8, 9| reboot-notifier|
| CentOS | 6, 7| - |
@@ -906,6 +938,7 @@ Deep Scan Modeでスキャンするためには、下記のパッケージが必
| Distribution | Release | Requirements |
|:-------------|-------------------:|:-------------|
| Alpine | 3.2 and later | - |
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8, 9| aptitude, reboot-notifier |
| CentOS | 6, 7| yum-plugin-changelog, yum-utils |
@@ -928,13 +961,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, /usr/bin/repoquery
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, /usr/bin/yum --color=never changelog all *
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, /usr/bin/repoquery
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, /usr/bin/yum --color=never changelog all *
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
@@ -1289,7 +1322,7 @@ CWE https://cwe.mitre.org/data/definitions/190.html
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636
CVE Details http://www.cvedetails.com/cve/CVE-2016-5636
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/...
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/...
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636
ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html
Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1
@@ -1578,6 +1611,18 @@ Vulsは、[CPE](https://nvd.nist.gov/cpe.cfm)に登録されているソフト
]
```
- Configuration
ネットワーク機器など、スキャン対象にSSH接続しない場合は`type="pseudo"`を指定する。
```
[servers]
[servers.172-31-4-82]
type = "pseudo"
cpeNames = [
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
]
```
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) は、プログラミング言語のライブラリを特定しCPEを推測、公開済みの脆弱性を検知するツール。
@@ -1835,7 +1880,8 @@ Run with --debug, --sql-debug option.
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
- Does Vuls accept ssh connections with fish-shell or old zsh as the login shell?
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
~~No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)~~
Yes, fixed in [#545](https://github.com/future-architect/vuls/pull/545)
- Windows
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)

View File

@@ -14,7 +14,6 @@ We have a slack team. [Join slack team](http://goo.gl/forms/xm5KFo35tu)
Twitter: [@vuls_en](https://twitter.com/vuls_en)
[README 日本語](https://github.com/future-architect/vuls/blob/master/README.ja.md)
[README in French](https://github.com/future-architect/vuls/blob/master/README.fr.md)
![Vuls-Abstract](img/vuls-abstract.png)
@@ -145,7 +144,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, SUSE Enterprise Linux and Raspbian
- Supports Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise Linux and Raspbian, FreeBSD
- Cloud, on-premise, Docker
- High quality scan
- Vuls uses Multiple vulnerability databases
@@ -332,6 +331,7 @@ $ goval-dictionary fetch-redhat 7
```
If you want to scan other than CentOS 7, fetch OVAL data according to the OS type and version of scan target server in advance.
- [Alpine](https://github.com/kotakanbe/goval-dictionary#usage-fetch-alpine-secdb-as-oval-data-type)
- [RedHat, CentOS](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-redhat)
- [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)
@@ -349,6 +349,16 @@ $ git clone https://github.com/future-architect/vuls.git
$ cd vuls
$ make install
```
If you have previously installed vuls and want to update, please do the following
```
$ rm -rf $GOPATH/pkg/linux_amd64/github.com/future-architect/vuls/
$ rm -rf $GOPATH/src/github.com/future-architect/vuls/
$ cd $GOPATH/src/github.com/future-architect
$ git clone https://github.com/future-architect/vuls.git
$ cd vuls
$ make install
```
The binary was built under `$GOPATH/bin`
If the installation process stops halfway, try increasing the instance type of EC2. An out of memory error may have occurred.
@@ -591,16 +601,16 @@ On the aggregation server, you can refer to the scanning result of each scan tar
| Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access <br>on scan tareget|
|:------------|:--------------------------------------:|:-------------------:|:----------:|:---------------------------------------:|
| CentOS | Fast |  No | Supported | No |
| Alpine | Fast |  No | Supported | No |
| CentOS | Fast |  No | Supported | No |
| RHEL | Fast |  No | Supported | No |
| 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 |
| SUSE Enterprise | Fast |  No | Supported | No|
| Amazon | Fast |  No | No | Need |
| SUSE Enterprise | Fast |  No | Supported | No |
---------
@@ -609,7 +619,8 @@ On the aggregation server, you can refer to the scanning result of each scan tar
| Distribution| Scan Speed | Need Root Privilege | OVAL | Need Internet Access <br>on scan tareget|
|:------------|:-------------------------------------:|:-------------------------:|:---------:|:---------------------------------------:|
| CentOS | Slow |  No | Supported | Need |
| Alpine | Fast |  No | Supported | No |
| CentOS | Slow |  No | Supported | Need |
| RHEL | Slow |  Need | Supported | Need |
| Oracle | Slow |  Need | Supported | Need |
| Ubuntu |1st time: Slow <br> From 2nd time: Fast| Need | Supported | Need |
@@ -617,7 +628,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|
| SUSE Enterprise | Fast |  No | Supported | No |
- On Ubuntu, Debian and Raspbian
@@ -632,7 +643,7 @@ Vuls issues `yum changelog` to get changelogs of upgradable packages at once and
- On RHEL, Oracle, Amazon and FreeBSD
Detect CVE IDs by using package manager.
- On SUSE Enterprise Linux
- On SUSE Enterprise Linux and Alpine Linux
Same as fast scan mode for now.
----
@@ -668,6 +679,7 @@ If there is a staging environment with the same configuration as the production
| FreeBSD | 10, 11|
| SUSE Enterprise | 11, 12|
| Raspbian | Jessie, Stretch |
| Alpine | 3.2 and later |
----
@@ -690,6 +702,7 @@ $ vuls discover 172.31.4.0/24
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
#legacyToken = "xoxp-11111111111-222222222222-3333333333"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
@@ -725,6 +738,7 @@ host = "172.31.4.82"
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#type = "pseudo"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
@@ -748,6 +762,7 @@ You can customize your configuration using this template.
```
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
#legacyToken = "xoxp-11111111111-222222222222-3333333333"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
@@ -755,8 +770,16 @@ You can customize your configuration using this template.
notifyUsers = ["@username"]
```
- hookURL : Incoming webhook's URL
- channel : channel name.
- hookURL or legacyToken.
If there are a lot of vulnerabilities, it is better to use legacyToken since the Slack notification will be flooded.
- hookURL : Incoming webhook's URL (hookURL is ignored when legacyToken is set.)
![Vuls-slack](img/vuls-slack-en.png)
- legacyToken : slack legacy token (https://api.slack.com/custom-integrations/legacy-tokens)
![Vuls-slack-thread](https://user-images.githubusercontent.com/8997330/31842418-02b703f2-b629-11e7-8ec3-beda5d3a397e.png)
- channel : channel name.
If you set `${servername}` to channel, the report will be sent to each channel.
In the following example, the report will be sent to the `#server1` and `#server2`.
Be sure to create these channels before scanning.
@@ -820,6 +843,7 @@ You can customize your configuration using this template.
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#type = "pseudo"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]
@@ -839,6 +863,7 @@ You can customize your configuration using this template.
- port: SSH Port number
- user: SSH username
- keyPath: SSH private key path
- type: "pseudo" for non-ssh scanning. see [#531](https://github.com/future-architect/vuls/pull/531)
- cpeNames: see [Usage: Scan vulnerability of non-OS package](#usage-scan-vulnerability-of-non-os-package)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: Add additional information to JSON report.
@@ -896,6 +921,7 @@ The configtest subcommand checks whether vuls is able to connect via SSH to serv
| Distribution | Release | Requirements |
|:-------------|-------------------:|:-------------|
| Alpine | 3.2 and later | - |
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8, 9| reboot-notifier|
| CentOS | 6, 7| - |
@@ -917,6 +943,7 @@ In order to scan with deep scan mode, the following dependencies are required, s
| Distribution | Release | Requirements |
|:-------------|-------------------:|:-------------|
| Alpine | 3.2 and later | - |
| Ubuntu | 12, 14, 16| - |
| Debian | 7, 8, 9| aptitude, reboot-notifier |
| CentOS | 6, 7| yum-plugin-changelog, yum-utils |
@@ -939,13 +966,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, /usr/bin/repoquery
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, /usr/bin/yum --color=never changelog all *
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, /usr/bin/repoquery
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, /usr/bin/yum --color=never changelog all *
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
```
@@ -1302,7 +1329,7 @@ CWE https://cwe.mitre.org/data/definitions/190.html
NVD https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-5636
MITRE https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-5636
CVE Details http://www.cvedetails.com/cve/CVE-2016-5636
CVSS Claculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/...
CVSS Calculator https://nvd.nist.gov/cvss/v2-calculator?name=CVE-2016-5636&vector=(AV:N/AC:L/...
RHEL-CVE https://access.redhat.com/security/cve/CVE-2016-5636
ALAS-2016-724 https://alas.aws.amazon.com/ALAS-2016-724.html
Package python27-2.7.10-4.119.amzn1 -> python27-2.7.12-2.120.amzn1
@@ -1383,6 +1410,17 @@ With this sample command, it will ..
- Only Report CVEs that CVSS score is over 7
```
$ vuls report \
-to-slack \
-cvss-over=7 \
-cvedb-path=$PWD/cve.sqlite3
```
With this sample command, it will ..
- Send scan results to slack
- Only Report CVEs that CVSS score is over 7
## Example: Put results in S3 bucket
To put results in S3 bucket, configure following settings in AWS before reporting.
- Create S3 bucket. see [Creating a Bucket](http://docs.aws.amazon.com/AmazonS3/latest/UG/CreatingaBucket.html)
@@ -1583,6 +1621,20 @@ To detect the vulnerability of Ruby on Rails v4.2.1, cpeNames needs to be set in
]
```
- type="pseudo"
Specify this when you want to detect vulnerability by specifying cpename without SSH connection.
The pseudo type does not do anything when scanning.
Search for NVD at report time and detect vulnerability of software specified as cpenamae.
```
[servers]
[servers.172-31-4-82]
type = "pseudo"
cpeNames = [
"cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
]
```
# Usage: Integrate with OWASP Dependency Check to Automatic update when the libraries are updated (Experimental)
[OWASP Dependency check](https://www.owasp.org/index.php/OWASP_Dependency_Check) is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities.
@@ -1808,7 +1860,8 @@ Run with --debug, --sql-debug option.
[Riak docs](http://docs.basho.com/riak/latest/ops/tuning/open-files-limit/) is awesome.
- Does Vuls accept SSH connections with fish-shell or old zsh as the login shell?
No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)
~~No, Vuls needs a user on the server for bash login. see also [#8](/../../issues/8)~~
Yes, fixed in [#545](https://github.com/future-architect/vuls/pull/545)
- Windows
Use Microsoft Baseline Security Analyzer. [MBSA](https://technet.microsoft.com/en-us/security/cc184924.aspx)

View File

@@ -92,6 +92,7 @@ func printConfigToml(ips []string) (err error) {
const tomlTemplate = `
[slack]
hookURL = "https://hooks.slack.com/services/abc123/defghijklmnopqrstuvwxyz"
#legacyToken = "xoxp-11111111111-222222222222-3333333333"
channel = "#channel-name"
#channel = "${servername}"
iconEmoji = ":ghost:"
@@ -131,6 +132,7 @@ host = "{{$ip}}"
#port = "22"
#user = "root"
#keyPath = "/home/username/.ssh/id_rsa"
#type = "pseudo"
#cpeNames = [
# "cpe:/a:rubyonrails:ruby_on_rails:4.2.1",
#]

View File

@@ -197,6 +197,7 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
c.Conf.RefreshCve = p.refreshCve
log.Info("Validating config...")
if !c.Conf.ValidateOnTui() {

View File

@@ -76,6 +76,14 @@ const (
// SUSEOpenstackCloud is
SUSEOpenstackCloud = "suse.openstack.cloud"
// Alpine is
Alpine = "alpine"
)
const (
// ServerTypePseudo is used for ServerInfo.Type
ServerTypePseudo = "pseudo"
)
//Config is struct of Configuration
@@ -379,10 +387,11 @@ func (c *SMTPConf) Validate() (errs []error) {
// SlackConf is slack config
type SlackConf struct {
HookURL string `valid:"url" json:"-"`
Channel string `json:"channel"`
IconEmoji string `json:"icon_emoji"`
AuthUser string `json:"username"`
HookURL string `valid:"url" json:"-"`
LegacyToken string `json:"token" toml:"legacyToken,omitempty"`
Channel string `json:"channel"`
IconEmoji string `json:"icon_emoji"`
AuthUser string `json:"username"`
NotifyUsers []string
Text string `json:"text"`
@@ -445,6 +454,9 @@ type ServerInfo struct {
// For CentOS, RHEL, Amazon
Enablerepo []string
// "pseudo" or ""
Type string
// used internal
LogMsgAnsiColor string // DebugLog Color
Container Container
@@ -457,7 +469,7 @@ func (s ServerInfo) GetServerName() string {
if len(s.Container.ContainerID) == 0 {
return s.ServerName
}
return fmt.Sprintf("%s@%s", s.Container.ContainerID, s.ServerName)
return fmt.Sprintf("%s@%s", s.Container.Name, s.ServerName)
}
// Distro has distribution info

View File

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

View File

@@ -61,46 +61,48 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
s := ServerInfo{ServerName: name}
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", name)
}
switch {
case v.Port != "":
s.Port = v.Port
case d.Port != "":
s.Port = d.Port
default:
s.Port = "22"
}
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", name)
if v.Type != ServerTypePseudo {
s.Host = v.Host
if len(s.Host) == 0 {
return fmt.Errorf("%s is invalid. host is empty", name)
}
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
if s.KeyPath != "" {
if _, err := os.Stat(s.KeyPath); err != nil {
return fmt.Errorf(
"%s is invalid. keypath: %s not exists", name, s.KeyPath)
switch {
case v.Port != "":
s.Port = v.Port
case d.Port != "":
s.Port = d.Port
default:
s.Port = "22"
}
}
// s.KeyPassword = keyPass
s.KeyPassword = v.KeyPassword
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
switch {
case v.User != "":
s.User = v.User
case d.User != "":
s.User = d.User
default:
if s.Port != "local" {
return fmt.Errorf("%s is invalid. User is empty", name)
}
}
s.KeyPath = v.KeyPath
if len(s.KeyPath) == 0 {
s.KeyPath = d.KeyPath
}
if s.KeyPath != "" {
if _, err := os.Stat(s.KeyPath); err != nil {
return fmt.Errorf(
"%s is invalid. keypath: %s not exists", name, s.KeyPath)
}
}
// s.KeyPassword = keyPass
s.KeyPassword = v.KeyPassword
if len(s.KeyPassword) == 0 {
s.KeyPassword = d.KeyPassword
}
}
s.CpeNames = v.CpeNames
@@ -175,6 +177,8 @@ func (c TOMLLoader) Load(pathToToml, keyPass string) error {
}
}
s.Type = v.Type
s.LogMsgAnsiColor = Colors[i%len(Colors)]
i++

View File

@@ -53,9 +53,11 @@
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
Alpine: apk
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
SUSE: zypper
FreeBSD: pkg<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
@@ -264,7 +266,7 @@ Debian/Ubuntu: aptitude changelog<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="183.35883739927397" y="2.000003510871693">Amazon
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
@@ -298,7 +300,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e4" source="n1" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-123.36984126984123" ty="0.0">
@@ -306,11 +307,13 @@ FreeBSD<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="74.6640625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">CentOS
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="102.9296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="77.078125" x="-97.68364242524859" y="5.005267793098369">Alpine Linux
CentOS
RHEL
Ubuntu
Debian
Oracle Linux<y:LabelModel>
Oracle Linux
Suse<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -323,7 +326,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e5" source="n4" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -364,7 +366,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e9" source="n3" target="n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -375,7 +376,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e10" source="n1" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
@@ -396,7 +396,6 @@ Oracle Linux<y:LabelModel>
</data>
</edge>
<edge id="e11" source="n10" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="-125.78842258255952" ty="0.0">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@@ -53,10 +53,12 @@
<y:Geometry height="90.44247787610618" width="268.0" x="309.6849206349206" y="86.0"/>
<y:Fill color="#E8EEF7" color2="#B7C9E3" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="60.53125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="14.95561393805309">Get installed packages
<y:NodeLabel alignment="right" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="88.796875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="170.763671875" x="48.61816406250006" y="0.8228014380530908">Get installed packages
Alpine Linux: apk
Debian/Ubuntu: dpkg-query
Amazon/RHEL/CentOS: rpm
FreeBSD: pkg<y:LabelModel>
FreeBSD: pkg
SUSE: zypper<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
@@ -235,7 +237,6 @@ Amazon / RHEL: yum changelog<y:LabelModel>
</node>
<node id="n13" yfiles.foldertype="group">
<data key="d4"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
@@ -315,13 +316,13 @@ Amazon / RHEL: yum changelog<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-66.95987036992159" y="-48.39843398912808">Debian
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.98046875" x="-257.65322875976574" y="2.0000035108718635">Debian
Ubuntu
Raspbian<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="right" ratio="0.02215389573439544" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.8652035780364729" 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>
@@ -379,13 +380,13 @@ Raspbian<y:LabelModel>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="10.125014629061297" y="-48.39843398912805">Amazon
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="46.3984375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="51.806640625" x="200.87829463898197" y="4.000003510871693">Amazon
RHEL
FreeBSD<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="1.9999999999998863" distanceToCenter="false" position="left" ratio="0.022401276994204813" segment="0"/>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="6.999999999999886" distanceToCenter="false" position="right" ratio="0.8192728556300707" 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>
@@ -442,7 +443,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e11" source="n11" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="-24.34091537610618">
@@ -455,7 +455,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e12" source="n8" target="n12">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -466,7 +465,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e13" source="n12" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
@@ -477,7 +475,6 @@ FreeBSD<y:LabelModel>
</data>
</edge>
<edge id="e14" source="n9" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="10.8330078125"/>
@@ -487,6 +484,30 @@ FreeBSD<y:LabelModel>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n1" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="999.0" y="226.44247787610618"/>
<y:Point x="999.0" y="570.8409153761062"/>
<y:Point x="743.3698412698412" y="570.8409153761062"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="none"/>
<y:EdgeLabel alignment="right" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="32.265625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.8203125" x="422.923942251054" y="13.867191010871807">Alpine Linux
SUSE<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.8856709076027529" 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>
</graph>
<data key="d7">
<y:Resources/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -29,7 +29,7 @@ import (
)
// Version of Vuls
var version = "0.4.0"
var version = "0.4.2"
// Revision of Git
var revision string

82
oval/alpine.go Normal file
View File

@@ -0,0 +1,82 @@
/* 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"
)
// Alpine is the struct of Alpine Linux
type Alpine struct {
Base
}
// NewAlpine creates OVAL client for SUSE
func NewAlpine() Alpine {
return Alpine{
Base{
family: config.Alpine,
},
}
}
// FillWithOval returns scan result after updating CVE info by OVAL
func (o Alpine) 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)
}
return nil
}
func (o Alpine) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
cveID := defPacks.def.Advisory.Cves[0].CveID
vinfo, ok := r.ScannedCves[cveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", cveID)
vinfo = models.VulnInfo{
CveID: cveID,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
vinfo.AffectedPackages.Sort()
r.ScannedCves[cveID] = vinfo
}
func (o Alpine) convertToModel(def *ovalmodels.Definition) *models.CveContent {
return &models.CveContent{
CveID: def.Advisory.Cves[0].CveID,
}
}

View File

@@ -64,7 +64,7 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
}

View File

@@ -21,7 +21,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/future-architect/vuls/config"
@@ -132,15 +131,14 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
}
}
major := strings.Split(release, ".")[0]
since := time.Now()
since = since.AddDate(0, 0, -3)
if lastModified.Before(since) {
util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
osFamily, major, lastModified)
osFamily, release, lastModified)
return false, nil
}
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major)
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, release)
return true, nil
}

View File

@@ -67,6 +67,35 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
return nil
}
var kernelRelatedPackNames = map[string]bool{
"kernel": true,
"kernel-aarch64": true,
"kernel-abi-whitelists": true,
"kernel-bootwrapper": true,
"kernel-debug": true,
"kernel-debug-devel": true,
"kernel-devel": true,
"kernel-doc": true,
"kernel-headers": true,
"kernel-kdump": true,
"kernel-kdump-devel": true,
"kernel-rt": true,
"kernel-rt-debug": true,
"kernel-rt-debug-devel": true,
"kernel-rt-debug-kvm": true,
"kernel-rt-devel": true,
"kernel-rt-doc": true,
"kernel-rt-kvm": true,
"kernel-rt-trace": true,
"kernel-rt-trace-devel": true,
"kernel-rt-trace-kvm": true,
"kernel-rt-virt": true,
"kernel-rt-virt-devel": true,
"kernel-tools": true,
"kernel-tools-libs": true,
"kernel-tools-libs-devel": true,
}
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
ctype := models.NewCveContentType(o.family)
for _, cve := range defPacks.def.Advisory.Cves {
@@ -100,7 +129,7 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
vinfo.AffectedPackages.Sort()
r.ScannedCves[cve.CveID] = vinfo
}

View File

@@ -96,7 +96,7 @@ func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
}
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
vinfo.AffectedPackages.Sort()
r.ScannedCves[defPacks.def.Title] = vinfo
}

View File

@@ -21,6 +21,8 @@ import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/cenkalti/backoff"
@@ -46,7 +48,7 @@ type defPacks struct {
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
func (e defPacks) toPackStatuses(family string) (ps models.PackageStatuses) {
for name, notFixedYet := range e.actuallyAffectedPackNames {
ps = append(ps, models.PackageStatus{
Name: name,
@@ -57,10 +59,13 @@ func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps model
}
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] = notFixedYet
return true
// alpine's entry is empty since Alpine secdb is not OVAL format
if def.DefinitionID != "" {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = notFixedYet
return true
}
}
}
e.entries = append(e.entries, defPacks{
@@ -145,7 +150,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
select {
case res := <-resChan:
for _, def := range res.defs {
affected, notFixedYet := isOvalDefAffected(def, r.Family, res.request)
affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
if !affected {
continue
}
@@ -252,7 +257,7 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult) (relatedDefs ovalResult,
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", r.Family, err)
}
for _, def := range definitions {
affected, notFixedYet := isOvalDefAffected(def, r.Family, req)
affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
if !affected {
continue
}
@@ -269,12 +274,35 @@ func getDefsByPackNameFromOvalDB(r *models.ScanResult) (relatedDefs ovalResult,
return
}
func isOvalDefAffected(def ovalmodels.Definition, family string, req request) (affected, notFixedYet bool) {
func major(version string) string {
ss := strings.SplitN(version, ":", 2)
ver := ""
if len(ss) == 1 {
ver = ss[0]
} else {
ver = ss[1]
}
return ver[0:strings.Index(ver, ".")]
}
func isOvalDefAffected(def ovalmodels.Definition, req request, family string, running models.Kernel) (affected, notFixedYet bool) {
for _, ovalPack := range def.AffectedPacks {
if req.packName != ovalPack.Name {
continue
}
if running.Release != "" {
switch family {
case config.RedHat, config.CentOS:
// For kernel related packages, ignore OVAL information with different major versions
if _, ok := kernelRelatedPackNames[ovalPack.Name]; ok {
if major(ovalPack.Version) != major(running.Release) {
continue
}
}
}
}
if ovalPack.NotFixedYet {
return true, true
}
@@ -320,10 +348,16 @@ func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, er
return false, err
}
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS, config.Oracle, config.SUSEEnterpriseServer:
case config.Oracle, config.SUSEEnterpriseServer, config.Alpine:
vera := rpmver.NewVersion(versionRelease)
verb := rpmver.NewVersion(packB.Version)
return vera.LessThan(verb), nil
case config.RedHat, config.CentOS: // TODO: Suport config.Scientific
rea := regexp.MustCompile(`\.[es]l(\d+)(?:_\d+)?(?:\.centos)?`)
reb := regexp.MustCompile(`\.el(\d+)(?:_\d+)?`)
vera := rpmver.NewVersion(rea.ReplaceAllString(versionRelease, ".el$1"))
verb := rpmver.NewVersion(reb.ReplaceAllString(packB.Version, ".el$1"))
return vera.LessThan(verb), nil
default:
util.Log.Errorf("Not implemented yet: %s", family)
}

View File

@@ -5,6 +5,7 @@ import (
"sort"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
@@ -116,7 +117,6 @@ func TestDefpacksToPackStatuses(t *testing.T) {
{
in: in{
family: "ubuntu",
packs: models.Packages{},
dp: defPacks{
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
@@ -154,7 +154,7 @@ func TestDefpacksToPackStatuses(t *testing.T) {
},
}
for i, tt := range tests {
actual := tt.in.dp.toPackStatuses(tt.in.family, tt.in.packs)
actual := tt.in.dp.toPackStatuses(tt.in.family)
sort.Slice(actual, func(i, j int) bool {
return actual[i].Name < actual[j].Name
})
@@ -167,8 +167,9 @@ func TestDefpacksToPackStatuses(t *testing.T) {
func TestIsOvalDefAffected(t *testing.T) {
type in struct {
def ovalmodels.Definition
family string
req request
family string
kernel models.Kernel
}
var tests = []struct {
in in
@@ -261,7 +262,7 @@ func TestIsOvalDefAffected(t *testing.T) {
// req.isSrcPack == false
// Version comparison
// oval vs NewVersion
// oval.version < installed.newVersion
// oval.version > installed.newVersion
{
in: in{
family: "ubuntu",
@@ -320,9 +321,670 @@ func TestIsOvalDefAffected(t *testing.T) {
affected: true,
notFixedYet: false,
},
// RedHat
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.9",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.6",
NewVersionRelease: "0:1.2.3-45.el6_7.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.6",
NewVersionRelease: "0:1.2.3-45.el6_7.8",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.6",
NewVersionRelease: "0:1.2.3-45.el6_7.9",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "redhat",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.8",
},
},
affected: false,
notFixedYet: false,
},
// CentOS
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.9",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.6",
NewVersionRelease: "0:1.2.3-45.el6.centos.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.6",
NewVersionRelease: "0:1.2.3-45.el6.centos.8",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.centos.6",
NewVersionRelease: "0:1.2.3-45.el6.centos.9",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.8",
},
},
affected: false,
notFixedYet: false,
},
// TODO: If vuls support Scientific, replace "centos" below to "scientific".
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.9",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.6",
NewVersionRelease: "0:1.2.3-45.sl6.7",
},
},
affected: true,
notFixedYet: true,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.6",
NewVersionRelease: "0:1.2.3-45.sl6.8",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.sl6.6",
NewVersionRelease: "0:1.2.3-45.sl6.9",
},
},
affected: true,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6_7.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6.8",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: "centos",
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "a",
NotFixedYet: false,
},
{
Name: "b",
NotFixedYet: false,
Version: "0:1.2.3-45.el6.8",
},
},
},
req: request{
packName: "b",
isSrcPack: false,
versionRelease: "0:1.2.3-45.el6_7.8",
},
},
affected: false,
notFixedYet: false,
},
// For kernel related packages, ignore OVAL with different major versions
{
in: in{
family: config.CentOS,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "kernel",
Version: "4.1.0",
NotFixedYet: false,
},
},
},
req: request{
packName: "kernel",
versionRelease: "3.0.0",
NewVersionRelease: "3.2.0",
},
kernel: models.Kernel{
Release: "3.0.0",
},
},
affected: false,
notFixedYet: false,
},
{
in: in{
family: config.CentOS,
def: ovalmodels.Definition{
AffectedPacks: []ovalmodels.Package{
{
Name: "kernel",
Version: "3.1.0",
NotFixedYet: false,
},
},
},
req: request{
packName: "kernel",
versionRelease: "3.0.0",
NewVersionRelease: "3.2.0",
},
kernel: models.Kernel{
Release: "3.0.0",
},
},
affected: true,
notFixedYet: false,
},
}
for i, tt := range tests {
affected, notFixedYet := isOvalDefAffected(tt.in.def, tt.in.family, tt.in.req)
affected, notFixedYet := isOvalDefAffected(tt.in.def, tt.in.req, tt.in.family, tt.in.kernel)
if tt.affected != affected {
t.Errorf("[%d] affected\nexpected: %v\n actual: %v\n", i, tt.affected, affected)
}
@@ -331,3 +993,25 @@ func TestIsOvalDefAffected(t *testing.T) {
}
}
}
func TestMajor(t *testing.T) {
var tests = []struct {
in string
expected string
}{
{
in: "4.1",
expected: "4",
},
{
in: "0:4.1",
expected: "4",
},
}
for i, tt := range tests {
a := major(tt.in)
if tt.expected != a {
t.Errorf("[%d]\nexpected: %s\n actual: %s\n", i, tt.expected, a)
}
}
}

View File

@@ -142,21 +142,11 @@ func (api cvedictClient) FetchCveDetailsFromCveDB(cveIDs []string) (cveDetails [
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType, cveconfig.Conf.DBPath, cveconfig.Conf.DebugSQL); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err := driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
for _, cveID := range cveIDs {
cveDetail := driver.Get(cveID)
if len(cveDetail.CveID) == 0 {
@@ -276,19 +266,11 @@ func (api cvedictClient) FetchCveDetailsByCpeNameFromDB(cpeName string) (cveDeta
cveconfig.Conf.DebugSQL = config.Conf.DebugSQL
var driver cvedb.DB
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType); err != nil {
if driver, err = cvedb.NewDB(cveconfig.Conf.DBType, cveconfig.Conf.DBPath, cveconfig.Conf.DebugSQL); err != nil {
log.Error(err)
return []*cve.CveDetail{}, fmt.Errorf("Failed to New DB. err: %s", err)
}
util.Log.Debugf("Opening DB (%s).", driver.Name())
if err = driver.OpenDB(
cveconfig.Conf.DBType,
cveconfig.Conf.DBPath,
cveconfig.Conf.DebugSQL,
); err != nil {
return []*cve.CveDetail{},
fmt.Errorf("Failed to open DB. err: %s", err)
}
return driver.GetByCpeName(cpeName), nil
}

View File

@@ -19,7 +19,6 @@ package report
import (
"fmt"
"strings"
"time"
c "github.com/future-architect/vuls/config"
@@ -181,19 +180,25 @@ func FillWithOval(r *models.ScanResult) (err error) {
// TODO other suse family
ovalClient = oval.NewSUSE()
ovalFamily = c.SUSEEnterpriseServer
case c.Alpine:
ovalClient = oval.NewAlpine()
ovalFamily = c.Alpine
case c.Amazon, c.Raspbian, c.FreeBSD, c.Windows:
return nil
case c.ServerTypePseudo:
return nil
default:
return fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
}
util.Log.Debugf("Check whether oval is already fetched: %s %s",
ovalFamily, r.Release)
ok, err := ovalClient.CheckIfOvalFetched(ovalFamily, r.Release)
if err != nil {
return err
}
if !ok {
major := strings.Split(r.Release, ".")[0]
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, major)
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. For details, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, r.Release)
return nil
}

View File

@@ -27,6 +27,7 @@ import (
"github.com/cenkalti/backoff"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/nlopes/slack"
"github.com/parnurzeal/gorequest"
log "github.com/sirupsen/logrus"
)
@@ -36,31 +37,23 @@ type field struct {
Value string `json:"value"`
Short bool `json:"short"`
}
type attachment struct {
Title string `json:"title"`
TitleLink string `json:"title_link"`
Fallback string `json:"fallback"`
Text string `json:"text"`
Pretext string `json:"pretext"`
Color string `json:"color"`
Fields []*field `json:"fields"`
MrkdwnIn []string `json:"mrkdwn_in"`
Footer string `json:"footer"`
}
type message struct {
Text string `json:"text"`
Username string `json:"username"`
IconEmoji string `json:"icon_emoji"`
Channel string `json:"channel"`
Attachments []*attachment `json:"attachments"`
Text string `json:"text"`
Username string `json:"username"`
IconEmoji string `json:"icon_emoji"`
Channel string `json:"channel"`
ThreadTimeStamp string `json:"thread_ts"`
Attachments []slack.Attachment `json:"attachments"`
}
// SlackWriter send report to slack
type SlackWriter struct{}
func (w SlackWriter) Write(rs ...models.ScanResult) error {
func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
conf := config.Conf.Slack
channel := conf.Channel
token := conf.LegacyToken
for _, r := range rs {
if channel == "${servername}" {
@@ -78,7 +71,7 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
IconEmoji: conf.IconEmoji,
Channel: channel,
}
if err := send(msg); err != nil {
if err = send(msg); err != nil {
return err
}
continue
@@ -88,7 +81,7 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
// Split into chunks with 100 elements
// https://api.slack.com/methods/chat.postMessage
maxAttachments := 100
m := map[int][]*attachment{}
m := map[int][]slack.Attachment{}
for i, a := range toSlackAttachments(r) {
m[i/maxAttachments] = append(m[i/maxAttachments], a)
}
@@ -98,21 +91,49 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
}
sort.Ints(chunkKeys)
for i, k := range chunkKeys {
txt := ""
if i == 0 {
txt = msgText(r)
// Send slack by API
if 0 < len(token) {
api := slack.New(token)
ParentMsg := slack.PostMessageParameters{
// Text: msgText(r),
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
}
msg := message{
Text: txt,
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Channel: channel,
Attachments: m[k],
}
if err := send(msg); err != nil {
var ts string
if _, ts, err = api.PostMessage(channel, msgText(r), ParentMsg); err != nil {
return err
}
for _, k := range chunkKeys {
params := slack.PostMessageParameters{
// Text: msgText(r),
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Attachments: m[k],
ThreadTimestamp: ts,
}
if _, _, err = api.PostMessage(channel, msgText(r), params); err != nil {
return err
}
}
} else {
for i, k := range chunkKeys {
txt := ""
if i == 0 {
txt = msgText(r)
}
msg := message{
Text: txt,
Username: conf.AuthUser,
IconEmoji: conf.IconEmoji,
Channel: channel,
Attachments: m[k],
}
if err = send(msg); err != nil {
return err
}
}
}
}
return nil
@@ -164,7 +185,7 @@ func msgText(r models.ScanResult) string {
r.ScannedCves.FormatCveSummary())
}
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
vinfos := r.ScannedCves.ToSortedSlice()
for _, vinfo := range vinfos {
curent := []string{}
@@ -196,12 +217,12 @@ func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
new = append(new, "?")
}
a := attachment{
Title: vinfo.CveID,
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family),
MrkdwnIn: []string{"text", "pretext"},
Fields: []*field{
a := slack.Attachment{
Title: vinfo.CveID,
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
Text: attachmentText(vinfo, r.Family),
MarkdownIn: []string{"text", "pretext"},
Fields: []slack.AttachmentField{
{
// Title: "Current Package/CPE",
Title: "Installed",
@@ -216,7 +237,7 @@ func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
},
Color: color(vinfo.MaxCvssScore().Value.Score),
}
attaches = append(attaches, &a)
attaches = append(attaches, a)
}
return
}

View File

@@ -288,7 +288,7 @@ func loadPrevious(current models.ScanResults) (previous models.ScanResults, err
}
if r.Family == result.Family && r.Release == result.Release {
previous = append(previous, *r)
util.Log.Infof("Privious json found: %s", path)
util.Log.Infof("Previous json found: %s", path)
break
}
}

171
scan/alpine.go Normal file
View File

@@ -0,0 +1,171 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package scan
import (
"bufio"
"fmt"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// inherit OsTypeInterface
type alpine struct {
base
}
// NewAlpine is constructor
func newAlpine(c config.ServerInfo) *alpine {
d := &alpine{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
}
// Alpine
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/alpine.rb
func detectAlpine(c config.ServerInfo) (itsMe bool, os osTypeInterface) {
os = newAlpine(c)
if r := exec(c, "ls /etc/alpine-release", noSudo); !r.isSuccess() {
return false, os
}
if r := exec(c, "cat /etc/alpine-release", noSudo); r.isSuccess() {
os.setDistro(config.Alpine, strings.TrimSpace(r.Stdout))
return true, os
}
return false, os
}
func (o *alpine) checkDependencies() error {
o.log.Infof("Dependencies... No need")
return nil
}
func (o *alpine) checkIfSudoNoPasswd() error {
o.log.Infof("sudo ... No need")
return nil
}
func (o *alpine) apkUpdate() error {
r := o.exec("apk update", noSudo)
if !r.isSuccess() {
return fmt.Errorf("Failed to SSH: %s", r)
}
return nil
}
func (o *alpine) scanPackages() error {
if err := o.apkUpdate(); err != nil {
return err
}
// collect the running kernel information
release, version, err := o.runningKernel()
if err != nil {
o.log.Errorf("Failed to scan the running kernel version: %s", err)
return err
}
o.Kernel = models.Kernel{
Release: release,
Version: version,
}
installed, err := o.scanInstalledPackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
updatable, err := o.scanUpdatablePackages()
if err != nil {
o.log.Errorf("Failed to scan installed packages: %s", err)
return err
}
installed.MergeNewVersion(updatable)
o.Packages = installed
return nil
}
func (o *alpine) scanInstalledPackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("apk info -v")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
return o.parseApkInfo(r.Stdout)
}
func (o *alpine) parseApkInfo(stdout string) (models.Packages, error) {
packs := models.Packages{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
ss := strings.Split(line, "-")
if len(ss) < 3 {
return nil, fmt.Errorf("Failed to parse apk info -v: %s", line)
}
name := strings.Join(ss[:len(ss)-2], "-")
packs[name] = models.Package{
Name: name,
Version: strings.Join(ss[len(ss)-2:], "-"),
}
}
return packs, nil
}
func (o *alpine) scanUpdatablePackages() (models.Packages, error) {
cmd := util.PrependProxyEnv("apk version")
r := o.exec(cmd, noSudo)
if !r.isSuccess() {
return nil, fmt.Errorf("Failed to SSH: %s", r)
}
return o.parseApkVersion(r.Stdout)
}
func (o *alpine) parseApkVersion(stdout string) (models.Packages, error) {
packs := models.Packages{}
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if !strings.Contains(line, "<") {
continue
}
ss := strings.Split(line, "<")
namever := strings.TrimSpace(ss[0])
tt := strings.Split(namever, "-")
name := strings.Join(tt[:len(tt)-2], "-")
packs[name] = models.Package{
Name: name,
NewVersion: strings.TrimSpace(ss[1]),
}
}
return packs, nil
}

75
scan/alpine_test.go Normal file
View File

@@ -0,0 +1,75 @@
package scan
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
)
func TestParseApkInfo(t *testing.T) {
var tests = []struct {
in string
packs models.Packages
}{
{
in: `musl-1.1.16-r14
busybox-1.26.2-r7
`,
packs: models.Packages{
"musl": {
Name: "musl",
Version: "1.1.16-r14",
},
"busybox": {
Name: "busybox",
Version: "1.26.2-r7",
},
},
},
}
d := newAlpine(config.ServerInfo{})
for i, tt := range tests {
pkgs, _ := d.parseApkInfo(tt.in)
if !reflect.DeepEqual(tt.packs, pkgs) {
t.Errorf("[%d] expected %v, actual %v", i, tt.packs, pkgs)
}
}
}
func TestParseApkVersion(t *testing.T) {
var tests = []struct {
in string
packs models.Packages
}{
{
in: `Installed: Available:
libcrypto1.0-1.0.1q-r0 < 1.0.2m-r0
libssl1.0-1.0.1q-r0 < 1.0.2m-r0
nrpe-2.14-r2 < 2.15-r5
`,
packs: models.Packages{
"libcrypto1.0": {
Name: "libcrypto1.0",
NewVersion: "1.0.2m-r0",
},
"libssl1.0": {
Name: "libssl1.0",
NewVersion: "1.0.2m-r0",
},
"nrpe": {
Name: "nrpe",
NewVersion: "2.15-r5",
},
},
},
}
d := newAlpine(config.ServerInfo{})
for i, tt := range tests {
pkgs, _ := d.parseApkVersion(tt.in)
if !reflect.DeepEqual(tt.packs, pkgs) {
t.Errorf("[%d] expected %v, actual %v", i, tt.packs, pkgs)
}
}
}

View File

@@ -29,8 +29,7 @@ import (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
"github.com/knqyf263/go-deb-version"
version "github.com/knqyf263/go-deb-version"
)
// inherit OsTypeInterface
@@ -240,7 +239,7 @@ func (o *debian) rebootRequired() (bool, error) {
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)
r := o.exec(`dpkg-query -W -f="\${binary:Package},\${db:Status-Abbrev},\${Version},\${Source},\${source:Version}\n"`, noSudo)
if !r.isSuccess() {
return nil, nil, nil, fmt.Errorf("Failed to SSH: %s", r)
}

View File

@@ -41,6 +41,7 @@ import (
type execResult struct {
Servername string
Container conf.Container
Host string
Port string
Cmd string
@@ -51,9 +52,16 @@ type execResult struct {
}
func (s execResult) String() string {
sname := ""
if s.Container.ContainerID == "" {
sname = s.Servername
} else {
sname = s.Container.Name + "@" + s.Servername
}
return fmt.Sprintf(
"execResult: servername: %s\n cmd: %s\n exitstatus: %d\n stdout: %s\n stderr: %s\n err: %s",
s.Servername, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error)
sname, s.Cmd, s.ExitStatus, s.Stdout, s.Stderr, s.Error)
}
func (s execResult) isSuccess(expectedStatusCodes ...int) bool {
@@ -167,10 +175,11 @@ func exec(c conf.ServerInfo, cmd string, sudo bool, log ...*logrus.Entry) (resul
func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult) {
cmdstr = decorateCmd(c, cmdstr, sudo)
var cmd *ex.Cmd
if c.Distro.Family == conf.FreeBSD {
switch c.Distro.Family {
// case conf.FreeBSD, conf.Alpine, conf.Debian:
// cmd = ex.Command("/bin/sh", "-c", cmdstr)
default:
cmd = ex.Command("/bin/sh", "-c", cmdstr)
} else {
cmd = ex.Command("/bin/bash", "-c", cmdstr)
}
var stdoutBuf, stderrBuf bytes.Buffer
cmd.Stdout = &stdoutBuf
@@ -196,6 +205,7 @@ func localExec(c conf.ServerInfo, cmdstr string, sudo bool) (result execResult)
func sshExecNative(c conf.ServerInfo, cmd string, sudo bool) (result execResult) {
result.Servername = c.ServerName
result.Container = c.Container
result.Host = c.Host
result.Port = c.Port
@@ -311,6 +321,7 @@ func sshExecExternal(c conf.ServerInfo, cmd string, sudo bool) (result execResul
result.Stdout = stdoutBuf.String()
result.Stderr = stderrBuf.String()
result.Servername = c.ServerName
result.Container = c.Container
result.Host = c.Host
result.Port = c.Port
result.Cmd = fmt.Sprintf("%s %s", sshBinaryPath, strings.Join(args, " "))
@@ -324,10 +335,19 @@ func getSSHLogger(log ...*logrus.Entry) *logrus.Entry {
return log[0]
}
func dockerShell(family string) string {
switch family {
// case conf.Alpine, conf.Debian:
// return "/bin/sh"
default:
// return "/bin/bash"
return "/bin/sh"
}
}
func decorateCmd(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 you are using pipe and you want to detect preprocessing errors, remove comment out
@@ -342,9 +362,11 @@ func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
if c.IsContainer() {
switch c.Containers.Type {
case "", "docker":
cmd = fmt.Sprintf(`docker exec --user 0 %s /bin/bash -c "%s"`, c.Container.ContainerID, cmd)
cmd = fmt.Sprintf(`docker exec --user 0 %s %s -c '%s'`,
c.Container.ContainerID, dockerShell(c.Distro.Family), cmd)
case "lxd":
cmd = fmt.Sprintf(`lxc exec %s -- /bin/bash -c "%s"`, c.Container.Name, cmd)
cmd = fmt.Sprintf(`lxc exec %s -- %s -c '%s'`,
c.Container.Name, dockerShell(c.Distro.Family), cmd)
}
}
// cmd = fmt.Sprintf("set -x; %s", cmd)

View File

@@ -63,7 +63,7 @@ func TestDecorateCmd(t *testing.T) {
conf: config.ServerInfo{User: "non-roor"},
cmd: "ls | grep hoge",
sudo: true,
expected: "sudo -S ls | sudo grep hoge",
expected: "sudo -S ls | grep hoge",
},
// -------------docker-------------
// root sudo false docker
@@ -75,7 +75,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: false,
expected: `docker exec --user 0 abc /bin/bash -c "ls"`,
expected: `docker exec --user 0 abc /bin/sh -c 'ls'`,
},
// root sudo true docker
{
@@ -86,7 +86,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: true,
expected: `docker exec --user 0 abc /bin/bash -c "ls"`,
expected: `docker exec --user 0 abc /bin/sh -c 'ls'`,
},
// non-root sudo false, docker
{
@@ -97,7 +97,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: false,
expected: `docker exec --user 0 abc /bin/bash -c "ls"`,
expected: `docker exec --user 0 abc /bin/sh -c 'ls'`,
},
// non-root sudo true, docker
{
@@ -108,7 +108,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: true,
expected: `docker exec --user 0 abc /bin/bash -c "ls"`,
expected: `docker exec --user 0 abc /bin/sh -c 'ls'`,
},
// non-root sudo true, docker
{
@@ -119,7 +119,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls | grep hoge",
sudo: true,
expected: `docker exec --user 0 abc /bin/bash -c "ls | grep hoge"`,
expected: `docker exec --user 0 abc /bin/sh -c 'ls | grep hoge'`,
},
// -------------lxd-------------
// root sudo false lxd
@@ -131,7 +131,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: false,
expected: `lxc exec def -- /bin/bash -c "ls"`,
expected: `lxc exec def -- /bin/sh -c 'ls'`,
},
// root sudo true lxd
{
@@ -142,7 +142,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: true,
expected: `lxc exec def -- /bin/bash -c "ls"`,
expected: `lxc exec def -- /bin/sh -c 'ls'`,
},
// non-root sudo false, lxd
{
@@ -153,7 +153,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: false,
expected: `lxc exec def -- /bin/bash -c "ls"`,
expected: `lxc exec def -- /bin/sh -c 'ls'`,
},
// non-root sudo true, lxd
{
@@ -164,7 +164,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls",
sudo: true,
expected: `lxc exec def -- /bin/bash -c "ls"`,
expected: `lxc exec def -- /bin/sh -c 'ls'`,
},
// non-root sudo true lxd
{
@@ -175,7 +175,7 @@ func TestDecorateCmd(t *testing.T) {
},
cmd: "ls | grep hoge",
sudo: true,
expected: `lxc exec def -- /bin/bash -c "ls | grep hoge"`,
expected: `lxc exec def -- /bin/sh -c 'ls | grep hoge'`,
},
}

66
scan/pseudo.go Normal file
View File

@@ -0,0 +1,66 @@
/* 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 (
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
)
// inherit OsTypeInterface
type pseudo struct {
base
}
func detectPseudo(c config.ServerInfo) (itsMe bool, pseudo osTypeInterface, err error) {
p := newPseudo(c)
p.setDistro(config.ServerTypePseudo, "")
return c.Type == config.ServerTypePseudo, p, nil
}
func newPseudo(c config.ServerInfo) *pseudo {
d := &pseudo{
base: base{
osPackages: osPackages{
Packages: models.Packages{},
VulnInfos: models.VulnInfos{},
},
},
}
d.log = util.NewCustomLogger(c)
d.setServerInfo(c)
return d
}
func (o *pseudo) checkIfSudoNoPasswd() error {
return nil
}
func (o *pseudo) checkDependencies() error {
return nil
}
func (o *pseudo) scanPackages() error {
return nil
}
func (o *pseudo) detectPlatform() {
o.setPlatform(models.Platform{Name: "other"})
return
}

View File

@@ -260,11 +260,13 @@ func (o *redhat) scanPackages() error {
}
func (o *redhat) rebootRequired() (bool, error) {
r := o.exec("rpm -q --last kernel | head -n1", noSudo)
if !r.isSuccess() {
return false, fmt.Errorf("Failed to detect the last installed kernel : %v", r)
r := o.exec("rpm -q --last kernel", noSudo)
scanner := bufio.NewScanner(strings.NewReader(r.Stdout))
if !r.isSuccess() || !scanner.Scan() {
o.log.Warn("Failed to detect the last installed kernel : %v", r)
return false, nil
}
lastInstalledKernelVer := strings.Fields(r.Stdout)[0]
lastInstalledKernelVer := strings.Fields(scanner.Text())[0]
running := fmt.Sprintf("kernel-%s", o.Kernel.Release)
return running != lastInstalledKernelVer, nil
}
@@ -397,7 +399,7 @@ func (o *redhat) parseUpdatablePacksLine(line string) (models.Package, error) {
}
func (o *redhat) scanUnsecurePackages(updatable models.Packages) (models.VulnInfos, error) {
if config.Conf.Deep {
if config.Conf.Deep && o.Distro.Family != config.Amazon {
//TODO Cache changelogs to bolt
if err := o.fillChangelogs(updatable); err != nil {
return nil, err
@@ -451,7 +453,7 @@ func (o *redhat) getAvailableChangelogs(packNames []string) (map[string]string,
if config.Conf.SkipBroken {
yumopts += " --skip-broken"
}
cmd := `yum --color=never %s changelog all %s | grep -A 10000 '==================== Available Packages ===================='`
cmd := `yum --color=never changelog all %s %s | grep -A 1000000 '==================== Available Packages ===================='`
cmd = fmt.Sprintf(cmd, yumopts, strings.Join(packNames, " "))
r := o.exec(util.PrependProxyEnv(cmd), o.sudo())

View File

@@ -41,7 +41,6 @@ type osTypeInterface interface {
detectPlatform()
getPlatform() models.Platform
// checkDependencies checks if dependencies are installed on the target server.
checkDependencies() error
checkIfSudoNoPasswd() error
@@ -75,6 +74,11 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
var itsMe bool
var fatalErr error
if itsMe, osType, _ = detectPseudo(c); itsMe {
util.Log.Debugf("Pseudo")
return
}
itsMe, osType, fatalErr = detectDebian(c)
if fatalErr != nil {
osType.setErrs([]error{
@@ -102,6 +106,11 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
return
}
if itsMe, osType = detectAlpine(c); itsMe {
util.Log.Debugf("Alpine. Host: %s:%s", c.Host, c.Port)
return
}
//TODO darwin https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/darwin.rb
osType.setErrs([]error{fmt.Errorf("Unknown OS Type")})
return

View File

@@ -157,8 +157,8 @@ func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
scanner := bufio.NewScanner(strings.NewReader(stdout))
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "S | Repository") ||
strings.HasPrefix(line, "--+----------------") {
if strings.Index(line, "S | Repository") != -1 ||
strings.Index(line, "--+----------------") != -1 {
continue
}
pack, err := o.parseZypperLUOneLine(line)

View File

@@ -109,7 +109,7 @@ func ProxyEnv() string {
return httpProxyEnv
}
// PrependProxyEnv prepends proxy enviroment variable
// PrependProxyEnv prepends proxy environment variable
func PrependProxyEnv(cmd string) string {
if len(config.Conf.HTTPProxy) == 0 {
return cmd