Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e788e6a5ad | ||
|
|
d00e912934 | ||
|
|
8ebb663368 | ||
|
|
445ffc4123 | ||
|
|
6af49f4d55 | ||
|
|
1de9e8c086 | ||
|
|
59b0812adf | ||
|
|
719785c1ed | ||
|
|
8e5f627e59 | ||
|
|
5ced3c72b8 | ||
|
|
c002f0168c | ||
|
|
00c690f516 | ||
|
|
ab68ad5cc5 | ||
|
|
5c84ebefab | ||
|
|
eb2acaff22 | ||
|
|
84d0655c52 | ||
|
|
e137ebb9c2 | ||
|
|
10d690d929 | ||
|
|
14611d2fd9 | ||
|
|
0665bfe15f | ||
|
|
473096d35d | ||
|
|
0eae26e261 | ||
|
|
a32845f652 | ||
|
|
15a0f7eadb | ||
|
|
5a0a6abf11 | ||
|
|
032b8d9572 | ||
|
|
5798e3af83 | ||
|
|
8e15b9ce1c | ||
|
|
7a1f132c1f | ||
|
|
a8483b2195 | ||
|
|
83bbbd0cb0 | ||
|
|
132432dce6 | ||
|
|
e5eb8e42f5 | ||
|
|
1095ebea24 | ||
|
|
1541a602b2 | ||
|
|
03a141c252 | ||
|
|
5f2183fc8e | ||
|
|
820831fa5d | ||
|
|
6d2d767c52 | ||
|
|
e0c3a728ae | ||
|
|
ec92f7797f | ||
|
|
0ba490c6df | ||
|
|
cfd668e11d |
24
.goreleaser.yml
Normal file
24
.goreleaser.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
project_name: vuls
|
||||
release:
|
||||
github:
|
||||
owner: future-architect
|
||||
name: vuls
|
||||
builds:
|
||||
- goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
main: .
|
||||
ldflags: -s -w -X main.version={{.Version}} -X main.revision={{.Commit}}
|
||||
binary: vuls
|
||||
archive:
|
||||
format: tar.gz
|
||||
name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
|
||||
.Arm }}{{ end }}'
|
||||
files:
|
||||
- LICENSE
|
||||
- NOTICE
|
||||
- README*
|
||||
- CHANGELOG.md
|
||||
snapshot:
|
||||
name_template: SNAPSHOT-{{ .Commit }}
|
||||
@@ -3,3 +3,5 @@ language: go
|
||||
go:
|
||||
- 1.8
|
||||
|
||||
after_success:
|
||||
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
|
||||
|
||||
@@ -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)*
|
||||
|
||||
@@ -31,10 +31,10 @@ depup:
|
||||
go get -u github.com/golang/dep/...
|
||||
dep ensure -update
|
||||
|
||||
build: main.go dep
|
||||
build: main.go dep pretest
|
||||
go build -ldflags "$(LDFLAGS)" -o vuls $<
|
||||
|
||||
install: main.go dep
|
||||
install: main.go dep pretest
|
||||
go install -ldflags "$(LDFLAGS)"
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ vet:
|
||||
echo $(PKGS) | xargs go vet || exit;
|
||||
|
||||
fmt:
|
||||
gofmt -w $(SRCS)
|
||||
gofmt -s -w $(SRCS)
|
||||
|
||||
fmtcheck:
|
||||
$(foreach file,$(SRCS),gofmt -d $(file);)
|
||||
$(foreach file,$(SRCS),gofmt -s -d $(file);)
|
||||
|
||||
pretest: lint vet fmtcheck
|
||||
|
||||
|
||||
91
Gopkg.lock
generated
91
Gopkg.lock
generated
@@ -4,14 +4,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/Azure/azure-sdk-for-go"
|
||||
packages = ["storage"]
|
||||
revision = "57db66900881e9fd21fd041a9d013514700ecab3"
|
||||
version = "v10.3.0-beta"
|
||||
revision = "7692b0cef22674113fcf71cc17ac3ccc1a7fef48"
|
||||
version = "v11.2.2-beta"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
packages = ["autorest","autorest/adal","autorest/azure","autorest/date"]
|
||||
revision = "77a52603f06947221c672f10275abc9bf2c7d557"
|
||||
version = "v8.3.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 = "264af29009637e0a9e5d4a276d0969c3ed918ffd"
|
||||
version = "v1.10.29"
|
||||
revision = "e4f7e38b704e3ed0acc4a7f8196b777696f6f1f3"
|
||||
version = "v1.12.30"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/boltdb/bolt"
|
||||
@@ -46,26 +46,26 @@
|
||||
[[projects]]
|
||||
name = "github.com/cheggaaa/pb"
|
||||
packages = ["."]
|
||||
revision = "0d6285554e726cc0620cbecc7e6969c945dcc63b"
|
||||
version = "v1.0.17"
|
||||
revision = "657164d0228d6bebe316fdf725c69f131a50fb10"
|
||||
version = "v1.0.18"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
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 = "19c1c2272e00c1aaa903cf574c746cd449f9cd3c"
|
||||
version = "v6.5.7"
|
||||
revision = "e5e021257bfc6d5fbf0beac33e30194311fb189f"
|
||||
version = "v6.7.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
@@ -106,8 +106,7 @@
|
||||
[[projects]]
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9"
|
||||
version = "0.2.2"
|
||||
revision = "0b12d6b5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jroimartin/gocui"
|
||||
@@ -119,7 +118,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/k0kubun/pp"
|
||||
packages = ["."]
|
||||
revision = "d1532fc5d94ecdf2da29e24d7b99721f3287de4a"
|
||||
revision = "e057ee7a28277be4d2af303443b6da377768181f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -137,7 +136,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/go-cve-dictionary"
|
||||
packages = ["config","db","jvn","log","models","nvd","util"]
|
||||
revision = "c20fa7e1d07f7c700baf12c855f7fcf61525f1b6"
|
||||
revision = "a64c5fc25cd9669b213986e1a02831c71a5c601d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kotakanbe/go-pingscanner"
|
||||
@@ -149,7 +148,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/goval-dictionary"
|
||||
packages = ["config","db","db/rdb","log","models"]
|
||||
revision = "3523cc174e68f285d0572d07c68ffa3a9290799c"
|
||||
revision = "dca4f21940cbf2245a50817177fed7eb6793e72c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -157,17 +156,11 @@
|
||||
packages = ["."]
|
||||
revision = "75edb2e85a38873f0318be05a458446681d1022f"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/labstack/gommon"
|
||||
packages = ["color","log"]
|
||||
revision = "779b8a8b9850a97acba6a3fe20feb628c39e17c1"
|
||||
version = "0.2.2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
packages = [".","hstore","oid"]
|
||||
revision = "e42267488fe361b9dc034be7a6bffef5b195bceb"
|
||||
revision = "8c6ee72f3e6bcb1542298dd5f76cb74af9742cec"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
@@ -178,8 +171,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "fc9e8d8ef48496124e79ae0df75490096eccf6fe"
|
||||
version = "v0.0.2"
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-runewidth"
|
||||
@@ -190,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"
|
||||
@@ -203,13 +196,19 @@
|
||||
branch = "master"
|
||||
name = "github.com/moul/http2curl"
|
||||
packages = ["."]
|
||||
revision = "4e24498b31dba4683efb9d35c1c8a91e2eda28c8"
|
||||
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"
|
||||
@@ -239,19 +238,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "84573d5f03ab3740f524c7842c3a9bf617961d32"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/bytebufferpool"
|
||||
packages = ["."]
|
||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/valyala/fasttemplate"
|
||||
packages = ["."]
|
||||
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||
revision = "95cd2b9c79aa5e72ab0bc69b7ccc2be15bf850f6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -263,29 +250,29 @@
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
|
||||
revision = "eb71ad9bd329b5ac0fd0148dd99bd62e8be8e035"
|
||||
revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","idna","publicsuffix"]
|
||||
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
|
||||
packages = ["context","idna","publicsuffix","websocket"]
|
||||
revision = "9dfe39835686865bff950a07b394c12a98ddc811"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix","windows"]
|
||||
revision = "07c182904dbd53199946ba614a412c61d3c548f5"
|
||||
revision = "0ac51a24ef1c37380f98ba8b98f56e3bffd59850"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "e56139fd9c5bc7244c76116c68e500765bb6db6b"
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "88f656faf3f37f690df1a32515b479415e1a6769"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "36d700add80d36c56484ed310b9a7e622b3e308ab22eb42bdfb02fd8f5c90407"
|
||||
inputs-digest = "58ae46498625e705c582d70591148e07dbac30bc4f75cadef3d2fda514cd5099"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@@ -88,3 +88,7 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/go-cve-dictionary"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/kotakanbe/goval-dictionary"
|
||||
|
||||
224
README.fr.md
224
README.fr.md
@@ -1,224 +0,0 @@
|
||||
|
||||
# Vuls: VULnerability Scanner
|
||||
|
||||
[](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)
|
||||
|
||||
[](https://asciinema.org/a/3y9zrf950agiko7klg8abvyck)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
----
|
||||
|
||||
# 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 d’environnement 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
----
|
||||
|
||||
For more information see [README in English](https://github.com/future-architect/vuls/blob/master/README.md)
|
||||
100
README.ja.md
100
README.ja.md
@@ -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, Raspbianに対応
|
||||
- Alpine, Ubuntu, Debian, CentOS, Amazon Linux, RHEL, Oracle Linux, SUSE Enterprise, Raspbian, FreeBSD に対応
|
||||
- クラウド、オンプレミス、Docker
|
||||
- 高精度なスキャン
|
||||
- Vulsは複数の脆弱性データベース、複数の検知方法を組み合わせることで高精度なスキャンを実現している
|
||||
@@ -324,10 +324,12 @@ $ 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)
|
||||
- [Oracle Linux](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
|
||||
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
|
||||
|
||||
## Step5. Deploy Vuls
|
||||
|
||||
@@ -339,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フォーマット)
|
||||
@@ -581,14 +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 |
|
||||
| FreeBSD | Fast | No | No | Need |
|
||||
| Amazon | Fast | No | No | Need |
|
||||
| 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 |
|
||||
|
||||
----
|
||||
|
||||
@@ -599,27 +614,32 @@ 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 |
|
||||
| Debian |1st time: Slow <br> From 2nd time: Fast| Need | Supported | Need |
|
||||
| Raspbian |1st time: Slow <br> From 2nd time: Fast| Need | No | Need |
|
||||
| FreeBSD | Fast | No | No | Need |
|
||||
| Amazon | Slow | No | No | Need |
|
||||
| Raspbian |1st time: Slow <br> From 2nd time: Fast| Need | No | Need |
|
||||
| SUSE Enterprise | Fast | No | Supported | No |
|
||||
|
||||
|
||||
- Ubuntu, Debian, Raspbian
|
||||
- On Ubuntu, Debian and Raspbian
|
||||
`apt-get changelog`でアップデート対象のパッケージのチェンジログを取得し、含まれるCVE IDをパースする。
|
||||
アップデート対象のパッケージが沢山ある場合、チェンジログの取得に時間がかかるので、初回のスキャンは遅い。
|
||||
ただ、2回目以降はキャッシュしたchangelogを使うので速くなる。
|
||||
|
||||
- CentOS
|
||||
- On CentOS
|
||||
`yum changelog`でアップデート対象のパッケージのチェンジログを取得し、含まれるCVE IDをパースする。
|
||||
|
||||
- Amazon, RHEL and FreeBSD
|
||||
- On RHEL, Oracle, Amazon and FreeBSD
|
||||
`yum changelog`でアップデート対象のパッケージのチェンジログを取得する(パースはしない)。
|
||||
|
||||
- On SUSE Enterprise Linux and Alpine Linux
|
||||
Same as fast scan mode for now.
|
||||
|
||||
----
|
||||
|
||||
# Use Cases
|
||||
@@ -640,12 +660,14 @@ 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|
|
||||
| CentOS | 6, 7|
|
||||
| Amazon Linux| All|
|
||||
| FreeBSD | 10, 11|
|
||||
| SUSE Enterprise | 11, 12|
|
||||
| Raspbian | Jessie, Stretch |
|
||||
|
||||
----
|
||||
@@ -669,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:"
|
||||
@@ -703,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",
|
||||
#]
|
||||
@@ -725,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:"
|
||||
@@ -732,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は無視される。)
|
||||

