mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
289 lines
9.4 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|