Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gosec for openSUSE:Factory checked 
in at 2025-09-22 19:29:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gosec (Old)
 and      /work/SRC/openSUSE:Factory/.gosec.new.27445 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gosec"

Mon Sep 22 19:29:23 2025 rev:27 rq:1306580 version:2.22.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/gosec/gosec.changes      2025-08-18 
16:09:49.398839454 +0200
+++ /work/SRC/openSUSE:Factory/.gosec.new.27445/gosec.changes   2025-09-22 
19:29:42.642936502 +0200
@@ -1,0 +2,19 @@
+Mon Sep 22 12:36:33 UTC 2025 - Felix Niederwanger <[email protected]>
+
+- Update to version 2.22.9:
+  * Update cosign to v2.6.0 and go in the CI to latest version
+  * fix(autofix): unnecessary conversion
+  * feat(autofix): update gemini sdk and add anthropic claude
+  * feat(G304): add os.Root remediation hint (Autofix) when Go >= 1.24
+  * chore(deps): update all dependencies
+  * refactor(G304): remove unused trackJoin helper; no functional change
+  * style: gofmt rules/readfile.go
+  * test(g304): add samples for var perm and var flag with cleaned path\n\n- 
Ensure G304 does not fire when only non-path args (flag/perm) are variables\n- 
Both samples use filepath.Clean on the path arg\n- Rules suite remains green 
(42 passed)
+  * rules(G304): analyze only path arg; ignore flag/perm vars; track Clean and 
safe Join; fix nil-context panic\n\n- Limit G304 checks to first arg (path) for 
os.Open/OpenFile/ReadFile, avoiding false positives when flag/perm are 
variables\n- Track filepath.Clean so cleaned identifiers are treated as safe\n- 
Consider safe joins: filepath.Join(const|resolvedBase, 
Clean(var)|cleanedIdent)\n- Record Join(...) assigned to identifiers and allow 
if later cleaned\n- Fix panic by passing non-nil context in 
trackJoinAssignStmt\n- All rules tests: 42 passed
+  * rules(G202): detect SQL concat in ValueSpec declarations; add test 
sample\n\n- Handle var query string = 'SELECT ...' + user style declarations\n- 
Reuse existing binary expr detection on ValueSpec.Values\n- Add postgres sample 
mirroring issue #1309 report\n- Rules tests: 42 passed
+  * chore(deps): update all dependencies
+  * chore(deps): update all dependencies
+  * chore(deps): update all dependencies
+  * Update gosec version to v2.22.8 in the Github action
+
+-------------------------------------------------------------------

Old:
----
  gosec-2.22.8.obscpio

New:
----
  gosec-2.22.9.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gosec.spec ++++++
--- /var/tmp/diff_new_pack.n7wk1u/_old  2025-09-22 19:29:44.399010454 +0200
+++ /var/tmp/diff_new_pack.n7wk1u/_new  2025-09-22 19:29:44.399010454 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           gosec
-Version:        2.22.8
+Version:        2.22.9
 Release:        0
 Summary:        CLI tool to scan the Go AST and SSA code representations for 
security problems
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.n7wk1u/_old  2025-09-22 19:29:44.439012139 +0200
+++ /var/tmp/diff_new_pack.n7wk1u/_new  2025-09-22 19:29:44.443012307 +0200
@@ -4,7 +4,7 @@
     <param name="filename">gosec</param>
     <param name="url">https://github.com/securego/gosec.git</param>
     <param name="scm">git</param>
-       <param name="revision">v2.22.8</param>
+       <param name="revision">v2.22.9</param>
        <param name="match-tag">v*</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.n7wk1u/_old  2025-09-22 19:29:44.459012981 +0200
+++ /var/tmp/diff_new_pack.n7wk1u/_new  2025-09-22 19:29:44.463013149 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/securego/gosec.git</param>
-              <param 
name="changesrevision">c9453023c4e81ebdb6dde29e22d9cd5e2285fb16</param></service></servicedata>
+              <param 
name="changesrevision">15d5c61e866bc2e2e8389376a31f1e5e09bde7d8</param></service></servicedata>
 (No newline at EOF)
 

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2025-02-13 15:25:36.000000000 +0100
@@ -0,0 +1,3 @@
+.osc
+/gosec
+/gosec-*.*.*.tar.xz

++++++ gosec-2.22.8.obscpio -> gosec-2.22.9.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/.github/workflows/ci.yml 
new/gosec-2.22.9/.github/workflows/ci.yml
--- old/gosec-2.22.8/.github/workflows/ci.yml   2025-08-14 14:38:12.000000000 
+0200
+++ new/gosec-2.22.9/.github/workflows/ci.yml   2025-09-22 10:42:03.000000000 
+0200
@@ -11,20 +11,20 @@
     strategy:
       matrix:
         version:
-        - go-version: '1.24.6'
-          golangci: 'latest'
-        - go-version: '1.25.0'
-          golangci: 'latest'
+          - 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-version }}
-        uses: actions/setup-go@v5
+        uses: actions/setup-go@v6
         with:
           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
@@ -34,7 +34,7 @@
       - 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:
