Skip to content

Commit 8c9e565

Browse files
authored
chore: make test result metadata more generic (#485)
1 parent da2c524 commit 8c9e565

File tree

10 files changed

+80
-58
lines changed

10 files changed

+80
-58
lines changed

internal/presenters/presenter_ufm_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@ func normalizeAutomationID(run map[string]interface{}) {
5555
if automationDetails, ok := run["automationDetails"].(map[string]interface{}); ok {
5656
if id, ok := automationDetails["id"].(string); ok {
5757
parts := strings.Split(id, "/")
58+
offset := 1
59+
if len(parts) >= 5 {
60+
offset = 2
61+
}
5862
if len(parts) >= 2 {
59-
automationDetails["id"] = strings.Join(parts[:len(parts)-1], "/") + "/*"
63+
automationDetails["id"] = strings.Join(parts[:len(parts)-offset], "/") + "/*"
6064
}
6165
}
6266
}
@@ -67,7 +71,6 @@ func normalizeAutomationID(run map[string]interface{}) {
6771
func normalizeToolProperties(run map[string]interface{}) {
6872
if tool, ok := run["tool"].(map[string]interface{}); ok {
6973
if driver, ok := tool["driver"].(map[string]interface{}); ok {
70-
delete(driver, "properties")
7174
normalizeRules(driver)
7275
}
7376
}
@@ -429,7 +432,7 @@ func normalizeSarifForComparison(t *testing.T, sarifJSON string) map[string]inte
429432
// Normalize automation ID (missing project name in actual output)
430433
normalizeAutomationID(run)
431434

432-
// Normalize tool properties and rules (artifactsScanned missing, CVSS formatting, license wording)
435+
// Normalize tool properties and rules (CVSS formatting, license wording)
433436
normalizeToolProperties(run)
434437

435438
// Normalize result messages (license issue wording)
@@ -514,7 +517,6 @@ func Test_UfmPresenter_Sarif(t *testing.T) {
514517
if !assert.JSONEq(t, string(expectedJSON), string(actualJSON),
515518
"SARIF output differs. Known gaps are normalized:\n"+
516519
"- Automation ID: missing project name\n"+
517-
"- Tool properties: missing artifactsScanned\n"+
518520
"- Suppressions: not included in original SARIF\n"+
519521
"- Fix packages: using vulnerable package instead of direct dependency\n"+
520522
"- Package versions: may differ based on dependency path selection\n"+

internal/presenters/templates/ufm.sarif.tmpl

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,49 @@
99
{{- range $findingTypeIndex, $findingType := $findingTypes }}
1010
{{- $issues := getIssuesFromTestResult $result $findingType }}
1111
{{- $issuesSize := sub (len $issues) 1 }}
12+
{{- $metadata := $result.GetMetadata }}
1213
{
1314
"tool": {
1415
"driver" : {
1516
"name" : "{{- convertTypeToDriverName (printf "%s" $findingType) }}",
1617
"semanticVersion" : "{{- getRuntimeInfo "version" }}",
1718
"version" : "{{- getRuntimeInfo "version" }}",
1819
"informationUri" : "https://docs.snyk.io/",
19-
"rules" : [
20-
{{- range $index, $issue := $issues }}
21-
{{- $tags := buildRuleTags $issue }}
22-
{{- $cvssScore := getRuleCVSSScore $issue }}
23-
{
24-
"id": {{ getQuotedString $issue.GetID }},
25-
"shortDescription": {
26-
"text": {{ getQuotedString (buildRuleShortDescription $issue) }}
27-
},
28-
"fullDescription": {
29-
"text": {{ getQuotedString (buildRuleFullDescription $issue) }}
30-
},
31-
"help": {
32-
"text": "",
33-
"markdown": {{ getQuotedString (buildRuleHelpMarkdown $issue $findingType) }}
34-
},
35-
"properties": {
36-
"tags": [
37-
{{- $tagsSize := sub (len $tags) 1 }}
38-
{{- range $tagIndex, $tag := $tags }}
39-
{{ getQuotedString (printf "%v" $tag) }}{{if lt $tagIndex $tagsSize}},{{end}}
40-
{{- end }}
41-
]{{if ge $cvssScore 0.0}},
42-
"cvssv3_baseScore": {{ $cvssScore }},
43-
"security-severity": {{ getQuotedString (printf "%.1f" $cvssScore) }}{{end}}
44-
}
45-
}{{if lt $index $issuesSize}},{{end}}
20+
"rules" : [
21+
{{- range $index, $issue := $issues }}
22+
{{- $tags := buildRuleTags $issue }}
23+
{{- $cvssScore := getRuleCVSSScore $issue }}
24+
{
25+
"id": {{ getQuotedString $issue.GetID }},
26+
"shortDescription": {
27+
"text": {{ getQuotedString (buildRuleShortDescription $issue) }}
28+
},
29+
"fullDescription": {
30+
"text": {{ getQuotedString (buildRuleFullDescription $issue) }}
31+
},
32+
"help": {
33+
"text": "",
34+
"markdown": {{ getQuotedString (buildRuleHelpMarkdown $issue $findingType) }}
35+
},
36+
"properties": {
37+
"tags": [
38+
{{- $tagsSize := sub (len $tags) 1 }}
39+
{{- range $tagIndex, $tag := $tags }}
40+
{{ getQuotedString (printf "%v" $tag) }}{{if lt $tagIndex $tagsSize}},{{end}}
41+
{{- end }}
42+
]{{if ge $cvssScore 0.0}},
43+
"cvssv3_baseScore": {{ $cvssScore }},
44+
"security-severity": {{ getQuotedString (printf "%.1f" $cvssScore) }}{{end}}
45+
}
46+
}{{if lt $index $issuesSize}},{{end}}
47+
{{- end }}
48+
]
49+
{{- $dependencyCount := index $metadata "dependency-count" }}
50+
{{- if $dependencyCount }},
51+
"properties": {
52+
"artifactsScanned": {{ $dependencyCount }}
53+
}
4654
{{- end }}
47-
]
4855
}
4956
},
5057
"results": [
@@ -200,8 +207,8 @@
200207
},
201208
*/ -}}
202209
"automationDetails": {
203-
{{- $projectName := index $result.GetMetadata "project-name" }}
204-
"id": "{{- getAutomationDetailsId $projectName (printf "%s" $findingType) }}"
210+
{{- $projectName := index $metadata "project-name" }}
211+
"id": "{{- getAutomationDetailsId (printf "%s/%d" $projectName $resultIndex) (printf "%s" $findingType) }}"
205212
}
206213
}{{if or (lt $findingTypeIndex $findingTypesSize) (lt $resultIndex (sub (len $.TestResults) 1))}},{{end}}{{- end}}
207214
{{- end}}

internal/presenters/testdata/ufm/multi_project.testresult.json

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"metadata": {
1313
"project-name": "tpwe",
1414
"display-target-file": "dotnet/obj/project.assets.json",
15-
"target-directory": "multi-project"
15+
"target-directory": "multi-project",
16+
"dependency-count": 84
1617
},
1718
"createdAt": "2025-11-13T15:29:14.585711Z",
1819
"testSubject": {
@@ -824,7 +825,8 @@
824825
"metadata": {
825826
"project-name": "package.json",
826827
"display-target-file": "ghost/package.json",
827-
"target-directory": "multi-project"
828+
"target-directory": "multi-project",
829+
"dependency-count": 23
828830
},
829831
"createdAt": "2025-11-13T15:29:14.491151Z",
830832
"testSubject": {
@@ -4388,7 +4390,8 @@
43884390
"metadata": {
43894391
"project-name": "golangproject",
43904392
"display-target-file": "golang/go.mod",
4391-
"target-directory": "multi-project"
4393+
"target-directory": "multi-project",
4394+
"dependency-count": 29
43924395
},
43934396
"createdAt": "2025-11-13T15:29:14.506347Z",
43944397
"testSubject": {
@@ -4543,7 +4546,8 @@
45434546
"metadata": {
45444547
"project-name": "demo:maven-demo",
45454548
"display-target-file": "maven/pom.xml",
4546-
"target-directory": "multi-project"
4549+
"target-directory": "multi-project",
4550+
"dependency-count": 4
45474551
},
45484552
"createdAt": "2025-11-13T15:29:14.480432Z",
45494553
"testSubject": {
@@ -4595,7 +4599,8 @@
45954599
"metadata": {
45964600
"project-name": "not-python",
45974601
"display-target-file": "not-python/requirements.txt",
4598-
"target-directory": "multi-project"
4602+
"target-directory": "multi-project",
4603+
"dependency-count": 7
45994604
},
46004605
"createdAt": "2025-11-13T15:29:14.440374Z",
46014606
"testSubject": {
@@ -6413,7 +6418,8 @@
64136418
"metadata": {
64146419
"project-name": "python",
64156420
"display-target-file": "python/requirements.txt",
6416-
"target-directory": "multi-project"
6421+
"target-directory": "multi-project",
6422+
"dependency-count": 7
64176423
},
64186424
"createdAt": "2025-11-13T15:29:18.009229Z",
64196425
"testSubject": {
@@ -8228,7 +8234,8 @@
82288234
"metadata": {
82298235
"project-name": "tsc",
82308236
"display-target-file": "tsc/package.json",
8231-
"target-directory": "multi-project"
8237+
"target-directory": "multi-project",
8238+
"dependency-count": 23
82328239
},
82338240
"createdAt": "2025-11-13T15:29:19.594887Z",
82348241
"testSubject": {

internal/presenters/testdata/ufm/testresult_cli.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"metadata": {
3636
"project-name": "snyk",
3737
"display-target-file": "package.json",
38-
"target-directory": "test-project"
38+
"target-directory": "test-project",
39+
"dependency-count": 552
3940
},
4041
"createdAt": "2025-10-28T15:42:29.689452Z",
4142
"testSubject": {

internal/presenters/testdata/ufm/webgoat.ignore.sarif.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"semanticVersion": "1.1301.0",
1010
"version": "1.1301.0",
1111
"informationUri": "https://docs.snyk.io/",
12+
"properties": {
13+
"artifactsScanned": 163
14+
},
1215
"rules": [
1316
{
1417
"id": "SNYK-JAVA-CHQOSLOGBACK-13169722",
@@ -6179,7 +6182,7 @@
61796182
}
61806183
],
61816184
"automationDetails": {
6182-
"id": "Snyk/Open Source/2025-11-12T17:36:30Z"
6185+
"id": "Snyk/Open Source/webgoat/2025-11-12T17:36:30Z"
61836186
}
61846187
}
61856188
]

internal/presenters/testdata/ufm/webgoat.ignore.testresult.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
}
1414
},
1515
"metadata": {
16-
"project-name": "",
16+
"project-name": "webgoat",
1717
"display-target-file": "pom.xml",
18-
"target-directory": "webgoat"
18+
"target-directory": "webgoat",
19+
"dependency-count": 163
1920
},
2021
"createdAt": "2025-11-12T17:39:33.615146Z",
2122
"testSubject": {

internal/presenters/testdata/ufm/webgoat.testresult.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"metadata": {
2727
"project-name": "org.owasp.webgoat:webgoat",
2828
"display-target-file": "pom.xml",
29-
"target-directory": "webgoat"
29+
"target-directory": "webgoat",
30+
"dependency-count": 163
3031
},
3132
"createdAt": "2025-11-06T14:45:12.489805Z",
3233
"testSubject": {

pkg/apiclients/mocks/testapi.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apiclients/testapi/testapi.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ type TestResult interface {
120120
GetEffectiveSummary() *FindingSummary
121121
GetRawSummary() *FindingSummary
122122

123-
SetMetadata(key string, value string)
124-
GetMetadata() map[string]string
123+
SetMetadata(key string, value interface{})
124+
GetMetadata() map[string]interface{}
125125

126126
Findings(ctx context.Context) (resultFindings []FindingData, complete bool, err error)
127127
}
@@ -200,7 +200,7 @@ type testResult struct {
200200
findingsError error // Error encountered during findings fetch, if any
201201
findingsOnce sync.Once // Ensures findings are fetched only once
202202

203-
metadata map[string]string
203+
metadata map[string]interface{}
204204

205205
// Unexported reference to the parent handle to access client and orgID for fetching
206206
handle *testHandle // Populated when testResult is created
@@ -246,12 +246,12 @@ func (r *testResult) GetEffectiveSummary() *FindingSummary { return r.EffectiveS
246246
func (r *testResult) GetRawSummary() *FindingSummary { return r.RawSummary }
247247

248248
// SetMetadata sets the metadata for the given key.
249-
func (r *testResult) SetMetadata(key string, value string) {
249+
func (r *testResult) SetMetadata(key string, value interface{}) {
250250
r.metadata[key] = value
251251
}
252252

253253
// GetMetadata returns the metadata for the given key.
254-
func (r *testResult) GetMetadata() map[string]string {
254+
func (r *testResult) GetMetadata() map[string]interface{} {
255255
return r.metadata
256256
}
257257

@@ -542,7 +542,7 @@ func (h *testHandle) fetchResultStatus(ctx context.Context, testID uuid.UUID) (T
542542
EffectiveSummary: attrs.EffectiveSummary,
543543
RawSummary: attrs.RawSummary,
544544
handle: h,
545-
metadata: make(map[string]string),
545+
metadata: make(map[string]interface{}),
546546
}
547547

548548
if attrs.State != nil {

pkg/utils/ufm/json_test_result.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type jsonTestResult struct {
2727
EffectiveSummary *testapi.FindingSummary `json:"effectiveSummary,omitempty"`
2828
RawSummary *testapi.FindingSummary `json:"rawSummary,omitempty"`
2929
FindingsComplete bool `json:"findingsComplete"`
30-
Metadata map[string]string `json:"metadata,omitempty"`
30+
Metadata map[string]interface{} `json:"metadata,omitempty"`
3131
// Optimized wire format: central problem store (optional, for serialization)
3232
ProblemStore map[string]json.RawMessage `json:"problemStore,omitempty"`
3333
ProblemRefs map[string][]string `json:"_problemRefs,omitempty"`
@@ -105,12 +105,12 @@ func (j *jsonTestResult) GetRawSummary() *testapi.FindingSummary {
105105
}
106106

107107
// SetMetadata sets the metadata for the given key.
108-
func (j *jsonTestResult) SetMetadata(key string, value string) {
108+
func (j *jsonTestResult) SetMetadata(key string, value interface{}) {
109109
j.Metadata[key] = value
110110
}
111111

112112
// GetMetadata returns the metadata for the given key.
113-
func (j *jsonTestResult) GetMetadata() map[string]string {
113+
func (j *jsonTestResult) GetMetadata() map[string]interface{} {
114114
return j.Metadata
115115
}
116116

0 commit comments

Comments
 (0)