Files
trivy/pkg/report/sarif_test.go
2022-08-15 21:40:29 +03:00

289 lines
9.4 KiB
Go

package report_test
import (
"bytes"
"encoding/json"
"testing"
"github.com/owenrumney/go-sarif/v2/sarif"
"github.com/stretchr/testify/assert"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func toPtr[T any](v T) *T {
return &v
}
func TestReportWriter_Sarif(t *testing.T) {
tests := []struct {
name string
input types.Results
wantRules []*sarif.ReportingDescriptor
wantResults []*sarif.Result
}{
{
name: "report with vulnerabilities",
input: types.Results{
{
Target: "library/test",
Class: types.ClassOSPkg,
Vulnerabilities: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2020-0001",
PkgName: "foo",
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
SeveritySource: "redhat",
Vulnerability: dbTypes.Vulnerability{
Title: "foobar",
Description: "baz",
Severity: "HIGH",
VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
vulnerability.NVD: dbTypes.SeverityCritical,
vulnerability.RedHat: dbTypes.SeverityHigh,
},
CVSS: map[dbTypes.SourceID]dbTypes.CVSS{
vulnerability.NVD: {
V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 9.8,
},
vulnerability.RedHat: {
V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
V3Score: 7.5,
},
},
},
},
},
},
},
wantRules: []*sarif.ReportingDescriptor{
{
ID: "CVE-2020-0001",
Name: toPtr("OsPackageVulnerability"),
ShortDescription: &sarif.MultiformatMessageString{Text: toPtr("CVE-2020-0001")},
FullDescription: &sarif.MultiformatMessageString{Text: toPtr("baz")},
DefaultConfiguration: &sarif.ReportingConfiguration{
Level: "error",
},
HelpURI: toPtr("https://avd.aquasec.com/nvd/cve-2020-0001"),
Properties: map[string]interface{}{
"tags": []interface{}{
"vulnerability",
"security",
"HIGH",
},
"precision": "very-high",
"security-severity": "7.5",
},
Help: &sarif.MultiformatMessageString{
Text: toPtr("Vulnerability CVE-2020-0001\nSeverity: HIGH\nPackage: foo\nFixed Version: 3.4.5\nLink: [CVE-2020-0001](https://avd.aquasec.com/nvd/cve-2020-0001)\nbaz"),
Markdown: toPtr("**Vulnerability CVE-2020-0001**\n| Severity | Package | Fixed Version | Link |\n| --- | --- | --- | --- |\n|HIGH|foo|3.4.5|[CVE-2020-0001](https://avd.aquasec.com/nvd/cve-2020-0001)|\n\nbaz"),
},
},
},
wantResults: []*sarif.Result{
{
RuleID: toPtr("CVE-2020-0001"),
RuleIndex: toPtr[uint](0),
Level: toPtr("error"),
Message: sarif.Message{Text: toPtr("Package: foo\nInstalled Version: 1.2.3\nVulnerability CVE-2020-0001\nSeverity: HIGH\nFixed Version: 3.4.5\nLink: [CVE-2020-0001](https://avd.aquasec.com/nvd/cve-2020-0001)")},
Locations: []*sarif.Location{
{
PhysicalLocation: &sarif.PhysicalLocation{
ArtifactLocation: &sarif.ArtifactLocation{
URI: toPtr("library/test"),
URIBaseId: toPtr("ROOTPATH"),
},
Region: &sarif.Region{
StartLine: toPtr(1),
EndLine: toPtr(1),
StartColumn: toPtr(1),
EndColumn: toPtr(1),
},
},
},
},
},
},
},
{
name: "report with misconfigurations",
input: types.Results{
{
Target: "library/test",
Class: types.ClassConfig,
Misconfigurations: []types.DetectedMisconfiguration{
{
Type: "Kubernetes Security Check",
ID: "KSV001",
Title: "Image tag ':latest' used",
Message: "Message",
Severity: "HIGH",
PrimaryURL: "https://avd.aquasec.com/appshield/ksv001",
Status: types.StatusFailure,
},
{
Type: "Kubernetes Security Check",
ID: "KSV002",
Title: "SYS_ADMIN capability added",
Message: "Message",
Severity: "CRITICAL",
PrimaryURL: "https://avd.aquasec.com/appshield/ksv002",
Status: types.StatusPassed,
},
},
},
},
wantResults: []*sarif.Result{
{
RuleID: toPtr("KSV001"),
RuleIndex: toPtr[uint](0),
Level: toPtr("error"),
Message: sarif.Message{Text: toPtr("Artifact: library/test\nType: \nVulnerability KSV001\nSeverity: HIGH\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")},
Locations: []*sarif.Location{
{
PhysicalLocation: &sarif.PhysicalLocation{
ArtifactLocation: &sarif.ArtifactLocation{
URI: toPtr("library/test"),
URIBaseId: toPtr("ROOTPATH"),
},
Region: &sarif.Region{
StartLine: toPtr(1),
EndLine: toPtr(1),
StartColumn: toPtr(1),
EndColumn: toPtr(1),
},
},
},
},
},
{
RuleID: toPtr("KSV002"),
RuleIndex: toPtr[uint](1),
Level: toPtr("error"),
Message: sarif.Message{Text: toPtr("Artifact: library/test\nType: \nVulnerability KSV002\nSeverity: CRITICAL\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")},
Locations: []*sarif.Location{
{
PhysicalLocation: &sarif.PhysicalLocation{
ArtifactLocation: &sarif.ArtifactLocation{
URI: toPtr("library/test"),
URIBaseId: toPtr("ROOTPATH"),
},
Region: &sarif.Region{
StartLine: toPtr(1),
EndLine: toPtr(1),
StartColumn: toPtr(1),
EndColumn: toPtr(1),
},
},
},
},
},
},
wantRules: []*sarif.ReportingDescriptor{
{
ID: "KSV001",
Name: toPtr("Misconfiguration"),
ShortDescription: &sarif.MultiformatMessageString{Text: toPtr("KSV001")},
FullDescription: &sarif.MultiformatMessageString{Text: toPtr("")},
DefaultConfiguration: &sarif.ReportingConfiguration{
Level: "error",
},
HelpURI: toPtr("https://avd.aquasec.com/appshield/ksv001"),
Properties: map[string]interface{}{
"tags": []interface{}{
"misconfiguration",
"security",
"HIGH",
},
"precision": "very-high",
"security-severity": "8.0",
},
Help: &sarif.MultiformatMessageString{
Text: toPtr("Misconfiguration KSV001\nType: Kubernetes Security Check\nSeverity: HIGH\nCheck: Image tag ':latest' used\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)\n"),
Markdown: toPtr("**Misconfiguration KSV001**\n| Type | Severity | Check | Message | Link |\n| --- | --- | --- | --- | --- |\n|Kubernetes Security Check|HIGH|Image tag ':latest' used|Message|[KSV001](https://avd.aquasec.com/appshield/ksv001)|\n\n"),
},
},
{
ID: "KSV002",
Name: toPtr("Misconfiguration"),
ShortDescription: &sarif.MultiformatMessageString{Text: toPtr("KSV002")},
FullDescription: &sarif.MultiformatMessageString{Text: toPtr("")},
DefaultConfiguration: &sarif.ReportingConfiguration{
Level: "error",
},
HelpURI: toPtr("https://avd.aquasec.com/appshield/ksv002"),
Properties: map[string]interface{}{
"tags": []interface{}{
"misconfiguration",
"security",
"CRITICAL",
},
"precision": "very-high",
"security-severity": "9.5",
},
Help: &sarif.MultiformatMessageString{
Text: toPtr("Misconfiguration KSV002\nType: Kubernetes Security Check\nSeverity: CRITICAL\nCheck: SYS_ADMIN capability added\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)\n"),
Markdown: toPtr("**Misconfiguration KSV002**\n| Type | Severity | Check | Message | Link |\n| --- | --- | --- | --- | --- |\n|Kubernetes Security Check|CRITICAL|SYS_ADMIN capability added|Message|[KSV002](https://avd.aquasec.com/appshield/ksv002)|\n\n"),
},
},
},
},
{
name: "no vulns",
wantResults: []*sarif.Result{},
wantRules: []*sarif.ReportingDescriptor{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sarifWritten := bytes.Buffer{}
err := report.Write(types.Report{Results: tt.input}, report.Option{
Format: "sarif",
Output: &sarifWritten,
})
assert.NoError(t, err)
result := &sarif.Report{}
err = json.Unmarshal(sarifWritten.Bytes(), result)
assert.NoError(t, err)
assert.Equal(t, tt.wantRules, result.Runs[0].Tool.Driver.Rules, tt.name)
assert.Equal(t, tt.wantResults, result.Runs[0].Results, tt.name)
})
}
}
func TestToPathUri(t *testing.T) {
tests := []struct {
input string
output string
}{
{
input: "almalinux@sha256:08042694fffd61e6a0b3a22dadba207c8937977915ff6b1879ad744fd6638837",
output: "library/almalinux",
},
{
input: "alpine:latest (alpine 3.13.4)",
output: "library/alpine",
},
{
input: "docker.io/my-organization/my-app:2c6912aee7bde44b84d810aed106ca84f40e2e29",
output: "my-organization/my-app",
},
}
for _, test := range tests {
got := report.ToPathUri(test.input)
if got != test.output {
t.Errorf("toPathUri(%q) got %q, wanted %q", test.input, got, test.output)
}
}
}