@@ -50,11 +50,11 @@
       GO111MODULE: on
     steps:
       - name: Setup go
-        uses: actions/setup-go@v5
+        uses: actions/setup-go@v6
         with:
-          go-version: '1.25.0'
+          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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/.github/workflows/release.yml 
new/gosec-2.22.9/.github/workflows/release.yml
--- old/gosec-2.22.8/.github/workflows/release.yml      2025-08-14 
14:38:12.000000000 +0200
+++ new/gosec-2.22.9/.github/workflows/release.yml      2025-09-22 
10:42:03.000000000 +0200
@@ -2,7 +2,7 @@
 on:
   push:
     tags:
-      - 'v*'
+      - "v*"
 jobs:
   build:
     runs-on: ubuntu-latest
@@ -11,17 +11,17 @@
       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.25.0'
+          go-version: "1.25.1"
       - name: Install Cosign
         uses: sigstore/cosign-installer@v3
         with:
-          cosign-release: 'v2.5.3'
+          cosign-release: "v2.6.0"
       - name: Store Cosign private key in a file
         run: 'echo "$COSIGN_KEY" > /tmp/cosign.key'
         shell: bash
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/.github/workflows/scan.yml 
new/gosec-2.22.9/.github/workflows/scan.yml
--- old/gosec-2.22.8/.github/workflows/scan.yml 2025-08-14 14:38:12.000000000 
+0200
+++ new/gosec-2.22.9/.github/workflows/scan.yml 2025-09-22 10:42:03.000000000 
+0200
@@ -13,7 +13,7 @@
     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:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/action.yml new/gosec-2.22.9/action.yml
--- old/gosec-2.22.8/action.yml 2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/action.yml 2025-09-22 10:42:03.000000000 +0200
@@ -10,7 +10,7 @@
 
 runs:
     using: 'docker'
-    image: 'docker://securego/gosec:2.22.7'
+    image: 'docker://securego/gosec:2.22.8'
     args:
       - ${{ inputs.args }}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/autofix/ai.go 
new/gosec-2.22.9/autofix/ai.go
--- old/gosec-2.22.8/autofix/ai.go      2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/autofix/ai.go      2025-09-22 10:42:03.000000000 +0200
@@ -4,97 +4,53 @@
        "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)
-}
-
-// 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")
-       }
-
-       if len(resp.Candidates[0].Content.Parts) == 0 {
-               return "", errors.New("nothing found in the first autofix 
returned by gemini")
-       }
-
-       // Return the first candidate
-       return fmt.Sprintf("%+v", resp.Candidates[0].Content.Parts[0]), nil
-}
+// 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
 
-// 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))
+       switch {
+       case strings.HasPrefix(model, "claude"):
+               client, err = NewClaudeClient(model, aiAPIKey)
+       case strings.HasPrefix(model, "gemini"):
+               client, err = NewGeminiClient(model, aiAPIKey)
        }
 
-       client, err := genai.NewClient(ctx, clientOptions...)
-       if err != nil {
-               return nil, fmt.Errorf("calling gemini API: %w", err)
+       switch {
+       case err != nil:
+               return fmt.Errorf("initializing AI client: %w", err)
+       case client == nil:
+               return fmt.Errorf("unsupported AI backend: %s", model)
        }
 
-       return &genAIClientWrapper{client: client}, nil
+       return generateSolution(client, issues)
 }
 
-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 @@
                }
 
                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 @@
        }
        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)
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/autofix/ai_test.go 
new/gosec-2.22.9/autofix/ai_test.go
--- old/gosec-2.22.8/autofix/ai_test.go 2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/autofix/ai_test.go 2025-09-22 10:42:03.000000000 +0200
@@ -17,22 +17,7 @@
        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 @@
        }
 
        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 @@
        }
 
        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 @@
        }
 
        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 @@
        }
 
        // 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")
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/autofix/claude.go 
new/gosec-2.22.9/autofix/claude.go
--- old/gosec-2.22.8/autofix/claude.go  1970-01-01 01:00:00.000000000 +0100
+++ new/gosec-2.22.9/autofix/claude.go  2025-09-22 10:42:03.000000000 +0200
@@ -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
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/autofix/gemini.go 
new/gosec-2.22.9/autofix/gemini.go
--- old/gosec-2.22.8/autofix/gemini.go  1970-01-01 01:00:00.000000000 +0100
+++ new/gosec-2.22.9/autofix/gemini.go  2025-09-22 10:42:03.000000000 +0200
@@ -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)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/cmd/gosec/main.go 
new/gosec-2.22.9/cmd/gosec/main.go
--- old/gosec-2.22.8/cmd/gosec/main.go  2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/cmd/gosec/main.go  2025-09-22 10:42:03.000000000 +0200
@@ -154,14 +154,11 @@
        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 @@
        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)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/go.mod new/gosec-2.22.9/go.mod
