mirror of
https://github.com/securego/gosec.git
synced 2026-01-15 09:53:40 +08:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15d5c61e86 | ||
|
|
7b8713e2c9 | ||
|
|
64ebfc0106 | ||
|
|
506407e7df | ||
|
|
3ead143f0a | ||
|
|
e81fba3c3a | ||
|
|
ab078db7b0 | ||
|
|
e6218c83ec | ||
|
|
79f835d9c7 | ||
|
|
40ac53017b | ||
|
|
4be6b11bbc | ||
|
|
5af1117217 | ||
|
|
287b46c018 | ||
|
|
cee0aeae8a | ||
|
|
c9453023c4 | ||
|
|
ef7adab98c | ||
|
|
e201bb86c2 | ||
|
|
ba592afef6 | ||
|
|
2ef6017991 | ||
|
|
32975f4bab | ||
|
|
6ea6b35e61 | ||
|
|
bc3f2145b5 | ||
|
|
925741b7ef | ||
|
|
59ae7e9e27 | ||
|
|
e7abd9e348 | ||
|
|
35e7bc1a94 | ||
|
|
2d1ed95a0b | ||
|
|
4a8cb4609f | ||
|
|
bcc8afbe30 |
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
@@ -10,17 +10,21 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
version: [{go: '1.23.10', golangci: 'latest'}, {go: '1.24.4', golangci: 'latest'}]
|
||||
version:
|
||||
- go-version: "1.24.7"
|
||||
golangci: "latest"
|
||||
- go-version: "1.25.1"
|
||||
golangci: "latest"
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Setup go ${{ matrix.version.go }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Setup go ${{ matrix.version.go-version }}
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ matrix.version.go }}
|
||||
go-version: ${{ matrix.version.go-version }}
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
@@ -30,7 +34,7 @@ jobs:
|
||||
- name: lint
|
||||
uses: golangci/golangci-lint-action@v8
|
||||
with:
|
||||
version: ${{ matrix.version.golangci }}
|
||||
version: ${{ matrix.version.golangci }}
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
@@ -46,11 +50,11 @@ jobs:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.24.4'
|
||||
go-version: "1.25.1"
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
|
||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -2,7 +2,7 @@ name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -11,17 +11,17 @@ jobs:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '1.24.4'
|
||||
go-version: "1.25.1"
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.5.0'
|
||||
cosign-release: "v2.6.0"
|
||||
- name: Store Cosign private key in a file
|
||||
run: 'echo "$COSIGN_KEY" > /tmp/cosign.key'
|
||||
shell: bash
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
tags: ${{steps.meta.outputs.tags}}
|
||||
labels: ${{steps.meta.outputs.labels}}
|
||||
push: true
|
||||
build-args: GO_VERSION=1.24
|
||||
build-args: GO_VERSION=1.25
|
||||
- name: Sign Docker Image
|
||||
run: cosign sign --yes --key /tmp/cosign.key ${DIGEST} --registry-username="$secrets.DOCKER_USERNAME" --registry-password="::add-mask::$secrets.DOCKER_PASSWORD"
|
||||
env:
|
||||
|
||||
2
.github/workflows/scan.yml
vendored
2
.github/workflows/scan.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Security Scan
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
|
||||
@@ -24,6 +24,9 @@ linters:
|
||||
rules:
|
||||
- name: dot-imports
|
||||
disabled: true
|
||||
- name: filename-format
|
||||
arguments:
|
||||
- ^[a-z][_a-z0-9]*.go$
|
||||
- name: redefines-builtin-id
|
||||
staticcheck:
|
||||
checks:
|
||||
|
||||
2
Makefile
2
Makefile
@@ -17,7 +17,7 @@ GOSEC ?= $(GOBIN)/gosec
|
||||
GINKGO ?= $(GOBIN)/ginkgo
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
GOVULN_MIN_VERSION = 17
|
||||
GO_VERSION = 1.24
|
||||
GO_VERSION = 1.25
|
||||
LDFLAGS = -ldflags "\
|
||||
-X 'main.Version=$(shell git describe --tags --always)' \
|
||||
-X 'main.GitTag=$(shell git describe --tags --abbrev=0)' \
|
||||
|
||||
@@ -10,7 +10,7 @@ inputs:
|
||||
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'docker://securego/gosec:2.22.3'
|
||||
image: 'docker://securego/gosec:2.22.8'
|
||||
args:
|
||||
- ${{ inputs.args }}
|
||||
|
||||
|
||||
@@ -197,12 +197,20 @@ func isFuncContainsCryptoRand(funcCall *ssa.Function) (bool, error) {
|
||||
}
|
||||
|
||||
func addToVarsMap(value ssa.Value, mapToAddTo map[string]*ssa.Value) {
|
||||
key := value.Name() + value.Type().String() + value.String() + value.Parent().String()
|
||||
var parent string
|
||||
if value.Parent() != nil {
|
||||
parent = value.Parent().String()
|
||||
}
|
||||
key := value.Name() + value.Type().String() + value.String() + parent
|
||||
mapToAddTo[key] = &value
|
||||
}
|
||||
|
||||
func isContainedInMap(value ssa.Value, mapToCheck map[string]*ssa.Value) bool {
|
||||
key := value.Name() + value.Type().String() + value.String() + value.Parent().String()
|
||||
var parent string
|
||||
if value.Parent() != nil {
|
||||
parent = value.Parent().String()
|
||||
}
|
||||
key := value.Name() + value.Type().String() + value.String() + parent
|
||||
_, contained := mapToCheck[key]
|
||||
return contained
|
||||
}
|
||||
|
||||
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: 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)
|
||||
}
|
||||
|
||||
62
go.mod
62
go.mod
@@ -1,59 +1,59 @@
|
||||
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.5.4
|
||||
github.com/gookit/color v1.6.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5
|
||||
github.com/onsi/ginkgo/v2 v2.23.4
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/text v0.26.0
|
||||
golang.org/x/tools v0.34.0
|
||||
google.golang.org/api v0.237.0
|
||||
github.com/onsi/ginkgo/v2 v2.25.3
|
||||
github.com/onsi/gomega v1.38.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/text v0.29.0
|
||||
golang.org/x/tools v0.37.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.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.7 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // 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
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect
|
||||
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.14.2 // 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.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
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/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // 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
|
||||
)
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
140
go.sum
140
go.sum
@@ -15,24 +15,18 @@ 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.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
|
||||
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/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
|
||||
cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
|
||||
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=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
|
||||
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=
|
||||
@@ -50,12 +44,16 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
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=
|
||||
@@ -160,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=
|
||||
@@ -179,8 +175,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18=
|
||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
@@ -194,15 +190,19 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
|
||||
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
|
||||
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
|
||||
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
|
||||
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=
|
||||
@@ -290,11 +290,11 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
|
||||
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
|
||||
github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
@@ -352,8 +352,18 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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=
|
||||
@@ -383,20 +393,18 @@ 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.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
@@ -408,6 +416,8 @@ go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -417,8 +427,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -452,8 +462,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -486,15 +496,13 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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=
|
||||
@@ -504,8 +512,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -546,25 +554,23 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
@@ -611,11 +617,13 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -632,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.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
|
||||
google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
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=
|
||||
@@ -641,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=
|
||||
@@ -672,12 +680,8 @@ 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-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
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=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
@@ -692,8 +696,8 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -704,8 +708,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Golang Security Checker</title>
|
||||
<link rel="shortcut icon" type="image/png" href="https://securego.io/img/favicon.png">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.3/css/bulma.min.css" integrity="sha512-4EnjWdm80dyWrJ7rh/tlhNt6fJL52dSDSHNEqfdVmBLpJLPrRYnFa+Kn4ZZL+FRkDL5/7lAXuHylzJkpzkSM2A==" crossorigin="anonymous"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/1.0.4/css/bulma.min.css" integrity="sha512-yh2RE0wZCVZeysGiqTwDTO/dKelCbS9bP2L94UvOFtl/FKXcNAje3Y2oBg/ZMZ3LS1sicYk4dYVGtDex75fvvA==" crossorigin="anonymous"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous"/>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/go.min.js" integrity="sha512-weC0VNVf2qQR6OY675qO0AEL92gt3h5f2VGjhMUvi/UqFHaWzIEL5S/8Dt763fWfKftchzb7GryvEj/2HC9Exw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.7.0/react.min.js" integrity="sha512-+TFn1Gqbwx/qgwW3NU1/YtFYTfHGeD1e/8YfJZzkb6TFEZP4SUwp1Az9DMeWh3qC0F+YPKXbV3YclMUwBTvO3g==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js" integrity="sha512-8C49ZG/SaQnWaUgCHTU1o8uIQNYE6R8me38SwF26g2Q0byEXF4Jlvm+T/JAMHMeTBiEVPslSZRv9Xt4AV0pfmw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js" integrity="sha512-Y37Caenc5CZqwSMwWZj+5uxkB3Loc9yJNHvb+eSwEsT6nhURSrPZo39vTnb5g8UvOGCNXRbQ+xQvnqr2rR9nRw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.28.4/babel.min.js" integrity="sha512-BCw4VuBF2HKIgxDP8K7DRHcHzazAAND+5+2E7GgX3CC1u1pteJ415mMJlQfUORjkFT64irzu1jIV93Vfvzkevw==" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.field-label {
|
||||
min-width: 80px;
|
||||
|
||||
@@ -25,9 +25,12 @@ import (
|
||||
type readfile struct {
|
||||
issue.MetaData
|
||||
gosec.CallList
|
||||
pathJoin gosec.CallList
|
||||
clean gosec.CallList
|
||||
pathJoin gosec.CallList
|
||||
clean gosec.CallList
|
||||
// cleanedVar maps the declaration node of an identifier to the Clean() call node
|
||||
cleanedVar map[any]ast.Node
|
||||
// joinedVar maps the declaration node of an identifier to the Join() call node
|
||||
joinedVar map[any]ast.Node
|
||||
}
|
||||
|
||||
// ID returns the identifier for this rule
|
||||
@@ -61,6 +64,7 @@ func (r *readfile) isJoinFunc(n ast.Node, c *gosec.Context) bool {
|
||||
|
||||
// isFilepathClean checks if there is a filepath.Clean for given variable
|
||||
func (r *readfile) isFilepathClean(n *ast.Ident, c *gosec.Context) bool {
|
||||
// quick lookup: was this var's declaration recorded as a Clean() call?
|
||||
if _, ok := r.cleanedVar[n.Obj.Decl]; ok {
|
||||
return true
|
||||
}
|
||||
@@ -90,37 +94,172 @@ func (r *readfile) trackFilepathClean(n ast.Node) {
|
||||
}
|
||||
}
|
||||
|
||||
// Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile`
|
||||
// trackJoinAssignStmt tracks assignments where RHS is a Join(...) call and LHS is an identifier
|
||||
func (r *readfile) trackJoinAssignStmt(node *ast.AssignStmt, c *gosec.Context) {
|
||||
if len(node.Rhs) == 0 {
|
||||
return
|
||||
}
|
||||
if call, ok := node.Rhs[0].(*ast.CallExpr); ok {
|
||||
if r.pathJoin.ContainsPkgCallExpr(call, c, false) != nil {
|
||||
// LHS must be an identifier (simple case)
|
||||
if len(node.Lhs) > 0 {
|
||||
if ident, ok := node.Lhs[0].(*ast.Ident); ok && ident.Obj != nil {
|
||||
r.joinedVar[ident.Obj.Decl] = call
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// osRootSuggestion returns an Autofix suggesting the use of os.Root where supported
|
||||
// to constrain file access under a fixed directory and mitigate traversal risks.
|
||||
func (r *readfile) osRootSuggestion() string {
|
||||
major, minor, _ := gosec.GoVersion()
|
||||
if major == 1 && minor >= 24 {
|
||||
return "Consider using os.Root to scope file access under a fixed root (Go >=1.24). Prefer root.Open/root.Stat over os.Open/os.Stat to prevent directory traversal."
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// isSafeJoin checks if path is baseDir + filepath.Clean(fn) joined.
|
||||
// improvements over earlier naive version:
|
||||
// - allow baseDir as a BasicLit or as an identifier that resolves to a string constant
|
||||
// - accept Clean(...) being either a CallExpr or an identifier previously recorded as Clean result
|
||||
func (r *readfile) isSafeJoin(call *ast.CallExpr, c *gosec.Context) bool {
|
||||
join := r.pathJoin.ContainsPkgCallExpr(call, c, false)
|
||||
if join == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// We expect join.Args to include a baseDir-like arg and a cleaned path arg.
|
||||
var foundBaseDir bool
|
||||
var foundCleanArg bool
|
||||
|
||||
for _, arg := range join.Args {
|
||||
switch a := arg.(type) {
|
||||
case *ast.BasicLit:
|
||||
// literal string or similar — treat as possible baseDir
|
||||
foundBaseDir = true
|
||||
case *ast.Ident:
|
||||
// If ident is resolvable to a constant string (TryResolve true), treat as baseDir.
|
||||
// Or if ident refers to a variable that was itself assigned from a constant BasicLit,
|
||||
// it's considered safe as baseDir.
|
||||
if gosec.TryResolve(a, c) {
|
||||
foundBaseDir = true
|
||||
} else {
|
||||
// It might be a cleaned variable: e.g. cleanPath := filepath.Clean(fn)
|
||||
if r.isFilepathClean(a, c) {
|
||||
foundCleanArg = true
|
||||
}
|
||||
}
|
||||
case *ast.CallExpr:
|
||||
// If an argument is a Clean() call directly, mark clean arg found.
|
||||
if r.clean.ContainsPkgCallExpr(a, c, false) != nil {
|
||||
foundCleanArg = true
|
||||
}
|
||||
default:
|
||||
// ignore other types
|
||||
}
|
||||
}
|
||||
|
||||
return foundBaseDir && foundCleanArg
|
||||
}
|
||||
|
||||
func (r *readfile) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
|
||||
// Track filepath.Clean usages so identifiers assigned from Clean() are known.
|
||||
if node := r.clean.ContainsPkgCallExpr(n, c, false); node != nil {
|
||||
r.trackFilepathClean(n)
|
||||
return nil, nil
|
||||
} else if node := r.ContainsPkgCallExpr(n, c, false); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
// handles path joining functions in Arg
|
||||
// eg. os.Open(filepath.Join("/tmp/", file))
|
||||
if callExpr, ok := arg.(*ast.CallExpr); ok {
|
||||
if r.isJoinFunc(callExpr, c) {
|
||||
return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
// handles binary string concatenation eg. ioutil.Readfile("/tmp/" + file + "/blob")
|
||||
if binExp, ok := arg.(*ast.BinaryExpr); ok {
|
||||
// resolve all found identities from the BinaryExpr
|
||||
if _, ok := gosec.FindVarIdentities(binExp, c); ok {
|
||||
return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok &&
|
||||
!gosec.TryResolve(ident, c) &&
|
||||
!r.isFilepathClean(ident, c) {
|
||||
return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
// Track Join assignments if we see an AssignStmt whose RHS is a Join call.
|
||||
if assign, ok := n.(*ast.AssignStmt); ok {
|
||||
// track join result assigned to a variable, e.g., fullPath := filepath.Join(baseDir, cleanPath)
|
||||
r.trackJoinAssignStmt(assign, c)
|
||||
// also track Clean assignment if present on RHS
|
||||
if len(assign.Rhs) > 0 {
|
||||
if call, ok := assign.Rhs[0].(*ast.CallExpr); ok {
|
||||
if r.clean.ContainsPkgCallExpr(call, c, false) != nil {
|
||||
r.trackFilepathClean(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
// continue, don't return here — other checks may apply
|
||||
}
|
||||
|
||||
// Now check for file-reading calls (os.Open, os.OpenFile, ioutil.ReadFile etc.)
|
||||
if node := r.ContainsPkgCallExpr(n, c, false); node != nil {
|
||||
if len(node.Args) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
arg := node.Args[0]
|
||||
|
||||
// If argument is a call expression, check for Join/Clean patterns.
|
||||
if callExpr, ok := arg.(*ast.CallExpr); ok {
|
||||
// If this call matches a safe Join(baseDir, Clean(...)) pattern, treat as safe.
|
||||
if r.isSafeJoin(callExpr, c) {
|
||||
// safe pattern detected; do not raise an issue
|
||||
return nil, nil
|
||||
}
|
||||
// If the argument is a Join call but not safe per above, flag it (as before)
|
||||
if r.isJoinFunc(callExpr, c) {
|
||||
iss := c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence)
|
||||
if s := r.osRootSuggestion(); s != "" {
|
||||
iss.Autofix = s
|
||||
}
|
||||
return iss, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If arg is an identifier that was assigned from a Join(...) call, check that recorded Join call.
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
if ident.Obj != nil {
|
||||
if joinCall, ok := r.joinedVar[ident.Obj.Decl]; ok {
|
||||
// If the identifier itself was later cleaned, treat as safe regardless of original Join args
|
||||
if r.isFilepathClean(ident, c) {
|
||||
return nil, nil
|
||||
}
|
||||
// joinCall is a *ast.CallExpr; check if that join is a safe join
|
||||
if jc, ok := joinCall.(*ast.CallExpr); ok {
|
||||
if r.isSafeJoin(jc, c) {
|
||||
return nil, nil
|
||||
}
|
||||
// join exists but is not safe: flag it
|
||||
iss := c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence)
|
||||
if s := r.osRootSuggestion(); s != "" {
|
||||
iss.Autofix = s
|
||||
}
|
||||
return iss, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handles binary string concatenation eg. ioutil.Readfile("/tmp/" + file + "/blob")
|
||||
if binExp, ok := arg.(*ast.BinaryExpr); ok {
|
||||
// resolve all found identities from the BinaryExpr
|
||||
if _, ok := gosec.FindVarIdentities(binExp, c); ok {
|
||||
iss := c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence)
|
||||
if s := r.osRootSuggestion(); s != "" {
|
||||
iss.Autofix = s
|
||||
}
|
||||
return iss, nil
|
||||
}
|
||||
}
|
||||
|
||||
// if it's a plain identifier, and not resolved and not cleaned, flag it
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok &&
|
||||
!gosec.TryResolve(ident, c) &&
|
||||
!r.isFilepathClean(ident, c) {
|
||||
iss := c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence)
|
||||
if s := r.osRootSuggestion(); s != "" {
|
||||
iss.Autofix = s
|
||||
}
|
||||
return iss, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -138,6 +277,7 @@ func NewReadFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
Confidence: issue.High,
|
||||
},
|
||||
cleanedVar: map[any]ast.Node{},
|
||||
joinedVar: map[any]ast.Node{},
|
||||
}
|
||||
rule.pathJoin.Add("path/filepath", "Join")
|
||||
rule.pathJoin.Add("path", "Join")
|
||||
@@ -149,5 +289,5 @@ func NewReadFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
rule.Add("os", "Open")
|
||||
rule.Add("os", "OpenFile")
|
||||
rule.Add("os", "Create")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil), (*ast.AssignStmt)(nil)}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,11 @@ func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issu
|
||||
if injection := s.findInjectionInBranch(ctx, decl.Rhs); injection != nil {
|
||||
return ctx.NewIssue(injection, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
case *ast.ValueSpec:
|
||||
// handle: var query string = "SELECT ...'" + user
|
||||
if injection := s.findInjectionInBranch(ctx, decl.Values); injection != nil {
|
||||
return ctx.NewIssue(injection, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -308,4 +308,32 @@ func main() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("postgres", "user=postgres password=password dbname=mydb sslmode=disable")
|
||||
if err!= nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var username string
|
||||
fmt.Println("请输入用户名:")
|
||||
fmt.Scanln(&username)
|
||||
|
||||
var query string = "SELECT * FROM users WHERE username = '" + username + "'"
|
||||
rows, err := db.Query(query)
|
||||
if err!= nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
`}, 1, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
@@ -301,5 +301,47 @@ func main() {
|
||||
package main
|
||||
|
||||
var THEWD string
|
||||
`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func open(fn string, perm os.FileMode) {
|
||||
fh, err := os.OpenFile(filepath.Clean(fn), os.O_RDONLY, perm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fh.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
fn := "filename"
|
||||
open(fn, 0o600)
|
||||
}
|
||||
`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func open(fn string, flag int) {
|
||||
fh, err := os.OpenFile(filepath.Clean(fn), flag, 0o600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fh.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
fn := "filename"
|
||||
open(fn, os.O_RDONLY)
|
||||
}
|
||||
`}, 0, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user