|
||||
|
||||
- legacyToken : slack legacy token (https://api.slack.com/custom-integrations/legacy-tokens)
|
||||

|
||||
|
||||
- channel : channel name.
|
||||
channelに`${servername}`を指定すると、結果レポートをサーバごとに別チャネルにすることが出来る。
|
||||
以下のサンプルでは、`#server1`チャネルと`#server2`チャネルに送信される。スキャン前にチャネルを作成する必要がある。
|
||||
**legacyTokenが設定されている場合、channelは実在するchannelでなければならない。**
|
||||
```
|
||||
[slack]
|
||||
channel = "${servername}"
|
||||
@@ -801,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"],
|
||||
@@ -817,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レポートに含めたい追加情報
|
||||
@@ -876,12 +914,14 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン
|
||||
|
||||
| Distribution | Release | Requirements |
|
||||
|:-------------|-------------------:|:-------------|
|
||||
| Alpine | 3.2 and later | - |
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8, 9| reboot-notifier|
|
||||
| CentOS | 6, 7| - |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 5, 6, 7 | - |
|
||||
| Oracle Linux | 5, 6, 7 | - |
|
||||
| SUSE Enterprise| 11, 12 | - |
|
||||
| FreeBSD | 10, 11 | - |
|
||||
| Raspbian | Jessie, Stretch | - |
|
||||
|
||||
@@ -889,7 +929,7 @@ configtestサブコマンドは、config.tomlで定義されたサーバ/コン
|
||||
|
||||
Deep Scan Modeではスキャン対象サーバ上にいくつかの依存パッケージが必要。
|
||||
configtestに--deepをつけて実行するとSSH接続に加えて以下もチェックする。
|
||||
- スキャン対象のサーバ上に依存パッケーがインストールされているか
|
||||
- スキャン対象のサーバ上に依存パッケージがインストールされているか
|
||||
- /etc/sudoers
|
||||
|
||||
### Dependencies and /etc/sudoers on Target Servers
|
||||
@@ -898,14 +938,16 @@ Deep Scan Modeでスキャンするためには、下記のパッケージが必
|
||||
|
||||
| Distribution | Release | Requirements |
|
||||
|:-------------|-------------------:|:-------------|
|
||||
| Alpine | 3.2 and later | - |
|
||||
| Ubuntu | 12, 14, 16| - |
|
||||
| Debian | 7, 8, 9| aptitude, reboot-notifier |
|
||||
| Debian | 7, 8, 9| aptitude, reboot-notifier |
|
||||
| CentOS | 6, 7| yum-plugin-changelog, yum-utils |
|
||||
| Amazon | All | yum-plugin-changelog, yum-utils |
|
||||
| RHEL | 5 | yum-utils, yum-security, yum-changelog |
|
||||
| RHEL | 6, 7 | yum-utils, yum-plugin-changelog |
|
||||
| Oracle Linux | 5 | yum-utils, yum-security, yum-changelog |
|
||||
| Oracle Linux | 6, 7 | yum-utils, yum-plugin-changelog |
|
||||
| SUSE Enterprise| 11, 12 | - |
|
||||
| FreeBSD | 10 | - |
|
||||
| Raspbian | Wheezy, Jessie | - |
|
||||
|
||||
@@ -919,13 +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
|
||||
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
|
||||
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"
|
||||
```
|
||||
|
||||
@@ -935,7 +977,7 @@ vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
|
||||
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
|
||||
```
|
||||
|
||||
- CentOS, Amazon Linux, FreeBSDは今のところRoot権限なしでスキャン可能
|
||||
- CentOS, Amazon Linux, SUSE Enterprise, FreeBSDは今のところRoot権限なしでスキャン可能
|
||||
|
||||
----
|
||||
|
||||
@@ -1152,6 +1194,7 @@ report:
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-to-email]
|
||||
[-to-slack]
|
||||
[-to-localfile]
|
||||
@@ -1176,7 +1219,8 @@ report:
|
||||
[-debug-sql]
|
||||
[-pipe]
|
||||
|
||||
[SERVER]...
|
||||
[RFC3339 datetime format under results dir]
|
||||
|
||||
-aws-profile string
|
||||
AWS profile to use (default "default")
|
||||
-aws-region string
|
||||
@@ -1225,6 +1269,8 @@ report:
|
||||
http://proxy-url:port (default: empty)
|
||||
-ignore-unscored-cves
|
||||
Don't report the unscored CVEs
|
||||
-ignore-unfixed
|
||||
Don't report the unfixed CVEs
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-log-dir string
|
||||
@@ -1276,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
|
||||
@@ -1337,7 +1383,7 @@ Confidence 100 / OvalMatch
|
||||
|
||||
| Detection Method | Confidence | OS |Description|
|
||||
|:-----------------------|-------------------:|:---------------------------------|:--|
|
||||
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian |Detection using OVAL |
|
||||
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian, SUSE |Detection using OVAL |
|
||||
| YumUpdateSecurityMatch | 100 | RHEL, Amazon, Oracle |Detection using yum-plugin-security|
|
||||
| ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version|
|
||||
| ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version|
|
||||
@@ -1565,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を推測)、公開済みの脆弱性を検知するツール。
|
||||
@@ -1712,6 +1770,7 @@ $ vuls report -ovaldb-url=http://192.168.0.1:1323
|
||||
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
|
||||
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
|
||||
- [Oracle](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
|
||||
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
|
||||
|
||||
----
|
||||
|
||||
@@ -1821,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)
|
||||
|
||||
110
README.md
110
README.md
@@ -4,6 +4,7 @@
|
||||
[](http://goo.gl/forms/xm5KFo35tu)
|
||||
[](https://github.com/future-architect/vuls/blob/master/LICENSE.txt)
|
||||
[](https://travis-ci.org/future-architect/vuls)
|
||||
[](https://goreportcard.com/report/github.com/future-architect/vuls)
|
||||
|
||||
|
||||

|
||||
@@ -13,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)
|
||||
|
||||

|
||||
|
||||
@@ -144,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 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
|
||||
@@ -331,10 +331,12 @@ $ 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)
|
||||
- [Oracle Linux](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
|
||||
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
|
||||
|
||||
## Step5. Deploy Vuls
|
||||
|
||||
@@ -347,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.
|
||||
|
||||
@@ -589,15 +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 |
|
||||
|
||||
| Amazon | Fast | No | No | Need |
|
||||
| SUSE Enterprise | Fast | No | Supported | No |
|
||||
|
||||
---------
|
||||
|
||||
@@ -606,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 |
|
||||
@@ -614,6 +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 |
|
||||
|
||||
|
||||
- On Ubuntu, Debian and Raspbian
|
||||
@@ -624,9 +639,13 @@ From the second time on, the scan speed is fast by using the local cache.
|
||||
|
||||
- On CentOS
|
||||
Vuls issues `yum changelog` to get changelogs of upgradable packages at once and parse the changelog.
|
||||
|
||||
- On RHEL, Oracle, Amazon and FreeBSD
|
||||
Detect CVE IDs by using package manager.
|
||||
|
||||
- On SUSE Enterprise Linux and Alpine Linux
|
||||
Same as fast scan mode for now.
|
||||
|
||||
----
|
||||
|
||||
# Use Cases
|
||||
@@ -658,7 +677,9 @@ If there is a staging environment with the same configuration as the production
|
||||
| CentOS | 6, 7|
|
||||
| Amazon Linux | All|
|
||||
| FreeBSD | 10, 11|
|
||||
| SUSE Enterprise | 11, 12|
|
||||
| Raspbian | Jessie, Stretch |
|
||||
| Alpine | 3.2 and later |
|
||||
|
||||
----
|
||||
|
||||
@@ -681,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:"
|
||||
@@ -716,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",
|
||||
#]
|
||||
@@ -739,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:"
|
||||
@@ -746,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.)
|
||||

|
||||
|
||||
- legacyToken : slack legacy token (https://api.slack.com/custom-integrations/legacy-tokens)
|
||||

|
||||
|
||||
- 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.
|
||||
@@ -811,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",
|
||||
#]
|
||||
@@ -830,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.
|
||||
@@ -887,12 +921,14 @@ 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| - |
|
||||
| Amazon | All | - |
|
||||
| RHEL | 5, 6, 7 | - |
|
||||
| Oracle Linux | 5, 6, 7 | - |
|
||||
| SUSE Enterprise| 11, 12 | - |
|
||||
| FreeBSD | 10, 11 | - |
|
||||
| Raspbian | Jessie, Stretch | - |
|
||||
|
||||
@@ -907,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 |
|
||||
@@ -915,6 +952,7 @@ In order to scan with deep scan mode, the following dependencies are required, s
|
||||
| RHEL | 6, 7 | yum-utils, yum-plugin-changelog |
|
||||
| Oracle Linux | 5 | yum-utils, yum-security, yum-changelog |
|
||||
| Oracle Linux | 6, 7 | yum-utils, yum-plugin-changelog |
|
||||
| SUSE Enterprise| 11, 12 | - |
|
||||
| FreeBSD | 10 | - |
|
||||
| Raspbian | Wheezy, Jessie | - |
|
||||
|
||||
@@ -928,13 +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
|
||||
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
|
||||
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"
|
||||
```
|
||||
|
||||
@@ -944,7 +982,7 @@ vuls ALL=(ALL) NOPASSWD: /usr/bin/apt-get update
|
||||
Defaults:vuls env_keep="http_proxy https_proxy HTTP_PROXY HTTPS_PROXY"
|
||||
```
|
||||
|
||||
- On CentOS, Amazon Linux, FreeBSD, it is possible to scan without root privilege for now.
|
||||
- On CentOS, Amazon Linux, SUSE Enterprise, FreeBSD, it is possible to scan without root privilege for now.
|
||||
|
||||
----
|
||||
|
||||
@@ -1163,6 +1201,7 @@ report:
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-to-email]
|
||||
[-to-slack]
|
||||
[-to-localfile]
|
||||
@@ -1187,7 +1226,8 @@ report:
|
||||
[-debug-sql]
|
||||
[-pipe]
|
||||
|
||||
[SERVER]...
|
||||
[RFC3339 datetime format under results dir]
|
||||
|
||||
-aws-profile string
|
||||
AWS profile to use (default "default")
|
||||
-aws-region string
|
||||
@@ -1236,6 +1276,8 @@ report:
|
||||
http://proxy-url:port (default: empty)
|
||||
-ignore-unscored-cves
|
||||
Don't report the unscored CVEs
|
||||
-ignore-unfixed
|
||||
Don't report the unfixed CVEs
|
||||
-lang string
|
||||
[en|ja] (default "en")
|
||||
-log-dir string
|
||||
@@ -1287,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
|
||||
@@ -1348,7 +1390,7 @@ Confidence 100 / OvalMatch
|
||||
|
||||
| Detection Method | Confidence | OS |Description|
|
||||
|:-----------------------|-------------------:|:---------------------------------|:--|
|
||||
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian |Detection using OVAL |
|
||||
| OvalMatch | 100 | CentOS, RHEL, Oracle, Ubuntu, Debian, SUSE |Detection using OVAL |
|
||||
| YumUpdateSecurityMatch | 100 | RHEL, Amazon, Oracle |Detection using yum-plugin-security|
|
||||
| ChangelogExactMatch | 95 | CentOS, Ubuntu, Debian, Raspbian |Exact version match between changelog and package version|
|
||||
| ChangelogLenientMatch | 50 | Ubuntu, Debian, Raspbian |Lenient version match between changelog and package version|
|
||||
@@ -1368,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)
|
||||
@@ -1568,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.
|
||||
|
||||
@@ -1599,13 +1666,16 @@ How to integrate Vuls with OWASP Dependency Check
|
||||
```
|
||||
tui:
|
||||
tui
|
||||
[-refresh-cve]
|
||||
[-cvedb-type=sqlite3|mysql|postgres]
|
||||
[-cvedb-path=/path/to/cve.sqlite3]
|
||||
[-cvedb-url=http://127.0.0.1:1323 DB connection string]
|
||||
[-ovaldb-type=sqlite3|mysql]
|
||||
[-ovaldb-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-refresh-cve]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-debug]
|
||||
@@ -1624,6 +1694,12 @@ tui:
|
||||
DB type for fetching OVAL dictionary (sqlite3 or mysql) (default "sqlite3")
|
||||
-ovaldb-url string
|
||||
http://goval-dictionary.com:1324 or mysql connection string
|
||||
-cvss-over float
|
||||
-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))
|
||||
-ignore-unfixed
|
||||
Don't report the unfixed CVEs
|
||||
-ignore-unscored-cves
|
||||
Don't report the unscored CVEs
|
||||
-debug
|
||||
debug mode
|
||||
-debug-sql
|
||||
@@ -1711,6 +1787,7 @@ $ vuls report -ovaldb-url=http://192.168.0.1:1323
|
||||
- [Ubuntu](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-ubuntu)
|
||||
- [Debian](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-debian)
|
||||
- [Oracle](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-oracle)
|
||||
- [SUSE](https://github.com/kotakanbe/goval-dictionary#usage-fetch-oval-data-from-suse)
|
||||
|
||||
----
|
||||
|
||||
@@ -1783,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)
|
||||
|
||||
4
cache/bolt.go
vendored
4
cache/bolt.go
vendored
@@ -163,7 +163,7 @@ func (b Bolt) GetChangelog(servername, packName string) (changelog string, err e
|
||||
err = b.db.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
return fmt.Errorf("Failed to get Bucket: %s", servername)
|
||||
}
|
||||
v := bkt.Get([]byte(packName))
|
||||
if v == nil {
|
||||
@@ -181,7 +181,7 @@ func (b Bolt) PutChangelog(servername, packName, changelog string) error {
|
||||
return b.db.Update(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket([]byte(servername))
|
||||
if bkt == nil {
|
||||
return fmt.Errorf("Faild to get Bucket: %s", servername)
|
||||
return fmt.Errorf("Failed to get Bucket: %s", servername)
|
||||
}
|
||||
if err := bkt.Put([]byte(packName), []byte(changelog)); err != nil {
|
||||
return err
|
||||
|
||||
@@ -57,6 +57,7 @@ func (p *DiscoverCmd) SetFlags(f *flag.FlagSet) {
|
||||
func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
// validate
|
||||
if len(f.Args()) == 0 {
|
||||
logrus.Errorf("Usage: " + p.Usage())
|
||||
return subcommands.ExitUsageError
|
||||
}
|
||||
|
||||
@@ -65,7 +66,6 @@ func (p *DiscoverCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface
|
||||
CIDR: cidr,
|
||||
PingOptions: []string{
|
||||
"-c1",
|
||||
"-t1",
|
||||
},
|
||||
NumOfConcurrency: 100,
|
||||
}
|
||||
@@ -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",
|
||||
#]
|
||||
|
||||
@@ -44,7 +44,9 @@ type ReportCmd struct {
|
||||
|
||||
cvssScoreOver float64
|
||||
ignoreUnscoredCves bool
|
||||
httpProxy string
|
||||
ignoreUnfixed bool
|
||||
|
||||
httpProxy string
|
||||
|
||||
cveDBType string
|
||||
cveDBPath string
|
||||
@@ -107,6 +109,7 @@ func (*ReportCmd) Usage() string {
|
||||
[-cvss-over=7]
|
||||
[-diff]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-to-email]
|
||||
[-to-slack]
|
||||
[-to-localfile]
|
||||
@@ -131,7 +134,7 @@ func (*ReportCmd) Usage() string {
|
||||
[-debug-sql]
|
||||
[-pipe]
|
||||
|
||||
[SERVER]...
|
||||
[RFC3339 datetime format under results dir]
|
||||
`
|
||||
}
|
||||
|
||||
@@ -213,6 +216,12 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
|
||||
false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.BoolVar(
|
||||
&p.ignoreUnfixed,
|
||||
"ignore-unfixed",
|
||||
false,
|
||||
"Don't report the unfixed CVEs")
|
||||
|
||||
f.StringVar(
|
||||
&p.httpProxy,
|
||||
"http-proxy",
|
||||
@@ -312,6 +321,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
c.Conf.OvalDBURL = p.ovalDBURL
|
||||
c.Conf.CvssScoreOver = p.cvssScoreOver
|
||||
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
|
||||
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
|
||||
c.Conf.HTTPProxy = p.httpProxy
|
||||
|
||||
c.Conf.FormatXML = p.formatXML
|
||||
|
||||
@@ -49,6 +49,10 @@ type TuiCmd struct {
|
||||
ovalDBPath string
|
||||
ovalDBURL string
|
||||
|
||||
cvssScoreOver float64
|
||||
ignoreUnscoredCves bool
|
||||
ignoreUnfixed bool
|
||||
|
||||
pipe bool
|
||||
}
|
||||
|
||||
@@ -62,6 +66,7 @@ func (*TuiCmd) Synopsis() string { return "Run Tui view to analyze vulnerabiliti
|
||||
func (*TuiCmd) Usage() string {
|
||||
return `tui:
|
||||
tui
|
||||
[-refresh-cve]
|
||||
[-config=/path/to/config.toml]
|
||||
[-cvedb-type=sqlite3|mysql|postgres]
|
||||
[-cvedb-path=/path/to/cve.sqlite3]
|
||||
@@ -69,7 +74,9 @@ func (*TuiCmd) Usage() string {
|
||||
[-ovaldb-type=sqlite3|mysql]
|
||||
[-ovaldb-path=/path/to/oval.sqlite3]
|
||||
[-ovaldb-url=http://127.0.0.1:1324 or DB connection string]
|
||||
[-refresh-cve]
|
||||
[-cvss-over=7]
|
||||
[-ignore-unscored-cves]
|
||||
[-ignore-unfixed]
|
||||
[-results-dir=/path/to/results]
|
||||
[-log-dir=/path/to/log]
|
||||
[-debug]
|
||||
@@ -139,6 +146,24 @@ func (p *TuiCmd) SetFlags(f *flag.FlagSet) {
|
||||
"",
|
||||
"http://goval-dictionary.example.com:1324 or mysql connection string")
|
||||
|
||||
f.Float64Var(
|
||||
&p.cvssScoreOver,
|
||||
"cvss-over",
|
||||
0,
|
||||
"-cvss-over=6.5 means reporting CVSS Score 6.5 and over (default: 0 (means report all))")
|
||||
|
||||
f.BoolVar(
|
||||
&p.ignoreUnscoredCves,
|
||||
"ignore-unscored-cves",
|
||||
false,
|
||||
"Don't report the unscored CVEs")
|
||||
|
||||
f.BoolVar(
|
||||
&p.ignoreUnfixed,
|
||||
"ignore-unfixed",
|
||||
false,
|
||||
"Don't report the unfixed CVEs")
|
||||
|
||||
f.BoolVar(
|
||||
&p.pipe,
|
||||
"pipe",
|
||||
@@ -169,6 +194,10 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s
|
||||
c.Conf.OvalDBType = p.ovalDBType
|
||||
c.Conf.OvalDBPath = p.ovalDBPath
|
||||
c.Conf.OvalDBURL = p.ovalDBURL
|
||||
c.Conf.CvssScoreOver = p.cvssScoreOver
|
||||
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
|
||||
c.Conf.IgnoreUnfixed = p.ignoreUnfixed
|
||||
c.Conf.RefreshCve = p.refreshCve
|
||||
|
||||
log.Info("Validating config...")
|
||||
if !c.Conf.ValidateOnTui() {
|
||||
|
||||
@@ -58,6 +58,32 @@ const (
|
||||
|
||||
// Raspbian is
|
||||
Raspbian = "raspbian"
|
||||
|
||||
// Windows is
|
||||
Windows = "windows"
|
||||
|
||||
// OpenSUSE is
|
||||
OpenSUSE = "opensuse"
|
||||
|
||||
// OpenSUSELeap is
|
||||
OpenSUSELeap = "opensuse.leap"
|
||||
|
||||
// SUSEEnterpriseServer is
|
||||
SUSEEnterpriseServer = "suse.linux.enterprise.server"
|
||||
|
||||
// SUSEEnterpriseDesktop is
|
||||
SUSEEnterpriseDesktop = "suse.linux.enterprise.desktop"
|
||||
|
||||
// SUSEOpenstackCloud is
|
||||
SUSEOpenstackCloud = "suse.openstack.cloud"
|
||||
|
||||
// Alpine is
|
||||
Alpine = "alpine"
|
||||
)
|
||||
|
||||
const (
|
||||
// ServerTypePseudo is used for ServerInfo.Type
|
||||
ServerTypePseudo = "pseudo"
|
||||
)
|
||||
|
||||
//Config is struct of Configuration
|
||||
@@ -73,6 +99,7 @@ type Config struct {
|
||||
|
||||
CvssScoreOver float64
|
||||
IgnoreUnscoredCves bool
|
||||
IgnoreUnfixed bool
|
||||
|
||||
SSHNative bool
|
||||
ContainersOnly bool
|
||||
@@ -360,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"`
|
||||
@@ -426,6 +454,9 @@ type ServerInfo struct {
|
||||
// For CentOS, RHEL, Amazon
|
||||
Enablerepo []string
|
||||
|
||||
// "pseudo" or ""
|
||||
Type string
|
||||
|
||||
// used internal
|
||||
LogMsgAnsiColor string // DebugLog Color
|
||||
Container Container
|
||||
@@ -438,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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
|
||||
|
||||
@@ -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 |
@@ -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 |
2
main.go
2
main.go
@@ -29,7 +29,7 @@ import (
|
||||
)
|
||||
|
||||
// Version of Vuls
|
||||
var version = "0.4.0"
|
||||
var version = "0.4.2"
|
||||
|
||||
// Revision of Git
|
||||
var revision string
|
||||
|
||||
@@ -229,6 +229,9 @@ const (
|
||||
// Oracle is Oracle Linux
|
||||
Oracle CveContentType = "oracle"
|
||||
|
||||
// SUSE is SUSE Linux
|
||||
SUSE CveContentType = "suse"
|
||||
|
||||
// Unknown is Unknown
|
||||
Unknown CveContentType = "unknown"
|
||||
)
|
||||
|
||||
@@ -18,4 +18,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package models
|
||||
|
||||
// JSONVersion is JSON Version
|
||||
const JSONVersion = 2
|
||||
const JSONVersion = 3
|
||||
|
||||
@@ -81,7 +81,7 @@ func (ps Packages) FindOne(f func(Package) bool) (string, Package, bool) {
|
||||
return "", Package{}, false
|
||||
}
|
||||
|
||||
// Package has installed packages.
|
||||
// Package has installed binary packages.
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
@@ -116,6 +116,8 @@ func (p Package) FormatVersionFromTo(notFixedYet bool) string {
|
||||
to := p.FormatNewVer()
|
||||
if notFixedYet {
|
||||
to = "Not Fixed Yet"
|
||||
} else if p.NewVersion == "" {
|
||||
to = "Unknown"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s -> %s", p.Name, p.FormatVer(), to)
|
||||
}
|
||||
@@ -151,3 +153,31 @@ type Changelog struct {
|
||||
Contents string
|
||||
Method DetectionMethod
|
||||
}
|
||||
|
||||
// SrcPackage has installed source package information.
|
||||
// Debian based Linux has both of package and source information in dpkg.
|
||||
// OVAL database often includes a source version (Not a binary version),
|
||||
// so it is also needed to capture source version for OVAL version comparison.
|
||||
// https://github.com/future-architect/vuls/issues/504
|
||||
type SrcPackage struct {
|
||||
Name string
|
||||
Version string
|
||||
BinaryNames []string
|
||||
}
|
||||
|
||||
// AddBinaryName add the name if not exists
|
||||
func (s *SrcPackage) AddBinaryName(name string) {
|
||||
found := false
|
||||
for _, n := range s.BinaryNames {
|
||||
if n == name {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
s.BinaryNames = append(s.BinaryNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
// SrcPackages is Map of SrcPackage
|
||||
// { "package-name": SrcPackage }
|
||||
type SrcPackages map[string]SrcPackage
|
||||
|
||||
@@ -87,3 +87,49 @@ func TestMerge(t *testing.T) {
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddBinaryName(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in SrcPackage
|
||||
name string
|
||||
expected SrcPackage
|
||||
}{
|
||||
{
|
||||
SrcPackage{Name: "hoge"},
|
||||
"curl",
|
||||
SrcPackage{
|
||||
Name: "hoge",
|
||||
BinaryNames: []string{"curl"},
|
||||
},
|
||||
},
|
||||
{
|
||||
SrcPackage{
|
||||
Name: "hoge",
|
||||
BinaryNames: []string{"curl"},
|
||||
},
|
||||
"curl",
|
||||
SrcPackage{
|
||||
Name: "hoge",
|
||||
BinaryNames: []string{"curl"},
|
||||
},
|
||||
},
|
||||
{
|
||||
SrcPackage{
|
||||
Name: "hoge",
|
||||
BinaryNames: []string{"curl"},
|
||||
},
|
||||
"openssh",
|
||||
SrcPackage{
|
||||
Name: "hoge",
|
||||
BinaryNames: []string{"curl", "openssh"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt.in.AddBinaryName(tt.name)
|
||||
if !reflect.DeepEqual(tt.in, tt.expected) {
|
||||
t.Errorf("expected %#v, actual %#v", tt.in, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,10 @@ type ScanResult struct {
|
||||
|
||||
RunningKernel Kernel
|
||||
Packages Packages
|
||||
Errors []string
|
||||
Optional [][]interface{}
|
||||
SrcPackages SrcPackages
|
||||
|
||||
Errors []string
|
||||
Optional [][]interface{}
|
||||
|
||||
Config struct {
|
||||
Scan config.Config
|
||||
@@ -76,10 +78,8 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
copiedScanResult := r
|
||||
copiedScanResult.ScannedCves = filtered
|
||||
return copiedScanResult
|
||||
r.ScannedCves = filtered
|
||||
return r
|
||||
}
|
||||
|
||||
// FilterIgnoreCves is filter function.
|
||||
@@ -92,9 +92,24 @@ func (r ScanResult) FilterIgnoreCves(cveIDs []string) ScanResult {
|
||||
}
|
||||
return true
|
||||
})
|
||||
copiedScanResult := r
|
||||
copiedScanResult.ScannedCves = filtered
|
||||
return copiedScanResult
|
||||
r.ScannedCves = filtered
|
||||
return r
|
||||
}
|
||||
|
||||
// FilterUnfixed is filter function.
|
||||
func (r ScanResult) FilterUnfixed() ScanResult {
|
||||
if !config.Conf.IgnoreUnfixed {
|
||||
return r
|
||||
}
|
||||
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
|
||||
NotFixedAll := true
|
||||
for _, p := range v.AffectedPackages {
|
||||
NotFixedAll = NotFixedAll && p.NotFixedYet
|
||||
}
|
||||
return !NotFixedAll
|
||||
})
|
||||
r.ScannedCves = filtered
|
||||
return r
|
||||
}
|
||||
|
||||
// ReportFileName returns the filename on localhost without extention
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
@@ -255,3 +256,83 @@ func TestFilterIgnoreCveIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterUnfixed(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in ScanResult
|
||||
out ScanResult
|
||||
}{
|
||||
{
|
||||
in: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0001": {
|
||||
CveID: "CVE-2017-0001",
|
||||
AffectedPackages: PackageStatuses{
|
||||
{
|
||||
Name: "a",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
AffectedPackages: PackageStatuses{
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
AffectedPackages: PackageStatuses{
|
||||
{
|
||||
Name: "c",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "d",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
out: ScanResult{
|
||||
ScannedCves: VulnInfos{
|
||||
"CVE-2017-0002": {
|
||||
CveID: "CVE-2017-0002",
|
||||
AffectedPackages: PackageStatuses{
|
||||
{
|
||||
Name: "b",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"CVE-2017-0003": {
|
||||
CveID: "CVE-2017-0003",
|
||||
AffectedPackages: PackageStatuses{
|
||||
{
|
||||
Name: "c",
|
||||
NotFixedYet: true,
|
||||
},
|
||||
{
|
||||
Name: "d",
|
||||
NotFixedYet: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
config.Conf.IgnoreUnfixed = true
|
||||
actual := tt.in.FilterUnfixed()
|
||||
if !reflect.DeepEqual(tt.out.ScannedCves, actual.ScannedCves) {
|
||||
o := pp.Sprintf("%v", tt.out.ScannedCves)
|
||||
a := pp.Sprintf("%v", actual.ScannedCves)
|
||||
t.Errorf("[%d] expected: %v\n actual: %v\n", i, o, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +546,8 @@ func (v VulnInfo) VendorLinks(family string) map[string]string {
|
||||
return links
|
||||
case config.Debian:
|
||||
links["Debian-CVE"] = "https://security-tracker.debian.org/tracker/" + v.CveID
|
||||
case config.SUSEEnterpriseServer:
|
||||
links["SUSE-CVE"] = "https://www.suse.com/security/cve/" + v.CveID
|
||||
case config.FreeBSD:
|
||||
for _, advisory := range v.DistroAdvisories {
|
||||
links["FreeBSD-VuXML"] = fmt.Sprintf("https://vuxml.freebsd.org/freebsd/%s.html", advisory.AdvisoryID)
|
||||
|
||||
82
oval/alpine.go
Normal file
82
oval/alpine.go
Normal 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,
|
||||
}
|
||||
}
|
||||
@@ -60,9 +60,11 @@ func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = true
|
||||
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
|
||||
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
|
||||
}
|
||||
@@ -107,11 +109,17 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
|
||||
|
||||
//Debian's uname gives both of kernel release(uname -r), version(kernel-image version)
|
||||
linuxImage := "linux-image-" + r.RunningKernel.Release
|
||||
|
||||
// Add linux and set the version of running kernel to search OVAL.
|
||||
newVer := ""
|
||||
if p, ok := r.Packages[linuxImage]; ok {
|
||||
newVer = p.NewVersion
|
||||
}
|
||||
if r.Container.ContainerID == "" {
|
||||
r.Packages["linux"] = models.Package{
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
Name: "linux",
|
||||
Version: r.RunningKernel.Version,
|
||||
NewVersion: newVer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +129,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -129,10 +137,10 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
|
||||
delete(r.Packages, "linux")
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
// Remove linux added above to search for oval
|
||||
// Remove "linux" added above for oval search
|
||||
// linux is not a real package name (key of affected packages in OVAL)
|
||||
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = true
|
||||
if notFixedYet, ok := defPacks.actuallyAffectedPackNames["linux"]; ok {
|
||||
defPacks.actuallyAffectedPackNames[linuxImage] = notFixedYet
|
||||
delete(defPacks.actuallyAffectedPackNames, "linux")
|
||||
for i, p := range defPacks.def.AffectedPacks {
|
||||
if p.Name == "linux" {
|
||||
@@ -141,6 +149,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) (err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
@@ -230,7 +239,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -240,7 +249,6 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
|
||||
// Remove "linux" added above to search for oval
|
||||
// "linux" is not a real package name (key of affected packages in OVAL)
|
||||
if _, ok := defPacks.actuallyAffectedPackNames["linux"]; !found && ok {
|
||||
@@ -253,6 +261,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) (err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,10 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -55,7 +58,8 @@ func TestPackNamesOfUpdateDebian(t *testing.T) {
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB"},
|
||||
{Name: "packB", NotFixedYet: true},
|
||||
{Name: "packC"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(
|
||||
o.family, r.Release, r.Packages); err != nil {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -68,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 {
|
||||
@@ -98,9 +126,10 @@ func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = true
|
||||
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family, r.Packages)
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[cve.CveID] = vinfo
|
||||
}
|
||||
@@ -156,7 +185,7 @@ func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:len(ss)], "/")
|
||||
return score, strings.Join(ss[1:], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
@@ -170,7 +199,7 @@ func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string
|
||||
if score, err = strconv.ParseFloat(ss[0], 64); err != nil {
|
||||
return 0, ""
|
||||
}
|
||||
return score, strings.Join(ss[1:len(ss)], "/")
|
||||
return score, strings.Join(ss[1:], "/")
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
@@ -102,7 +102,10 @@ func TestPackNamesOfUpdate(t *testing.T) {
|
||||
in: models.ScanResult{
|
||||
ScannedCves: models.VulnInfos{
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{{Name: "packA"}},
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB", NotFixedYet: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -125,7 +128,7 @@ func TestPackNamesOfUpdate(t *testing.T) {
|
||||
"CVE-2000-1000": models.VulnInfo{
|
||||
AffectedPackages: models.PackageStatuses{
|
||||
{Name: "packA"},
|
||||
{Name: "packB"},
|
||||
{Name: "packB", NotFixedYet: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
120
oval/suse.go
Normal file
120
oval/suse.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package oval
|
||||
|
||||
import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// SUSE is the struct of SUSE Linux
|
||||
type SUSE struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// NewSUSE creates OVAL client for SUSE
|
||||
func NewSUSE() SUSE {
|
||||
// TODO implement other family
|
||||
return SUSE{
|
||||
Base{
|
||||
family: config.SUSEEnterpriseServer,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FillWithOval returns scan result after updating CVE info by OVAL
|
||||
func (o SUSE) FillWithOval(r *models.ScanResult) (err error) {
|
||||
var relatedDefs ovalResult
|
||||
if o.isFetchViaHTTP() {
|
||||
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if relatedDefs, err = getDefsByPackNameFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, defPacks := range relatedDefs.entries {
|
||||
o.update(r, defPacks)
|
||||
}
|
||||
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.SUSE]; ok {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
vuln.CveContents[models.SUSE] = cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o SUSE) update(r *models.ScanResult, defPacks defPacks) {
|
||||
ovalContent := *o.convertToModel(&defPacks.def)
|
||||
ovalContent.Type = models.NewCveContentType(o.family)
|
||||
vinfo, ok := r.ScannedCves[defPacks.def.Title]
|
||||
if !ok {
|
||||
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Title)
|
||||
vinfo = models.VulnInfo{
|
||||
CveID: defPacks.def.Title,
|
||||
Confidence: models.OvalMatch,
|
||||
CveContents: models.NewCveContents(ovalContent),
|
||||
}
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
util.Log.Debugf("%s OVAL will be overwritten", defPacks.def.Title)
|
||||
} else {
|
||||
util.Log.Debugf("%s is also detected by OVAL", defPacks.def.Title)
|
||||
cveContents = models.CveContents{}
|
||||
}
|
||||
if vinfo.Confidence.Score < models.OvalMatch.Score {
|
||||
vinfo.Confidence = models.OvalMatch
|
||||
}
|
||||
cveContents[ctype] = ovalContent
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
|
||||
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
|
||||
for _, pack := range vinfo.AffectedPackages {
|
||||
notFixedYet, _ := defPacks.actuallyAffectedPackNames[pack.Name]
|
||||
defPacks.actuallyAffectedPackNames[pack.Name] = notFixedYet
|
||||
}
|
||||
vinfo.AffectedPackages = defPacks.toPackStatuses(r.Family)
|
||||
vinfo.AffectedPackages.Sort()
|
||||
r.ScannedCves[defPacks.def.Title] = vinfo
|
||||
}
|
||||
|
||||
func (o SUSE) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
var refs []models.Reference
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
Link: r.RefURL,
|
||||
Source: r.Source,
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
CveID: def.Title,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
References: refs,
|
||||
}
|
||||
}
|
||||
305
oval/util.go
305
oval/util.go
@@ -21,6 +21,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
@@ -40,114 +42,61 @@ type ovalResult struct {
|
||||
}
|
||||
|
||||
type defPacks struct {
|
||||
def ovalmodels.Definition
|
||||
def ovalmodels.Definition
|
||||
|
||||
// BinaryPackageName : NotFixedYet
|
||||
actuallyAffectedPackNames map[string]bool
|
||||
}
|
||||
|
||||
func (e defPacks) toPackStatuses(family string, packs models.Packages) (ps models.PackageStatuses) {
|
||||
switch family {
|
||||
case config.Ubuntu:
|
||||
packNotFixedYet := map[string]bool{}
|
||||
for _, p := range e.def.AffectedPacks {
|
||||
packNotFixedYet[p.Name] = p.NotFixedYet
|
||||
}
|
||||
for k := range e.actuallyAffectedPackNames {
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: k,
|
||||
NotFixedYet: packNotFixedYet[k],
|
||||
})
|
||||
}
|
||||
|
||||
case config.CentOS, config.Debian:
|
||||
// There are many packages that has been fixed in RedHat, but not been fixed in CentOS
|
||||
for name := range e.actuallyAffectedPackNames {
|
||||
pack, ok := packs[name]
|
||||
if !ok {
|
||||
util.Log.Warnf("Faild to find in Package list: %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
ovalPackVer := ""
|
||||
for _, p := range e.def.AffectedPacks {
|
||||
if p.Name == name {
|
||||
ovalPackVer = p.Version
|
||||
break
|
||||
}
|
||||
}
|
||||
if ovalPackVer == "" {
|
||||
util.Log.Warnf("Faild to find in Oval Package list: %s", name)
|
||||
return
|
||||
}
|
||||
|
||||
if pack.NewVersion == "" {
|
||||
// compare version: installed vs oval
|
||||
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
|
||||
verb := rpmver.NewVersion(ovalPackVer)
|
||||
notFixedYet := false
|
||||
if vera.LessThan(verb) {
|
||||
notFixedYet = true
|
||||
}
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
} else {
|
||||
// compare version: newVer vs oval
|
||||
packNewVer := fmt.Sprintf("%s-%s", pack.NewVersion, pack.NewRelease)
|
||||
vera := rpmver.NewVersion(packNewVer)
|
||||
verb := rpmver.NewVersion(ovalPackVer)
|
||||
notFixedYet := false
|
||||
if vera.LessThan(verb) {
|
||||
notFixedYet = true
|
||||
}
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
for k := range e.actuallyAffectedPackNames {
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: k,
|
||||
})
|
||||
}
|
||||
func (e defPacks) toPackStatuses(family string) (ps models.PackageStatuses) {
|
||||
for name, notFixedYet := range e.actuallyAffectedPackNames {
|
||||
ps = append(ps, models.PackageStatus{
|
||||
Name: name,
|
||||
NotFixedYet: notFixedYet,
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
|
||||
for i, entry := range e.entries {
|
||||
if entry.def.DefinitionID == def.DefinitionID {
|
||||
e.entries[i].actuallyAffectedPackNames[packName] = true
|
||||
return true
|
||||
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string, notFixedYet bool) (upserted bool) {
|
||||
// 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{
|
||||
def: def,
|
||||
actuallyAffectedPackNames: map[string]bool{packName: true},
|
||||
actuallyAffectedPackNames: map[string]bool{packName: notFixedYet},
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type request struct {
|
||||
pack models.Package
|
||||
packName string
|
||||
versionRelease string
|
||||
NewVersionRelease string
|
||||
binaryPackNames []string
|
||||
isSrcPack bool
|
||||
}
|
||||
|
||||
type response struct {
|
||||
pack *models.Package
|
||||
defs []ovalmodels.Definition
|
||||
request request
|
||||
defs []ovalmodels.Definition
|
||||
}
|
||||
|
||||
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
|
||||
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
relatedDefs ovalResult, err error) {
|
||||
|
||||
reqChan := make(chan request, len(r.Packages))
|
||||
resChan := make(chan response, len(r.Packages))
|
||||
errChan := make(chan error, len(r.Packages))
|
||||
nReq := len(r.Packages) + len(r.SrcPackages)
|
||||
reqChan := make(chan request, nReq)
|
||||
resChan := make(chan response, nReq)
|
||||
errChan := make(chan error, nReq)
|
||||
defer close(reqChan)
|
||||
defer close(resChan)
|
||||
defer close(errChan)
|
||||
@@ -155,14 +104,26 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
go func() {
|
||||
for _, pack := range r.Packages {
|
||||
reqChan <- request{
|
||||
pack: pack,
|
||||
packName: pack.Name,
|
||||
versionRelease: pack.FormatVer(),
|
||||
NewVersionRelease: pack.FormatVer(),
|
||||
isSrcPack: false,
|
||||
}
|
||||
for _, pack := range r.SrcPackages {
|
||||
reqChan <- request{
|
||||
packName: pack.Name,
|
||||
binaryPackNames: pack.BinaryNames,
|
||||
versionRelease: pack.Version,
|
||||
isSrcPack: true,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
concurrency := 10
|
||||
tasks := util.GenWorkers(concurrency)
|
||||
for range r.Packages {
|
||||
for i := 0; i < nReq; i++ {
|
||||
tasks <- func() {
|
||||
select {
|
||||
case req := <-reqChan:
|
||||
@@ -171,13 +132,13 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
"packs",
|
||||
r.Family,
|
||||
r.Release,
|
||||
req.pack.Name,
|
||||
req.packName,
|
||||
)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
util.Log.Debugf("HTTP Request to %s", url)
|
||||
httpGet(url, &req.pack, resChan, errChan)
|
||||
httpGet(url, req, resChan, errChan)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,26 +146,21 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
|
||||
timeout := time.After(2 * 60 * time.Second)
|
||||
var errs []error
|
||||
for range r.Packages {
|
||||
for i := 0; i < nReq; i++ {
|
||||
select {
|
||||
case res := <-resChan:
|
||||
for _, def := range res.defs {
|
||||
for _, p := range def.AffectedPacks {
|
||||
if res.pack.Name != p.Name {
|
||||
continue
|
||||
}
|
||||
affected, notFixedYet := isOvalDefAffected(def, res.request, r.Family, r.RunningKernel)
|
||||
if !affected {
|
||||
continue
|
||||
}
|
||||
|
||||
if p.NotFixedYet {
|
||||
relatedDefs.upsert(def, p.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if less, err := lessThan(r.Family, *res.pack, p); err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s", err)
|
||||
util.Log.Debugf("%#v\n%#v", *res.pack, p)
|
||||
} else if less {
|
||||
relatedDefs.upsert(def, p.Name)
|
||||
if res.request.isSrcPack {
|
||||
for _, n := range res.request.binaryPackNames {
|
||||
relatedDefs.upsert(def, n, false)
|
||||
}
|
||||
} else {
|
||||
relatedDefs.upsert(def, res.request.packName, notFixedYet)
|
||||
}
|
||||
}
|
||||
case err := <-errChan:
|
||||
@@ -219,7 +175,7 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
|
||||
return
|
||||
}
|
||||
|
||||
func httpGet(url string, pack *models.Package, resChan chan<- response, errChan chan<- error) {
|
||||
func httpGet(url string, req request, resChan chan<- response, errChan chan<- error) {
|
||||
var body string
|
||||
var errs []error
|
||||
var resp *http.Response
|
||||
@@ -257,14 +213,12 @@ func httpGet(url string, pack *models.Package, resChan chan<- response, errChan
|
||||
return
|
||||
}
|
||||
resChan <- response{
|
||||
pack: pack,
|
||||
defs: defs,
|
||||
request: req,
|
||||
defs: defs,
|
||||
}
|
||||
}
|
||||
|
||||
func getDefsByPackNameFromOvalDB(family, osRelease string,
|
||||
installedPacks models.Packages) (relatedDefs ovalResult, err error) {
|
||||
|
||||
func getDefsByPackNameFromOvalDB(r *models.ScanResult) (relatedDefs ovalResult, err error) {
|
||||
ovallog.Initialize(config.Conf.LogDir)
|
||||
path := config.Conf.OvalDBURL
|
||||
if config.Conf.OvalDBType == "sqlite3" {
|
||||
@@ -273,48 +227,119 @@ func getDefsByPackNameFromOvalDB(family, osRelease string,
|
||||
util.Log.Debugf("Open oval-dictionary db (%s): %s", config.Conf.OvalDBType, path)
|
||||
|
||||
var ovaldb db.DB
|
||||
if ovaldb, err = db.NewDB(
|
||||
family,
|
||||
config.Conf.OvalDBType,
|
||||
path,
|
||||
config.Conf.DebugSQL,
|
||||
); err != nil {
|
||||
if ovaldb, err = db.NewDB(r.Family, config.Conf.OvalDBType,
|
||||
path, config.Conf.DebugSQL); err != nil {
|
||||
return
|
||||
}
|
||||
defer ovaldb.CloseDB()
|
||||
for _, installedPack := range installedPacks {
|
||||
definitions, err := ovaldb.GetByPackName(osRelease, installedPack.Name)
|
||||
|
||||
requests := []request{}
|
||||
for _, pack := range r.Packages {
|
||||
requests = append(requests, request{
|
||||
packName: pack.Name,
|
||||
versionRelease: pack.FormatVer(),
|
||||
NewVersionRelease: pack.FormatNewVer(),
|
||||
isSrcPack: false,
|
||||
})
|
||||
}
|
||||
for _, pack := range r.SrcPackages {
|
||||
requests = append(requests, request{
|
||||
packName: pack.Name,
|
||||
binaryPackNames: pack.BinaryNames,
|
||||
versionRelease: pack.Version,
|
||||
isSrcPack: true,
|
||||
})
|
||||
}
|
||||
|
||||
for _, req := range requests {
|
||||
definitions, err := ovaldb.GetByPackName(r.Release, req.packName)
|
||||
if err != nil {
|
||||
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
|
||||
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", r.Family, err)
|
||||
}
|
||||
for _, def := range definitions {
|
||||
for _, ovalPack := range def.AffectedPacks {
|
||||
if installedPack.Name != ovalPack.Name {
|
||||
continue
|
||||
}
|
||||
affected, notFixedYet := isOvalDefAffected(def, req, r.Family, r.RunningKernel)
|
||||
if !affected {
|
||||
continue
|
||||
}
|
||||
|
||||
if ovalPack.NotFixedYet {
|
||||
relatedDefs.upsert(def, installedPack.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
less, err := lessThan(family, installedPack, ovalPack)
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s", err)
|
||||
util.Log.Debugf("%#v\n%#v", installedPack, ovalPack)
|
||||
} else if less {
|
||||
relatedDefs.upsert(def, installedPack.Name)
|
||||
if req.isSrcPack {
|
||||
for _, n := range req.binaryPackNames {
|
||||
relatedDefs.upsert(def, n, false)
|
||||
}
|
||||
} else {
|
||||
relatedDefs.upsert(def, req.packName, notFixedYet)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bool, error) {
|
||||
func 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
|
||||
}
|
||||
|
||||
less, err := lessThan(family, req.versionRelease, ovalPack)
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s, Ver: %#v, OVAL: %#v, DefID: %s",
|
||||
err, req.versionRelease, ovalPack, def.DefinitionID)
|
||||
return false, false
|
||||
}
|
||||
|
||||
if less {
|
||||
if req.isSrcPack {
|
||||
// Unable to judge whether fixed or not fixed of src package(Ubuntu, Debian)
|
||||
return true, false
|
||||
}
|
||||
if req.NewVersionRelease == "" {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// compare version: newVer vs oval
|
||||
less, err := lessThan(family, req.NewVersionRelease, ovalPack)
|
||||
if err != nil {
|
||||
util.Log.Debugf("Failed to parse versions: %s, NewVer: %#v, OVAL: %#v, DefID: %s",
|
||||
err, req.NewVersionRelease, ovalPack, def.DefinitionID)
|
||||
return false, false
|
||||
}
|
||||
return true, less
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func lessThan(family, versionRelease string, packB ovalmodels.Package) (bool, error) {
|
||||
switch family {
|
||||
case config.Debian, config.Ubuntu:
|
||||
vera, err := debver.NewVersion(packA.Version)
|
||||
vera, err := debver.NewVersion(versionRelease)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -323,10 +348,18 @@ func lessThan(family string, packA models.Package, packB ovalmodels.Package) (bo
|
||||
return false, err
|
||||
}
|
||||
return vera.LessThan(verb), nil
|
||||
case config.RedHat, config.CentOS, config.Oracle:
|
||||
vera := rpmver.NewVersion(fmt.Sprintf("%s-%s", packA.Version, packA.Release))
|
||||
case config.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)
|
||||
}
|
||||
return false, fmt.Errorf("Package version comparison not supported: %s", family)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
@@ -81,6 +80,10 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro
|
||||
for _, r := range filled {
|
||||
r = r.FilterByCvssOver(c.Conf.CvssScoreOver)
|
||||
r = r.FilterIgnoreCves(c.Conf.Servers[r.ServerName].IgnoreCves)
|
||||
r = r.FilterUnfixed()
|
||||
if c.Conf.IgnoreUnscoredCves {
|
||||
r.ScannedCves = r.ScannedCves.FindScoredVulns()
|
||||
}
|
||||
filtered = append(filtered, r)
|
||||
}
|
||||
return filtered, nil
|
||||
@@ -173,19 +176,29 @@ func FillWithOval(r *models.ScanResult) (err error) {
|
||||
case c.Oracle:
|
||||
ovalClient = oval.NewOracle()
|
||||
ovalFamily = c.Oracle
|
||||
case c.Amazon, c.Raspbian, c.FreeBSD:
|
||||
case c.SUSEEnterpriseServer:
|
||||
// TODO other suse family
|
||||
ovalClient = oval.NewSUSE()
|
||||
ovalFamily = c.SUSEEnterpriseServer
|
||||
case c.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
|
||||
}
|
||||
|
||||
|
||||
107
report/slack.go
107
report/slack.go
@@ -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,14 +185,8 @@ func msgText(r models.ScanResult) string {
|
||||
r.ScannedCves.FormatCveSummary())
|
||||
}
|
||||
|
||||
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
|
||||
var vinfos []models.VulnInfo
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
vinfos = r.ScannedCves.FindScoredVulns().ToSortedSlice()
|
||||
} else {
|
||||
vinfos = r.ScannedCves.ToSortedSlice()
|
||||
}
|
||||
|
||||
func toSlackAttachments(r models.ScanResult) (attaches []slack.Attachment) {
|
||||
vinfos := r.ScannedCves.ToSortedSlice()
|
||||
for _, vinfo := range vinfos {
|
||||
curent := []string{}
|
||||
for _, affected := range vinfo.AffectedPackages {
|
||||
@@ -202,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",
|
||||
@@ -222,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
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ var currentChangelogLimitY int
|
||||
// RunTui execute main logic
|
||||
func RunTui(results models.ScanResults) subcommands.ExitStatus {
|
||||
scanResults = results
|
||||
sort.Slice(scanResults, func(i, j int) bool {
|
||||
if scanResults[i].ServerName == scanResults[j].ServerName {
|
||||
return scanResults[i].Container.Name < scanResults[j].Container.Name
|
||||
}
|
||||
return scanResults[i].ServerName < scanResults[j].ServerName
|
||||
})
|
||||
|
||||
// g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
g := gocui.NewGui()
|
||||
|
||||
@@ -93,12 +93,7 @@ func formatShortPlainText(r models.ScanResult) string {
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
vulns := r.ScannedCves
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
vulns = vulns.FindScoredVulns()
|
||||
}
|
||||
|
||||
if len(vulns) == 0 {
|
||||
if len(r.ScannedCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
@@ -109,7 +104,7 @@ No CVE-IDs are found in updatable packages.
|
||||
stable := uitable.New()
|
||||
stable.MaxColWidth = maxColWidth
|
||||
stable.Wrap = true
|
||||
for _, vuln := range vulns.ToSortedSlice() {
|
||||
for _, vuln := range r.ScannedCves.ToSortedSlice() {
|
||||
summaries := vuln.Summaries(config.Conf.Lang, r.Family)
|
||||
links := vuln.CveContents.SourceLinks(
|
||||
config.Conf.Lang, r.Family, vuln.CveID)
|
||||
@@ -167,12 +162,7 @@ func formatFullPlainText(r models.ScanResult) string {
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
vulns := r.ScannedCves
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
vulns = vulns.FindScoredVulns()
|
||||
}
|
||||
|
||||
if len(vulns) == 0 {
|
||||
if len(r.ScannedCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
@@ -183,7 +173,7 @@ No CVE-IDs are found in updatable packages.
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
for _, vuln := range vulns.ToSortedSlice() {
|
||||
for _, vuln := range r.ScannedCves.ToSortedSlice() {
|
||||
table.AddRow(vuln.CveID)
|
||||
table.AddRow("----------------")
|
||||
table.AddRow("Max Score", vuln.FormatMaxCvssScore())
|
||||
@@ -298,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
171
scan/alpine.go
Normal 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
75
scan/alpine_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,6 +313,7 @@ func (l *base) convertToModel() models.ScanResult {
|
||||
ScannedCves: l.VulnInfos,
|
||||
RunningKernel: l.Kernel,
|
||||
Packages: l.Packages,
|
||||
SrcPackages: l.SrcPackages,
|
||||
Optional: l.ServerInfo.Optional,
|
||||
Errors: errs,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -176,7 +175,6 @@ func (o *debian) checkDependencies() error {
|
||||
}
|
||||
|
||||
for _, name := range packNames {
|
||||
//TODO --show-format
|
||||
cmd := "dpkg-query -W " + name
|
||||
if r := o.exec(cmd, noSudo); !r.isSuccess() {
|
||||
msg := fmt.Sprintf("%s is not installed", name)
|
||||
@@ -206,12 +204,13 @@ func (o *debian) scanPackages() error {
|
||||
RebootRequired: rebootRequired,
|
||||
}
|
||||
|
||||
installed, updatable, err := o.scanInstalledPackages()
|
||||
installed, updatable, srcPacks, err := o.scanInstalledPackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan installed packages: %s", err)
|
||||
return err
|
||||
}
|
||||
o.Packages = installed
|
||||
o.SrcPackages = srcPacks
|
||||
|
||||
if config.Conf.Deep || o.Distro.Family == config.Raspbian {
|
||||
unsecures, err := o.scanUnsecurePackages(updatable)
|
||||
@@ -238,34 +237,72 @@ func (o *debian) rebootRequired() (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, error) {
|
||||
installed, updatable := models.Packages{}, models.Packages{}
|
||||
r := o.exec("dpkg-query -W", noSudo)
|
||||
func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, models.SrcPackages, error) {
|
||||
installed, updatable, srcPacks := models.Packages{}, models.Packages{}, models.SrcPackages{}
|
||||
r := o.exec(`dpkg-query -W -f="\${binary:Package},\${db:Status-Abbrev},\${Version},\${Source},\${source:Version}\n"`, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
return nil, nil, nil, fmt.Errorf("Failed to SSH: %s", r)
|
||||
}
|
||||
|
||||
// e.g.
|
||||
// curl 7.19.7-40.el6_6.4
|
||||
// openldap 2.4.39-8.el6
|
||||
// e.g.
|
||||
// curl,ii ,7.38.0-4+deb8u2,,7.38.0-4+deb8u2
|
||||
// openssh-server,ii ,1:6.7p1-5+deb8u3,openssh,1:6.7p1-5+deb8u3
|
||||
// tar,ii ,1.27.1-2+b1,tar (1.27.1-2),1.27.1-2
|
||||
lines := strings.Split(r.Stdout, "\n")
|
||||
for _, line := range lines {
|
||||
if trimmed := strings.TrimSpace(line); len(trimmed) != 0 {
|
||||
name, version, err := o.parseScannedPackagesLine(trimmed)
|
||||
name, status, version, srcName, srcVersion, err := o.parseScannedPackagesLine(trimmed)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf(
|
||||
return nil, nil, nil, fmt.Errorf(
|
||||
"Debian: Failed to parse package line: %s", line)
|
||||
}
|
||||
|
||||
packageStatus := status[1]
|
||||
// Package status:
|
||||
// n = Not-installed
|
||||
// c = Config-files
|
||||
// H = Half-installed
|
||||
// U = Unpacked
|
||||
// F = Half-configured
|
||||
// W = Triggers-awaiting
|
||||
// t = Triggers-pending
|
||||
// i = Installed
|
||||
if packageStatus != 'i' {
|
||||
o.log.Debugf("%s package status is '%c', ignoring", name, packageStatus)
|
||||
continue
|
||||
}
|
||||
installed[name] = models.Package{
|
||||
Name: name,
|
||||
Version: version,
|
||||
}
|
||||
|
||||
if srcName != "" && srcName != name {
|
||||
if pack, ok := srcPacks[srcName]; ok {
|
||||
pack.AddBinaryName(name)
|
||||
srcPacks[srcName] = pack
|
||||
} else {
|
||||
srcPacks[srcName] = models.SrcPackage{
|
||||
Name: srcName,
|
||||
Version: srcVersion,
|
||||
BinaryNames: []string{name},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove "linux"
|
||||
// kernel-related packages are showed "linux" as source package name
|
||||
// If "linux" is left, oval detection will cause trouble, so delete.
|
||||
delete(srcPacks, "linux")
|
||||
// Remove duplicate
|
||||
for name := range installed {
|
||||
delete(srcPacks, name)
|
||||
}
|
||||
|
||||
updatableNames, err := o.getUpdatablePackNames()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
for _, name := range updatableNames {
|
||||
for _, pack := range installed {
|
||||
@@ -279,28 +316,30 @@ func (o *debian) scanInstalledPackages() (models.Packages, models.Packages, erro
|
||||
// Fill the candidate versions of upgradable packages
|
||||
err = o.fillCandidateVersion(updatable)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
|
||||
return nil, nil, nil, fmt.Errorf("Failed to fill candidate versions. err: %s", err)
|
||||
}
|
||||
installed.MergeNewVersion(updatable)
|
||||
|
||||
return installed, updatable, nil
|
||||
return installed, updatable, srcPacks, nil
|
||||
}
|
||||
|
||||
var packageLinePattern = regexp.MustCompile(`^([^\t']+)\t(.+)$`)
|
||||
|
||||
func (o *debian) parseScannedPackagesLine(line string) (name, version string, err error) {
|
||||
result := packageLinePattern.FindStringSubmatch(line)
|
||||
if len(result) == 3 {
|
||||
func (o *debian) parseScannedPackagesLine(line string) (name, status, version, srcName, srcVersion string, err error) {
|
||||
ss := strings.Split(line, ",")
|
||||
if len(ss) == 5 {
|
||||
// remove :amd64, i386...
|
||||
name = result[1]
|
||||
name = ss[0]
|
||||
if i := strings.IndexRune(name, ':'); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
version = result[2]
|
||||
status = ss[1]
|
||||
version = ss[2]
|
||||
// remove version. ex: tar (1.27.1-2)
|
||||
srcName = strings.Split(ss[3], " ")[0]
|
||||
srcVersion = ss[4]
|
||||
return
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("Unknown format: %s", line)
|
||||
return "", "", "", "", "", fmt.Errorf("Unknown format: %s", line)
|
||||
}
|
||||
|
||||
func (o *debian) aptGetUpdate() error {
|
||||
|
||||
@@ -29,35 +29,6 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func TestParseScannedPackagesLineDebian(t *testing.T) {
|
||||
|
||||
var packagetests = []struct {
|
||||
in string
|
||||
name string
|
||||
version string
|
||||
}{
|
||||
{"base-passwd 3.5.33", "base-passwd", "3.5.33"},
|
||||
{"bzip2 1.0.6-5", "bzip2", "1.0.6-5"},
|
||||
{"adduser 3.113+nmu3ubuntu3", "adduser", "3.113+nmu3ubuntu3"},
|
||||
{"bash 4.3-7ubuntu1.5", "bash", "4.3-7ubuntu1.5"},
|
||||
{"bsdutils 1:2.20.1-5.1ubuntu20.4", "bsdutils", "1:2.20.1-5.1ubuntu20.4"},
|
||||
{"ca-certificates 20141019ubuntu0.14.04.1", "ca-certificates", "20141019ubuntu0.14.04.1"},
|
||||
{"apt 1.0.1ubuntu2.8", "apt", "1.0.1ubuntu2.8"},
|
||||
}
|
||||
|
||||
d := newDebian(config.ServerInfo{})
|
||||
for _, tt := range packagetests {
|
||||
n, v, _ := d.parseScannedPackagesLine(tt.in)
|
||||
if n != tt.name {
|
||||
t.Errorf("name: expected %s, actual %s", tt.name, n)
|
||||
}
|
||||
if v != tt.version {
|
||||
t.Errorf("version: expected %s, actual %s", tt.version, v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetCveIDsFromChangelog(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
66
scan/pseudo.go
Normal 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
|
||||
}
|
||||
@@ -153,6 +153,10 @@ func (o *redhat) checkIfSudoNoPasswd() error {
|
||||
{"yum --color=never --security updateinfo updates", zero},
|
||||
}
|
||||
}
|
||||
|
||||
if o.Distro.Family == config.RedHat {
|
||||
cmds = append(cmds, cmd{"repoquery -h", zero})
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range cmds {
|
||||
@@ -256,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
|
||||
}
|
||||
@@ -276,14 +282,7 @@ func (o *redhat) scanInstalledPackages() (models.Packages, error) {
|
||||
}
|
||||
|
||||
installed := models.Packages{}
|
||||
var cmd string
|
||||
majorVersion, _ := o.Distro.MajorVersion()
|
||||
if majorVersion < 6 {
|
||||
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
} else {
|
||||
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
}
|
||||
r := o.exec(cmd, noSudo)
|
||||
r := o.exec(rpmQa(o.Distro), noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Scan packages failed: %s", r)
|
||||
}
|
||||
@@ -300,14 +299,13 @@ func (o *redhat) scanInstalledPackages() (models.Packages, error) {
|
||||
// Kernel package may be isntalled multiple versions.
|
||||
// From the viewpoint of vulnerability detection,
|
||||
// pay attention only to the running kernel
|
||||
if pack.Name == "kernel" {
|
||||
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
|
||||
if o.Kernel.Release != ver {
|
||||
o.log.Debugf("Not a running kernel: %s, uname: %s", ver, release)
|
||||
isKernel, running := isRunningKernel(pack, o.Distro.Family, o.Kernel)
|
||||
if isKernel {
|
||||
if !running {
|
||||
o.log.Debugf("Not a running kernel. pack: %#v, kernel: %#v", pack, o.Kernel)
|
||||
continue
|
||||
} else {
|
||||
o.log.Debugf("Running kernel: %s, uname: %s", ver, release)
|
||||
}
|
||||
o.log.Debugf("Found a running kernel. pack: %#v, kernel: %#v", pack, o.Kernel)
|
||||
}
|
||||
installed[pack.Name] = pack
|
||||
}
|
||||
@@ -389,7 +387,7 @@ func (o *redhat) parseUpdatablePacksLine(line string) (models.Package, error) {
|
||||
ver = fmt.Sprintf("%s:%s", epoch, fields[2])
|
||||
}
|
||||
|
||||
repos := strings.Join(fields[4:len(fields)], " ")
|
||||
repos := strings.Join(fields[4:], " ")
|
||||
|
||||
p := models.Package{
|
||||
Name: fields[0],
|
||||
@@ -401,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
|
||||
@@ -455,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())
|
||||
@@ -820,7 +818,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
|
||||
inDesctiption, inCves = true, false
|
||||
ss := strings.Split(line, " : ")
|
||||
advisory.Description += fmt.Sprintf("%s\n",
|
||||
strings.Join(ss[1:len(ss)], " : "))
|
||||
strings.Join(ss[1:], " : "))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -834,7 +832,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
|
||||
if inDesctiption {
|
||||
if ss := strings.Split(line, ": "); 1 < len(ss) {
|
||||
advisory.Description += fmt.Sprintf("%s\n",
|
||||
strings.Join(ss[1:len(ss)], ": "))
|
||||
strings.Join(ss[1:], ": "))
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -842,7 +840,7 @@ func (o *redhat) parseYumUpdateinfo(stdout string) (result []distroAdvisoryCveID
|
||||
if found := o.isCvesHeaderLine(line); found {
|
||||
inCves = true
|
||||
ss := strings.Split(line, "CVEs : ")
|
||||
line = strings.Join(ss[1:len(ss)], " ")
|
||||
line = strings.Join(ss[1:], " ")
|
||||
cveIDs := o.parseYumUpdateinfoLineToGetCveIDs(line)
|
||||
for _, cveID := range cveIDs {
|
||||
cveIDsSetInThisSection[cveID] = true
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -61,6 +60,9 @@ type osPackages struct {
|
||||
// installed packages
|
||||
Packages models.Packages
|
||||
|
||||
// installed source packages (Debian based only)
|
||||
SrcPackages models.SrcPackages
|
||||
|
||||
// unsecure packages
|
||||
VulnInfos models.VulnInfos
|
||||
|
||||
@@ -72,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{
|
||||
@@ -89,11 +96,21 @@ func detectOS(c config.ServerInfo) (osType osTypeInterface) {
|
||||
return
|
||||
}
|
||||
|
||||
if itsMe, osType = detectSUSE(c); itsMe {
|
||||
util.Log.Debugf("SUSE Linux. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
if itsMe, osType = detectFreebsd(c); itsMe {
|
||||
util.Log.Debugf("FreeBSD. Host: %s:%s", c.Host, c.Port)
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
185
scan/suse.go
Normal file
185
scan/suse.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
// inherit OsTypeInterface
|
||||
type suse struct {
|
||||
redhat
|
||||
}
|
||||
|
||||
// NewRedhat is constructor
|
||||
func newSUSE(c config.ServerInfo) *suse {
|
||||
r := &suse{
|
||||
redhat: redhat{
|
||||
base: base{
|
||||
osPackages: osPackages{
|
||||
Packages: models.Packages{},
|
||||
VulnInfos: models.VulnInfos{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
r.log = util.NewCustomLogger(c)
|
||||
r.setServerInfo(c)
|
||||
return r
|
||||
}
|
||||
|
||||
// https://github.com/mizzy/specinfra/blob/master/lib/specinfra/helper/detect_os/suse.rb
|
||||
func detectSUSE(c config.ServerInfo) (itsMe bool, suse osTypeInterface) {
|
||||
suse = newSUSE(c)
|
||||
|
||||
if r := exec(c, "ls /etc/os-release", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "cat /etc/os-release", noSudo); r.isSuccess() {
|
||||
name := ""
|
||||
if strings.Contains(r.Stdout, "ID=opensuse") {
|
||||
//TODO check opensuse or opensuse.leap
|
||||
name = config.OpenSUSE
|
||||
} else if strings.Contains(r.Stdout, `NAME="SLES"`) {
|
||||
name = config.SUSEEnterpriseServer
|
||||
} else {
|
||||
util.Log.Warn("Failed to parse SUSE edition: %s", r)
|
||||
return true, suse
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`VERSION_ID=\"(\d+\.\d+|\d+)\"`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) != 2 {
|
||||
util.Log.Warn("Failed to parse SUSE Linux version: %s", r)
|
||||
return true, suse
|
||||
}
|
||||
suse.setDistro(name, result[1])
|
||||
return true, suse
|
||||
}
|
||||
}
|
||||
} else if r := exec(c, "ls /etc/SuSE-release", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "zypper -V", noSudo); r.isSuccess() {
|
||||
if r := exec(c, "cat /etc/SuSE-release", noSudo); r.isSuccess() {
|
||||
re := regexp.MustCompile(`openSUSE (\d+\.\d+|\d+)`)
|
||||
result := re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) == 2 {
|
||||
//TODO check opensuse or opensuse.leap
|
||||
suse.setDistro(config.OpenSUSE, result[1])
|
||||
return true, suse
|
||||
}
|
||||
|
||||
re = regexp.MustCompile(`VERSION = (\d+)`)
|
||||
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) == 2 {
|
||||
version := result[1]
|
||||
re = regexp.MustCompile(`PATCHLEVEL = (\d+)`)
|
||||
result = re.FindStringSubmatch(strings.TrimSpace(r.Stdout))
|
||||
if len(result) == 2 {
|
||||
suse.setDistro(config.SUSEEnterpriseServer,
|
||||
fmt.Sprintf("%s.%s", version, result[1]))
|
||||
return true, suse
|
||||
}
|
||||
}
|
||||
util.Log.Warn("Failed to parse SUSE Linux version: %s", r)
|
||||
return true, suse
|
||||
}
|
||||
}
|
||||
}
|
||||
util.Log.Debugf("Not SUSE Linux. servername: %s", c.ServerName)
|
||||
return false, suse
|
||||
}
|
||||
|
||||
func (o *suse) checkDependencies() error {
|
||||
o.log.Infof("Dependencies... No need")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *suse) checkIfSudoNoPasswd() error {
|
||||
// SUSE doesn't need root privilege
|
||||
o.log.Infof("sudo ... No need")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *suse) scanPackages() error {
|
||||
installed, err := o.scanInstalledPackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan installed packages: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
rebootRequired, err := o.rebootRequired()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to detect the kernel reboot required: %s", err)
|
||||
return err
|
||||
}
|
||||
o.Kernel.RebootRequired = rebootRequired
|
||||
|
||||
updatable, err := o.scanUpdatablePackages()
|
||||
if err != nil {
|
||||
o.log.Errorf("Failed to scan updatable packages: %s", err)
|
||||
return err
|
||||
}
|
||||
installed.MergeNewVersion(updatable)
|
||||
o.Packages = installed
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *suse) rebootRequired() (bool, error) {
|
||||
r := o.exec("rpm -q --last kernel-default | head -n1", noSudo)
|
||||
if !r.isSuccess() {
|
||||
return false, fmt.Errorf("Failed to detect the last installed kernel : %v", r)
|
||||
}
|
||||
stdout := strings.Fields(r.Stdout)[0]
|
||||
return !strings.Contains(stdout, strings.TrimSuffix(o.Kernel.Release, "-default")), nil
|
||||
}
|
||||
|
||||
func (o *suse) scanUpdatablePackages() (models.Packages, error) {
|
||||
cmd := ""
|
||||
if v, _ := o.Distro.MajorVersion(); v < 12 {
|
||||
cmd = "zypper -q lu"
|
||||
} else {
|
||||
cmd = "zypper --no-color -q lu"
|
||||
}
|
||||
r := o.exec(cmd, noSudo)
|
||||
if !r.isSuccess() {
|
||||
return nil, fmt.Errorf("Failed to scan updatable packages: %v", r)
|
||||
}
|
||||
return o.parseZypperLULines(r.Stdout)
|
||||
}
|
||||
|
||||
func (o *suse) parseZypperLULines(stdout string) (models.Packages, error) {
|
||||
updatables := models.Packages{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.Index(line, "S | Repository") != -1 ||
|
||||
strings.Index(line, "--+----------------") != -1 {
|
||||
continue
|
||||
}
|
||||
pack, err := o.parseZypperLUOneLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
updatables[pack.Name] = *pack
|
||||
}
|
||||
return updatables, nil
|
||||
}
|
||||
|
||||
func (o *suse) parseZypperLUOneLine(line string) (*models.Package, error) {
|
||||
fs := strings.Fields(line)
|
||||
if len(fs) != 11 {
|
||||
return nil, fmt.Errorf("zypper -q lu Unknown format: %s", line)
|
||||
}
|
||||
available := strings.Split(fs[8], "-")
|
||||
return &models.Package{
|
||||
Name: fs[4],
|
||||
NewVersion: available[0],
|
||||
NewRelease: available[1],
|
||||
Arch: fs[10],
|
||||
}, nil
|
||||
}
|
||||
106
scan/suse_test.go
Normal file
106
scan/suse_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
func TestScanUpdatablePackages(t *testing.T) {
|
||||
r := newSUSE(config.ServerInfo{})
|
||||
r.Distro = config.Distro{Family: "sles"}
|
||||
stdout := `S | Repository | Name | Current Version | Available Version | Arch
|
||||
--+---------------------------------------------+-------------------------------+-----------------------------+-----------------------------+-------
|
||||
v | SLES12-SP2-Updates | SUSEConnect | 0.3.0-19.8.1 | 0.3.1-19.11.2 | x86_64
|
||||
v | SLES12-SP2-Updates | SuSEfirewall2 | 3.6.312-2.3.1 | 3.6.312-2.10.1 | noarch`
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
out models.Packages
|
||||
}{
|
||||
{
|
||||
stdout,
|
||||
models.NewPackages(
|
||||
models.Package{
|
||||
Name: "SUSEConnect",
|
||||
NewVersion: "0.3.1",
|
||||
NewRelease: "19.11.2",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
models.Package{
|
||||
Name: "SuSEfirewall2",
|
||||
NewVersion: "3.6.312",
|
||||
NewRelease: "2.10.1",
|
||||
Arch: "noarch",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
packages, err := r.parseZypperLULines(tt.in)
|
||||
if err != nil {
|
||||
t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in)
|
||||
return
|
||||
}
|
||||
for name, ePack := range tt.out {
|
||||
if !reflect.DeepEqual(ePack, packages[name]) {
|
||||
e := pp.Sprintf("%v", ePack)
|
||||
a := pp.Sprintf("%v", packages[name])
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanUpdatablePackage(t *testing.T) {
|
||||
r := newSUSE(config.ServerInfo{})
|
||||
r.Distro = config.Distro{Family: "sles"}
|
||||
stdout := `v | SLES12-SP2-Updates | SUSEConnect | 0.3.0-19.8.1 | 0.3.1-19.11.2 | x86_64`
|
||||
|
||||
var tests = []struct {
|
||||
in string
|
||||
out models.Package
|
||||
}{
|
||||
{
|
||||
stdout,
|
||||
models.Package{
|
||||
Name: "SUSEConnect",
|
||||
NewVersion: "0.3.1",
|
||||
NewRelease: "19.11.2",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
pack, err := r.parseZypperLUOneLine(tt.in)
|
||||
if err != nil {
|
||||
t.Errorf("Error has occurred, err: %s\ntt.in: %v", err, tt.in)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(*pack, tt.out) {
|
||||
e := pp.Sprintf("%v", tt.out)
|
||||
a := pp.Sprintf("%v", pack)
|
||||
t.Errorf("expected %s, actual %s", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
69
scan/utils.go
Normal file
69
scan/utils.go
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/future-architect/vuls/util"
|
||||
)
|
||||
|
||||
func isRunningKernel(pack models.Package, family string, kernel models.Kernel) (isKernel, running bool) {
|
||||
switch family {
|
||||
case config.SUSEEnterpriseServer:
|
||||
if pack.Name == "kernel-default" {
|
||||
// Remove the last period and later because uname don't show that.
|
||||
ss := strings.Split(pack.Release, ".")
|
||||
rel := strings.Join(ss[0:len(ss)-1], ".")
|
||||
ver := fmt.Sprintf("%s-%s-default", pack.Version, rel)
|
||||
return true, kernel.Release == ver
|
||||
}
|
||||
return false, false
|
||||
|
||||
case config.RedHat, config.Oracle, config.CentOS, config.Amazon:
|
||||
if pack.Name == "kernel" {
|
||||
ver := fmt.Sprintf("%s-%s.%s", pack.Version, pack.Release, pack.Arch)
|
||||
return true, kernel.Release == ver
|
||||
}
|
||||
return false, false
|
||||
|
||||
default:
|
||||
util.Log.Warnf("Reboot required is not implemented yet: %s, %s", family, kernel)
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func rpmQa(distro config.Distro) string {
|
||||
const old = "rpm -qa --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
const new = "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
switch distro.Family {
|
||||
case config.SUSEEnterpriseServer:
|
||||
if v, _ := distro.MajorVersion(); v < 12 {
|
||||
return old
|
||||
}
|
||||
return new
|
||||
default:
|
||||
if v, _ := distro.MajorVersion(); v < 6 {
|
||||
return old
|
||||
}
|
||||
return new
|
||||
}
|
||||
}
|
||||
117
scan/utils_test.go
Normal file
117
scan/utils_test.go
Normal file
@@ -0,0 +1,117 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
func TestIsRunningKernelSUSE(t *testing.T) {
|
||||
r := newSUSE(config.ServerInfo{})
|
||||
r.Distro = config.Distro{Family: config.SUSEEnterpriseServer}
|
||||
|
||||
kernel := models.Kernel{
|
||||
Release: "4.4.74-92.35-default",
|
||||
Version: "",
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
pack models.Package
|
||||
family string
|
||||
kernel models.Kernel
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
pack: models.Package{
|
||||
Name: "kernel-default",
|
||||
Version: "4.4.74",
|
||||
Release: "92.35.1",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
family: config.SUSEEnterpriseServer,
|
||||
kernel: kernel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pack: models.Package{
|
||||
Name: "kernel-default",
|
||||
Version: "4.4.59",
|
||||
Release: "92.20.2",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
family: config.SUSEEnterpriseServer,
|
||||
kernel: kernel,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, actual := isRunningKernel(tt.pack, tt.family, tt.kernel)
|
||||
if tt.expected != actual {
|
||||
t.Errorf("[%d] expected %t, actual %t", i, tt.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsRunningKernelRedHatLikeLinux(t *testing.T) {
|
||||
r := newRedhat(config.ServerInfo{})
|
||||
r.Distro = config.Distro{Family: config.Amazon}
|
||||
|
||||
kernel := models.Kernel{
|
||||
Release: "4.9.43-17.38.amzn1.x86_64",
|
||||
Version: "",
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
pack models.Package
|
||||
family string
|
||||
kernel models.Kernel
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
pack: models.Package{
|
||||
Name: "kernel",
|
||||
Version: "4.9.43",
|
||||
Release: "17.38.amzn1",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
family: config.Amazon,
|
||||
kernel: kernel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pack: models.Package{
|
||||
Name: "kernel",
|
||||
Version: "4.9.38",
|
||||
Release: "16.35.amzn1",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
family: config.Amazon,
|
||||
kernel: kernel,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
_, actual := isRunningKernel(tt.pack, tt.family, tt.kernel)
|
||||
if tt.expected != actual {
|
||||
t.Errorf("[%d] expected %t, actual %t", i, tt.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,7 +201,7 @@ $ docker run --rm -it \
|
||||
```console
|
||||
$docker run -dt \
|
||||
-v $PWD:/vuls \
|
||||
-p 80:80 \
|
||||
-p 5111:5111 \
|
||||
vuls/vulsrepo
|
||||
```
|
||||
|
||||
|
||||
@@ -1,31 +1,18 @@
|
||||
FROM httpd:2.4
|
||||
FROM alpine:3.6
|
||||
|
||||
MAINTAINER hikachan sadayuki-matsuno
|
||||
# install packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
vim \
|
||||
git \
|
||||
libcgi-pm-perl \
|
||||
libjson-perl \
|
||||
&& rm -r /var/lib/apt/lists/*
|
||||
|
||||
# env
|
||||
ENV HTTPD_PREFIX /usr/local/apache2
|
||||
MAINTAINER hikachan sadayuki-matsuno usiusi360
|
||||
|
||||
VOLUME /vuls
|
||||
|
||||
WORKDIR ${HTTPD_PREFIX}/htdocs
|
||||
RUN git clone https://github.com/usiusi360/vulsrepo.git \
|
||||
&& echo "LoadModule cgid_module modules/mod_cgid.so" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo "<Directory \"$HTTPD_PREFIX/htdocs/vulsrepo/dist/cgi\">" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo " Options +ExecCGI +FollowSymLinks" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo " AddHandler cgi-script cgi" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& echo "</Directory>" >> $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& sed -i -e 's/User daemon/#User/g' $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& sed -i -e 's/Group daemon/#Group/g' $HTTPD_PREFIX/conf/httpd.conf \
|
||||
&& ln -snf /vuls/results /usr/local/apache2/htdocs/vulsrepo/results
|
||||
RUN apk --no-cache add git \
|
||||
&& git clone https://github.com/usiusi360/vulsrepo.git
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["httpd-foreground"]
|
||||
RUN cd /vulsrepo/server \
|
||||
&& cp vulsrepo-config.toml.sample vulsrepo-config.toml \
|
||||
&& sed -i -e 's/\/home\/vuls-user//g' vulsrepo-config.toml \
|
||||
&& sed -i -e 's/\/opt//g' vulsrepo-config.toml \
|
||||
&& mkdir /lib64 \
|
||||
&& ln -s /lib/ld-musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
|
||||
|
||||
EXPOSE 5111
|
||||
CMD ["/vulsrepo/server/vulsrepo-server"]
|
||||
|
||||
@@ -23,7 +23,7 @@ VulsRepo is visualized based on the json report output in [vuls](https://github.
|
||||
```console
|
||||
$docker run -dt \
|
||||
-v $PWD:/vuls \
|
||||
-p 80:80 \
|
||||
-p 5111:5111 \
|
||||
vuls/vulsrepo
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user