Repository navigation
Expand file tree
/
Copy pathbugfind.go
More file actions
121 lines (107 loc) · 3.1 KB
/
Copy pathbugfind.go
File metadata and controls
121 lines (107 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
)
// bugFindPrompt generates a comprehensive bug-finding prompt from a list of
// file paths and/or diff content. It instructs the LLM to look for bugs,
// security issues, race conditions, resource leaks, and more.
func bugFindPrompt(filePaths []string, diffContent string) string {
var b strings.Builder
b.WriteString(`Analyze the following code for potential issues:
- Bugs and logic errors
- Security vulnerabilities (injection, path traversal, etc.)
- Race conditions and concurrency issues
- Resource leaks (unclosed files, connections, channels)
- Null/nil pointer dereference risks
- Error handling gaps (ignored errors, missing checks)
- Off-by-one errors and boundary conditions
Report each issue with:
- File: the file path
- Line: approximate line number (if available)
- Severity: critical, high, medium, or low
- Description: what the issue is
- Fix suggestion: how to resolve it
`)
if diffContent != "" {
b.WriteString("## Diff to analyze\n\n```diff\n")
b.WriteString(diffContent)
b.WriteString("\n```\n\n")
}
for _, path := range filePaths {
data, err := os.ReadFile(path) // #nosec G304 -- path comes from internal filePaths list built from git diff output
if err != nil {
b.WriteString(fmt.Sprintf("## %s\n\n(could not read: %v)\n\n", path, err))
continue
}
ext := filepath.Ext(path)
lang := langFromExt(ext)
b.WriteString(fmt.Sprintf("## %s\n\n```%s\n%s\n```\n\n", path, lang, string(data)))
}
return b.String()
}
// langFromExt maps file extensions to Markdown code-fence language identifiers.
func langFromExt(ext string) string {
switch strings.ToLower(ext) {
case ".go":
return "go"
case ".py":
return "python"
case ".js":
return "javascript"
case ".ts", ".tsx":
return "typescript"
case ".rs":
return "rust"
case ".java":
return "java"
case ".rb":
return "ruby"
case ".c", ".h":
return "c"
case ".cpp", ".cc", ".hpp":
return "cpp"
default:
return ""
}
}
// formatBugReport takes raw LLM output and wraps it in a clean report format
// with severity-based section headers.
func formatBugReport(findings string) string {
if strings.TrimSpace(findings) == "" {
return "No issues found."
}
var b strings.Builder
b.WriteString("=== Bug Report ===\n\n")
b.WriteString(findings)
b.WriteString("\n")
// Count severities for a summary line.
lower := strings.ToLower(findings)
critical := strings.Count(lower, "critical")
high := strings.Count(lower, "high")
medium := strings.Count(lower, "medium")
low := strings.Count(lower, "low")
total := critical + high + medium + low
if total > 0 {
b.WriteString(fmt.Sprintf("\n--- Summary: %d issue(s) ", total))
parts := []string{}
if critical > 0 {
parts = append(parts, fmt.Sprintf("%d critical", critical))
}
if high > 0 {
parts = append(parts, fmt.Sprintf("%d high", high))
}
if medium > 0 {
parts = append(parts, fmt.Sprintf("%d medium", medium))
}
if low > 0 {
parts = append(parts, fmt.Sprintf("%d low", low))
}
b.WriteString("(")
b.WriteString(strings.Join(parts, ", "))
b.WriteString(") ---\n")
}
return b.String()
}