Skip to content

Commit e880cea

Browse files
committed
fix: deduplicate SARIF rules by problem ID in UFM output
1 parent 67257bb commit e880cea

5 files changed

Lines changed: 83 additions & 6 deletions

File tree

internal/presenters/funcs.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,27 @@ func getDefaultTemplateFuncMap(config configuration.Configuration, ri runtimeinf
386386
defaultMap["getTargetId"] = func(path string) (string, error) {
387387
return target.GetTargetId(path, target.AutoDetectedTargetId, target.WithConfiguredRepository(config))
388388
}
389+
defaultMap["deduplicateIssuesByProblemID"] = deduplicateIssuesByProblemID
389390

390391
return defaultMap
391392
}
392393

394+
func deduplicateIssuesByProblemID(issues []testapi.Issue) []testapi.Issue {
395+
seen := make(map[string]bool)
396+
result := make([]testapi.Issue, 0, len(issues))
397+
for _, issue := range issues {
398+
id := issue.GetProblemID()
399+
if id == "" {
400+
id = issue.GetID()
401+
}
402+
if !seen[id] {
403+
seen[id] = true
404+
result = append(result, issue)
405+
}
406+
}
407+
return result
408+
}
409+
393410
func convertTypeToIssueName(findingType testapi.FindingType) string {
394411
// Map backend finding types to human-friendly issue group names
395412
switch findingType {

internal/presenters/templates/ufm.sarif.tmpl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
"version" : "{{- getRuntimeInfo "version" }}",
1818
"informationUri" : "https://docs.snyk.io/",
1919
"rules" : [
20-
{{- range $index, $issue := $issues }}
20+
{{- $uniqueRules := deduplicateIssuesByProblemID $issues }}
21+
{{- $rulesSize := sub (len $uniqueRules) 1 }}
22+
{{- range $index, $issue := $uniqueRules }}
2123
{{- $tags := buildRuleTags $issue }}
2224
{{- $cvssScore := getRuleCVSSScore $issue }}
2325
{{- $problemID := $issue.GetProblemID }}
@@ -46,7 +48,7 @@
4648
"cvssv3_baseScore": {{ $cvssScore }},
4749
"security-severity": {{ getQuotedString (printf "%.1f" $cvssScore) }}{{end}}
4850
}
49-
}{{if lt $index $issuesSize}},{{end}}
51+
}{{if lt $index $rulesSize}},{{end}}
5052
{{- end }}
5153
]
5254
{{- $dependencyCount := index $metadata "dependency-count" }}

internal/presenters/testdata/ufm/secrets.human.readable

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
Testing () ...
22

3-
Open Secrets issues: 2
3+
Open Secrets issues: 3
44

55
✗ [HIGH] Generic API Key
66
Finding ID: 99999999-8888-7777-6665-000000000000
77
Info: Do not hardcode passwords or other secrets directly in the source code. Use a secure secret management system instead.
88
Path: config.env, line 3 to 44
99

10+
✗ [HIGH] Generic API Key
11+
Finding ID: dddddddd-8888-7777-6665-000000000000
12+
Info: Do not hardcode passwords or other secrets directly in the source code. Use a secure secret management system instead.
13+
Path: credentials.yml, line 7 to 7
14+
1015
! [PENDING IGNORE...] [CRITICAL] Generic Secret
1116
Finding ID: bbbbbbbb-bbbb-cccc-dddd-eeeeeeeeeeee
1217
Info: Do not hardcode passwords or other secrets directly in the source code. Use a secure secret management system instead.
@@ -35,8 +40,8 @@
3540
│ Test type: Secret Detection │
3641
│ Project path: │
3742
│ │
38-
│ Total secrets issues: 3
43+
│ Total secrets issues: 4
3944
│ Ignored: 1 [ 0 CRITICAL  1 HIGH  0 MEDIUM  0 LOW ] │
40-
│ Open : 2 [ 1 CRITICAL  1 HIGH  0 MEDIUM  0 LOW ] │
45+
│ Open : 3 [ 1 CRITICAL  2 HIGH  0 MEDIUM  0 LOW ] │
4146
╰─────────────────────────────────────────────────────────╯
4247

internal/presenters/testdata/ufm/secrets.sarif.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,28 @@
144144
"kind": "external"
145145
}
146146
]
147+
},
148+
{
149+
"ruleId": "generic-api-key",
150+
"level": "error",
151+
"message": {
152+
"text": "This file contains a high severity Generic API Key vulnerability."
153+
},
154+
"locations": [
155+
{
156+
"physicalLocation": {
157+
"artifactLocation": {
158+
"uri": "credentials.yml"
159+
},
160+
"region": {
161+
"startLine": 7,
162+
"startColumn": 10,
163+
"endLine": 7,
164+
"endColumn": 52
165+
}
166+
}
167+
}
168+
]
147169
}
148170
],"automationDetails": {
149171
"id": "Snyk/Secrets/0/2026-02-03T11:03:16Z"

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@
104104
"_problemRefs": {
105105
"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee": ["generic-api-key", "CWE-798"],
106106
"aaaaaaab-bbbb-cccc-dddd-eeeeeeeeeeee": ["generic-api-key-2", "CWE-798"],
107-
"399f629d-d638-441c-82a0-379e327dc941": ["generic-secret", "CWE-798"]
107+
"399f629d-d638-441c-82a0-379e327dc941": ["generic-secret", "CWE-798"],
108+
"cccccccc-bbbb-cccc-dddd-eeeeeeeeeeee": ["generic-api-key", "CWE-798"]
108109
},
109110
"findings": [
110111
{
@@ -254,6 +255,36 @@
254255
}
255256
},
256257
"type": "findings"
258+
},
259+
{
260+
"attributes": {
261+
"cause_of_failure": false,
262+
"description": "Do not hardcode passwords or other secrets directly in the source code. Use a secure secret management system instead.",
263+
"evidence": [],
264+
"finding_type": "secret",
265+
"key": "dddddddd-8888-7777-6665-000000000000",
266+
"locations": [
267+
{
268+
"file_path": "credentials.yml",
269+
"from_column": 10,
270+
"from_line": 7,
271+
"to_column": 52,
272+
"to_line": 7,
273+
"type": "source"
274+
}
275+
],
276+
"policy_modifications": [],
277+
"problems": null,
278+
"rating": {
279+
"severity": "high"
280+
},
281+
"risk": {},
282+
"title": "Generic API Key"
283+
},
284+
"id": "cccccccc-bbbb-cccc-dddd-eeeeeeeeeeee",
285+
"links": {},
286+
"relationships": {},
287+
"type": "findings"
257288
}
258289
]
259290
}

0 commit comments

Comments
 (0)