Skip to content

Commit aff2b00

Browse files
committed
cmd/vulnreport: add option to use AI in vulnreport create
Add flag "-ai" that causes `vulnreport create` to attempt to auto-populate an AI-generated draft of a summary and description for a report. (This functionality can still be accessed post-create via command `vulnreport suggest`.) Change-Id: I0a22579b56fa1fb5c302d54afebea96fdcc5504a Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/559597 Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 37ae45f commit aff2b00

File tree

3 files changed

+54
-16
lines changed

3 files changed

+54
-16
lines changed

cmd/vulnreport/create.go

+23
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"golang.org/x/vulndb/internal/cveclient"
1616
"golang.org/x/vulndb/internal/cveschema5"
1717
"golang.org/x/vulndb/internal/derrors"
18+
"golang.org/x/vulndb/internal/genai"
1819
"golang.org/x/vulndb/internal/genericosv"
1920
"golang.org/x/vulndb/internal/ghsa"
2021
"golang.org/x/vulndb/internal/gitrepo"
@@ -29,6 +30,7 @@ var (
2930
closedOk = flag.Bool("closed-ok", false, "for create & create-excluded, allow closed issues to be created")
3031
graphQL = flag.Bool("graphql", false, "for create, fetch GHSAs from the Github GraphQL API instead of the OSV database")
3132
issueRepo = flag.String("issue-repo", "github.com/golang/vulndb", "for create, repo locate Github issues")
33+
useAI = flag.Bool("ai", false, "for create, use AI to write draft summary and description when creating report")
3234
)
3335

3436
func create(ctx context.Context, issueNumber int, cfg *createCfg) (err error) {
@@ -126,6 +128,7 @@ type createCfg struct {
126128
existingByFile map[string]*report.Report
127129
existingByIssue map[int]*report.Report
128130
allowClosed bool
131+
aiClient *genai.GeminiClient
129132
}
130133

131134
func setupCreate(ctx context.Context, args []string) ([]int, *createCfg, error) {
@@ -148,13 +151,21 @@ func setupCreate(ctx context.Context, args []string) ([]int, *createCfg, error)
148151
if err != nil {
149152
return nil, nil, err
150153
}
154+
var aiClient *genai.GeminiClient
155+
if *useAI {
156+
aiClient, err = genai.NewGeminiClient(ctx)
157+
if err != nil {
158+
return nil, nil, err
159+
}
160+
}
151161
return githubIDs, &createCfg{
152162
issuesClient: issues.NewClient(ctx, &issues.Config{Owner: owner, Repo: repoName, Token: *githubToken}),
153163
ghsaClient: ghsa.NewClient(ctx, *githubToken),
154164
proxyClient: proxy.NewDefaultClient(),
155165
existingByFile: existingByFile,
156166
existingByIssue: existingByIssue,
157167
allowClosed: *closedOk,
168+
aiClient: aiClient,
158169
}, nil
159170
}
160171

@@ -244,6 +255,18 @@ func createReport(ctx context.Context, cfg *createCfg, iss *issues.Issue) (r *re
244255
// Find any additional aliases referenced by the source aliases.
245256
addMissingAliases(ctx, r, cfg.ghsaClient)
246257

258+
if cfg.aiClient != nil {
259+
suggestions, err := suggest(ctx, cfg.aiClient, r, 1)
260+
if err != nil {
261+
warnlog.Printf("failed to get AI-generated suggestions for %s: %v\n", r.ID, err)
262+
} else if len(suggestions) == 0 {
263+
warnlog.Printf("failed to get AI-generated suggestions for %s (none generated)\n", r.ID)
264+
} else {
265+
infolog.Printf("applying AI-generated suggestion for %s", r.ID)
266+
applySuggestion(r, suggestions[0])
267+
}
268+
}
269+
247270
addTODOs(r)
248271
return r, nil
249272
}

cmd/vulnreport/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func main() {
128128
case "lint":
129129
cmdFunc = func(ctx context.Context, name string) error { return lint(ctx, name, pc) }
130130
case "suggest":
131-
cmdFunc = func(ctx context.Context, name string) error { return suggest(ctx, name) }
131+
cmdFunc = func(ctx context.Context, name string) error { return suggestCmd(ctx, name) }
132132
case "commit":
133133
cmdFunc = func(ctx context.Context, name string) error { return commit(ctx, name, ghsaClient, pc, *force) }
134134
case "cve":

cmd/vulnreport/suggest.go

+30-15
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var (
2020
palm = flag.Bool("palm", false, "use the legacy PaLM API instead of the Gemini API")
2121
)
2222

23-
func suggest(ctx context.Context, filename string) (err error) {
23+
func suggestCmd(ctx context.Context, filename string) (err error) {
2424
defer derrors.Wrap(&err, "suggest(%q)", filename)
2525

2626
r, err := report.Read(filename)
@@ -40,21 +40,11 @@ func suggest(ctx context.Context, filename string) (err error) {
4040
}
4141
}
4242

43-
suggestions, err := genai.Suggest(ctx, c, &genai.Input{
44-
Module: r.Modules[0].Module,
45-
Description: r.Description.String(),
46-
})
43+
suggestions, err := suggest(ctx, c, r, *numSuggestions)
4744
if err != nil {
48-
return fmt.Errorf("GenAI API error: %s", err)
49-
}
50-
if len(suggestions) > *numSuggestions {
51-
suggestions = suggestions[:*numSuggestions]
45+
return err
5246
}
53-
5447
found := len(suggestions)
55-
if found == 0 {
56-
return fmt.Errorf("could not generate any valid suggestions for report %s (try again?)", r.ID)
57-
}
5848

5949
outlog.Printf("== AI-generated suggestions for report %s ==\n", r.ID)
6050

@@ -77,8 +67,7 @@ func suggest(ctx context.Context, filename string) (err error) {
7767
}
7868
switch choice {
7969
case "a":
80-
r.Summary = report.Summary(s.Summary)
81-
r.Description = report.Description(s.Description)
70+
applySuggestion(r, s)
8271
if err := r.Write(filename); err != nil {
8372
errlog.Println(err)
8473
}
@@ -93,3 +82,29 @@ func suggest(ctx context.Context, filename string) (err error) {
9382

9483
return nil
9584
}
85+
86+
func suggest(ctx context.Context, c genai.Client, r *report.Report, max int) (suggestions []*genai.Suggestion, err error) {
87+
suggestions, err = genai.Suggest(ctx, c, &genai.Input{
88+
Module: r.Modules[0].Module,
89+
Description: r.Description.String(),
90+
})
91+
if err != nil {
92+
return nil, fmt.Errorf("GenAI API error: %s", err)
93+
}
94+
if len(suggestions) > max {
95+
suggestions = suggestions[:max]
96+
}
97+
98+
found := len(suggestions)
99+
if found == 0 {
100+
return nil, fmt.Errorf("could not generate any valid suggestions for report %s (try again?)", r.ID)
101+
}
102+
103+
return suggestions, nil
104+
}
105+
106+
func applySuggestion(r *report.Report, s *genai.Suggestion) {
107+
r.Summary = report.Summary(s.Summary)
108+
r.Description = report.Description(s.Description)
109+
r.Fix(nil)
110+
}

0 commit comments

Comments
 (0)