Repository: securego/gosec (language: go)

Issue:
Add an option to set the Go version
### Summary 

Hello,

I know that my request is really specific to golangci-lint, and if you don't want to do that I will understand.

The new way for gosec to detect the Go version has a huge impact on golangci-lint performances.

- #1119

The best solution for us is to have an option to set the Go version to bypass the usage of `go list`.

### Steps to reproduce the behavior

https://github.com/golangci/golangci-lint/issues/4735

### gosec version

v2.20.0

### Go version (output of 'go version')

```console
$ go version
go version go1.22.3 linux/amd64
```

### Operating system / Environment

Not dependent on the OS




Discussion / hints:
@ldez The new approach is using the `go list` to parse the go version first from the local go module file, if this is not found will fall back to runtime version.  Is `go list` introducing performance issues? 

I am thinking to keep the current behaviour as default and introduce an environment variable to bypass the go list parsing something like `GOSECNOLOCALVERSION`. Would this work for you? 
> Is `go list` introducing performance issues?

Yes because golangci-lint loads the rules for each package, so a `go list` is done a lot of times (nb of rules that require the Go version)*(nb of packages).

> I am thinking to keep the current behaviour as default and introduce an environment variable to bypass the go list parsing something like `GOSECNOLOCALVERSION`. Would this work for you?

An env var can work.

Note: currently, only 2 rules (G601 and G113) use the `GoVersion` function but the more you will use it and more you will also be impacted by this performance problem.
@ldez I added an environment variable which should disabled the parsing of Go version from module file present in the project. Please have a look at the [README](https://github.com/securego/gosec?tab=readme-ov-file#go-version).
@ccojocar I was expected to be able to set the go version through the env var because the `runtime` version will not be right.

```go
// GoVersion returns parsed version of Go mod version and fallback to runtime version if not found.
func GoVersion() (int, int, int) {
	if env, ok := os.LookupEnv("GOSECGOVERSION"); ok {
		return parseGoVersion(strings.TrimPrefix(env, "go"))
	}

	goVersion, err := goModVersion()
	if err != nil {
		return parseGoVersion(strings.TrimPrefix(runtime.Version(), "go"))
	}

	return parseGoVersion(goVersion)
}
```

Fix patch:
diff --git a/README.md b/README.md
index 9d5ed3fbf0..946536a944 100644
--- a/README.md
+++ b/README.md
@@ -234,7 +234,7 @@ You can also configure the hard-coded credentials rule `G101` with additional pa
 
 Some rules require a specific Go version which is retrieved from the Go module file present in the project. If this version cannot be found, it will fallback to Go runtime version.
 
-The Go module version is parsed using the `go list` command which in some cases might lead to performance degradation. In this situation, the go module version can be easily disabled by setting the environment variable `GOSECNOMODVERSION=on`.
+The Go module version is parsed using the `go list` command which in some cases might lead to performance degradation. In this situation, the go module version can be easily provided by setting the environment variable `GOSECGOVERSION=go1.21.1`.
 
 ### Dependencies
 
diff --git a/helpers.go b/helpers.go
index 5493efebd5..1089f52c0f 100644
--- a/helpers.go
+++ b/helpers.go
@@ -32,8 +32,8 @@ import (
 	"strings"
 )
 
-// noGoModVersion disables the parsing of  go version from go module file present in the project
-const noGoModVersion = "GOSECNOMODVERSION"
+// envGoModVersion overrides the Go version detection.
+const envGoModVersion = "GOSECGOVERSION"
 
 // MatchCallByPackage ensures that the specified package is imported,
 // adjusts the name for any aliases and ignores cases that are
@@ -501,13 +501,16 @@ func RootPath(root string) (string, error) {
 
 // GoVersion returns parsed version of Go mod version and fallback to runtime version if not found.
 func GoVersion() (int, int, int) {
-	_, ok := os.LookupEnv(noGoModVersion)
-	if !ok {
-		if goModVersion, err := goModVersion(); err == nil {
-			return parseGoVersion(goModVersion)
-		}
+	if env, ok := os.LookupEnv(envGoModVersion); ok {
+		return parseGoVersion(strings.TrimPrefix(env, "go"))
 	}
-	return parseGoVersion(strings.TrimPrefix(runtime.Version(), "go"))
+
+	goVersion, err := goModVersion()
+	if err != nil {
+		return parseGoVersion(strings.TrimPrefix(runtime.Version(), "go"))
+	}
+
+	return parseGoVersion(goVersion)
 }
 
 type goListOutput struct {


Test patch: