diff --git a/proposesolution/ai.go b/autofix/ai.go similarity index 87% rename from proposesolution/ai.go rename to autofix/ai.go index 9dcf744292..1abd55bd67 100644 --- a/proposesolution/ai.go +++ b/autofix/ai.go @@ -1,4 +1,4 @@ -package proposesolution +package autofix import ( "context" @@ -36,7 +36,11 @@ func generateSolutionByGemini(aiApiKey string, issues []*issue.Issue) error { return fmt.Errorf("gemini generating content: %w", err) } - issue.ProposedSolution = fmt.Sprintf("%+v", resp.Candidates[0].Content.Parts[0]) + if len(resp.Candidates) == 0 { + return fmt.Errorf("gemini no candidates found") + } + + issue.AutoFix = fmt.Sprintf("%+v", resp.Candidates[0].Content.Parts[0]) } return nil } diff --git a/proposesolution/ai_test.go b/autofix/ai_test.go similarity index 66% rename from proposesolution/ai_test.go rename to autofix/ai_test.go index 691f32b3b0..04349d3743 100644 --- a/proposesolution/ai_test.go +++ b/autofix/ai_test.go @@ -1,14 +1,13 @@ -package proposesolution_test +package autofix import ( "testing" "github.com/securego/gosec/v2/issue" - "github.com/securego/gosec/v2/proposesolution" ) func TestGenerateSolution(t *testing.T) { - aiApiProvider := proposesolution.GeminiProvider + aiApiProvider := GeminiProvider aiApiKey := "test-api-key" // Replace with a valid API key for actual testing issues := []*issue.Issue{ @@ -17,13 +16,13 @@ func TestGenerateSolution(t *testing.T) { }, } - err := proposesolution.GenerateSolution(aiApiProvider, aiApiKey, issues) + err := GenerateSolution(aiApiProvider, aiApiKey, issues) if err != nil { t.Fatalf("Expected no error, got %v", err) } for _, issue := range issues { - if issue.ProposedSolution == "" { + if issue.AutoFix == "" { t.Errorf("Expected a proposed solution, got an empty string") } } diff --git a/cmd/gosec/main.go b/cmd/gosec/main.go index 8c1f089135..723cab29e3 100644 --- a/cmd/gosec/main.go +++ b/cmd/gosec/main.go @@ -25,9 +25,9 @@ import ( "strings" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/autofix" "github.com/securego/gosec/v2/cmd/vflag" "github.com/securego/gosec/v2/issue" - proposeSolution "github.com/securego/gosec/v2/proposesolution" "github.com/securego/gosec/v2/report" "github.com/securego/gosec/v2/rules" ) @@ -466,9 +466,9 @@ func main() { // Call ai request to solve the issues if *flagAiApiProvider != "" && *flagAiApiKey != "" { - err := proposeSolution.GenerateSolution(*flagAiApiProvider, *flagAiApiKey, issues) + err := autofix.GenerateSolution(*flagAiApiProvider, *flagAiApiKey, issues) if err != nil { - logger.Fatal(err) + logger.Print(err) } } diff --git a/issue/issue.go b/issue/issue.go index f8394a5048..fa861b1dc4 100644 --- a/issue/issue.go +++ b/issue/issue.go @@ -97,18 +97,18 @@ var ruleToCWE = map[string]string{ // Issue is returned by a gosec rule if it discovers an issue with the scanned code. type Issue struct { - Severity Score `json:"severity"` // issue severity (how problematic it is) - Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) - Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID - RuleID string `json:"rule_id"` // Human readable explanation - What string `json:"details"` // Human readable explanation - File string `json:"file"` // File name we found it in - Code string `json:"code"` // Impacted code line - Line string `json:"line"` // Line number in file - Col string `json:"column"` // Column number in line - NoSec bool `json:"nosec"` // true if the issue is nosec - Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue - ProposedSolution string `json:"proposed_solution,omitempty"` // Proposed solution to fix the issue + Severity Score `json:"severity"` // issue severity (how problematic it is) + Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) + Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID + RuleID string `json:"rule_id"` // Human readable explanation + What string `json:"details"` // Human readable explanation + File string `json:"file"` // File name we found it in + Code string `json:"code"` // Impacted code line + Line string `json:"line"` // Line number in file + Col string `json:"column"` // Column number in line + NoSec bool `json:"nosec"` // true if the issue is nosec + Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue + AutoFix string `json:"autofix,omitempty"` // Proposed auto fix the issue } // SuppressionInfo object is to record the kind and the justification that used diff --git a/report/junit/formatter.go b/report/junit/formatter.go index bc2315fd74..aef6fc7384 100644 --- a/report/junit/formatter.go +++ b/report/junit/formatter.go @@ -18,7 +18,7 @@ func generatePlaintext(issue *issue.Issue) string { issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) + ", Severity: " + strconv.Itoa(int(issue.Severity)) + ", CWE: " + cweID + ")\n" + "> " + html.EscapeString(issue.Code) + - "\n Proposed Solution: " + issue.ProposedSolution + "\n Auto Fix: " + issue.AutoFix } // GenerateReport Convert a gosec report to a JUnit Report diff --git a/report/text/template.txt b/report/text/template.txt index 24890127d0..450fa95611 100644 --- a/report/text/template.txt +++ b/report/text/template.txt @@ -8,7 +8,7 @@ Golang errors in file: [{{ $filePath }}]: {{ range $index, $issue := .Issues }} [{{ highlight $issue.FileLocation $issue.Severity $issue.NoSec }}] - {{ $issue.RuleID }}{{ if $issue.NoSec }} ({{- success "NoSec" -}}){{ end }} ({{ if $issue.Cwe }}{{$issue.Cwe.SprintID}}{{ else }}{{"CWE"}}{{ end }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }}) {{ printCode $issue }} -{{ "Proposed Solution" }}: {{ $issue.ProposedSolution }} +{{ "Auto Fix" }}: {{ $issue.AutoFix }} {{ end }} {{ notice "Summary:" }} Gosec : {{.GosecVersion}}