Skip to content

Commit

Permalink
Generate cve report
Browse files Browse the repository at this point in the history
Signed-off-by: Tamal Saha <[email protected]>
  • Loading branch information
tamalsaha committed Oct 23, 2024
1 parent 5aacedb commit 4c2fbdd
Show file tree
Hide file tree
Showing 35 changed files with 3,286 additions and 139 deletions.
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ go 1.22.1
toolchain go1.23.1

require (
github.com/Masterminds/semver/v3 v3.3.0
github.com/google/go-containerregistry v0.19.1
github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.8.1
gomodules.xyz/go-sh v0.1.0
gomodules.xyz/logs v0.0.7
Expand All @@ -15,16 +17,15 @@ require (
k8s.io/client-go v0.30.2
k8s.io/component-base v0.30.2
k8s.io/klog/v2 v2.130.1
kmodules.xyz/client-go v0.30.28
kmodules.xyz/client-go v0.30.31
kmodules.xyz/go-containerregistry v0.0.12
kmodules.xyz/resource-metadata v0.20.0
kmodules.xyz/resource-metadata v0.20.1-0.20241023065426-3eebc133ebfb
kubeops.dev/scanner v0.0.19
sigs.k8s.io/yaml v1.4.0
)

require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand Down Expand Up @@ -58,6 +59,7 @@ require (
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
Expand Down Expand Up @@ -236,6 +238,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
Expand Down Expand Up @@ -584,12 +588,12 @@ k8s.io/kube-openapi v0.0.0-20240703190633-0aa61b46e8c2 h1:T5TEV4a+pEjc+j9Xui3MGG
k8s.io/kube-openapi v0.0.0-20240703190633-0aa61b46e8c2/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
kmodules.xyz/client-go v0.30.28 h1:YD5AZaUm3xakIbwLoKblLbE6mvayNZhIomgdXyuLo40=
kmodules.xyz/client-go v0.30.28/go.mod h1:CAu+JlA8RVGtj6LQHu0Q1w2mnFUajuti49c7T1AvGdM=
kmodules.xyz/client-go v0.30.31 h1:P+ZslW5QcgMnMoxo1ZMJrNNcwRwA1BiFct1JvcRQEj0=
kmodules.xyz/client-go v0.30.31/go.mod h1:CAu+JlA8RVGtj6LQHu0Q1w2mnFUajuti49c7T1AvGdM=
kmodules.xyz/go-containerregistry v0.0.12 h1:Tl32QGmSqRVm9PUEb/f3dgDeu9zW5fVzt3qmAFIE37I=
kmodules.xyz/go-containerregistry v0.0.12/go.mod h1:KgeNg0hDsgeda+qc0NzWk0iVRdF0+ZIg/oRzGoYh78I=
kmodules.xyz/resource-metadata v0.20.0 h1:3nb/K0F6N2s0BOMrHC6+moK60q0Jqvac8UjWI3kW0kY=
kmodules.xyz/resource-metadata v0.20.0/go.mod h1:yPqwR6xSTXuIgOjR6kNPDDJUe++sS+h7gtCmevJR60g=
kmodules.xyz/resource-metadata v0.20.1-0.20241023065426-3eebc133ebfb h1:X5HgQQwpeyJm6u0g5hBlsDF46NGQXvS8w+lfwsebK0k=
kmodules.xyz/resource-metadata v0.20.1-0.20241023065426-3eebc133ebfb/go.mod h1:xmYpw8x7MJdEUWsD4kPE0oL4ulyXyAzmaDZNGljNd/E=
kubeops.dev/scanner v0.0.19 h1:J8C94k4j3NY3Y8UGHcG4nCZtmpSqPneCmkuvGNUOv4s=
kubeops.dev/scanner v0.0.19/go.mod h1:FAKPsS+FhrOPwsbwXJOE1vLvIFWBcBcSXpaAztezvu4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
Expand Down
274 changes: 274 additions & 0 deletions pkg/cmds/cve_report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/*
Copyright AppsCode Inc. and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmds

import (
"bytes"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"

"kmodules.xyz/image-packer/pkg/lib"

"github.com/spf13/cobra"
//"kubedb.dev/installer/cmd/lib"
"github.com/olekukonko/tablewriter"
shell "gomodules.xyz/go-sh"
"k8s.io/klog/v2"
"kubeops.dev/scanner/apis/trivy"
)

func NewCmdGenerateCVEReport() *cobra.Command {
var (
files []string
outDir string
)
cmd := &cobra.Command{
Use: "generate-cve-report",
Short: "Generate Image Vulnerability report using Trivy",
DisableFlagsInUseLine: true,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
reports, err := GatherReports(files)
if err != nil {
return err
}
data := GenerateMarkdownReport(reports)

readmeFile := filepath.Join(outDir, "catalog", "README.md")
return os.WriteFile(readmeFile, data, 0o644)
},
}
cmd.Flags().StringSliceVar(&files, "src", files, "List of source files (http url or local file)")
cmd.Flags().StringVar(&outDir, "output-dir", "", "Output directory")

return cmd
}

type CVEReport struct {
Ref string
Digest string
OS string
Critical Stats
High Stats
Medium Stats
Low Stats
Unknown Stats
}

func (r *CVEReport) MarkAsMissing() {
r.Critical.OS = -1
r.Critical.Other = -1

r.High.OS = -1
r.High.Other = -1

r.Medium.OS = -1
r.Medium.Other = -1

r.Low.OS = -1
r.Low.Other = -1

r.Unknown.OS = -1
r.Unknown.Other = -1
}

type Stats struct {
OS int
Other int
}

func (s Stats) PrettyPrint() string {
b, a := "0", "0"
if s.OS >= 0 {
b = strconv.Itoa(s.OS)
}
if s.Other >= 0 {
a = strconv.Itoa(s.Other)
}
if s.OS > 0 {
return fmt.Sprintf("**%s**, %s", b, a)
}
return fmt.Sprintf("%s, %s", b, a)
}

func (s Stats) String() string {
b, a := "0", "0"
if s.OS >= 0 {
b = strconv.Itoa(s.OS)
}
if s.Other >= 0 {
a = strconv.Itoa(s.Other)
}
return fmt.Sprintf("%s, %s", b, a)
}

func (s Stats) Zero() bool {
return s.OS+s.Other == 0
}

func (r CVEReport) NoCVE() bool {
return r.Critical.Zero() &&
r.High.Zero() &&
r.Medium.Zero() &&
r.Low.Zero() &&
r.Unknown.Zero()
}

func (r CVEReport) Headers() []string {
return []string{
"Image Ref",
"OS",
"Critical<br>(os, other)",
"High<br>(os, other)",
"Medium<br>(os, other)",
"Low<br>(os, other)",
"Unknown<br>(os, other)",
}
}

func (r CVEReport) Strings() []string {
ref := r.Ref
if r.Digest != "" {
ref += "<br>" + r.Digest
}
return []string{
ref,
r.OS,
r.Critical.PrettyPrint(),
r.High.PrettyPrint(),
r.Medium.String(),
r.Low.String(),
r.Unknown.String(),
}
}

// "Class": "os-pkgs",
func GatherReports(files []string) ([]CVEReport, error) {
images, err := generateImageList(files)
if err != nil {
return nil, err
}

sh := lib.NewShell()

reports := make([]CVEReport, 0, len(images))
for _, ref := range images {
cveReport, err := gatherReport(sh, ref)
if err != nil {
klog.Warningln(err.Error())
continue
}
reports = append(reports, cveReport)
}

return reports, nil
}

func gatherReport(sh *shell.Session, ref string) (CVEReport, error) {
cveReport := CVEReport{
Ref: ref,
}
if digest, found, err := lib.ImageDigest(ref); err != nil {
cveReport.MarkAsMissing()
return cveReport, err
} else if found {
cveReport.Digest = digest
report, err := lib.Scan(sh, ref)
if err != nil {
cveReport.MarkAsMissing()
return cveReport, err
}
setReport(report, &cveReport)
}
return cveReport, nil
}

func setReport(report *trivy.SingleReport, result *CVEReport) {
result.OS = fmt.Sprintf("%s %s", report.Metadata.Os.Family, report.Metadata.Os.Name)

for _, rpt := range report.Results {
for _, tv := range rpt.Vulnerabilities {
switch tv.Severity {
case "CRITICAL":
if rpt.Class == "os-pkgs" {
result.Critical.OS += 1
} else {
result.Critical.Other += 1
}
case "HIGH":
if rpt.Class == "os-pkgs" {
result.High.OS += 1
} else {
result.High.Other += 1
}
case "MEDIUM":
if rpt.Class == "os-pkgs" {
result.Medium.OS += 1
} else {
result.Medium.Other += 1
}
case "LOW":
if rpt.Class == "os-pkgs" {
result.Low.OS += 1
} else {
result.Low.Other += 1
}
case "UNKNOWN":
if rpt.Class == "os-pkgs" {
result.Unknown.OS += 1
} else {
result.Unknown.Other += 1
}
}
}
}
}

func GenerateMarkdownReport(reports []CVEReport) []byte {
var buf bytes.Buffer
buf.WriteString("# CVE Report")
buf.WriteRune('\n')
buf.Write(generateMarkdownTable(reports))

return buf.Bytes()
}

func generateMarkdownTable(reports []CVEReport) []byte {
var tr CVEReport

data := make([][]string, 0, len(reports))
for _, r := range reports {
data = append(data, r.Strings())
}
sort.Slice(data, func(i, j int) bool {
return data[i][0] < data[j][0]
})

var buf bytes.Buffer

table := tablewriter.NewWriter(&buf)
table.SetHeader(tr.Headers())
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.AppendBulk(data) // Add Bulk Data
table.Render()

return buf.Bytes()
}
3 changes: 2 additions & 1 deletion pkg/cmds/gcp_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ func NewCmdGenerateGCPScript() *cobra.Command {
var gcpImageMap = map[string]string{
"defaultbackend-amd64": "ingress-nginx-defaultbackend",
"fluxcd/helm-controller": "flux-helm-controller",
"fluxcd/source-controller": "flux-source-controller",
"fluxcd/kustomize-controller": "flux-kustomize-controller",
"fluxcd/notification-controller": "flux-notification-controller",
"fluxcd/source-controller": "flux-source-controller",
"ingress-nginx/controller": "ingress-nginx-controller",
"ingress-nginx/kube-webhook-certgen": "ingress-nginx-kube-webhook-certgen",
"kedacore/http-add-on-interceptor": "keda-http-add-on-interceptor",
"kedacore/http-add-on-operator": "keda-http-add-on-operator",
"kedacore/http-add-on-scaler": "keda-http-add-on-scaler",
"prometheus/node-exporter": "prometheus-node-exporter",
"sig-storage/livenessprobe": "csi-driver-livenessprobe",
}

Expand Down
1 change: 1 addition & 0 deletions pkg/cmds/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func NewRootCmd() *cobra.Command {
rootCmd.AddCommand(NewCmdListFeatureCharts())
rootCmd.AddCommand(NewCmdGenerateScripts())
rootCmd.AddCommand(NewCmdGenerateGCPScript())
rootCmd.AddCommand(NewCmdGenerateCVEReport())
rootCmd.AddCommand(NewCmdCompletion())
rootCmd.AddCommand(v.NewCmdVersion())

Expand Down
16 changes: 16 additions & 0 deletions vendor/github.com/mattn/go-runewidth/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4c2fbdd

Please sign in to comment.