Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: load compliance report from file system #3161

Merged
merged 11 commits into from
Nov 20, 2022
49 changes: 49 additions & 0 deletions docs/docs/kubernetes/cli/compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,52 @@ $ trivy k8s cluster --compliance=nsa --report summary --format json
```
$ trivy k8s cluster --compliance=nsa --report all --format json
```

## Custom compliance report

The Trivy K8s CLI allows you to create a custom compliance specification and pass it to trivy for generating scan report .

The report is generated based on scanning result mapping between users define controls and trivy checks ID.
The supported checks are from two types and can be found at [Aqua vulnerability DB](https://avd.aquasec.com/):
- [misconfiguration](https://avd.aquasec.com/misconfig/)
- [vulnerabilities](https://avd.aquasec.com/nvd)


### Compliance spec format

The compliance spec file format should look as follow :


```yaml
---
spec:
id: "0001" # report unique identifier
title: nsa # report title
description: National Security Agency - Kubernetes Hardening Guidance # description of the report
relatedResources :
- https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/ # reference is related to public or internal spec
version: "1.0" # spec version
controls:
- name: Non-root containers # short control naming
description: 'Check that container is not running as root' # long control description
id: '1.0' # control identifier
checks: # list of trivy checks which associated to control
- id: AVD-KSV-0012 # check ID (midconfiguration ot vulnerability) must start with `AVD-` or `CVE-`
severity: 'MEDIUM' # control severity
- name: Immutable container file systems
description: 'Check that container root file system is immutable'
id: '1.1'
checks:
- id: AVD-KSV-0014
severity: 'LOW'
```

## Custom report CLI Commands

To generate the custom report, an custom spec file path should be passed to the `--compliance` flag with `@` prefix as follow:


```
$ trivy k8s cluster --compliance=@/spec/my_complaince.yaml --report summary
```

16 changes: 16 additions & 0 deletions pkg/compliance/spec/compliance.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package spec

import (
"fmt"
"os"
"strings"

"golang.org/x/exp/maps"
"golang.org/x/xerrors"

sp "github.com/aquasecurity/defsec/pkg/spec"
"github.com/aquasecurity/trivy/pkg/types"
)

Expand Down Expand Up @@ -96,3 +99,16 @@ func securityCheckByCheckID(checkID string) types.SecurityCheck {
return types.SecurityCheckUnknown
}
}

// GetComlianceSpec accepct compliance flag name/path and return builtin or file system loaded spec
func GetComplianceSpec(specNameOrPath string) ([]byte, error) {
if strings.HasPrefix(specNameOrPath, "@") {
buf, err := os.ReadFile(strings.TrimPrefix(specNameOrPath, "@"))
if err != nil {
return []byte{}, fmt.Errorf("error retrieving compliance spec from path: %w", err)
}
return buf, nil
}
return []byte(sp.NewSpecLoader().GetSpecByName(specNameOrPath)), nil

}
7 changes: 3 additions & 4 deletions pkg/flag/report_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,11 @@ func (f *ReportFlagGroup) ToOptions(out io.Writer) (ReportOptions, error) {
}, nil
}

func parseComplianceTypes(compliance interface{}) (string, error) {
complianceString, ok := compliance.(string)
if !ok || (len(complianceString) > 0 && !slices.Contains(types.Compliances, complianceString)) {
func parseComplianceTypes(compliance string) (string, error) {
if len(compliance) > 0 && !slices.Contains(types.Compliances, compliance) && !strings.HasPrefix(compliance, "@") {
return "", xerrors.Errorf("unknown compliance : %v", compliance)
}
return complianceString, nil
return compliance, nil
}

func (f *ReportFlagGroup) forceListAllPkgs(format string, listAllPkgs, dependencyTree bool) bool {
Expand Down
9 changes: 5 additions & 4 deletions pkg/k8s/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"golang.org/x/xerrors"
"gopkg.in/yaml.v3"

sp "github.com/aquasecurity/defsec/pkg/spec"
"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
"github.com/aquasecurity/trivy-kubernetes/pkg/k8s"
cmd "github.com/aquasecurity/trivy/pkg/commands/artifact"
Expand Down Expand Up @@ -84,11 +83,13 @@ func (r *runner) run(ctx context.Context, artifacts []*artifacts.Artifact) error
var complianceSpec spec.ComplianceSpec
// set scanners types by spec
if r.flagOpts.ReportOptions.Compliance != "" {
cs := sp.NewSpecLoader().GetSpecByName(r.flagOpts.ReportOptions.Compliance)
if err = yaml.Unmarshal([]byte(cs), &complianceSpec); err != nil {
cs, err := spec.GetComplianceSpec(r.flagOpts.ReportOptions.Compliance)
if err != nil {
return xerrors.Errorf("spec loading from file system error: %w", err)
}
if err = yaml.Unmarshal(cs, &complianceSpec); err != nil {
return xerrors.Errorf("yaml unmarshal error: %w", err)
}

securityChecks, err := complianceSpec.SecurityChecks()
if err != nil {
return xerrors.Errorf("security check error: %w", err)
Expand Down