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 }