reorganize report class

This commit is contained in:
jgsqware 2016-05-20 18:33:32 +02:00
parent 21c68cd795
commit f8d0f10c78
8 changed files with 65 additions and 158 deletions

View File

@ -1,46 +1,17 @@
package clair package clair
import ( import (
"math"
"strconv" "strconv"
"strings" "strings"
"github.com/coreos/clair/api/v1" "github.com/coreos/clair/api/v1"
"github.com/coreos/clair/cmd/clairctl/xstrings" "github.com/coreos/clair/cmd/clairctl/xstrings"
"github.com/coreos/clair/utils/types"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var uri string var uri string
var healthURI string var healthURI string
//Report Reporting Config value
var Report ReportConfig
//VulnerabiliesCounts Total count of vulnerabilities by type
type vulnerabiliesCounts map[types.Priority]int
//Total return to total of Vulnerabilities
func (v vulnerabiliesCounts) Total() int {
var c int
for _, count := range v {
c += count
}
return c
}
//Count return count of severities in Vulnerabilities
func (v vulnerabiliesCounts) Count(severity string) int {
return v[types.Priority(severity)]
}
//RelativeCount get the percentage of vulnerabilities of a severity
func (v vulnerabiliesCounts) RelativeCount(severity string) float64 {
count := v[types.Priority(severity)]
result := float64(count) / float64(v.Total()) * 100
return math.Ceil(result*100) / 100
}
//ImageAnalysis Full image analysis //ImageAnalysis Full image analysis
type ImageAnalysis struct { type ImageAnalysis struct {
Registry, ImageName, Tag string Registry, ImageName, Tag string
@ -51,41 +22,11 @@ func (imageAnalysis ImageAnalysis) String() string {
return imageAnalysis.Registry + "/" + imageAnalysis.ImageName + ":" + imageAnalysis.Tag return imageAnalysis.Registry + "/" + imageAnalysis.ImageName + ":" + imageAnalysis.Tag
} }
// CountVulnerabilities counts all image vulnerability
func (imageAnalysis ImageAnalysis) countVulnerabilities(l v1.Layer) int {
count := 0
for _, f := range l.Features {
count += len(f.Vulnerabilities)
}
return count
}
// CountAllVulnerabilities Total count of vulnerabilities
func (imageAnalysis ImageAnalysis) CountAllVulnerabilities() vulnerabiliesCounts {
result := make(vulnerabiliesCounts)
l := imageAnalysis.Layers[len(imageAnalysis.Layers)-1]
for _, f := range l.Layer.Features {
for _, v := range f.Vulnerabilities {
result[types.Priority(v.Severity)]++
}
}
return result
}
//LastLayer return the last layer of ImageAnalysis //LastLayer return the last layer of ImageAnalysis
func (imageAnalysis ImageAnalysis) LastLayer() *v1.Layer { func (imageAnalysis ImageAnalysis) LastLayer() *v1.Layer {
return imageAnalysis.Layers[len(imageAnalysis.Layers)-1].Layer return imageAnalysis.Layers[len(imageAnalysis.Layers)-1].Layer
} }
type vulnerabilityWithFeature struct {
v1.Vulnerability
Feature string
}
func fmtURI(u string, port int) string { func fmtURI(u string, port int) string {
if port != 0 { if port != 0 {

View File

@ -7,13 +7,15 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/Sirupsen/logrus"
) )
func getSampleAnalysis() []byte { func getSampleAnalysis() []byte {
file, err := ioutil.ReadFile("./samples/clair_report.json") file, err := ioutil.ReadFile("./samples/clair_report.json")
if err != nil { if err != nil {
fmt.Printf("File error: %v\n", err) logrus.Errorf("File error: %v\n", err)
} }
return file return file
@ -28,7 +30,7 @@ func newServer(httpStatus int) *httptest.Server {
func TestIsHealthy(t *testing.T) { func TestIsHealthy(t *testing.T) {
server := newServer(http.StatusOK) server := newServer(http.StatusOK)
defer server.Close() defer server.Close()
uri = server.URL healthURI = server.URL
if h := IsHealthy(); !h { if h := IsHealthy(); !h {
t.Errorf("IsHealthy() => %v, want %v", h, true) t.Errorf("IsHealthy() => %v, want %v", h, true)
} }
@ -43,37 +45,6 @@ func TestIsNotHealthy(t *testing.T) {
} }
} }
// func TestCountAllVulnerabilities(t *testing.T) {
// var analysis ImageAnalysis
// err := json.Unmarshal([]byte(getSampleAnalysis()), &analysis)
// if err != nil {
// t.Errorf("Failing with error: %v", err)
// }
// vulnerabilitiesCount := analysis.CountAllVulnerabilities()
// if vulnerabilitiesCount.Total != 77 {
// t.Errorf("analysis.CountAllVulnerabilities().Total => %v, want 77", vulnerabilitiesCount.Total)
// }
// if vulnerabilitiesCount.High != 1 {
// t.Errorf("analysis.CountAllVulnerabilities().High => %v, want 1", vulnerabilitiesCount.High)
// }
// if vulnerabilitiesCount.Medium != 18 {
// t.Errorf("analysis.CountAllVulnerabilities().Medium => %v, want 18", vulnerabilitiesCount.Medium)
// }
// if vulnerabilitiesCount.Low != 57 {
// t.Errorf("analysis.CountAllVulnerabilities().Low => %v, want 57", vulnerabilitiesCount.Low)
// }
// if vulnerabilitiesCount.Negligible != 1 {
// t.Errorf("analysis.CountAllVulnerabilities().Negligible => %v, want 1", vulnerabilitiesCount.Negligible)
// }
// }
func TestRelativeCount(t *testing.T) { func TestRelativeCount(t *testing.T) {
var analysis ImageAnalysis var analysis ImageAnalysis
err := json.Unmarshal([]byte(getSampleAnalysis()), &analysis) err := json.Unmarshal([]byte(getSampleAnalysis()), &analysis)
@ -82,8 +53,8 @@ func TestRelativeCount(t *testing.T) {
t.Errorf("Failing with error: %v", err) t.Errorf("Failing with error: %v", err)
} }
vulnerabilitiesCount := analysis.CountAllVulnerabilities() vulnerabilitiesCount := allVulnerabilities(analysis)
fmt.Printf("v: %v\n", vulnerabilitiesCount)
if vulnerabilitiesCount.RelativeCount("High") != 1.3 { if vulnerabilitiesCount.RelativeCount("High") != 1.3 {
t.Errorf("analysis.CountAllVulnerabilities().RelativeCount(\"High\") => %v, want 1.3", vulnerabilitiesCount.RelativeCount("High")) t.Errorf("analysis.CountAllVulnerabilities().RelativeCount(\"High\") => %v, want 1.3", vulnerabilitiesCount.RelativeCount("High"))
} }
@ -96,34 +67,3 @@ func TestRelativeCount(t *testing.T) {
t.Errorf("analysis.CountAllVulnerabilities().RelativeCount(\"Low\") => %v, want 74.03", vulnerabilitiesCount.RelativeCount("Low")) t.Errorf("analysis.CountAllVulnerabilities().RelativeCount(\"Low\") => %v, want 74.03", vulnerabilitiesCount.RelativeCount("Low"))
} }
} }
// func TestFeatureWeight(t *testing.T) {
// feature := Feature{
// Vulnerabilities: []Vulnerability{},
// }
// v1 := Vulnerability{
// Severity: "High",
// }
// v2 := Vulnerability{
// Severity: "Medium",
// }
// v3 := Vulnerability{
// Severity: "Low",
// }
// v4 := Vulnerability{
// Severity: "Negligible",
// }
// feature.Vulnerabilities = append(feature.Vulnerabilities, v1)
// feature.Vulnerabilities = append(feature.Vulnerabilities, v2)
// feature.Vulnerabilities = append(feature.Vulnerabilities, v3)
// feature.Vulnerabilities = append(feature.Vulnerabilities, v4)
// if feature.Weight() != 10 {
// t.Errorf("feature.Weigh => %v, want 6", feature.Weight())
// }
// }

View File

@ -1,19 +1,17 @@
package clair package clair
import ( import (
"fmt"
"net/http" "net/http"
"os"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
) )
//IsHealthy return Health clair result
func IsHealthy() bool { func IsHealthy() bool {
logrus.Debugln("requesting health on: " + healthURI) logrus.Debugln("requesting health on: " + healthURI)
response, err := http.Get(healthURI) response, err := http.Get(healthURI)
if err != nil { if err != nil {
logrus.Errorf("requesting Clair health: %v", err)
fmt.Fprintf(os.Stderr, "requesting Clair health: %v", err)
return false return false
} }
defer response.Body.Close() defer response.Body.Close()

View File

@ -3,6 +3,7 @@ package clair
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math"
"text/template" "text/template"
"github.com/coreos/clair/api/v1" "github.com/coreos/clair/api/v1"
@ -12,6 +13,9 @@ import (
//execute go generate ./clair //execute go generate ./clair
//go:generate go-bindata -pkg clair -o templates.go templates/... //go:generate go-bindata -pkg clair -o templates.go templates/...
//Report Reporting Config value
var Report ReportConfig
//ReportConfig Reporting configuration //ReportConfig Reporting configuration
type ReportConfig struct { type ReportConfig struct {
Path string Path string
@ -27,7 +31,8 @@ func ReportAsHTML(analyzes ImageAnalysis) (string, error) {
funcs := template.FuncMap{ funcs := template.FuncMap{
"vulnerabilities": vulnerabilities, "vulnerabilities": vulnerabilities,
"sortedVulnerabilities": SortedVulnerabilities, "allVulnerabilities": allVulnerabilities,
"sortedVulnerabilities": sortedVulnerabilities,
} }
templte := template.Must(template.New("analysis-template").Funcs(funcs).Parse(string(asset))) templte := template.Must(template.New("analysis-template").Funcs(funcs).Parse(string(asset)))
@ -49,6 +54,51 @@ func invertedPriorities() []types.Priority {
} }
type vulnerabilityWithFeature struct {
v1.Vulnerability
Feature string
}
//VulnerabiliesCounts Total count of vulnerabilities by type
type vulnerabiliesCounts map[types.Priority]int
//Total return to total of Vulnerabilities
func (v vulnerabiliesCounts) Total() int {
var c int
for _, count := range v {
c += count
}
return c
}
//Count return count of severities in Vulnerabilities
func (v vulnerabiliesCounts) Count(severity string) int {
return v[types.Priority(severity)]
}
//RelativeCount get the percentage of vulnerabilities of a severity
func (v vulnerabiliesCounts) RelativeCount(severity string) float64 {
count := v[types.Priority(severity)]
result := float64(count) / float64(v.Total()) * 100
return math.Ceil(result*100) / 100
}
// allVulnerabilities Total count of vulnerabilities
func allVulnerabilities(imageAnalysis ImageAnalysis) vulnerabiliesCounts {
result := make(vulnerabiliesCounts)
l := imageAnalysis.Layers[len(imageAnalysis.Layers)-1]
for _, f := range l.Layer.Features {
for _, v := range f.Vulnerabilities {
result[types.Priority(v.Severity)]++
}
}
return result
}
//Vulnerabilities return a list a vulnerabilities //Vulnerabilities return a list a vulnerabilities
func vulnerabilities(imageAnalysis ImageAnalysis) map[types.Priority][]vulnerabilityWithFeature { func vulnerabilities(imageAnalysis ImageAnalysis) map[types.Priority][]vulnerabilityWithFeature {
@ -66,7 +116,7 @@ func vulnerabilities(imageAnalysis ImageAnalysis) map[types.Priority][]vulnerabi
} }
// SortedVulnerabilities get all vulnerabilities sorted by Severity // SortedVulnerabilities get all vulnerabilities sorted by Severity
func SortedVulnerabilities(imageAnalysis ImageAnalysis) []v1.Feature { func sortedVulnerabilities(imageAnalysis ImageAnalysis) []v1.Feature {
features := []v1.Feature{} features := []v1.Feature{}
l := imageAnalysis.Layers[len(imageAnalysis.Layers)-1] l := imageAnalysis.Layers[len(imageAnalysis.Layers)-1]

View File

@ -34,7 +34,7 @@ func TestReportAsHtml(t *testing.T) {
func TestInvertedPriorities(t *testing.T) { func TestInvertedPriorities(t *testing.T) {
expected := []types.Priority{types.Defcon1, types.Critical, types.High, types.Medium, types.Low, types.Negligible, types.Unknown} expected := []types.Priority{types.Defcon1, types.Critical, types.High, types.Medium, types.Low, types.Negligible, types.Unknown}
ip := InvertedPriorities() ip := invertedPriorities()
fmt.Printf("%v - %v", len(expected), len(ip)) fmt.Printf("%v - %v", len(expected), len(ip))
for i, v := range ip { for i, v := range ip {
if v != expected[i] { if v != expected[i] {

View File

@ -1553,28 +1553,6 @@
} }
] ]
} }
},
{
"Layer": {
"Name": "sha256:9e0bc8a71bde464f710bc2b593a1fc21521517671e918687892303151331fa56",
"Namespace": "ubuntu:14.04",
"ParentName": "sha256:27aa681c95e5165caf287dcfe896532df4ae8b10e099500f2f8f71acf4002a89",
"IndexedByVersion": 2
}
},
{
"Layer": {
"Name": "sha256:27aa681c95e5165caf287dcfe896532df4ae8b10e099500f2f8f71acf4002a89",
"Namespace": "ubuntu:14.04",
"ParentName": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"IndexedByVersion": 2
}
},
{
"Layer": {
"Name": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4",
"IndexedByVersion": 2
}
} }
] ]
} }

File diff suppressed because one or more lines are too long

View File

@ -452,10 +452,11 @@
<div class="app-intro clearfix"> <div class="app-intro clearfix">
<h2>Image: {{.ImageName}}</h2> <h2>Image: {{.ImageName}}</h2>
{{ $ia := .}}
<section class="summary"> <section class="summary">
<div> <div>
{{with $vulnerabilitiesCount := .CountAllVulnerabilities}} {{with $vulnerabilitiesCount := allVulnerabilities $ia}}
<p><span class="lead"><strong>Total : {{$vulnerabilitiesCount.Total}} vulnerabilities</strong></span></p> <p><span class="lead"><strong>Total : {{$vulnerabilitiesCount.Total}} vulnerabilities</strong></span></p>
</p> </p>
<div class="summary-text"> <div class="summary-text">
@ -489,7 +490,6 @@
</section> </section>
<div class="graph"> <div class="graph">
{{ $ia := .}}
{{range $k,$v := vulnerabilities $ia}} {{range $k,$v := vulnerabilities $ia}}
{{range $v}} {{range $v}}
<a class="node {{.Severity}}" href="#{{ .Name }}"> <a class="node {{.Severity}}" href="#{{ .Name }}">