Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/cli/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Critical findings exit non-zero. Non-critical findings are sanitized.`,

// Print findings
for _, f := range allFindings {
printer.StepWarn(fmt.Sprintf("[%s] %s: %s", f.Severity, f.Scanner, f.Detail))
printer.StepWarn(security.FormatFinding(f))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] behavior-change

The input scan switches from f.Scanner to f.Name via FormatFinding, changing operator-visible output. Authorized by issue #234 and documented in godoc. Log consumers keyed on the old format will stop matching.

}

// Generate trace ID for finding correlation.
Expand Down Expand Up @@ -255,7 +255,7 @@ Only scans known context filenames (AGENTS.md, CLAUDE.md, .cursorrules, etc.).`,
if len(normResult.Findings)+len(injResult.Findings) > 0 {
printer.StepWarn(fmt.Sprintf("%s: %d finding(s)", path, len(normResult.Findings)+len(injResult.Findings)))
for _, f := range append(normResult.Findings, injResult.Findings...) {
printer.StepWarn(fmt.Sprintf(" [%s] %s: %s", f.Severity, f.Name, f.Detail))
printer.StepWarn(" " + security.FormatFinding(f))
}
} else {
printer.StepDone(fmt.Sprintf("%s: clean", path))
Expand Down
11 changes: 11 additions & 0 deletions internal/security/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// and evaluation results.
package security

import "fmt"

// Finding represents a single security issue detected by a scanner.
type Finding struct {
Scanner string // "secret_redactor", "ssrf_validator", "context_injection", "unicode_normalizer"
Expand Down Expand Up @@ -101,6 +103,15 @@ func OutputPipeline() *Pipeline {
)
}

// FormatFinding returns a human-readable summary of a single finding.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] style

FormatFinding godoc is multi-line while comparable HasCriticalFindings uses single-line. The extra context explaining Name-over-Scanner is useful, so this is a reasonable tradeoff.

// The canonical format is "[severity] name: detail", using the Name field
// (pattern name or category) rather than Scanner (scanner identifier).
// All callers that print individual findings should use this function to
// prevent format drift.
func FormatFinding(f Finding) string {
return fmt.Sprintf("[%s] %s: %s", f.Severity, f.Name, f.Detail)
}

// HasCriticalFindings reports whether any finding has critical severity.
func HasCriticalFindings(findings []Finding) bool {
for _, f := range findings {
Expand Down
11 changes: 11 additions & 0 deletions internal/security/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,17 @@ func TestSSRFValidator_DNSResolution(t *testing.T) {
})
}

func TestFormatFinding(t *testing.T) {
f := Finding{
Scanner: "secret_redactor",
Name: "github_pat",
Severity: "critical",
Detail: "GitHub personal access token detected",
}
got := FormatFinding(f)
assert.Equal(t, "[critical] github_pat: GitHub personal access token detected", got)
}

func hasFinding(r ScanResult, name string) bool {
for _, f := range r.Findings {
if f.Name == name {
Expand Down
Loading