Skip to content

Commit 0da1e9b

Browse files
committed
feat: [DGP-789] treat ignored/pending ignore issues in line with legacy CLI
- pending ignores are counted as Open issues and labeled with [ PENDING IGNORE... ] - ignored issues are labeled [ IGNORED ] and counted in Total Issues but not Open issues - both are prepended with "!" instead of "X"
1 parent 5f034d6 commit 0da1e9b

File tree

4 files changed

+108
-22
lines changed

4 files changed

+108
-22
lines changed

internal/presenters/funcs.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,12 @@ func isOpenFinding() func(obj any) bool {
248248
if !ok {
249249
return false
250250
}
251-
// A finding is considered "open" if it has no suppression information.
252-
// A rejected suppression is not represented as a status, but by the absence of a suppression object.
253-
return finding.Attributes.Suppression == nil
251+
// Treat findings as open unless they are explicitly ignored.
252+
// Pending ignore approvals and other statuses remain visible as open issues.
253+
if finding.Attributes == nil || finding.Attributes.Suppression == nil {
254+
return true
255+
}
256+
return finding.Attributes.Suppression.Status != testapi.SuppressionStatusIgnored
254257
}
255258
}
256259

@@ -314,12 +317,12 @@ func isNotLicenseFindingFilter() func(obj any) bool {
314317

315318
// hasSuppression checks if a finding has any suppression.
316319
func hasSuppression(finding testapi.FindingData) bool {
317-
if finding.Attributes.Suppression == nil {
320+
if finding.Attributes == nil || finding.Attributes.Suppression == nil {
318321
return false
319322
}
320323

321-
// If a suppression object exists, the finding is considered suppressed (either ignored or pending).
322-
return true
324+
// Treat as suppressed unless the suppression status is "other" (treating as rejected).
325+
return finding.Attributes.Suppression.Status != testapi.SuppressionStatusOther
323326
}
324327

325328
// getCliTemplateFuncMap returns the template function map for CLI rendering.
@@ -475,12 +478,12 @@ func getSummaryResultsByIssueType(issueType string, findings []testapi.FindingDa
475478

476479
totalBySeverity[severity]++
477480

478-
// Determine suppression state
481+
// Determine suppression state: only explicit "ignored" should reduce open counts.
479482
isIgnored := false
480483
isOpen := true
481484
if f.Attributes != nil && f.Attributes.Suppression != nil {
482-
isOpen = false
483485
isIgnored = f.Attributes.Suppression.Status == testapi.SuppressionStatusIgnored
486+
isOpen = !isIgnored
484487
}
485488

486489
if isOpen {

internal/presenters/presenter_unified_finding_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,78 @@ func TestUnifiedFindingPresenter_CliOutput(t *testing.T) {
332332
assert.NotContains(t, out, "Total security issues")
333333
})
334334
}
335+
336+
// TestUnifiedFindingPresenter_PendingIgnore_ShownAsOpenWithLabelAndBang verifies that pending ignores are shown as open with a label and ! marker.
337+
func TestUnifiedFindingPresenter_PendingIgnore_ShownAsOpenWithLabelAndBang(t *testing.T) {
338+
config := configuration.New()
339+
buffer := &bytes.Buffer{}
340+
// Use ASCII to avoid color codes in assertions
341+
lipgloss.SetColorProfile(termenv.Ascii)
342+
343+
pendingFinding := testapi.FindingData{
344+
Id: util.Ptr(uuid.New()),
345+
Type: util.Ptr(testapi.Findings),
346+
Attributes: &testapi.FindingAttributes{
347+
Title: "Pending Suppression Finding",
348+
Rating: testapi.Rating{Severity: testapi.Severity("low")},
349+
Suppression: &testapi.Suppression{Status: testapi.SuppressionStatusPendingIgnoreApproval},
350+
},
351+
}
352+
353+
projectResult := &presenters.UnifiedProjectResult{
354+
Findings: []testapi.FindingData{pendingFinding},
355+
Summary: &json_schemas.TestSummary{
356+
Type: "open-source",
357+
Path: "test/path",
358+
SeverityOrderAsc: []string{"low", "medium", "high", "critical"},
359+
Results: []json_schemas.TestSummaryResult{{Severity: "low", Open: 1, Total: 1}},
360+
},
361+
}
362+
363+
presenter := presenters.NewUnifiedFindingsRenderer([]*presenters.UnifiedProjectResult{projectResult}, config, buffer)
364+
err := presenter.RenderTemplate(presenters.DefaultTemplateFiles, presenters.DefaultMimeType)
365+
assert.NoError(t, err)
366+
367+
out := buffer.String()
368+
// Label should be inline on the issue line (after the title) with a preceding space
369+
assert.Contains(t, out, " ! [LOW] Pending Suppression Finding [ PENDING IGNORE... ]")
370+
}
371+
372+
// TestUnifiedFindingPresenter_Ignored_ShownInIgnoredSectionWithBang verifies that ignored findings are shown in the ignored section with a ! marker.
373+
func TestUnifiedFindingPresenter_Ignored_ShownInIgnoredSectionWithBang(t *testing.T) {
374+
config := configuration.New()
375+
buffer := &bytes.Buffer{}
376+
// Ensure ignored section is rendered
377+
config.Set("include-ignores", true)
378+
// Use ASCII to avoid color codes in assertions
379+
lipgloss.SetColorProfile(termenv.Ascii)
380+
381+
ignoredFinding := testapi.FindingData{
382+
Id: util.Ptr(uuid.New()),
383+
Type: util.Ptr(testapi.Findings),
384+
Attributes: &testapi.FindingAttributes{
385+
Title: "Ignored Suppression Finding",
386+
Rating: testapi.Rating{Severity: testapi.Severity("medium")},
387+
Suppression: &testapi.Suppression{Status: testapi.SuppressionStatusIgnored},
388+
},
389+
}
390+
391+
projectResult := &presenters.UnifiedProjectResult{
392+
Findings: []testapi.FindingData{ignoredFinding},
393+
Summary: &json_schemas.TestSummary{
394+
Type: "open-source",
395+
Path: "test/path",
396+
SeverityOrderAsc: []string{"low", "medium", "high", "critical"},
397+
Results: []json_schemas.TestSummaryResult{{Severity: "medium", Ignored: 1, Total: 1}},
398+
},
399+
}
400+
401+
presenter := presenters.NewUnifiedFindingsRenderer([]*presenters.UnifiedProjectResult{projectResult}, config, buffer)
402+
err := presenter.RenderTemplate(presenters.DefaultTemplateFiles, presenters.DefaultMimeType)
403+
assert.NoError(t, err)
404+
405+
out := buffer.String()
406+
assert.Contains(t, out, "Ignored Issues")
407+
// Ignored entries appear with ! and IGNORED label
408+
assert.Contains(t, out, " ! [IGNORED] [MEDIUM] Ignored Suppression Finding")
409+
}

internal/presenters/templates/finding.component.tmpl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
{{define "finding"}}
22
{{- (renderToString "severity" . | toUpperCase | renderInSeverityColor )}} {{print .Attributes.Title | bold}}
3-
{{- if .Attributes.Suppression }}{{if eq .Attributes.Suppression.Status "ignored" }} [ IGNORED ]
4-
{{- else if eq .Attributes.Suppression.Status "pending_ignore_approval" }} [ PENDING IGNORE... ] {{- else}}{{end -}}
5-
{{- else}}{{end}}
3+
{{- template "suppression_label" . -}}
64

75
Finding ID: {{ getFindingId . }}
86
{{- range $location := .Attributes.Locations}}
@@ -26,4 +24,12 @@
2624
{{- define "severity"}}
2725
{{- if not (hasSuppression .) }} ✗ {{ else }} ! {{ end -}}
2826
[{{.Attributes.Rating.Severity}}]
29-
{{- end}}
27+
{{- end}}
28+
29+
{{- define "suppression_label" -}}
30+
{{- if .Attributes.Suppression -}}
31+
{{- if eq .Attributes.Suppression.Status "ignored" }} [ IGNORED ]
32+
{{- else if eq .Attributes.Suppression.Status "pending_ignore_approval" }} [ PENDING IGNORE... ]
33+
{{- else }}{{- end -}}
34+
{{- end -}}
35+
{{- end -}}

internal/presenters/templates/unified_finding.tmpl

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@
4242
{{- range $finding := $openFindings }}
4343
{{- $severity := getFieldValueFrom $finding "Attributes.Rating.Severity" -}}
4444
{{- $title := getFieldValueFrom $finding "Attributes.Title" -}}
45-
{{- printf " ✗ %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
45+
{{- if not (hasSuppression $finding) -}}
46+
{{- printf " ✗ %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
47+
{{- else -}}
48+
{{- printf " ! %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
49+
{{- end -}}
50+
{{- template "suppression_label" $finding -}}
4651
{{- template "finding_details" $finding -}}
4752
{{- end }}
4853
{{- end }}
@@ -51,15 +56,12 @@
5156
{{- range $finding := $licenseFindings }}
5257
{{- $severity := getFieldValueFrom $finding "Attributes.Rating.Severity" -}}
5358
{{- $title := getFieldValueFrom $finding "Attributes.Title" -}}
54-
{{- printf " ✗ %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
55-
{{- template "finding_details" $finding -}}
56-
{{- end }}
57-
{{- end }}
58-
{{- if $hasPendingIgnoreFindings }}
59-
{{- range $finding := $pendingIgnoreFindings }}
60-
{{- $severity := getFieldValueFrom $finding "Attributes.Rating.Severity" -}}
61-
{{- $title := getFieldValueFrom $finding "Attributes.Title" -}}
62-
{{- printf " ! [PENDING] %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
59+
{{- if not (hasSuppression $finding) -}}
60+
{{- printf " ✗ %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
61+
{{- else -}}
62+
{{- printf " ! %s %s" (printf "[%s]" ($severity | toUpperCase) | renderInSeverityColor) ($title | bold) -}}
63+
{{- end -}}
64+
{{- template "suppression_label" $finding -}}
6365
{{- template "finding_details" $finding -}}
6466
{{- end }}
6567
{{- end }}

0 commit comments

Comments
 (0)