mirror of
https://github.com/securego/gosec.git
synced 2025-11-23 22:15:04 +02:00
feat(autofix): update gemini sdk and add anthropic claude
* upgrade gemini sdk to google.golang.org/genai v1.25.0 * support newer gemini models * add anthropic claude
This commit is contained in:
committed by
Cosmin Cojocar
parent
506407e7df
commit
64ebfc0106
113
autofix/ai.go
113
autofix/ai.go
@@ -4,97 +4,53 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/generative-ai-go/genai"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"github.com/securego/gosec/v2/issue"
|
||||
)
|
||||
|
||||
const (
|
||||
GeminiModel = "gemini-1.5-flash"
|
||||
AIPrompt = `Provide a brief explanation and a solution to fix this security issue
|
||||
AIProviderFlagHelp = `AI API provider to generate auto fixes to issues. Valid options are:
|
||||
- gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite, gemini-2.0-flash, gemini-2.0-flash-lite (gemini, default);
|
||||
- claude-sonnet-4-0 (claude, default), claude-opus-4-0, claude-opus-4-1, claude-sonnet-3-7`
|
||||
|
||||
AIPrompt = `Provide a brief explanation and a solution to fix this security issue
|
||||
in Go programming language: %q.
|
||||
Answer in markdown format and keep the response limited to 200 words.`
|
||||
GeminiProvider = "gemini"
|
||||
|
||||
timeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// GenAIClient defines the interface for the GenAI client.
|
||||
type GenAIClient interface {
|
||||
// Close clean up and close the client.
|
||||
Close() error
|
||||
// GenerativeModel build the generative mode.
|
||||
GenerativeModel(name string) GenAIGenerativeModel
|
||||
GenerateSolution(ctx context.Context, prompt string) (string, error)
|
||||
}
|
||||
|
||||
// GenAIGenerativeModel defines the interface for the Generative Model.
|
||||
type GenAIGenerativeModel interface {
|
||||
// GenerateContent generates an response for given prompt.
|
||||
GenerateContent(ctx context.Context, prompt string) (string, error)
|
||||
}
|
||||
// GenerateSolution generates a solution for the given issues using the specified AI provider
|
||||
func GenerateSolution(model, aiAPIKey string, issues []*issue.Issue) (err error) {
|
||||
var client GenAIClient
|
||||
|
||||
// genAIClientWrapper wraps the genai.Client to implement GenAIClient.
|
||||
type genAIClientWrapper struct {
|
||||
client *genai.Client
|
||||
}
|
||||
|
||||
// Close closes the gen AI client.
|
||||
func (w *genAIClientWrapper) Close() error {
|
||||
return w.client.Close()
|
||||
}
|
||||
|
||||
// GenerativeModel builds the generative Model.
|
||||
func (w *genAIClientWrapper) GenerativeModel(name string) GenAIGenerativeModel {
|
||||
return &genAIGenerativeModelWrapper{model: w.client.GenerativeModel(name)}
|
||||
}
|
||||
|
||||
// genAIGenerativeModelWrapper wraps the genai.GenerativeModel to implement GenAIGenerativeModel
|
||||
type genAIGenerativeModelWrapper struct {
|
||||
// model is the underlying generative model
|
||||
model *genai.GenerativeModel
|
||||
}
|
||||
|
||||
// GenerateContent generates a response for the given prompt using gemini API.
|
||||
func (w *genAIGenerativeModelWrapper) GenerateContent(ctx context.Context, prompt string) (string, error) {
|
||||
resp, err := w.model.GenerateContent(ctx, genai.Text(prompt))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating autofix: %w", err)
|
||||
}
|
||||
if len(resp.Candidates) == 0 {
|
||||
return "", errors.New("no autofix returned by gemini")
|
||||
switch {
|
||||
case strings.HasPrefix(model, "claude"):
|
||||
client, err = NewClaudeClient(model, aiAPIKey)
|
||||
case strings.HasPrefix(model, "gemini"):
|
||||
client, err = NewGeminiClient(model, aiAPIKey)
|
||||
}
|
||||
|
||||
if len(resp.Candidates[0].Content.Parts) == 0 {
|
||||
return "", errors.New("nothing found in the first autofix returned by gemini")
|
||||
switch {
|
||||
case err != nil:
|
||||
return fmt.Errorf("initializing AI client: %w", err)
|
||||
case client == nil:
|
||||
return fmt.Errorf("unsupported AI backend: %s", model)
|
||||
}
|
||||
|
||||
// Return the first candidate
|
||||
return fmt.Sprintf("%+v", resp.Candidates[0].Content.Parts[0]), nil
|
||||
return generateSolution(client, issues)
|
||||
}
|
||||
|
||||
// NewGenAIClient creates a new gemini API client.
|
||||
func NewGenAIClient(ctx context.Context, aiAPIKey, endpoint string) (GenAIClient, error) {
|
||||
clientOptions := []option.ClientOption{option.WithAPIKey(aiAPIKey)}
|
||||
if endpoint != "" {
|
||||
clientOptions = append(clientOptions, option.WithEndpoint(endpoint))
|
||||
}
|
||||
|
||||
client, err := genai.NewClient(ctx, clientOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("calling gemini API: %w", err)
|
||||
}
|
||||
|
||||
return &genAIClientWrapper{client: client}, nil
|
||||
}
|
||||
|
||||
func generateSolutionByGemini(client GenAIClient, issues []*issue.Issue) error {
|
||||
func generateSolution(client GenAIClient, issues []*issue.Issue) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
model := client.GenerativeModel(GeminiModel)
|
||||
cachedAutofix := make(map[string]string)
|
||||
for _, issue := range issues {
|
||||
if val, ok := cachedAutofix[issue.What]; ok {
|
||||
@@ -103,7 +59,7 @@ func generateSolutionByGemini(client GenAIClient, issues []*issue.Issue) error {
|
||||
}
|
||||
|
||||
prompt := fmt.Sprintf(AIPrompt, issue.What)
|
||||
resp, err := model.GenerateContent(ctx, prompt)
|
||||
resp, err := client.GenerateSolution(ctx, prompt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating autofix with gemini: %w", err)
|
||||
}
|
||||
@@ -117,26 +73,3 @@ func generateSolutionByGemini(client GenAIClient, issues []*issue.Issue) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateSolution generates a solution for the given issues using the specified AI provider
|
||||
func GenerateSolution(aiAPIProvider, aiAPIKey, endpoint string, issues []*issue.Issue) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
var client GenAIClient
|
||||
|
||||
switch aiAPIProvider {
|
||||
case GeminiProvider:
|
||||
var err error
|
||||
client, err = NewGenAIClient(ctx, aiAPIKey, endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating autofix: %w", err)
|
||||
}
|
||||
default:
|
||||
return errors.New("ai provider not supported")
|
||||
}
|
||||
|
||||
defer client.Close()
|
||||
|
||||
return generateSolutionByGemini(client, issues)
|
||||
}
|
||||
|
||||
@@ -17,22 +17,7 @@ type MockGenAIClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockGenAIClient) Close() error {
|
||||
args := m.Called()
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockGenAIClient) GenerativeModel(name string) GenAIGenerativeModel {
|
||||
args := m.Called(name)
|
||||
return args.Get(0).(GenAIGenerativeModel)
|
||||
}
|
||||
|
||||
// MockGenAIGenerativeModel is a mock of the GenAIGenerativeModel interface
|
||||
type MockGenAIGenerativeModel struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockGenAIGenerativeModel) GenerateContent(ctx context.Context, prompt string) (string, error) {
|
||||
func (m *MockGenAIClient) GenerateSolution(ctx context.Context, prompt string) (string, error) {
|
||||
args := m.Called(ctx, prompt)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
@@ -44,17 +29,15 @@ func TestGenerateSolutionByGemini_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
mockClient := new(MockGenAIClient)
|
||||
mockModel := new(MockGenAIGenerativeModel)
|
||||
mockClient.On("GenerativeModel", GeminiModel).Return(mockModel).Once()
|
||||
mockModel.On("GenerateContent", mock.Anything, mock.Anything).Return("Autofix for issue 1", nil).Once()
|
||||
mockClient.On("GenerateSolution", mock.Anything, mock.Anything).Return("Autofix for issue 1", nil).Once()
|
||||
|
||||
// Act
|
||||
err := generateSolutionByGemini(mockClient, issues)
|
||||
err := generateSolution(mockClient, issues)
|
||||
|
||||
// Assert
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []*issue.Issue{{What: "Example issue 1", Autofix: "Autofix for issue 1"}}, issues)
|
||||
mock.AssertExpectationsForObjects(t, mockClient, mockModel)
|
||||
mock.AssertExpectationsForObjects(t, mockClient)
|
||||
}
|
||||
|
||||
func TestGenerateSolutionByGemini_NoCandidates(t *testing.T) {
|
||||
@@ -64,16 +47,14 @@ func TestGenerateSolutionByGemini_NoCandidates(t *testing.T) {
|
||||
}
|
||||
|
||||
mockClient := new(MockGenAIClient)
|
||||
mockModel := new(MockGenAIGenerativeModel)
|
||||
mockClient.On("GenerativeModel", GeminiModel).Return(mockModel).Once()
|
||||
mockModel.On("GenerateContent", mock.Anything, mock.Anything).Return("", nil).Once()
|
||||
mockClient.On("GenerateSolution", mock.Anything, mock.Anything).Return("", nil).Once()
|
||||
|
||||
// Act
|
||||
err := generateSolutionByGemini(mockClient, issues)
|
||||
err := generateSolution(mockClient, issues)
|
||||
|
||||
// Assert
|
||||
require.EqualError(t, err, "no autofix returned by gemini")
|
||||
mock.AssertExpectationsForObjects(t, mockClient, mockModel)
|
||||
mock.AssertExpectationsForObjects(t, mockClient)
|
||||
}
|
||||
|
||||
func TestGenerateSolutionByGemini_APIError(t *testing.T) {
|
||||
@@ -83,16 +64,14 @@ func TestGenerateSolutionByGemini_APIError(t *testing.T) {
|
||||
}
|
||||
|
||||
mockClient := new(MockGenAIClient)
|
||||
mockModel := new(MockGenAIGenerativeModel)
|
||||
mockClient.On("GenerativeModel", GeminiModel).Return(mockModel).Once()
|
||||
mockModel.On("GenerateContent", mock.Anything, mock.Anything).Return("", errors.New("API error")).Once()
|
||||
mockClient.On("GenerateSolution", mock.Anything, mock.Anything).Return("", errors.New("API error")).Once()
|
||||
|
||||
// Act
|
||||
err := generateSolutionByGemini(mockClient, issues)
|
||||
err := generateSolution(mockClient, issues)
|
||||
|
||||
// Assert
|
||||
require.EqualError(t, err, "generating autofix with gemini: API error")
|
||||
mock.AssertExpectationsForObjects(t, mockClient, mockModel)
|
||||
mock.AssertExpectationsForObjects(t, mockClient)
|
||||
}
|
||||
|
||||
func TestGenerateSolution_UnsupportedProvider(t *testing.T) {
|
||||
@@ -102,8 +81,8 @@ func TestGenerateSolution_UnsupportedProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
// Act
|
||||
err := GenerateSolution("unsupported-provider", "test-api-key", "", issues)
|
||||
err := GenerateSolution("unsupported-provider", "test-api-key", issues)
|
||||
|
||||
// Assert
|
||||
require.EqualError(t, err, "ai provider not supported")
|
||||
require.EqualError(t, err, "unsupported AI backend: unsupported-provider")
|
||||
}
|
||||
|
||||
74
autofix/claude.go
Normal file
74
autofix/claude.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package autofix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/anthropics/anthropic-sdk-go"
|
||||
"github.com/anthropics/anthropic-sdk-go/option"
|
||||
)
|
||||
|
||||
const (
|
||||
ModelClaudeOpus4_0 = anthropic.ModelClaudeOpus4_0
|
||||
ModelClaudeOpus4_1 = anthropic.ModelClaudeOpus4_1_20250805
|
||||
ModelClaudeSonnet4_0 = anthropic.ModelClaudeSonnet4_0
|
||||
)
|
||||
|
||||
var _ GenAIClient = (*claudeWrapper)(nil)
|
||||
|
||||
type claudeWrapper struct {
|
||||
client anthropic.Client
|
||||
model anthropic.Model
|
||||
}
|
||||
|
||||
func NewClaudeClient(model, apiKey string) (GenAIClient, error) {
|
||||
var options []option.RequestOption
|
||||
|
||||
if apiKey != "" {
|
||||
options = append(options, option.WithAPIKey(apiKey))
|
||||
}
|
||||
|
||||
anthropicModel := parseAnthropicModel(model)
|
||||
|
||||
return &claudeWrapper{
|
||||
client: anthropic.NewClient(options...),
|
||||
model: anthropicModel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *claudeWrapper) GenerateSolution(ctx context.Context, prompt string) (string, error) {
|
||||
resp, err := c.client.Messages.New(ctx, anthropic.MessageNewParams{
|
||||
Model: anthropic.Model(c.model),
|
||||
MaxTokens: 1024,
|
||||
Messages: []anthropic.MessageParam{
|
||||
anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating autofix: %w", err)
|
||||
}
|
||||
|
||||
if resp == nil || len(resp.Content) == 0 {
|
||||
return "", errors.New("no autofix returned by claude")
|
||||
}
|
||||
|
||||
if len(resp.Content[0].Text) == 0 {
|
||||
return "", errors.New("nothing found in the first autofix returned by claude")
|
||||
}
|
||||
|
||||
return resp.Content[0].Text, nil
|
||||
}
|
||||
|
||||
func parseAnthropicModel(model string) anthropic.Model {
|
||||
switch model {
|
||||
case "claude-sonnet-3-7":
|
||||
return anthropic.ModelClaude3_7SonnetLatest
|
||||
case "claude-opus", "claude-opus-4-0":
|
||||
return anthropic.ModelClaudeOpus4_0
|
||||
case "claude-opus-4-1":
|
||||
return anthropic.ModelClaudeOpus4_1_20250805
|
||||
}
|
||||
|
||||
return anthropic.ModelClaudeSonnet4_0
|
||||
}
|
||||
91
autofix/gemini.go
Normal file
91
autofix/gemini.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package autofix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/genai"
|
||||
)
|
||||
|
||||
// https://ai.google.dev/gemini-api/docs/models
|
||||
type GenAIModel string
|
||||
|
||||
const (
|
||||
ModelGeminiPro2_5 GenAIModel = "gemini-2.5-pro"
|
||||
ModelGeminiFlash2_5 GenAIModel = "gemini-2.5-flash"
|
||||
ModelGeminiFlash2_5Lite GenAIModel = "gemini-2.5-flash-lite"
|
||||
ModelGeminiFlash2_0 GenAIModel = "gemini-2.0-flash"
|
||||
ModelGeminiFlash2_0Lite GenAIModel = "gemini-2.0-flash-lite"
|
||||
// Deprecated: Use Gemini 2.x models.
|
||||
ModelGeminiFlash1_5 GenAIModel = "gemini-1.5-flash"
|
||||
)
|
||||
|
||||
var _ GenAIClient = (*geminiWrapper)(nil)
|
||||
|
||||
type geminiWrapper struct {
|
||||
client *genai.Client
|
||||
model GenAIModel
|
||||
}
|
||||
|
||||
func NewGeminiClient(model, apiKey string) (GenAIClient, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
genaiModel, err := parseGeminiModel(model)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := genai.ClientConfig{
|
||||
APIKey: apiKey,
|
||||
Backend: genai.BackendUnspecified,
|
||||
}
|
||||
|
||||
client, err := genai.NewClient(ctx, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating gemini client: %w", err)
|
||||
}
|
||||
|
||||
return &geminiWrapper{
|
||||
client: client,
|
||||
model: genaiModel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *geminiWrapper) GenerateSolution(ctx context.Context, prompt string) (string, error) {
|
||||
var config genai.GenerateContentConfig
|
||||
|
||||
resp, err := g.client.Models.GenerateContent(ctx, string(g.model), genai.Text(prompt), &config)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("generating autofix: %w", err)
|
||||
}
|
||||
|
||||
if resp == nil || len(resp.Candidates) == 0 {
|
||||
return "", errors.New("no autofix returned by gemini")
|
||||
}
|
||||
|
||||
if len(resp.Candidates[0].Content.Parts) == 0 {
|
||||
return "", errors.New("nothing found in the first autofix returned by gemini")
|
||||
}
|
||||
|
||||
return resp.Text(), nil
|
||||
}
|
||||
|
||||
func parseGeminiModel(model string) (GenAIModel, error) {
|
||||
switch model {
|
||||
case "gemini-2.5-pro":
|
||||
return ModelGeminiPro2_5, nil
|
||||
case "gemini-2.5-flash":
|
||||
return ModelGeminiFlash2_5, nil
|
||||
case "gemini-2.5-flash-lite":
|
||||
return ModelGeminiFlash2_5Lite, nil
|
||||
case "gemini-2.0-flash":
|
||||
return ModelGeminiFlash2_0, nil
|
||||
case "gemini-2.0-flash-lite", "gemini": // Default
|
||||
return ModelGeminiFlash2_0Lite, nil
|
||||
case "gemini-1.5-flash":
|
||||
return ModelGeminiFlash1_5, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unsupported gemini model: %s", model)
|
||||
}
|
||||
@@ -154,14 +154,11 @@ var (
|
||||
flagTerse = flag.Bool("terse", false, "Shows only the results and summary")
|
||||
|
||||
// AI platform provider to generate solutions to issues
|
||||
flagAiAPIProvider = flag.String("ai-api-provider", "", "AI API provider to generate auto fixes to issues.\nValid options are: gemini")
|
||||
flagAiAPIProvider = flag.String("ai-api-provider", "", autofix.AIProviderFlagHelp)
|
||||
|
||||
// key to implementing AI provider services
|
||||
flagAiAPIKey = flag.String("ai-api-key", "", "Key to access the AI API")
|
||||
|
||||
// endpoint to the AI provider
|
||||
flagAiEndpoint = flag.String("ai-endpoint", "", "Endpoint AI API.\nThis is optional, the default API endpoint will be used when not provided.")
|
||||
|
||||
// exclude the folders from scan
|
||||
flagDirsExclude arrayFlags
|
||||
|
||||
@@ -508,8 +505,11 @@ func main() {
|
||||
if aiAPIKey == "" {
|
||||
aiAPIKey = *flagAiAPIKey
|
||||
}
|
||||
if *flagAiAPIProvider != "" && aiAPIKey != "" {
|
||||
err := autofix.GenerateSolution(*flagAiAPIProvider, aiAPIKey, *flagAiEndpoint, issues)
|
||||
|
||||
aiEnabled := *flagAiAPIProvider != ""
|
||||
|
||||
if len(issues) > 0 && aiEnabled {
|
||||
err := autofix.GenerateSolution(*flagAiAPIProvider, aiAPIKey, issues)
|
||||
if err != nil {
|
||||
logger.Print(err)
|
||||
}
|
||||
|
||||
16
go.mod
16
go.mod
@@ -1,8 +1,8 @@
|
||||
module github.com/securego/gosec/v2
|
||||
|
||||
require (
|
||||
github.com/anthropics/anthropic-sdk-go v1.12.0
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4
|
||||
github.com/google/generative-ai-go v0.20.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.6.0
|
||||
github.com/lib/pq v1.10.9
|
||||
@@ -13,17 +13,14 @@ require (
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/text v0.29.0
|
||||
golang.org/x/tools v0.37.0
|
||||
google.golang.org/api v0.249.0
|
||||
google.golang.org/genai v1.25.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.121.2 // indirect
|
||||
cloud.google.com/go/ai v0.12.1 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.7 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
@@ -35,11 +32,15 @@ require (
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
@@ -48,11 +49,8 @@ require (
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
|
||||
36
go.sum
36
go.sum
@@ -15,12 +15,8 @@ cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZ
|
||||
cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU=
|
||||
cloud.google.com/go v0.121.2 h1:v2qQpN6Dx9x2NmwrqlesOt3Ys4ol5/lFZ6Mg1B7OJCg=
|
||||
cloud.google.com/go v0.121.2/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
|
||||
cloud.google.com/go/ai v0.12.1 h1:m1n/VjUuHS+pEO/2R4/VbuuEIkgk0w67fDQvFaMngM0=
|
||||
cloud.google.com/go/ai v0.12.1/go.mod h1:5vIPNe1ZQsVZqCliXIPL4QnhObQQY4d9hAGHdVc4iw4=
|
||||
cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
@@ -31,8 +27,6 @@ cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcao
|
||||
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
@@ -58,6 +52,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anthropics/anthropic-sdk-go v1.12.0 h1:xPqlGnq7rWrTiHazIvCiumA0u7mGQnwDQtvA1M82h9U=
|
||||
github.com/anthropics/anthropic-sdk-go v1.12.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
|
||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
||||
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
@@ -162,8 +158,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.1.1/go.mod h1:FDKqPvSXawb2ecErVRrD+nfy23RCzyl7eqVCEmlT1Zs=
|
||||
github.com/google/generative-ai-go v0.20.1 h1:6dEIujpgN2V0PgLhr6c/M1ynRdc7ARtiIDPFzj45uNQ=
|
||||
github.com/google/generative-ai-go v0.20.1/go.mod h1:TjOnZJmZKzarWbjUJgy+r3Ee7HGBRVLhOIgupnwR4Bg=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -207,6 +201,8 @@ github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b0
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
@@ -358,6 +354,16 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -387,8 +393,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
@@ -499,8 +503,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -569,8 +571,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -640,8 +640,6 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w=
|
||||
google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -649,6 +647,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
|
||||
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genai v1.25.0 h1:Cpyh2nmEoOS1eM3mT9XKuA/qWTEDoktfP2gsN3EduPE=
|
||||
google.golang.org/genai v1.25.0/go.mod h1:OClfdf+r5aaD+sCd4aUSkPzJItmg2wD/WON9lQnRPaY=
|
||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -680,10 +680,6 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
|
||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
|
||||
Reference in New Issue
Block a user