Skip to content

Commit

Permalink
initial commit to vuls_exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
jbkc85 committed Apr 12, 2022
0 parents commit 8e02746
Show file tree
Hide file tree
Showing 17 changed files with 94,122 additions and 0 deletions.
32 changes: 32 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
root = "."
tmp_dir = "airbin"

[build]
bin = "./airbin/vuls_exporter server -r ./results"
cmd = "go build -o ./airbin/vuls_exporter ."
delay = 1000
exclude_dir = ["assets", "airbin", "vendor"]
exclude_file = []
exclude_regex = []
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
log = "build-errors.log"
send_interrupt = false
stop_on_error = true

[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
time = false

[misc]
clean_on_exit = false
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
airbin

# ignore local development YAML configurations
config.yaml
config.yml

vuls_exporter-*
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
VERSION ?= $(shell cat VERSION)

build:
GOOS=linux GOARCH=amd64 go build -o vuls_exporter-${VERSION}-linux .
go build -o vuls_exporter-${VERSION}-darwin .
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# vuls_exporter
Prometheus Exporter that provides metric output for Vuls.io security scan results
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v1.0.0
69 changes: 69 additions & 0 deletions collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"log"
"time"

"github.com/prometheus/client_golang/prometheus"
)

const PROMETHEUS_NAMESPACE = "vuls"

type vulsCollector struct {
cveContents *prometheus.Desc
resultLastScanned *prometheus.Desc
}

func newVulsCollector() *vulsCollector {
return &vulsCollector{
cveContents: prometheus.NewDesc(
prometheus.BuildFQName(PROMETHEUS_NAMESPACE, "cve", "contents"),
"Aggregated Findings from CVE Contents exported by Vuls",
[]string{"database", "severity", "serverName"}, nil,
),
resultLastScanned: prometheus.NewDesc(
prometheus.BuildFQName(PROMETHEUS_NAMESPACE, "server", "last_scanned"),
"Gauge to provide a timestamp on the server results Last Scanned by Vuls",
[]string{"serverName"}, nil,
),
}
}

func (collector *vulsCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- collector.cveContents
ch <- collector.resultLastScanned
}

func (collector *vulsCollector) Collect(ch chan<- prometheus.Metric) {
vulsResults := parseResults()

for server, results := range vulsResults {
timestamp, _ := time.Parse(time.RFC3339Nano, results.ScannedAt)
ch <- prometheus.MustNewConstMetric(
collector.resultLastScanned,
prometheus.GaugeValue,
float64(timestamp.Unix()),
server,
)

databaseSeverities := results.aggregateSeverities()

for database, severities := range databaseSeverities {
_, none := severities["none"]
if len(severities) == 1 && none {
if flagVerbose {
log.Printf("[TRACE] no severities found for %s - skipping.", database)
}
continue
}
for severity, findings := range severities {
ch <- prometheus.MustNewConstMetric(
collector.cveContents,
prometheus.GaugeValue,
float64(findings),
database, severity, server,
)
}
}
}
}
54 changes: 54 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"log"
"os"

"github.com/spf13/viper"
)

type config struct {
Verbose bool `json:"verbose" yaml:"verbose"`
}

var (
exporterConfig config

flagPort string
flagResultsPath string
flagVerbose bool
)

func init() {
cwd, _ := os.Getwd()
vulsExporter.PersistentFlags().StringVarP(&flagPort, "port", "p", "41337", "Port for vuls_exporter to bind to and expose openmetrics")
vulsExporter.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Enable Verbose mode for log output")
vulsExporter.PersistentFlags().StringVarP(&flagResultsPath, "results", "r", cwd, "Path to Results directory generated by Vuls")
vulsExporter.AddCommand(exporterServerCmd)
vulsExporter.AddCommand(exporterParseCommand)
}

func initializeConfig() {
//registerCVEMetrics()
//registerResultsMetrics()
// configure viper to look into the following directories for
// a 'config.yaml' configuration file.
// 1. $HOME/.kafkactl, 2. /etc/kafkactl, 3. $PWD
viper.SetConfigType("yaml")
viper.AddConfigPath("$HOME/.vuls_exporter")
viper.AddConfigPath("/opt/vuls_exporter")
viper.AddConfigPath(".")
viper.SetConfigName("config")

err := viper.ReadInConfig() // Find and read the config file
if err != nil {
log.Printf("[DEBUG] Unable to find configuration file. (message: %s)", err)
} else {
err = viper.Unmarshal(&exporterConfig)
if err != nil {
if flagVerbose {
log.Fatalf("[FATAL] Configuration file found but unable to parse (err :%s)", err)
}
}
}
}
68 changes: 68 additions & 0 deletions cve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import "github.com/prometheus/client_golang/prometheus"

type cve struct {
AffectedPackages []cveAffectedPackages `json:"affectedPackages"`
CVEID string `json:"cveID"`
Confidence []cveConfidences `json:"confidences"`
Contents map[string][]cveContent `json:"cveContents"`
}

type cveConfidences struct {
DetectionMethod string `json:"detectionMethod"`
Score int `json:"score"`
}

type cveAffectedPackages struct {
FixState string `json:"fixState"`
Name string `json:"name"`
NotFixedYet bool `json:"notFixedYet"`
}

type cveContent struct {
CVEID string `json:"cveID"`
CVSS2Score float32 `json:"cvss2Score"`
CVSS2Severity string `json:"cvss2Severity"`
CVSS3Score float32 `json:"cvss3Score"`
CVSS3Severity string `json:"cvss3Severity"`
Title string `json:"title"`
Type string `json:"type"`
}

var (
cveContentsGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: "vuls",
Subsystem: "cve",
Name: "contents",
Help: "CVE Contents found on the matched group or scanned hosts.",
}, []string{"name", "database", "severity", "serverName"},
)
)

func (cc *cveContent) returnCVSSeverity() string {
var score float32
if cc.CVSS3Score != 0 {
score = cc.CVSS3Score
} else {
score = cc.CVSS2Score
}

switch {
case score == 0:
return "none"
case score > 0 && score < 4:
return "low"
case score > 3.9 && score < 7:
return "medium"
case score > 6.9 && score < 9:
return "high"
default:
return "critical"
}
}

func registerCVEMetrics() {
prometheus.MustRegister(cveContentsGauge)
}
Loading

0 comments on commit 8e02746

Please sign in to comment.