package report_test
import (
"bytes"
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/types"
)
func TestReportWriter_Template(t *testing.T) {
testCases := []struct {
name string
detectedVulns []types.DetectedVulnerability
template string
expected string
}{
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityHigh.String()},
},
{
VulnerabilityID: "CVE-2019-0001",
PkgName: "baz",
Vulnerability: dbTypes.Vulnerability{
Severity: dbTypes.SeverityCritical.String(),
},
},
},
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
},
{
name: "happy path",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "123",
PkgName: `foo \ test`,
InstalledVersion: "1.2.3",
FixedVersion: "3.4.5",
Vulnerability: dbTypes.Vulnerability{
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
Description: `curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
Severity: "HIGH",
},
},
},
template: `
{{- range . -}}
{{- $failures := len .Vulnerabilities }}
{{- if not (eq .Type "") }}
{{- end -}}
{{ range .Vulnerabilities }}
{{ escapeXML .Description }}
{{- end }}
{{- end }}
`,
expected: `
curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.
`,
},
{
name: "happy path with/without period description should return with period",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
},
},
},
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."CVE-2019-0000 "with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close()."`,
},
{
name: "Calculate using sprig",
detectedVulns: []types.DetectedVulnerability{
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "foo",
Vulnerability: dbTypes.Vulnerability{
Description: "without period",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: "with period.",
Severity: dbTypes.SeverityCritical.String(),
},
},
{
VulnerabilityID: "CVE-2019-0000",
PkgName: "bar",
Vulnerability: dbTypes.Vulnerability{
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
Severity: dbTypes.SeverityHigh.String(),
},
},
},
template: `{{ $high := 0 }}{{ $critical := 0 }}{{ range . }}{{ range .Vulnerabilities}}{{ if eq .Severity "HIGH" }}{{ $high = add $high 1 }}{{ end }}{{ if eq .Severity "CRITICAL" }}{{ $critical = add $critical 1 }}{{ end }}{{ end }}Critical: {{ $critical }}, High: {{ $high }}{{ end }}`,
expected: `Critical: 2, High: 1`,
},
{
name: "happy path: env var parsing and getCurrentTime",
detectedVulns: []types.DetectedVulnerability{},
template: `{{ toLower (getEnv "AWS_ACCOUNT_ID") }} {{ getCurrentTime }}`,
expected: `123456789012 2020-08-10T07:28:17.000958601Z`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
report.Now = func() time.Time {
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
}
os.Setenv("AWS_ACCOUNT_ID", "123456789012")
tmplWritten := bytes.Buffer{}
inputReport := report.Report{
Results: report.Results{
{
Target: "foojunit",
Type: "test",
Vulnerabilities: tc.detectedVulns,
},
},
}
assert.NoError(t, report.Write("template", &tmplWritten, nil, inputReport, tc.template, false, false))
assert.Equal(t, tc.expected, tmplWritten.String())
})
}
}
func TestReportWriter_Template_SARIF(t *testing.T) {
testCases := []struct {
name string
target string
detectedVulns []types.DetectedVulnerability
want string
}{
//TODO: refactor tests
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
templateFile := "../../contrib/sarif.tpl"
got := bytes.Buffer{}
template, err := ioutil.ReadFile(templateFile)
require.NoError(t, err, tc.name)
inputReport := report.Report{
Results: report.Results{
{
Target: tc.target,
Type: "footype",
Vulnerabilities: tc.detectedVulns,
},
},
}
assert.NoError(t, report.Write("template", &got, nil, inputReport, string(template), false, false), tc.name)
assert.JSONEq(t, tc.want, got.String(), tc.name)
})
}
}