--- old/gosec-2.22.8/go.mod     2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/go.mod     2025-09-22 10:42:03.000000000 +0200
@@ -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.38.0
-       github.com/stretchr/testify v1.10.0
-       golang.org/x/crypto v0.41.0
-       golang.org/x/text v0.28.0
-       golang.org/x/tools v0.36.0
-       google.golang.org/api v0.246.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.3 // 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.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.27.0 // indirect
-       golang.org/x/net v0.43.0 // indirect
-       golang.org/x/oauth2 v0.30.0 // indirect
-       golang.org/x/sync v0.16.0 // indirect
-       golang.org/x/sys v0.35.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-20250728155136-f173205681a0 // indirect
-       google.golang.org/grpc v1.74.2 // 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.24.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/go.sum new/gosec-2.22.9/go.sum
--- old/gosec-2.22.8/go.sum     2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/go.sum     2025-09-22 10:42:03.000000000 +0200
@@ -15,24 +15,18 @@
 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.3 
h1:kabzoQ9/bobUmnseYnBO6qQG7q4a/CffFRlJSxv2wCc=
-cloud.google.com/go/auth v0.16.3/go.mod 
h1:NucRGjaXfzP1ltpcQ7On/VTZ0H4kWB5Jy+Y9Dnm76fA=
-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/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 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-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=
@@ -196,13 +192,17 @@
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod 
h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 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/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
-github.com/gookit/color v1.5.4/go.mod 
h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
+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.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.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
-github.com/onsi/gomega v1.38.0/go.mod 
h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
+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.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.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/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-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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod 
h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+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.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.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+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-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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+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-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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.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-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.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
-golang.org/x/sys v0.35.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.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod 
h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod 
h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+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-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.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod 
h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+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.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.246.0 h1:H0ODDs5PnMZVZAEtdLMn2Ul2eQi7QNjqM2DIFp8TlTM=
-google.golang.org/api v0.246.0/go.mod 
h1:dMVhVcylamkirHdzEBAIQWUCgqY885ivNeZYd7VAVr8=
 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.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-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-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-20250728155136-f173205681a0 
h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20250728155136-f173205681a0/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.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.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
-google.golang.org/grpc v1.74.2/go.mod 
h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
+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.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=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/report/html/template.html 
new/gosec-2.22.9/report/html/template.html
--- old/gosec-2.22.8/report/html/template.html  2025-08-14 14:38:12.000000000 
+0200
+++ new/gosec-2.22.9/report/html/template.html  2025-09-22 10:42:03.000000000 
+0200
@@ -10,7 +10,7 @@
   <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.28.2/babel.min.js";
 
integrity="sha512-AEhA/tSQr2Jwvfb/CmMAIVkOmPdIB5EBZXiHs3TXmarNdhcwX7ufSddMMVh2dVD2/8M33nWExg2KK33MLen09Q=="
 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;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/rules/readfile.go 
new/gosec-2.22.9/rules/readfile.go
--- old/gosec-2.22.8/rules/readfile.go  2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/rules/readfile.go  2025-09-22 10:42:03.000000000 +0200
@@ -25,9 +25,12 @@
 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 @@
 
 // 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,35 +94,170 @@
        }
 }
 
-// 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
+       }
+
+       // 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)
                                }
                        }
-                       // 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
+               }
+               // 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
+                                       }
                                }
                        }
+               }
 
-                       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
+               // 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
                        }
                }
        }
@@ -138,6 +277,7 @@
                        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 @@
        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)}
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/rules/sql.go 
new/gosec-2.22.9/rules/sql.go
--- old/gosec-2.22.8/rules/sql.go       2025-08-14 14:38:12.000000000 +0200
+++ new/gosec-2.22.9/rules/sql.go       2025-09-22 10:42:03.000000000 +0200
@@ -191,6 +191,11 @@
                        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
+                       }
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/testutils/g202_samples.go 
new/gosec-2.22.9/testutils/g202_samples.go
--- old/gosec-2.22.8/testutils/g202_samples.go  2025-08-14 14:38:12.000000000 
+0200
+++ new/gosec-2.22.9/testutils/g202_samples.go  2025-09-22 10:42:03.000000000 
+0200
@@ -308,4 +308,32 @@
        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()},
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gosec-2.22.8/testutils/g304_samples.go 
new/gosec-2.22.9/testutils/g304_samples.go
--- old/gosec-2.22.8/testutils/g304_samples.go  2025-08-14 14:38:12.000000000 
+0200
+++ new/gosec-2.22.9/testutils/g304_samples.go  2025-09-22 10:42:03.000000000 
+0200
@@ -302,4 +302,46 @@
 
 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()},
 }

++++++ gosec.obsinfo ++++++
--- /var/tmp/diff_new_pack.n7wk1u/_old  2025-09-22 19:29:44.695022920 +0200
+++ /var/tmp/diff_new_pack.n7wk1u/_new  2025-09-22 19:29:44.699023089 +0200
@@ -1,5 +1,5 @@
 name: gosec
-version: 2.22.8
-mtime: 1755175092
-commit: c9453023c4e81ebdb6dde29e22d9cd5e2285fb16
+version: 2.22.9
+mtime: 1758530523
+commit: 15d5c61e866bc2e2e8389376a31f1e5e09bde7d8
 

++++++ vendor.tar.xz ++++++
++++ 222683 lines of diff (skipped)

Reply via email to