Scanners Integration (#18)

* Research scanning

* Move files around

* Reports the list

* Scanner happens

* Commit

* Work on alternative

* refactorings

* Progress

* Save the state

* Commit

* Display trivy Results

* Checkov also reports

* Better display

* Correct trivy numbers

* Scan pre-install manifest

* Readme items

* Static checks
This commit is contained in:
Andrey Pokhilko
2022-10-17 13:41:08 +01:00
committed by GitHub
parent 5cae4b5adf
commit f86a4a93a7
22 changed files with 995 additions and 439 deletions

View File

@@ -0,0 +1,107 @@
package scanners
import (
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"strconv"
"strings"
)
type Checkov struct {
Data *subproc.DataLayer
}
func (c *Checkov) Name() string {
return "Checkov"
}
func (c *Checkov) Test() bool {
res, err := utils.RunCommand([]string{"checkov", "--version"}, nil)
if err != nil {
return false
}
log.Infof("Discovered Checkov version: %s", strings.TrimSpace(res))
return true
}
func (c *Checkov) ScanManifests(mnf string) (*subproc.ScanResults, error) {
fname, fclose, err := utils.TempFile(mnf)
if err != nil {
return nil, err
}
defer fclose()
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "cli", "--file", fname}
out, err := utils.RunCommand(cmd, nil)
if err != nil {
return nil, err
}
res := &subproc.ScanResults{}
res.OrigReport = out
return res, nil
}
func (c *Checkov) ScanResource(ns string, kind string, name string) (*subproc.ScanResults, error) {
carp := v1.Carp{}
carp.Kind = kind
carp.Name = name
mnf, err := c.Data.GetResourceYAML(ns, &carp)
if err != nil {
return nil, err
}
fname, fclose, err := utils.TempFile(mnf)
if err != nil {
return nil, err
}
defer fclose()
cmd := []string{"checkov", "--quiet", "--soft-fail", "--framework", "kubernetes", "--output", "cli", "--file", fname}
out, err := utils.RunCommand(cmd, nil)
if err != nil {
return nil, err
}
res := subproc.ScanResults{}
_, out, _ = strings.Cut(out, "\n") // kubernetes scan results:
_, out, _ = strings.Cut(out, "\n") // empty line
line, out, found := strings.Cut(out, "\n") // status line
if found {
parts := strings.FieldsFunc(line, func(r rune) bool {
return r == ':' || r == ','
})
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
res.PassedCount = cnt
} else {
log.Warnf("Failed to parse Checkov output: %s", err)
}
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[3])); err == nil {
res.FailedCount = cnt
} else {
log.Warnf("Failed to parse Checkov output: %s", err)
}
} else {
log.Warnf("Failed to parse Checkov output")
}
res.OrigReport = strings.TrimSpace(out)
return &res, nil
}
type CheckovResults struct {
Summary CheckovSummary
}
type CheckovSummary struct {
Failed int `json:"failed"`
Passed int `json:"passed"`
ResourceCount int `json:"resource_count"`
// parsing errors?
// skipped ?
}

View File

@@ -0,0 +1,87 @@
package scanners
import (
"github.com/komodorio/helm-dashboard/pkg/dashboard/subproc"
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
log "github.com/sirupsen/logrus"
"strconv"
"strings"
)
type Trivy struct {
Data *subproc.DataLayer
}
func (c *Trivy) Name() string {
return "Trivy"
}
func (c *Trivy) Test() bool {
res, err := utils.RunCommand([]string{"trivy", "--version"}, nil)
if err != nil {
return false
}
parts := strings.Split(res, "\n")
log.Infof("Discovered Trivy: %s", strings.TrimSpace(parts[0]))
return true
}
func (c *Trivy) ScanManifests(_ string) (*subproc.ScanResults, error) {
return nil, nil // Trivy is unable to scan manifests
}
func (c *Trivy) scanResource(ns string, kind string, name string) (string, error) {
cmd := []string{"trivy", "kubernetes", "--quiet", "--format", "table", "--report", "all", "--no-progress",
"--context", c.Data.KubeContext, "--namespace", ns, kind + "/" + name}
out, err := utils.RunCommand(cmd, nil)
if err != nil {
return "", err
}
return out, nil
}
func (c *Trivy) ScanResource(ns string, kind string, name string) (*subproc.ScanResults, error) {
res := subproc.ScanResults{}
resource, err := c.scanResource(ns, kind, name)
if err != nil {
return nil, err
}
for _, line := range strings.Split(resource, "\n") {
if strings.HasPrefix(line, "Tests:") {
parts := strings.FieldsFunc(line, func(r rune) bool {
return r == ':' || r == ',' || r == ')'
})
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[2])); err == nil {
res.PassedCount += cnt
} else {
log.Warnf("Failed to parse Trivy output: %s", err)
}
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[4])); err == nil {
res.FailedCount += cnt
} else {
log.Warnf("Failed to parse Trivy output: %s", err)
}
}
if strings.HasPrefix(line, "Total:") {
parts := strings.FieldsFunc(line, func(r rune) bool {
return r == ':' || r == ',' || r == '('
})
if cnt, err := strconv.Atoi(strings.TrimSpace(parts[1])); err == nil {
res.FailedCount += cnt
} else {
log.Warnf("Failed to parse Trivy output: %s", err)
}
}
}
res.OrigReport = resource
return &res, nil
}