github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/report/template_test.go (about)

     1  package report_test
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    13  	"github.com/devseccon/trivy/pkg/clock"
    14  	"github.com/devseccon/trivy/pkg/report"
    15  	"github.com/devseccon/trivy/pkg/types"
    16  )
    17  
    18  func TestReportWriter_Template(t *testing.T) {
    19  	testCases := []struct {
    20  		name          string
    21  		detectedVulns []types.DetectedVulnerability
    22  		template      string
    23  		expected      string
    24  	}{
    25  		{
    26  			name: "happy path",
    27  			detectedVulns: []types.DetectedVulnerability{
    28  				{
    29  					VulnerabilityID: "CVE-2019-0000",
    30  					PkgName:         "foo",
    31  					Vulnerability: dbTypes.Vulnerability{
    32  						Severity: dbTypes.SeverityHigh.String(),
    33  						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
    34  							"nvd": 1,
    35  						},
    36  					},
    37  				},
    38  				{
    39  					VulnerabilityID: "CVE-2019-0000",
    40  					PkgName:         "bar",
    41  					Vulnerability: dbTypes.Vulnerability{
    42  						Severity: dbTypes.SeverityHigh.String()},
    43  				},
    44  				{
    45  					VulnerabilityID: "CVE-2019-0001",
    46  					PkgName:         "baz",
    47  					Vulnerability: dbTypes.Vulnerability{
    48  						Severity: dbTypes.SeverityCritical.String(),
    49  					},
    50  				},
    51  			},
    52  			template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
    53  			expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
    54  		},
    55  		{
    56  			name: "happy path",
    57  			detectedVulns: []types.DetectedVulnerability{
    58  				{
    59  					VulnerabilityID:  "123",
    60  					PkgName:          `foo \ test`,
    61  					InstalledVersion: "1.2.3",
    62  					FixedVersion:     "3.4.5",
    63  					Vulnerability: dbTypes.Vulnerability{
    64  						Title:       `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
    65  						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.`,
    66  						Severity:    "HIGH",
    67  					},
    68  				},
    69  			},
    70  			template: `<testsuites>
    71  {{- range . -}}
    72  {{- $failures := len .Vulnerabilities }}
    73      <testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{  .Target }}" errors="0" skipped="0" time="">
    74  	{{- if not (eq .Type "") }}
    75          <properties>
    76              <property name="type" value="{{ .Type }}"></property>
    77          </properties>
    78          {{- end -}}
    79          {{ range .Vulnerabilities }}
    80          <testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
    81              <failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
    82          </testcase>
    83      {{- end }}
    84  	</testsuite>
    85  {{- end }}
    86  </testsuites>`,
    87  
    88  			expected: `<testsuites>
    89      <testsuite tests="1" failures="1" name="foojunit" errors="0" skipped="0" time="">
    90          <properties>
    91              <property name="type" value="test"></property>
    92          </properties>
    93          <testcase classname="foo \ test-1.2.3" name="[HIGH] 123" time="">
    94              <failure message="gcc: POWER9 &#34;DARN&#34; RNG intrinsic produces repeated output" type="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 &lt; 7.20.0 and curl &gt;= 7.60.0.</failure>
    95          </testcase>
    96  	</testsuite>
    97  </testsuites>`,
    98  		},
    99  		{
   100  			name: "happy path with/without period description should return with period",
   101  			detectedVulns: []types.DetectedVulnerability{
   102  				{
   103  					VulnerabilityID: "CVE-2019-0000",
   104  					PkgName:         "foo",
   105  					Vulnerability: dbTypes.Vulnerability{
   106  						Description: "without period",
   107  					},
   108  				},
   109  				{
   110  					VulnerabilityID: "CVE-2019-0000",
   111  					PkgName:         "bar",
   112  					Vulnerability: dbTypes.Vulnerability{
   113  						Description: "with period.",
   114  					},
   115  				},
   116  				{
   117  					VulnerabilityID: "CVE-2019-0000",
   118  					PkgName:         "bar",
   119  					Vulnerability: dbTypes.Vulnerability{
   120  						Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
   121  					},
   122  				},
   123  			},
   124  			template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
   125  			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 &#39;easy&#39; handle in Curl_close()."`,
   126  		},
   127  		{
   128  			name: "Calculate using sprig",
   129  			detectedVulns: []types.DetectedVulnerability{
   130  				{
   131  					VulnerabilityID: "CVE-2019-0000",
   132  					PkgName:         "foo",
   133  					Vulnerability: dbTypes.Vulnerability{
   134  						Description: "without period",
   135  						Severity:    dbTypes.SeverityCritical.String(),
   136  					},
   137  				},
   138  				{
   139  					VulnerabilityID: "CVE-2019-0000",
   140  					PkgName:         "bar",
   141  					Vulnerability: dbTypes.Vulnerability{
   142  						Description: "with period.",
   143  						Severity:    dbTypes.SeverityCritical.String(),
   144  					},
   145  				},
   146  				{
   147  					VulnerabilityID: "CVE-2019-0000",
   148  					PkgName:         "bar",
   149  					Vulnerability: dbTypes.Vulnerability{
   150  						Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
   151  						Severity:    dbTypes.SeverityHigh.String(),
   152  					},
   153  				},
   154  			},
   155  			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 }}`,
   156  			expected: `Critical: 2, High: 1`,
   157  		},
   158  		{
   159  			name:          "happy path: env var parsing",
   160  			detectedVulns: []types.DetectedVulnerability{},
   161  			template:      `{{ lower (env "AWS_ACCOUNT_ID") }}`,
   162  			expected:      `123456789012`,
   163  		},
   164  	}
   165  	for _, tc := range testCases {
   166  		t.Run(tc.name, func(t *testing.T) {
   167  			clock.SetFakeTime(t, time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC))
   168  
   169  			os.Setenv("AWS_ACCOUNT_ID", "123456789012")
   170  			got := bytes.Buffer{}
   171  			inputReport := types.Report{
   172  				Results: types.Results{
   173  					{
   174  						Target:          "foojunit",
   175  						Type:            "test",
   176  						Vulnerabilities: tc.detectedVulns,
   177  					},
   178  				},
   179  			}
   180  
   181  			w, err := report.NewTemplateWriter(&got, tc.template)
   182  			require.NoError(t, err)
   183  			err = w.Write(inputReport)
   184  			assert.NoError(t, err)
   185  			assert.Equal(t, tc.expected, got.String())
   186  		})
   187  	}
   188  }