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

     1  package report_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"testing"
     7  
     8  	"github.com/owenrumney/go-sarif/v2/sarif"
     9  	"github.com/samber/lo"
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    13  	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
    14  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    15  	"github.com/devseccon/trivy/pkg/report"
    16  	"github.com/devseccon/trivy/pkg/types"
    17  )
    18  
    19  func TestReportWriter_Sarif(t *testing.T) {
    20  	tests := []struct {
    21  		name  string
    22  		input types.Report
    23  		want  *sarif.Report
    24  	}{
    25  		{
    26  			name: "report with vulnerabilities",
    27  			input: types.Report{
    28  				ArtifactName: "debian:9",
    29  				ArtifactType: ftypes.ArtifactContainerImage,
    30  				Metadata: types.Metadata{
    31  					RepoTags: []string{
    32  						"debian:9",
    33  					},
    34  					RepoDigests: []string{
    35  						"debian@sha256:a8cc1744bbdd5266678e3e8b3e6387e45c053218438897e86876f2eb104e5534",
    36  					},
    37  				},
    38  				Results: types.Results{
    39  					{
    40  						Target: "library/test",
    41  						Class:  types.ClassOSPkg,
    42  						Packages: []ftypes.Package{
    43  							{
    44  								Name:    "foo",
    45  								Version: "1.2.3",
    46  								Locations: []ftypes.Location{
    47  									{
    48  										StartLine: 5,
    49  										EndLine:   10,
    50  									},
    51  									{
    52  										StartLine: 15,
    53  										EndLine:   20,
    54  									},
    55  								},
    56  							},
    57  						},
    58  						Vulnerabilities: []types.DetectedVulnerability{
    59  							{
    60  								VulnerabilityID:  "CVE-2020-0001",
    61  								PkgName:          "foo",
    62  								InstalledVersion: "1.2.3",
    63  								FixedVersion:     "3.4.5",
    64  								PrimaryURL:       "https://avd.aquasec.com/nvd/cve-2020-0001",
    65  								SeveritySource:   "redhat",
    66  								Vulnerability: dbTypes.Vulnerability{
    67  									Title:       "foobar",
    68  									Description: "baz",
    69  									Severity:    "HIGH",
    70  									VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
    71  										vulnerability.NVD:    dbTypes.SeverityCritical,
    72  										vulnerability.RedHat: dbTypes.SeverityHigh,
    73  									},
    74  									CVSS: map[dbTypes.SourceID]dbTypes.CVSS{
    75  										vulnerability.NVD: {
    76  											V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
    77  											V3Score:  9.8,
    78  										},
    79  										vulnerability.RedHat: {
    80  											V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
    81  											V3Score:  7.5,
    82  										},
    83  									},
    84  								},
    85  							},
    86  						},
    87  					},
    88  				},
    89  			},
    90  			want: &sarif.Report{
    91  				Version: "2.1.0",
    92  				Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
    93  				Runs: []*sarif.Run{
    94  					{
    95  						Tool: sarif.Tool{
    96  							Driver: &sarif.ToolComponent{
    97  								FullName:       lo.ToPtr("Trivy Vulnerability Scanner"),
    98  								Name:           "Trivy",
    99  								Version:        lo.ToPtr(""),
   100  								InformationURI: lo.ToPtr("https://github.com/devseccon/trivy"),
   101  								Rules: []*sarif.ReportingDescriptor{
   102  									{
   103  										ID:               "CVE-2020-0001",
   104  										Name:             lo.ToPtr("OsPackageVulnerability"),
   105  										ShortDescription: &sarif.MultiformatMessageString{Text: lo.ToPtr("foobar")},
   106  										FullDescription:  &sarif.MultiformatMessageString{Text: lo.ToPtr("baz")},
   107  										DefaultConfiguration: &sarif.ReportingConfiguration{
   108  											Level: "error",
   109  										},
   110  										HelpURI: lo.ToPtr("https://avd.aquasec.com/nvd/cve-2020-0001"),
   111  										Properties: map[string]interface{}{
   112  											"tags": []interface{}{
   113  												"vulnerability",
   114  												"security",
   115  												"HIGH",
   116  											},
   117  											"precision":         "very-high",
   118  											"security-severity": "7.5",
   119  										},
   120  										Help: &sarif.MultiformatMessageString{
   121  											Text:     lo.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"),
   122  											Markdown: lo.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"),
   123  										},
   124  									},
   125  								},
   126  							},
   127  						},
   128  						Results: []*sarif.Result{
   129  							{
   130  								RuleID:    lo.ToPtr("CVE-2020-0001"),
   131  								RuleIndex: lo.ToPtr[uint](0),
   132  								Level:     lo.ToPtr("error"),
   133  								Message:   sarif.Message{Text: lo.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)")},
   134  								Locations: []*sarif.Location{
   135  									{
   136  										Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")},
   137  										PhysicalLocation: &sarif.PhysicalLocation{
   138  											ArtifactLocation: &sarif.ArtifactLocation{
   139  												URI:       lo.ToPtr("library/test"),
   140  												URIBaseId: lo.ToPtr("ROOTPATH"),
   141  											},
   142  											Region: &sarif.Region{
   143  												StartLine:   lo.ToPtr(5),
   144  												EndLine:     lo.ToPtr(10),
   145  												StartColumn: lo.ToPtr(1),
   146  												EndColumn:   lo.ToPtr(1),
   147  											},
   148  										},
   149  									},
   150  									{
   151  										Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")},
   152  										PhysicalLocation: &sarif.PhysicalLocation{
   153  											ArtifactLocation: &sarif.ArtifactLocation{
   154  												URI:       lo.ToPtr("library/test"),
   155  												URIBaseId: lo.ToPtr("ROOTPATH"),
   156  											},
   157  											Region: &sarif.Region{
   158  												StartLine:   lo.ToPtr(15),
   159  												EndLine:     lo.ToPtr(20),
   160  												StartColumn: lo.ToPtr(1),
   161  												EndColumn:   lo.ToPtr(1),
   162  											},
   163  										},
   164  									},
   165  								},
   166  							},
   167  						},
   168  						ColumnKind: "utf16CodeUnits",
   169  						OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{
   170  							"ROOTPATH": {
   171  								URI: lo.ToPtr("file:///"),
   172  							},
   173  						},
   174  						PropertyBag: sarif.PropertyBag{
   175  							Properties: map[string]interface{}{
   176  								"imageName":   "debian:9",
   177  								"repoDigests": []interface{}{"debian@sha256:a8cc1744bbdd5266678e3e8b3e6387e45c053218438897e86876f2eb104e5534"},
   178  								"repoTags":    []interface{}{"debian:9"},
   179  							},
   180  						},
   181  					},
   182  				},
   183  			},
   184  		},
   185  		{
   186  			name: "report with misconfigurations",
   187  			input: types.Report{
   188  				Results: types.Results{
   189  					{
   190  						Target: "library/test",
   191  						Class:  types.ClassConfig,
   192  						Misconfigurations: []types.DetectedMisconfiguration{
   193  							{
   194  								Type:       "Kubernetes Security Check",
   195  								ID:         "KSV001",
   196  								Title:      "Image tag ':latest' used",
   197  								Message:    "Message",
   198  								Severity:   "HIGH",
   199  								PrimaryURL: "https://avd.aquasec.com/appshield/ksv001",
   200  								Status:     types.StatusFailure,
   201  							},
   202  							{
   203  								Type:       "Kubernetes Security Check",
   204  								ID:         "KSV002",
   205  								Title:      "SYS_ADMIN capability added",
   206  								Message:    "Message",
   207  								Severity:   "CRITICAL",
   208  								PrimaryURL: "https://avd.aquasec.com/appshield/ksv002",
   209  								Status:     types.StatusPassed,
   210  							},
   211  						},
   212  					},
   213  				},
   214  			},
   215  			want: &sarif.Report{
   216  				Version: "2.1.0",
   217  				Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
   218  				Runs: []*sarif.Run{
   219  					{
   220  						Tool: sarif.Tool{
   221  							Driver: &sarif.ToolComponent{
   222  								FullName:       lo.ToPtr("Trivy Vulnerability Scanner"),
   223  								Name:           "Trivy",
   224  								Version:        lo.ToPtr(""),
   225  								InformationURI: lo.ToPtr("https://github.com/devseccon/trivy"),
   226  								Rules: []*sarif.ReportingDescriptor{
   227  									{
   228  										ID:               "KSV001",
   229  										Name:             lo.ToPtr("Misconfiguration"),
   230  										ShortDescription: &sarif.MultiformatMessageString{Text: lo.ToPtr("Image tag ':latest' used")},
   231  										FullDescription:  &sarif.MultiformatMessageString{Text: lo.ToPtr("")},
   232  										DefaultConfiguration: &sarif.ReportingConfiguration{
   233  											Level: "error",
   234  										},
   235  										HelpURI: lo.ToPtr("https://avd.aquasec.com/appshield/ksv001"),
   236  										Properties: map[string]interface{}{
   237  											"tags": []interface{}{
   238  												"misconfiguration",
   239  												"security",
   240  												"HIGH",
   241  											},
   242  											"precision":         "very-high",
   243  											"security-severity": "8.0",
   244  										},
   245  										Help: &sarif.MultiformatMessageString{
   246  											Text:     lo.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"),
   247  											Markdown: lo.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"),
   248  										},
   249  									},
   250  									{
   251  										ID:               "KSV002",
   252  										Name:             lo.ToPtr("Misconfiguration"),
   253  										ShortDescription: &sarif.MultiformatMessageString{Text: lo.ToPtr("SYS_ADMIN capability added")},
   254  										FullDescription:  &sarif.MultiformatMessageString{Text: lo.ToPtr("")},
   255  										DefaultConfiguration: &sarif.ReportingConfiguration{
   256  											Level: "error",
   257  										},
   258  										HelpURI: lo.ToPtr("https://avd.aquasec.com/appshield/ksv002"),
   259  										Properties: map[string]interface{}{
   260  											"tags": []interface{}{
   261  												"misconfiguration",
   262  												"security",
   263  												"CRITICAL",
   264  											},
   265  											"precision":         "very-high",
   266  											"security-severity": "9.5",
   267  										},
   268  										Help: &sarif.MultiformatMessageString{
   269  											Text:     lo.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"),
   270  											Markdown: lo.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"),
   271  										},
   272  									},
   273  								},
   274  							},
   275  						},
   276  						Results: []*sarif.Result{
   277  							{
   278  								RuleID:    lo.ToPtr("KSV001"),
   279  								RuleIndex: lo.ToPtr[uint](0),
   280  								Level:     lo.ToPtr("error"),
   281  								Message:   sarif.Message{Text: lo.ToPtr("Artifact: library/test\\nType: \\nVulnerability KSV001\\nSeverity: HIGH\\nMessage: Message\\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")},
   282  								Locations: []*sarif.Location{
   283  									{
   284  										Message: &sarif.Message{Text: lo.ToPtr("library/test")},
   285  										PhysicalLocation: &sarif.PhysicalLocation{
   286  											ArtifactLocation: &sarif.ArtifactLocation{
   287  												URI:       lo.ToPtr("library/test"),
   288  												URIBaseId: lo.ToPtr("ROOTPATH"),
   289  											},
   290  											Region: &sarif.Region{
   291  												StartLine:   lo.ToPtr(1),
   292  												EndLine:     lo.ToPtr(1),
   293  												StartColumn: lo.ToPtr(1),
   294  												EndColumn:   lo.ToPtr(1),
   295  											},
   296  										},
   297  									},
   298  								},
   299  							},
   300  							{
   301  								RuleID:    lo.ToPtr("KSV002"),
   302  								RuleIndex: lo.ToPtr[uint](1),
   303  								Level:     lo.ToPtr("error"),
   304  								Message:   sarif.Message{Text: lo.ToPtr("Artifact: library/test\\nType: \\nVulnerability KSV002\\nSeverity: CRITICAL\\nMessage: Message\\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")},
   305  								Locations: []*sarif.Location{
   306  									{
   307  										Message: &sarif.Message{Text: lo.ToPtr("library/test")},
   308  										PhysicalLocation: &sarif.PhysicalLocation{
   309  											ArtifactLocation: &sarif.ArtifactLocation{
   310  												URI:       lo.ToPtr("library/test"),
   311  												URIBaseId: lo.ToPtr("ROOTPATH"),
   312  											},
   313  											Region: &sarif.Region{
   314  												StartLine:   lo.ToPtr(1),
   315  												EndLine:     lo.ToPtr(1),
   316  												StartColumn: lo.ToPtr(1),
   317  												EndColumn:   lo.ToPtr(1),
   318  											},
   319  										},
   320  									},
   321  								},
   322  							},
   323  						},
   324  						ColumnKind: "utf16CodeUnits",
   325  						OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{
   326  							"ROOTPATH": {
   327  								URI: lo.ToPtr("file:///"),
   328  							},
   329  						},
   330  					},
   331  				},
   332  			},
   333  		},
   334  		{
   335  			name: "report with secrets",
   336  			input: types.Report{
   337  				Results: types.Results{
   338  					{
   339  						Target: "library/test",
   340  						Class:  types.ClassSecret,
   341  						Secrets: []ftypes.SecretFinding{
   342  							{
   343  								RuleID:    "aws-secret-access-key",
   344  								Category:  "AWS",
   345  								Severity:  "CRITICAL",
   346  								Title:     "AWS Secret Access Key",
   347  								StartLine: 1,
   348  								EndLine:   1,
   349  								Match:     "'AWS_secret_KEY'=\"****************************************\"",
   350  							},
   351  						},
   352  					},
   353  				},
   354  			},
   355  			want: &sarif.Report{
   356  				Version: "2.1.0",
   357  				Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
   358  				Runs: []*sarif.Run{
   359  					{
   360  						Tool: sarif.Tool{
   361  							Driver: &sarif.ToolComponent{
   362  								FullName:       lo.ToPtr("Trivy Vulnerability Scanner"),
   363  								Name:           "Trivy",
   364  								Version:        lo.ToPtr(""),
   365  								InformationURI: lo.ToPtr("https://github.com/devseccon/trivy"),
   366  								Rules: []*sarif.ReportingDescriptor{
   367  									{
   368  										ID:               "aws-secret-access-key",
   369  										Name:             lo.ToPtr("Secret"),
   370  										ShortDescription: &sarif.MultiformatMessageString{Text: lo.ToPtr("AWS Secret Access Key")},
   371  										FullDescription:  &sarif.MultiformatMessageString{Text: lo.ToPtr("\u0026#39;AWS_secret_KEY\u0026#39;=\u0026#34;****************************************\u0026#34;")},
   372  										DefaultConfiguration: &sarif.ReportingConfiguration{
   373  											Level: "error",
   374  										},
   375  										HelpURI: lo.ToPtr("https://github.com/devseccon/trivy/blob/main/pkg/fanal/secret/builtin-rules.go"),
   376  										Properties: map[string]interface{}{
   377  											"tags": []interface{}{
   378  												"secret",
   379  												"security",
   380  												"CRITICAL",
   381  											},
   382  											"precision":         "very-high",
   383  											"security-severity": "9.5",
   384  										},
   385  										Help: &sarif.MultiformatMessageString{
   386  											Text:     lo.ToPtr("Secret AWS Secret Access Key\\nSeverity: CRITICAL\\nMatch: 'AWS_secret_KEY'=\"****************************************\""),
   387  											Markdown: lo.ToPtr("**Secret AWS Secret Access Key**\\n| Severity | Match |\\n| --- | --- |\\n|CRITICAL|'AWS_secret_KEY'=\"****************************************\"|"),
   388  										},
   389  									},
   390  								},
   391  							},
   392  						},
   393  						Results: []*sarif.Result{
   394  							{
   395  								RuleID:    lo.ToPtr("aws-secret-access-key"),
   396  								RuleIndex: lo.ToPtr[uint](0),
   397  								Level:     lo.ToPtr("error"),
   398  								Message:   sarif.Message{Text: lo.ToPtr("Artifact: library/test\\nType: \\nSecret AWS Secret Access Key\\nSeverity: CRITICAL\\nMatch: 'AWS_secret_KEY'=\"****************************************\"")},
   399  								Locations: []*sarif.Location{
   400  									{
   401  										Message: &sarif.Message{Text: lo.ToPtr("library/test")},
   402  										PhysicalLocation: &sarif.PhysicalLocation{
   403  											ArtifactLocation: &sarif.ArtifactLocation{
   404  												URI:       lo.ToPtr("library/test"),
   405  												URIBaseId: lo.ToPtr("ROOTPATH"),
   406  											},
   407  											Region: &sarif.Region{
   408  												StartLine:   lo.ToPtr(1),
   409  												EndLine:     lo.ToPtr(1),
   410  												StartColumn: lo.ToPtr(1),
   411  												EndColumn:   lo.ToPtr(1),
   412  											},
   413  										},
   414  									},
   415  								},
   416  							},
   417  						},
   418  						ColumnKind: "utf16CodeUnits",
   419  						OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{
   420  							"ROOTPATH": {
   421  								URI: lo.ToPtr("file:///"),
   422  							},
   423  						},
   424  					},
   425  				},
   426  			},
   427  		},
   428  		{
   429  			name: "report with licenses",
   430  			input: types.Report{
   431  				Results: types.Results{
   432  					{
   433  						Target: "OS Packages",
   434  						Class:  "license",
   435  						Licenses: []types.DetectedLicense{
   436  							{
   437  								Severity:   "HIGH",
   438  								Category:   "restricted",
   439  								PkgName:    "alpine-base",
   440  								FilePath:   "",
   441  								Name:       "GPL-3.0",
   442  								Confidence: 1,
   443  								Link:       "",
   444  							},
   445  						},
   446  					},
   447  				},
   448  			},
   449  			want: &sarif.Report{
   450  				Version: "2.1.0",
   451  				Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
   452  				Runs: []*sarif.Run{
   453  					{
   454  						Tool: sarif.Tool{
   455  							Driver: &sarif.ToolComponent{
   456  								FullName:       lo.ToPtr("Trivy Vulnerability Scanner"),
   457  								Name:           "Trivy",
   458  								Version:        lo.ToPtr(""),
   459  								InformationURI: lo.ToPtr("https://github.com/devseccon/trivy"),
   460  								Rules: []*sarif.ReportingDescriptor{
   461  									{
   462  										ID:                   "alpine-base:GPL-3.0",
   463  										Name:                 lo.ToPtr("License"),
   464  										ShortDescription:     sarif.NewMultiformatMessageString("GPL-3.0 in alpine-base"),
   465  										FullDescription:      sarif.NewMultiformatMessageString("GPL-3.0 in alpine-base"),
   466  										DefaultConfiguration: sarif.NewReportingConfiguration().WithLevel("error"),
   467  										Help: sarif.NewMultiformatMessageString("License GPL-3.0\\nClassification: restricted\\nPkgName: alpine-base\\nPath: ").
   468  											WithMarkdown("**License GPL-3.0**\\n| PkgName | Classification | Path |\\n| --- | --- | --- |\\n|alpine-base|restricted||"),
   469  										Properties: map[string]interface{}{
   470  											"tags": []interface{}{
   471  												"license",
   472  												"security",
   473  												"HIGH",
   474  											},
   475  											"precision":         "very-high",
   476  											"security-severity": "8.0",
   477  										},
   478  									},
   479  								},
   480  							},
   481  						},
   482  						Results: []*sarif.Result{
   483  							{
   484  								RuleID:    lo.ToPtr("alpine-base:GPL-3.0"),
   485  								RuleIndex: lo.ToPtr(uint(0)),
   486  								Level:     lo.ToPtr("error"),
   487  								Message:   sarif.Message{Text: lo.ToPtr("Artifact: OS Packages\\nLicense GPL-3.0\\nPkgName: restricted\\n Classification: alpine-base\\n Path: ")},
   488  								Locations: []*sarif.Location{
   489  									{
   490  										Message: sarif.NewTextMessage(""),
   491  										PhysicalLocation: &sarif.PhysicalLocation{
   492  											ArtifactLocation: &sarif.ArtifactLocation{
   493  												URI:       lo.ToPtr("OS Packages"),
   494  												URIBaseId: lo.ToPtr("ROOTPATH"),
   495  											},
   496  											Region: &sarif.Region{
   497  												StartLine:   lo.ToPtr(1),
   498  												EndLine:     lo.ToPtr(1),
   499  												StartColumn: lo.ToPtr(1),
   500  												EndColumn:   lo.ToPtr(1),
   501  											},
   502  										},
   503  									},
   504  								},
   505  							},
   506  						},
   507  						ColumnKind: "utf16CodeUnits",
   508  						OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{
   509  							"ROOTPATH": {
   510  								URI: lo.ToPtr("file:///"),
   511  							},
   512  						},
   513  					},
   514  				},
   515  			},
   516  		},
   517  		{
   518  			name: "no vulns",
   519  			want: &sarif.Report{
   520  				Version: "2.1.0",
   521  				Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
   522  				Runs: []*sarif.Run{
   523  					{
   524  						Tool: sarif.Tool{
   525  							Driver: &sarif.ToolComponent{
   526  								FullName:       lo.ToPtr("Trivy Vulnerability Scanner"),
   527  								Name:           "Trivy",
   528  								Version:        lo.ToPtr(""),
   529  								InformationURI: lo.ToPtr("https://github.com/devseccon/trivy"),
   530  								Rules:          []*sarif.ReportingDescriptor{},
   531  							},
   532  						},
   533  						Results:    []*sarif.Result{},
   534  						ColumnKind: "utf16CodeUnits",
   535  						OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{
   536  							"ROOTPATH": {
   537  								URI: lo.ToPtr("file:///"),
   538  							},
   539  						},
   540  					},
   541  				},
   542  			},
   543  		},
   544  	}
   545  
   546  	for _, tt := range tests {
   547  		t.Run(tt.name, func(t *testing.T) {
   548  			sarifWritten := bytes.NewBuffer(nil)
   549  			w := report.SarifWriter{
   550  				Output: sarifWritten,
   551  			}
   552  			err := w.Write(tt.input)
   553  			assert.NoError(t, err)
   554  
   555  			result := &sarif.Report{}
   556  			err = json.Unmarshal(sarifWritten.Bytes(), result)
   557  			assert.NoError(t, err)
   558  			assert.Equal(t, tt.want, result)
   559  		})
   560  	}
   561  }
   562  
   563  func TestToPathUri(t *testing.T) {
   564  	tests := []struct {
   565  		input       string
   566  		resultClass types.ResultClass
   567  		output      string
   568  	}{
   569  		{
   570  			input:       "almalinux@sha256:08042694fffd61e6a0b3a22dadba207c8937977915ff6b1879ad744fd6638837",
   571  			resultClass: types.ClassOSPkg,
   572  			output:      "library/almalinux",
   573  		},
   574  		{
   575  			input:       "alpine:latest (alpine 3.13.4)",
   576  			resultClass: types.ClassOSPkg,
   577  			output:      "library/alpine",
   578  		},
   579  		{
   580  			input:       "docker.io/my-organization/my-app:2c6912aee7bde44b84d810aed106ca84f40e2e29",
   581  			resultClass: types.ClassOSPkg,
   582  			output:      "my-organization/my-app",
   583  		},
   584  		{
   585  			input:       "lib/test",
   586  			resultClass: types.ClassLangPkg,
   587  			output:      "lib/test",
   588  		},
   589  		{
   590  			input:       "lib(2)/test",
   591  			resultClass: types.ClassSecret,
   592  			output:      "lib(2)/test",
   593  		},
   594  	}
   595  
   596  	for _, test := range tests {
   597  		got := report.ToPathUri(test.input, test.resultClass)
   598  		if got != test.output {
   599  			t.Errorf("toPathUri(%q) got %q, wanted %q", test.input, got, test.output)
   600  		}
   601  	}
   602  }