diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 731b6bb484..18f14f027a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,54 +7,60 @@ on:
     branches:
       - master
 jobs:
-  tests-go-1-16:
+  test:
+    strategy:
+      matrix:
+        version: [{go: '1.23.11', golangci: 'latest'}, {go: '1.24.5', golangci: 'latest'}]
     runs-on: ubuntu-latest
     env:
       GO111MODULE: on
     steps:
-      - name: Checkout Source 
-        uses: actions/checkout@v2
-      - name: Run Tests
-        uses: cedrickring/golang-action@1.7.0
+      - name: Setup go ${{ matrix.version.go }}
+        uses: actions/setup-go@v5
         with:
-          args: make test
-  tests-go-1-15:
-    runs-on: ubuntu-latest
-    env:
-      GO111MODULE: on
-    steps:
-      - name: Checkout Source 
-        uses: actions/checkout@v2
-      - name: Run Tests
-        uses: cedrickring/golang-action/go1.15@1.7.0
+          go-version: ${{ matrix.version.go }}
+      - name: Checkout Source
+        uses: actions/checkout@v4
+      - uses: actions/cache@v4
         with:
-          args: make test
-  tests-go-1-14:
-    runs-on: ubuntu-latest
-    env:
-      GO111MODULE: on
-    steps:
-      - name: Checkout Source 
-        uses: actions/checkout@v2
-      - name: Run Tests
-        uses: cedrickring/golang-action/go1.14@1.7.0
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      - name: lint
+        uses: golangci/golangci-lint-action@v8
+        with:
+         version: ${{ matrix.version.golangci }}
+      - name: Run Gosec Security Scanner
+        uses: securego/gosec@master
         with:
-          args: make test
+          args: ./...
+      - name: Run Tests
+        run: make test
+      - name: Perf Diff
+        run: make perf-diff
   coverage:
-    needs: [tests-go-1-16, tests-go-1-15, tests-go-1-14]
+    needs: [test]
     runs-on: ubuntu-latest
     env:
       GO111MODULE: on
     steps:
-      - name: Checkout Source 
-        uses: actions/checkout@v2
-      - name: Create Test Coverage
-        uses: cedrickring/golang-action@1.7.0
+      - name: Setup go
+        uses: actions/setup-go@v5
         with:
-          args: make test-coverage
+          go-version: '1.24.5'
+      - name: Checkout Source
+        uses: actions/checkout@v4
+      - uses: actions/cache@v4
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      - name: Create Test Coverage
+        run: make test-coverage
       - name: Upload Test Coverage
-        uses: codecov/codecov-action@v1
+        uses: codecov/codecov-action@v5
         with:
           token: ${{ secrets.CODECOV_TOKEN }}
-          file: ./coverage.txt
           fail_ci_if_error: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 26310d59ab..eac3a27a0a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -10,30 +10,68 @@ jobs:
       GO111MODULE: on
       ACTIONS_ALLOW_UNSECURE_COMMANDS: true
     steps:
-      - name: Checkout Source 
-        uses: actions/checkout@v2
+      - name: Checkout Source
+        uses: actions/checkout@v4
       - name: Unshallow
         run: git fetch --prune --unshallow
       - name: Set up Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v5
         with:
-          go-version: 1.16.x
-      - name : Get release version
-        id: get_version
-        run: echo ::set-env name=RELEASE_VERSION::$(echo ${GITHUB_REF:10})
+          go-version: '1.24.5'
+      - name: Install Cosign
+        uses: sigstore/cosign-installer@v3
+        with:
+          cosign-release: 'v2.5.0'
+      - name: Store Cosign private key in a file
+        run: 'echo "$COSIGN_KEY" > /tmp/cosign.key'
+        shell: bash
+        env:
+          COSIGN_KEY: ${{secrets.COSIGN_KEY}}
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to DockerHub
+        uses: docker/login-action@v3
+        with:
+          username: ${{secrets.DOCKER_USERNAME}}
+          password: ${{secrets.DOCKER_PASSWORD}}
+      - name: Generate SBOM
+        uses: CycloneDX/gh-gomod-generate-sbom@v2
+        with:
+          version: v1
+          args: mod -licenses -json -output bom.json
+      - name: Docker meta
+        uses: docker/metadata-action@v5
+        id: meta
+        with:
+          images: securego/gosec
+          flavor: |
+            latest=true
+          tags: |
+            type=sha,format=long
+            type=semver,pattern={{version}}
       - name: Release Binaries
-        uses: goreleaser/goreleaser-action@v2
+        uses: goreleaser/goreleaser-action@v6
         with:
           version: latest
-          args: release --rm-dist
+          args: release --clean
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+          COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
       - name: Release Docker Image
-        uses: elgohr/Publish-Docker-Github-Action@master
+        uses: docker/build-push-action@v6
+        id: relimage
         with:
-            name: securego/gosec
-            username: ${{ secrets.DOCKER_USERNAME }}
-            password: ${{ secrets.DOCKER_PASSWORD }}
-            buildargs: GO_VERSION=1.16
-            tags: "latest,${{ env.RELEASE_VERSION }}"
-            tag_names: true
+          platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le
+          tags: ${{steps.meta.outputs.tags}}
+          labels: ${{steps.meta.outputs.labels}}
+          push: true
+          build-args: GO_VERSION=1.24
+      - 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:
+          TAGS: ${{steps.meta.outputs.tags}}
+          COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
+          COSIGN_PRIVATE_KEY: /tmp/cosign.key
+          DIGEST: ${{steps.relimage.outputs.digest}}
diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml
new file mode 100644
index 0000000000..c97a3631c7
--- /dev/null
+++ b/.github/workflows/scan.yml
@@ -0,0 +1,26 @@
+name: "Security Scan"
+
+# Run workflow each time code is pushed to your repository and on a schedule.
+# The scheduled workflow runs every at 00:00 on Sunday UTC time.
+on:
+  push:
+  pull_request:
+  schedule:
+  - cron: '0 0 * * 0'
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v4
+    - name: Security Scan
+      uses: securego/gosec@master
+      with:
+        # we let the report trigger content trigger a failure using the GitHub Security features.
+        args: '-no-fail -fmt sarif -out results.sarif ./...'
+    - name: Upload SARIF file
+      uses: github/codeql-action/upload-sarif@v3
+      with:
+        # Path to SARIF file relative to the root of the repository
+        sarif_file: results.sarif
diff --git a/.gitignore b/.gitignore
index f282cda248..45460260f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,8 @@ _testmain.go
 .DS_Store
 
 .vscode
+.idea
+
+# SBOMs generated during CI
+/bom.json
+1
\ No newline at end of file
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000000..969bc5ddfe
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,65 @@
+version: "2"
+linters:
+  enable:
+    - asciicheck
+    - bodyclose
+    - copyloopvar
+    - dogsled
+    - durationcheck
+    - errorlint
+    - ginkgolinter
+    - gochecknoinits
+    - gosec
+    - importas
+    - misspell
+    - nakedret
+    - nolintlint
+    - revive
+    - testifylint
+    - unconvert
+    - unparam
+    - wastedassign
+  settings:
+    revive:
+      rules:
+        - name: dot-imports
+          disabled: true
+        - name: filename-format
+          arguments:
+            - ^[a-z][_a-z0-9]*.go$
+        - name: redefines-builtin-id
+    staticcheck:
+      checks:
+        - all
+        - -SA1019
+    testifylint:
+      enable-all: true
+  exclusions:
+    generated: lax
+    presets:
+      - comments
+      - common-false-positives
+      - legacy
+      - std-error-handling
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+formatters:
+  enable:
+    - gci
+    - gofmt
+    - gofumpt
+    - goimports
+  settings:
+    gci:
+      sections:
+        - standard
+        - default
+        - prefix(github.com/securego)
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 4f8fc4128b..7ef0d7a3d7 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,13 +1,16 @@
 ---
+version: 2
 project_name: gosec
 
 release:
+  extra_files:
+    - glob: ./bom.json
   github:
     owner: securego
     name: gosec
 
 builds:
-  - main : ./cmd/gosec/
+  - main: ./cmd/gosec/
     binary: gosec
     goos:
       - darwin
@@ -15,6 +18,21 @@ builds:
       - windows
     goarch:
       - amd64
+      - arm64
+      - s390x
+      - ppc64le
     ldflags: -X main.Version={{.Version}} -X main.GitTag={{.Tag}} -X main.BuildDate={{.Date}}
     env:
       - CGO_ENABLED=0
+
+signs:
+- cmd: cosign
+  stdin: '{{ .Env.COSIGN_PASSWORD}}'
+  args:
+  - "sign-blob"
+  - "--key=/tmp/cosign.key"
+  - "--output=${signature}"
+  - "${artifact}"
+  - "--yes"
+  artifacts: all
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..32752ad592
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,81 @@
+# Contributing
+
+## Adding a new rule
+
+New rules can be implemented in two ways:
+
+- as a `gosec.Rule` -- these define an arbitrary function which will be called on every AST node in the analyzed file, and are appropriate for rules that mostly need to reason about a single statement.
+- as an Analyzer -- these can operate on the entire program, and receive an [SSA](https://pkg.go.dev/golang.org/x/tools/go/ssa) representation of the package. This type of rule is useful when you need to perform a more complex analysis that requires a great deal of context.
+
+### Adding a gosec.Rule
+
+1. Copy an existing rule file as a starting point-- `./rules/unsafe.go` is a good option, as it implements a very simple rule with no additional supporting logic. Put the copied file in the `./rules/` directory.
+2. Change the name of the rule constructor function and of the types in the rule file you've copied so they will be unique.
+3. Edit the `Generate` function in `./rules/rulelist.go` to include your rule.
+4. Add a RuleID to CWE ID mapping for your rule to the `ruleToCWE` map in `./issue/issue.go`. If you need a CWE that isn't already defined in `./cwe/data.go`, add it to the `idWeaknessess` map in that file.
+5. Use `make` to compile `gosec`. The binary will now contain your rule.
+
+To make your rule actually useful, you will likely want to use the support functions defined in `./resolve.go`, `./helpers.go` and `./call_list.go`. There are inline comments explaining the purpose of most of these functions, and you can find usage examples in the existing rule files.
+
+### Adding an Analyzer
+
+1. Create a new go file under `./analyzers/` with the following scaffolding in it:
+
+```go
+package analyzers
+
+import (
+        "fmt"
+
+        "golang.org/x/tools/go/analysis"
+        "golang.org/x/tools/go/analysis/passes/buildssa"
+        "github.com/securego/gosec/v2/issue"
+)
+
+const defaultIssueDescriptionMyAnalyzer = "My new analyzer!"
+
+func newMyAnalyzer(id string, description string) *analysis.Analyzer {
+        return &analysis.Analyzer{
+                Name:     id,
+                Doc:      description,
+                Run:      runMyAnalyzer,
+                Requires: []*analysis.Analyzer{buildssa.Analyzer},
+        }
+}
+
+func runMyAnalyzer(pass *analysis.Pass) (interface{}, error) {
+        ssaResult, err := getSSAResult(pass)
+        if err != nil {
+                return nil, fmt.Errorf("building ssa representation: %w", err)
+        }
+        var issues []*issue.Issue
+        fmt.Printf("My Analyzer ran! %+v\n", ssaResult)
+
+        return issues, nil
+}
+```
+
+2. Add the analyzer to `./analyzers/analyzerslist.go` in the `defaultAnalyzers` variable under an entry like `{"G999", "My test analyzer", newMyAnalyzer}`
+3. Add a RuleID to CWE ID mapping for your rule to the `ruleToCWE` map in `./issue/issue.go`. If you need a CWE that isn't already defined in `./cwe/data.go`, add it to the `idWeaknessess` map in that file.
+4. `make`; then run the `gosec` binary produced. You should see the output from our print statement.
+5. You now have a working example analyzer to play with-- look at the other implemented analyzers for ideas on how to make useful rules.
+
+## Developing your rule
+
+There are some utility tools which are useful for analyzing the SSA and AST representation `gosec` works with before writing rules or analyzers.
+
+For instance to dump the SSA, the [ssadump](https://pkg.go.dev/golang.org/x/tools/cmd/ssadump) tool can be used as following:
+
+```bash
+ssadump -build F main.go
+```
+
+Consult the documentation for ssadump for an overview of available output flags and options.
+
+For outputting the AST and supporting information, there is a utility tool in <https://github.com/securego/gosec/blob/master/cmd/gosecutil/tools.go> which can be compiled and used as standalone.
+
+```bash
+gosecutil -tool ast main.go
+```
+
+Valid tool arguments for this command are `ast`, `callobj`, `uses`, `types`, `defs`, `comments`, and `imports`.
diff --git a/Dockerfile b/Dockerfile
index c937d52554..1bf94da7d7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,14 +1,14 @@
 ARG GO_VERSION
 FROM golang:${GO_VERSION}-alpine AS builder
-RUN apk add --update --no-cache ca-certificates make git curl gcc libc-dev
-RUN mkdir -p /build
+RUN apk add --no-cache ca-certificates make git curl gcc libc-dev \
+    && mkdir -p /build
 WORKDIR /build
 COPY . /build/
-RUN go mod download
-RUN make build-linux
+RUN go mod download \
+    && make build-linux
 
 FROM golang:${GO_VERSION}-alpine 
-RUN apk add --update --no-cache ca-certificates bash git gcc libc-dev
+RUN apk add --no-cache ca-certificates bash git gcc libc-dev openssh
 ENV GO111MODULE on
 COPY --from=builder /build/gosec /bin/gosec
 COPY entrypoint.sh /bin/entrypoint.sh
diff --git a/Makefile b/Makefile
index b434b60ad7..53054e1326 100644
--- a/Makefile
+++ b/Makefile
@@ -2,52 +2,77 @@ GIT_TAG?= $(shell git describe --always --tags)
 BIN = gosec
 FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
 IMAGE_REPO = securego
-BUILDFLAGS := '-w -s'
+DATE_FMT=+%Y-%m-%d
+ifdef SOURCE_DATE_EPOCH
+    BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)")
+else
+    BUILD_DATE ?= $(shell date "$(DATE_FMT)")
+endif
+BUILDFLAGS := "-w -s -X 'main.Version=$(GIT_TAG)' -X 'main.GitTag=$(GIT_TAG)' -X 'main.BuildDate=$(BUILD_DATE)'"
 CGO_ENABLED = 0
 GO := GO111MODULE=on go
-GO_NOMOD :=GO111MODULE=off go
 GOPATH ?= $(shell $(GO) env GOPATH)
 GOBIN ?= $(GOPATH)/bin
-GOLINT ?= $(GOBIN)/golint
 GOSEC ?= $(GOBIN)/gosec
 GINKGO ?= $(GOBIN)/ginkgo
-GO_VERSION = 1.15
+GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
+GOVULN_MIN_VERSION = 17
+GO_VERSION = 1.24
+LDFLAGS = -ldflags "\
+	-X 'main.Version=$(shell git describe --tags --always)' \
+	-X 'main.GitTag=$(shell git describe --tags --abbrev=0)' \
+	-X 'main.BuildDate=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)'"
 
 default:
 	$(MAKE) build
 
 install-test-deps:
-	$(GO_NOMOD) get -u github.com/onsi/ginkgo/ginkgo
-	$(GO_NOMOD) get -u golang.org/x/crypto/ssh
-	$(GO_NOMOD) get -u github.com/lib/pq
+	go install github.com/onsi/ginkgo/v2/ginkgo@latest
+	go install golang.org/x/crypto/...@latest
+	go install github.com/lib/pq/...@latest
 
-test: install-test-deps build fmt lint sec
-	$(GINKGO) -r -v
+install-govulncheck:
+	@if [ $(GO_MINOR_VERSION) -gt $(GOVULN_MIN_VERSION) ]; then \
+		go install golang.org/x/vuln/cmd/govulncheck@latest; \
+	fi
+
+test: install-test-deps build-race fmt vet sec govulncheck
+	$(GINKGO) -v --fail-fast
 
 fmt:
 	@echo "FORMATTING"
 	@FORMATTED=`$(GO) fmt ./...`
-	@([[ ! -z "$(FORMATTED)" ]] && printf "Fixed unformatted files:\n$(FORMATTED)") || true
+	@([ ! -z "$(FORMATTED)" ] && printf "Fixed unformatted files:\n$(FORMATTED)") || true
 
-lint:
-	@echo "LINTING"
-	$(GO_NOMOD) get -u golang.org/x/lint/golint
-	$(GOLINT) -set_exit_status ./...
+vet:
 	@echo "VETTING"
 	$(GO) vet ./...
 
+golangci:
+	@echo "LINTING: golangci-lint"
+	golangci-lint run
+
 sec:
 	@echo "SECURITY SCANNING"
 	./$(BIN) ./...
 
+govulncheck: install-govulncheck
+	@echo "CHECKING VULNERABILITIES"
+	@if [ $(GO_MINOR_VERSION) -gt $(GOVULN_MIN_VERSION) ]; then \
+		govulncheck ./...; \
+	fi
+
 test-coverage: install-test-deps
-	go test -race -coverprofile=coverage.txt -covermode=atomic
+	go test -race -v -count=1 -coverprofile=coverage.out ./...
 
 build:
-	go build -o $(BIN) ./cmd/gosec/
+	go build $(LDFLAGS) -o $(BIN) ./cmd/gosec/
+
+build-race:
+	go build -race $(LDFLAGS) -o $(BIN) ./cmd/gosec/
 
 clean:
-	rm -rf build vendor dist coverage.txt
+	rm -rf build vendor dist coverage.out
 	rm -f release image $(BIN)
 
 release:
@@ -55,7 +80,7 @@ release:
 	goreleaser release
 
 build-linux:
-	CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build -ldflags $(BUILDFLAGS) -o $(BIN) ./cmd/gosec/
+	CGO_ENABLED=$(CGO_ENABLED) GOOS=linux go build -ldflags=$(BUILDFLAGS) -o $(BIN) ./cmd/gosec/
 
 image:
 	@echo "Building the Docker image..."
@@ -68,4 +93,10 @@ image-push: image
 	docker push $(IMAGE_REPO)/$(BIN):$(GIT_TAG)
 	docker push $(IMAGE_REPO)/$(BIN):latest
 
-.PHONY: test build clean release image image-push
+tlsconfig:
+	go generate ./...
+
+perf-diff:
+	./perf-diff.sh
+
+.PHONY: test build clean release image image-push tlsconfig perf-diff
diff --git a/README.md b/README.md
index d04deee5b7..cbd614b2eb 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 
-# gosec - Golang Security Checker
+# gosec - Go Security Checker
 
-Inspects source code for security problems by scanning the Go AST.
+Inspects source code for security problems by scanning the Go AST and SSA code representation.
 
 <img src="https://securego.io/img/gosec.png" width="320">
 
@@ -16,12 +16,13 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3218/badge)](https://bestpractices.coreinfrastructure.org/projects/3218)
 [![Build Status](https://github.com/securego/gosec/workflows/CI/badge.svg)](https://github.com/securego/gosec/actions?query=workflows%3ACI)
 [![Coverage Status](https://codecov.io/gh/securego/gosec/branch/master/graph/badge.svg)](https://codecov.io/gh/securego/gosec)
-[![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/badge/github.com/securego/gosec)
-[![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec)
+[![GoReport](https://goreportcard.com/badge/github.com/securego/gosec)](https://goreportcard.com/report/github.com/securego/gosec)
+[![GoDoc](https://pkg.go.dev/badge/github.com/securego/gosec/v2)](https://pkg.go.dev/github.com/securego/gosec/v2)
 [![Docs](https://readthedocs.org/projects/docs/badge/?version=latest)](https://securego.io/)
 [![Downloads](https://img.shields.io/github/downloads/securego/gosec/total.svg)](https://github.com/securego/gosec/releases)
 [![Docker Pulls](https://img.shields.io/docker/pulls/securego/gosec.svg)](https://hub.docker.com/r/securego/gosec/tags)
-[![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com)
+[![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](http://securego.slack.com)
+[![go-recipes](https://raw.githubusercontent.com/nikolaydubina/go-recipes/main/badge.svg?raw=true)](https://github.com/nikolaydubina/go-recipes)
 
 ## Install
 
@@ -47,6 +48,7 @@ echo "<check sum from the check sum file>  gosec_vX.Y.Z_OS.tar.gz" | sha256sum -
 
 gosec --help
 ```
+
 ### GitHub Action
 
 You can run `gosec` as a GitHub action as follows:
@@ -67,7 +69,7 @@ jobs:
       GO111MODULE: on
     steps:
       - name: Checkout Source
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Run Gosec Security Scanner
         uses: securego/gosec@master
         with:
@@ -97,14 +99,14 @@ jobs:
       GO111MODULE: on
     steps:
       - name: Checkout Source
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
       - name: Run Gosec Security Scanner
         uses: securego/gosec@master
         with:
           # we let the report trigger content trigger a failure using the GitHub Security features.
           args: '-no-fail -fmt sarif -out results.sarif ./...'
       - name: Upload SARIF file
-        uses: github/codeql-action/upload-sarif@v1
+        uses: github/codeql-action/upload-sarif@v2
         with:
           # Path to SARIF file relative to the root of the repository
           sarif_file: results.sarif
@@ -113,7 +115,7 @@ jobs:
 ### Local Installation
 
 ```bash
-go get -u github.com/securego/gosec/v2/cmd/gosec
+go install github.com/securego/gosec/v2/cmd/gosec@latest
 ```
 
 ## Usage
@@ -123,7 +125,6 @@ paths, and produce reports in different formats. By default all rules will be
 run against the supplied input files. To recursively scan from the current
 directory you can supply `./...` as the input argument.
 
-
 ### Available rules
 
 - G101: Look for hard coded credentials
@@ -135,6 +136,10 @@ directory you can supply `./...` as the input argument.
 - G108: Profiling endpoint automatically exposed on /debug/pprof
 - G109: Potential Integer overflow made by strconv.Atoi result conversion to int16/32
 - G110: Potential DoS vulnerability via decompression bomb
+- G111: Potential directory traversal
+- G112: Potential slowloris attack
+- G114: Use of net/http serve function that has no support for setting timeouts
+- G115: Potential integer overflow when converting between integer types
 - G201: SQL query construction using format string
 - G202: SQL query construction using string concatenation
 - G203: Use of unescaped data in HTML templates
@@ -145,21 +150,29 @@ directory you can supply `./...` as the input argument.
 - G304: File path provided as taint input
 - G305: File traversal when extracting zip/tar archive
 - G306: Poor file permissions used when writing to a new file
-- G307: Deferring a method which returns an error
-- G401: Detect the usage of DES, RC4, MD5 or SHA1
+- G307: Poor file permissions used when creating a file with os.Create
+- G401: Detect the usage of MD5 or SHA1
 - G402: Look for bad TLS connection settings
 - G403: Ensure minimum RSA key length of 2048 bits
 - G404: Insecure random number source (rand)
+- G405: Detect the usage of DES or RC4
+- G406: Detect the usage of MD4 or RIPEMD160
+- G407: Detect the usage of hardcoded Initialization Vector(IV)/Nonce
 - G501: Import blocklist: crypto/md5
 - G502: Import blocklist: crypto/des
 - G503: Import blocklist: crypto/rc4
 - G504: Import blocklist: net/http/cgi
 - G505: Import blocklist: crypto/sha1
-- G601: Implicit memory aliasing of items from a range statement
+- G506: Import blocklist: golang.org/x/crypto/md4
+- G507: Import blocklist: golang.org/x/crypto/ripemd160
+- G601: Implicit memory aliasing of items from a range statement (only for Go 1.21 or lower)
+- G602: Slice access out of bounds
 
 ### Retired rules
 
 - G105: Audit the use of math/big.Int.Exp - [CVE is fixed](https://github.com/golang/go/issues/15184)
+- G113: Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772). This affected Go <1.16.14 and Go <1.17.7, which are no longer supported by gosec. 
+- G307: Deferring a method which returns an error - causing more inconvenience than fixing a security issue, despite the details from this [blog post](https://www.joeshaw.org/dont-defer-close-on-writable-files/)
 
 ### Selecting rules
 
@@ -173,9 +186,10 @@ $ gosec -include=G101,G203,G401 ./...
 # Run everything except for rule G303
 $ gosec -exclude=G303 ./...
 ```
+
 ### CWE Mapping
 
-Every issue detected by `gosec` is mapped to a [CWE (Common Weakness Enumeration)](http://cwe.mitre.org/data/index.html) which describes in more generic terms the vulnerability. The exact mapping can be found  [here](https://github.com/securego/gosec/blob/master/issue.go#L49).
+Every issue detected by `gosec` is mapped to a [CWE (Common Weakness Enumeration)](http://cwe.mitre.org/data/index.html) which describes in more generic terms the vulnerability. The exact mapping can be found  [here](https://github.com/securego/gosec/blob/master/issue/issue.go#L50).
 
 ### Configuration
 
@@ -197,34 +211,20 @@ A number of global settings can be provided in a configuration file as follows:
 # Run with a global configuration file
 $ gosec -conf config.json .
 ```
-Also some rules accept configuration. For instance on rule `G104`, it is possible to define packages along with a list
-of functions which will be skipped when auditing the not checked errors:
 
-```JSON
-{
-    "G104": {
-        "io/ioutil": ["WriteFile"]
-    }
-}
-```
+#### Rule Configuration
 
-You can also configure the hard-coded credentials rule `G101` with additional patters, or adjust the entropy threshold:
+Some rules accept configuration flags as well; these flags are documented in [RULES.md](https://github.com/securego/gosec/blob/master/RULES.md).
 
-```JSON
-{
-    "G101": {
-        "pattern": "(?i)passwd|pass|password|pwd|secret|private_key|token",
-         "ignore_entropy": false,
-         "entropy_threshold": "80.0",
-         "per_char_threshold": "3.0",
-         "truncate": "32"
-    }
-}
-```
+#### Go version
+
+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 provided by setting the environment variable `GOSECGOVERSION=go1.21.1`.
 
 ### Dependencies
 
-gosec will fetch automatically the dependencies of the code which is being analyzed when go module is turned on (e.g.` GO111MODULE=on`). If this is not the case,
+gosec will fetch automatically the dependencies of the code which is being analyzed when go module is turned on (e.g.`GO111MODULE=on`). If this is not the case,
 the dependencies need to be explicitly downloaded by running the `go get -d` command before the scan.
 
 ### Excluding test files and folders
@@ -234,7 +234,6 @@ gosec will ignore test files across all packages and any dependencies in your ve
 The scanning of test files can be enabled with the following flag:
 
 ```bash
-
 gosec -tests ./...
 ```
 
@@ -244,35 +243,74 @@ Also additional folders can be excluded as follows:
  gosec -exclude-dir=rules -exclude-dir=cmd ./...
 ```
 
-### Annotating code
+### Excluding generated files
 
-As with all automated detection tools, there will be cases of false positives. In cases where gosec reports a failure that has been manually verified as being safe,
-it is possible to annotate the code with a `#nosec` comment.
+gosec can ignore generated go files with default generated code comment.
 
-The annotation causes gosec to stop processing any further nodes within the
-AST so can apply to a whole block or more granularly to a single expression.
+```
+// Code generated by some generator DO NOT EDIT.
+```
 
-```go
+```bash
+gosec -exclude-generated ./...
+```
 
-import "md5" // #nosec
+### Auto fixing vulnerabilities
+gosec can suggest fixes based on AI recommendation. It will call an AI API to receive a suggestion for a security finding.
 
+You can enable this feature by providing the following command line arguments:
+- `ai-api-provider`: the name of the AI API provider, currently only `gemini`is supported.
+- `ai-api-key` or set the environment variable `GOSEC_AI_API_KEY`: the key to access the AI API,
+For gemini, you can create an API key following [these instructions](https://ai.google.dev/gemini-api/docs/api-key).
+- `ai-endpoint`: the endpoint of the AI provider, this is optional argument.
 
-func main(){
 
-    /* #nosec */
-    if x > y {
-        h := md5.New() // this will also be ignored
-    }
+```bash
+gosec -ai-api-provider="gemini" -ai-api-key="your_key" ./...
+```
 
-}
+### Annotating code
+
+As with all automated detection tools, there will be cases of false positives.
+In cases where gosec reports a failure that has been manually verified as being safe,
+it is possible to annotate the code with a comment that starts with `#nosec`.
 
+The `#nosec` comment should have the format `#nosec [RuleList] [-- Justification]`.
+
+The `#nosec` comment needs to be placed on the line where the warning is reported.
+
+```go
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: true, // #nosec G402
+		},
+	}
+
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
 ```
 
-When a specific false positive has been identified and verified as safe, you may wish to suppress only that single rule (or a specific set of rules)
-within a section of code, while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within
-the `#nosec` annotation, e.g: `/* #nosec G401 */` or `// #nosec G201 G202 G203`
+When a specific false positive has been identified and verified as safe, you may
+wish to suppress only that single rule (or a specific set of rules) within a section of code,
+while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within
+the `#nosec` annotation, e.g: `/* #nosec G401 */` or `//#nosec G201 G202 G203`
+
+You could put the description or justification text for the annotation. The
+justification should be after the rule(s) to suppress and start with two or
+more dashes, e.g: `//#nosec G101 G102 -- This is a false positive`
+
+Alternatively, gosec also supports the `//gosec:disable` directive, which functions similar to `#nosec`:
+
+```go
+//gosec:disable G101 -- This is a false positive
+```
 
-In some cases you may also want to revisit places where `#nosec` annotations
+In some cases you may also want to revisit places where `#nosec` or `//gosec:disable` annotations
 have been used. To run the scanner and ignore any `#nosec` annotations you
 can do the following:
 
@@ -280,13 +318,34 @@ can do the following:
 gosec -nosec=true ./...
 ```
 
+### Tracking suppressions
+
+As described above, we could suppress violations externally (using `-include`/
+`-exclude`) or inline (using `#nosec` annotations) in gosec. This suppression
+inflammation can be used to generate corresponding signals for auditing
+purposes.
+
+We could track suppressions by the `-track-suppressions` flag as follows:
+
+```bash
+gosec -track-suppressions -exclude=G101 -fmt=sarif -out=results.sarif ./...
+```
+
+- For external suppressions, gosec records suppression info where `kind` is
+`external` and `justification` is a certain sentence "Globally suppressed".
+- For inline suppressions, gosec records suppression info where `kind` is
+`inSource` and `justification` is the text after two or more dashes in the
+comment.
+
+**Note:** Only SARIF and JSON formats support tracking suppressions.
+
 ### Build tags
 
-gosec is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer.
+gosec is able to pass your [Go build tags](https://pkg.go.dev/go/build/) to the analyzer.
 They can be provided as a comma separated list as follows:
 
 ```bash
-gosec -tag debug,ignore ./...
+gosec -tags debug,ignore ./...
 ```
 
 ### Output formats
@@ -300,20 +359,51 @@ file. The output format is controlled by the `-fmt` flag, and the output file is
 $ gosec -fmt=json -out=results.json *.go
 ```
 
+Results will be reported to stdout as well as to the provided output file by `-stdout` flag. The `-verbose` flag overrides the
+output format when stdout the results while saving them in the output file
+```bash
+# Write output in json format to results.json as well as stdout
+$ gosec -fmt=json -out=results.json -stdout *.go
+
+# Overrides the output format to 'text' when stdout the results, while writing it to results.json
+$ gosec -fmt=json -out=results.json -stdout -verbose=text *.go
+```
+
 **Note:** gosec generates the [generic issue import format](https://docs.sonarqube.org/latest/analysis/generic-issue/) for SonarQube, and a report has to be imported into SonarQube using `sonar.externalIssuesReportPaths=path/to/gosec-report.json`.
 
 ## Development
 
+[CONTRIBUTING.md](https://github.com/securego/gosec/blob/master/CONTRIBUTING.md) contains detailed information about adding new rules to gosec.
+
 ### Build
 
 You can build the binary with:
+
 ```bash
 make
 ```
 
+### Note on Sarif Types Generation
+
+Install the tool with :
+
+```bash
+go get -u github.com/a-h/generate/cmd/schema-generate
+```
+
+Then generate the types with :
+
+```bash
+schema-generate -i sarif-schema-2.1.0.json -o mypath/types.go
+```
+
+Most of the MarshallJSON/UnmarshalJSON are removed except the one for PropertyBag which is handy to inline the additional properties. The rest can be removed.
+The URI,ID, UUID, GUID were renamed so it fits the Go convention defined [here](https://github.com/golang/lint/blob/master/lint.go#L700)
+
 ### Tests
 
 You can run all unit tests using:
+
 ```bash
 make test
 ```
@@ -330,6 +420,19 @@ git push origin v1.0.0
 The GitHub [release workflow](.github/workflows/release.yml) triggers immediately after the tag is pushed upstream. This flow will
 release the binaries using the [goreleaser](https://goreleaser.com/actions/) action and then it will build and publish the docker image into Docker Hub.
 
+The released artifacts are signed using [cosign](https://docs.sigstore.dev/). You can use the public key from [cosign.pub](cosign.pub)
+file to verify the signature of docker image and binaries files.
+
+The docker image signature can be verified with the following command:
+```
+cosign verify --key cosign.pub securego/gosec:<TAG>
+```
+
+The binary files signature can be verified with the following command:
+```
+cosign verify-blob --key cosign.pub --signature gosec_<VERSION>_darwin_amd64.tar.gz.sig  gosec_<VERSION>_darwin_amd64.tar.gz
+```
+
 ### Docker image
 
 You can also build locally the docker image by using the command:
@@ -344,7 +447,8 @@ into a volume as follows:
 ```bash
 docker run --rm -it -w /<PROJECT>/ -v <YOUR PROJECT PATH>/<PROJECT>:/<PROJECT> securego/gosec /<PROJECT>/...
 ```
-**Note:** the current working directory needs to be set with `-w` option in order to get successfully resolved the dependencies from go module file 
+
+**Note:** the current working directory needs to be set with `-w` option in order to get successfully resolved the dependencies from go module file
 
 ### Generate TLS rule
 
@@ -367,3 +471,9 @@ This will generate the `rules/tls_config.go` file which will contain the current
 ## Who is using gosec?
 
 This is a [list](USERS.md) with some of the gosec's users.
+
+## Sponsors
+
+Support this project by becoming a sponsor. Your logo will show up here with a link to your website
+
+<a href="https://github.com/mercedes-benz" target="_blank"><img src="https://avatars.githubusercontent.com/u/34240465?s=80&v=4"></a>
diff --git a/RULES.md b/RULES.md
new file mode 100644
index 0000000000..94cfd76a84
--- /dev/null
+++ b/RULES.md
@@ -0,0 +1,61 @@
+# Rule Documentation
+
+## Rules accepting parameters
+
+As [README.md](https://github.com/securego/gosec/blob/master/README.md) mentions, some rules can be configured by adding parameters to the gosec JSON config. Per rule configs are encoded as top level objects in the gosec config, with the rule ID (`Gxxx`) as the key.
+
+Currently, the following rules accept parameters. This list is manually maintained; if you notice an omission please add it!
+
+### G101
+
+The hard-coded credentials rule `G101` can be configured with additional patterns, and the entropy threshold can be adjusted:
+
+```JSON
+{
+    "G101": {
+        "pattern": "(?i)passwd|pass|password|pwd|secret|private_key|token",
+         "ignore_entropy": false,
+         "entropy_threshold": "80.0",
+         "per_char_threshold": "3.0",
+         "truncate": "32"
+    }
+}
+```
+
+### G104
+
+The unchecked error value rule `G104` can be configured with additional functions that should be permitted to be called without checking errors.
+
+```JSON
+{
+    "G104": {
+        "ioutil": ["WriteFile"]
+    }
+}
+```
+
+### G111
+
+The HTTP Directory serving rule `G111` can be configured with a different regex for detecting potentially overly permissive servers. Note that this *replaces* the default pattern of `http\.Dir\("\/"\)|http\.Dir\('\/'\)`.
+
+```JSON
+{
+    "G111": {
+        "pattern": "http\\.Dir\\(\"\\\/\"\\)|http\\.Dir\\('\\\/'\\)"
+    }
+}
+
+```
+
+### G301, G302, G306, G307
+
+The various file and directory permission checking rules can be configured with a different maximum allowable file permission.
+
+```JSON
+{
+    "G301":"0o600",
+    "G302":"0o600",
+    "G306":"0o750",
+    "G307":"0o750"
+}
+```
diff --git a/USERS.md b/USERS.md
index 73369ceec4..7fc9c3e35f 100644
--- a/USERS.md
+++ b/USERS.md
@@ -14,6 +14,9 @@ This is a list of gosec's users. Please send a pull request with your organisati
 8. [1Password](https://github.com/1Password/srp)
 9. [PingCAP/tidb](https://github.com/pingcap/tidb)
 10. [Checkmarx](https://www.checkmarx.com/)
+11. [SeatGeek](https://www.seatgeek.com/)
+12. [reMarkable](https://remarkable.com)
+13. [SSOJet](https://ssojet.com)
 
 ## Projects
 
diff --git a/action.yml b/action.yml
index aab6c8039d..85e6cda238 100644
--- a/action.yml
+++ b/action.yml
@@ -10,7 +10,7 @@ inputs:
 
 runs:
     using: 'docker'
-    image: 'docker://securego/gosec'
+    image: 'docker://securego/gosec:2.22.7'
     args:
       - ${{ inputs.args }}
 
diff --git a/analyzer.go b/analyzer.go
index d4aae3ad31..34ac82b5a4 100644
--- a/analyzer.go
+++ b/analyzer.go
@@ -16,6 +16,7 @@
 package gosec
 
 import (
+	"errors"
 	"fmt"
 	"go/ast"
 	"go/build"
@@ -28,10 +29,15 @@ import (
 	"reflect"
 	"regexp"
 	"strconv"
-
 	"strings"
+	"sync"
 
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
 	"golang.org/x/tools/go/packages"
+
+	"github.com/securego/gosec/v2/analyzers"
+	"github.com/securego/gosec/v2/issue"
 )
 
 // LoadMode controls the amount of details to return when loading the packages
@@ -42,11 +48,94 @@ const LoadMode = packages.NeedName |
 	packages.NeedTypes |
 	packages.NeedTypesSizes |
 	packages.NeedTypesInfo |
-	packages.NeedSyntax
+	packages.NeedSyntax |
+	packages.NeedModule |
+	packages.NeedEmbedFiles |
+	packages.NeedEmbedPatterns
+
+const externalSuppressionJustification = "Globally suppressed."
+
+const aliasOfAllRules = "*"
+
+var directiveRegexp = regexp.MustCompile("^//gosec:disable(?: (.+))?$")
+
+type ignore struct {
+	start        int
+	end          int
+	suppressions map[string][]issue.SuppressionInfo
+}
+
+type ignores map[string][]ignore
+
+func newIgnores() ignores {
+	return make(map[string][]ignore)
+}
+
+func (i ignores) parseLine(line string) (int, int) {
+	parts := strings.Split(line, "-")
+	start, err := strconv.Atoi(parts[0])
+	if err != nil {
+		start = 0
+	}
+	end := start
+	if len(parts) > 1 {
+		if e, err := strconv.Atoi(parts[1]); err == nil {
+			end = e
+		}
+	}
+	return start, end
+}
+
+func (i ignores) add(file string, line string, suppressions map[string]issue.SuppressionInfo) {
+	is := []ignore{}
+	if _, ok := i[file]; ok {
+		is = i[file]
+	}
+	found := false
+	start, end := i.parseLine(line)
+	for _, ig := range is {
+		if ig.start <= start && ig.end >= end {
+			found = true
+			for r, s := range suppressions {
+				ss, ok := ig.suppressions[r]
+				if !ok {
+					ss = []issue.SuppressionInfo{}
+				}
+				ss = append(ss, s)
+				ig.suppressions[r] = ss
+			}
+			break
+		}
+	}
+	if !found {
+		ig := ignore{
+			start:        start,
+			end:          end,
+			suppressions: map[string][]issue.SuppressionInfo{},
+		}
+		for r, s := range suppressions {
+			ig.suppressions[r] = []issue.SuppressionInfo{s}
+		}
+		is = append(is, ig)
+	}
+	i[file] = is
+}
+
+func (i ignores) get(file string, line string) map[string][]issue.SuppressionInfo {
+	start, end := i.parseLine(line)
+	if is, ok := i[file]; ok {
+		for _, i := range is {
+			if i.start <= start && i.end >= end || start <= i.start && end >= i.end {
+				return i.suppressions
+			}
+		}
+	}
+	return map[string][]issue.SuppressionInfo{}
+}
 
 // The Context is populated with data parsed from the source code as it is scanned.
 // It is passed through to all rule functions as they are called. Rules may use
-// this data in conjunction withe the encountered AST node.
+// this data in conjunction with the encountered AST node.
 type Context struct {
 	FileSet      *token.FileSet
 	Comments     ast.CommentMap
@@ -54,12 +143,24 @@ type Context struct {
 	Pkg          *types.Package
 	PkgFiles     []*ast.File
 	Root         *ast.File
-	Config       Config
 	Imports      *ImportTracker
-	Ignores      []map[string]bool
+	Config       Config
+	Ignores      ignores
 	PassedValues map[string]interface{}
 }
 
+// GetFileAtNodePos returns the file at the node position in the file set available in the context.
+func (ctx *Context) GetFileAtNodePos(node ast.Node) *token.File {
+	return ctx.FileSet.File(node.Pos())
+}
+
+// NewIssue creates a new issue
+func (ctx *Context) NewIssue(node ast.Node, ruleID, desc string,
+	severity, confidence issue.Score,
+) *issue.Issue {
+	return issue.New(ctx.GetFileAtNodePos(node), node, ruleID, desc, severity, confidence)
+}
+
 // Metrics used when reporting information about a scanning run.
 type Metrics struct {
 	NumFiles int `json:"files"`
@@ -71,40 +172,55 @@ type Metrics struct {
 // Analyzer object is the main object of gosec. It has methods traverse an AST
 // and invoke the correct checking rules as on each node as required.
 type Analyzer struct {
-	ignoreNosec bool
-	ruleset     RuleSet
-	context     *Context
-	config      Config
-	logger      *log.Logger
-	issues      []*Issue
-	stats       *Metrics
-	errors      map[string][]Error // keys are file paths; values are the golang errors in those files
-	tests       bool
+	ignoreNosec       bool
+	ruleset           RuleSet
+	context           *Context
+	config            Config
+	logger            *log.Logger
+	issues            []*issue.Issue
+	stats             *Metrics
+	errors            map[string][]Error // keys are file paths; values are the golang errors in those files
+	tests             bool
+	excludeGenerated  bool
+	showIgnored       bool
+	trackSuppressions bool
+	concurrency       int
+	analyzerSet       *analyzers.AnalyzerSet
+	mu                sync.Mutex
 }
 
 // NewAnalyzer builds a new analyzer.
-func NewAnalyzer(conf Config, tests bool, logger *log.Logger) *Analyzer {
+func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, trackSuppressions bool, concurrency int, logger *log.Logger) *Analyzer {
 	ignoreNoSec := false
 	if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
 		ignoreNoSec = enabled
 	}
+	showIgnored := false
+	if enabled, err := conf.IsGlobalEnabled(ShowIgnored); err == nil {
+		showIgnored = enabled
+	}
 	if logger == nil {
 		logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
 	}
 	return &Analyzer{
-		ignoreNosec: ignoreNoSec,
-		ruleset:     make(RuleSet),
-		context:     &Context{},
-		config:      conf,
-		logger:      logger,
-		issues:      make([]*Issue, 0, 16),
-		stats:       &Metrics{},
-		errors:      make(map[string][]Error),
-		tests:       tests,
+		ignoreNosec:       ignoreNoSec,
+		showIgnored:       showIgnored,
+		ruleset:           NewRuleSet(),
+		context:           &Context{},
+		config:            conf,
+		logger:            logger,
+		issues:            make([]*issue.Issue, 0, 16),
+		stats:             &Metrics{},
+		errors:            make(map[string][]Error),
+		tests:             tests,
+		concurrency:       concurrency,
+		excludeGenerated:  excludeGenerated,
+		trackSuppressions: trackSuppressions,
+		analyzerSet:       analyzers.NewAnalyzerSet(),
 	}
 }
 
-// SetConfig upates the analyzer configuration
+// SetConfig updates the analyzer configuration
 func (gosec *Analyzer) SetConfig(conf Config) {
 	gosec.config = conf
 }
@@ -116,10 +232,19 @@ func (gosec *Analyzer) Config() Config {
 
 // LoadRules instantiates all the rules to be used when analyzing source
 // packages
-func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
+func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder, ruleSuppressed map[string]bool) {
 	for id, def := range ruleDefinitions {
 		r, nodes := def(id, gosec.config)
-		gosec.ruleset.Register(r, nodes...)
+		gosec.ruleset.Register(r, ruleSuppressed[id], nodes...)
+	}
+}
+
+// LoadAnalyzers instantiates all the analyzers to be used when analyzing source
+// packages
+func (gosec *Analyzer) LoadAnalyzers(analyzerDefinitions map[string]analyzers.AnalyzerDefinition, analyzerSuppressed map[string]bool) {
+	for id, def := range analyzerDefinitions {
+		r := def.Create(def.ID, def.Description)
+		gosec.analyzerSet.Register(r, analyzerSuppressed[id])
 	}
 }
 
@@ -131,18 +256,68 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
 		Tests:      gosec.tests,
 	}
 
+	type result struct {
+		pkgPath string
+		pkgs    []*packages.Package
+		err     error
+	}
+
+	results := make(chan result)
+	jobs := make(chan string, len(packagePaths))
+	quit := make(chan struct{})
+
+	var wg sync.WaitGroup
+
+	worker := func(j chan string, r chan result, quit chan struct{}) {
+		for {
+			select {
+			case s := <-j:
+				pkgs, err := gosec.load(s, config)
+				select {
+				case r <- result{pkgPath: s, pkgs: pkgs, err: err}:
+				case <-quit:
+					// we've been told to stop, probably an error while
+					// processing a previous result.
+					wg.Done()
+					return
+				}
+			default:
+				// j is empty and there are no jobs left
+				wg.Done()
+				return
+			}
+		}
+	}
+
+	// fill the buffer
 	for _, pkgPath := range packagePaths {
-		pkgs, err := gosec.load(pkgPath, config)
-		if err != nil {
-			gosec.AppendError(pkgPath, err)
+		jobs <- pkgPath
+	}
+
+	for i := 0; i < gosec.concurrency; i++ {
+		wg.Add(1)
+		go worker(jobs, results, quit)
+	}
+
+	go func() {
+		wg.Wait()
+		close(results)
+	}()
+
+	for r := range results {
+		if r.err != nil {
+			gosec.AppendError(r.pkgPath, r.err)
 		}
-		for _, pkg := range pkgs {
+		for _, pkg := range r.pkgs {
 			if pkg.Name != "" {
 				err := gosec.ParseErrors(pkg)
 				if err != nil {
-					return fmt.Errorf("parsing errors in pkg %q: %v", pkg.Name, err)
+					close(quit)
+					wg.Wait() // wait for the goroutines to stop
+					return fmt.Errorf("parsing errors in pkg %q: %w", pkg.Name, err)
 				}
-				gosec.Check(pkg)
+				gosec.CheckRules(pkg)
+				gosec.CheckAnalyzers(pkg)
 			}
 		}
 	}
@@ -161,10 +336,12 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
 	// step 1/3 create build context.
 	buildD := build.Default
 	// step 2/3: add build tags to get env dependent files into basePackage.
+	gosec.mu.Lock()
 	buildD.BuildTags = conf.BuildFlags
+	gosec.mu.Unlock()
 	basePackage, err := buildD.ImportDir(pkgPath, build.ImportComment)
 	if err != nil {
-		return []*packages.Package{}, fmt.Errorf("importing dir %q: %v", pkgPath, err)
+		return []*packages.Package{}, fmt.Errorf("importing dir %q: %w", pkgPath, err)
 	}
 
 	var packageFiles []string
@@ -176,7 +353,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
 	}
 
 	if gosec.tests {
-		testsFiles := []string{}
+		testsFiles := make([]string, 0)
 		testsFiles = append(testsFiles, basePackage.TestGoFiles...)
 		testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
 		for _, filename := range testsFiles {
@@ -185,24 +362,36 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
 	}
 
 	// step 3/3 remove build tags from conf to proceed build correctly.
+	gosec.mu.Lock()
 	conf.BuildFlags = nil
+	defer gosec.mu.Unlock()
 	pkgs, err := packages.Load(conf, packageFiles...)
 	if err != nil {
-		return []*packages.Package{}, fmt.Errorf("loading files from package %q: %v", pkgPath, err)
+		return []*packages.Package{}, fmt.Errorf("loading files from package %q: %w", pkgPath, err)
 	}
 	return pkgs, nil
 }
 
-// Check runs analysis on the given package
-func (gosec *Analyzer) Check(pkg *packages.Package) {
+// CheckRules runs analysis on the given package.
+func (gosec *Analyzer) CheckRules(pkg *packages.Package) {
 	gosec.logger.Println("Checking package:", pkg.Name)
 	for _, file := range pkg.Syntax {
-		checkedFile := pkg.Fset.File(file.Pos()).Name()
+		fp := pkg.Fset.File(file.Pos())
+		if fp == nil {
+			// skip files which cannot be located
+			continue
+		}
+		checkedFile := fp.Name()
 		// Skip the no-Go file from analysis (e.g. a Cgo files is expanded in 3 different files
 		// stored in the cache which do not need to by analyzed)
 		if filepath.Ext(checkedFile) != ".go" {
 			continue
 		}
+		if gosec.excludeGenerated && ast.IsGenerated(file) {
+			gosec.logger.Println("Ignoring generated file:", checkedFile)
+			continue
+		}
+
 		gosec.logger.Println("Checking file:", checkedFile)
 		gosec.context.FileSet = pkg.Fset
 		gosec.context.Config = gosec.config
@@ -212,14 +401,115 @@ func (gosec *Analyzer) Check(pkg *packages.Package) {
 		gosec.context.Pkg = pkg.Types
 		gosec.context.PkgFiles = pkg.Syntax
 		gosec.context.Imports = NewImportTracker()
-		gosec.context.Imports.TrackFile(file)
 		gosec.context.PassedValues = make(map[string]interface{})
+		gosec.updateIgnores()
 		ast.Walk(gosec, file)
 		gosec.stats.NumFiles++
 		gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount()
 	}
 }
 
+// CheckAnalyzers runs analyzers on a given package.
+func (gosec *Analyzer) CheckAnalyzers(pkg *packages.Package) {
+	ssaResult, err := gosec.buildSSA(pkg)
+	if err != nil || ssaResult == nil {
+		gosec.logger.Printf("Error building the SSA representation of the package %q: %s", pkg.Name, err)
+		return
+	}
+
+	resultMap := map[*analysis.Analyzer]interface{}{
+		buildssa.Analyzer: &analyzers.SSAAnalyzerResult{
+			Config: gosec.Config(),
+			Logger: gosec.logger,
+			SSA:    ssaResult.(*buildssa.SSA),
+		},
+	}
+
+	generatedFiles := gosec.generatedFiles(pkg)
+
+	for _, analyzer := range gosec.analyzerSet.Analyzers {
+		pass := &analysis.Pass{
+			Analyzer:          analyzer,
+			Fset:              pkg.Fset,
+			Files:             pkg.Syntax,
+			OtherFiles:        pkg.OtherFiles,
+			IgnoredFiles:      pkg.IgnoredFiles,
+			Pkg:               pkg.Types,
+			TypesInfo:         pkg.TypesInfo,
+			TypesSizes:        pkg.TypesSizes,
+			ResultOf:          resultMap,
+			Report:            func(d analysis.Diagnostic) {},
+			ImportObjectFact:  nil,
+			ExportObjectFact:  nil,
+			ImportPackageFact: nil,
+			ExportPackageFact: nil,
+			AllObjectFacts:    nil,
+			AllPackageFacts:   nil,
+		}
+		result, err := pass.Analyzer.Run(pass)
+		if err != nil {
+			gosec.logger.Printf("Error running analyzer %s: %s\n", analyzer.Name, err)
+			continue
+		}
+		if result != nil {
+			if passIssues, ok := result.([]*issue.Issue); ok {
+				for _, iss := range passIssues {
+					if gosec.excludeGenerated {
+						if _, ok := generatedFiles[iss.File]; ok {
+							continue
+						}
+					}
+					gosec.updateIssues(iss)
+				}
+			}
+		}
+	}
+}
+
+func (gosec *Analyzer) generatedFiles(pkg *packages.Package) map[string]bool {
+	generatedFiles := map[string]bool{}
+	for _, file := range pkg.Syntax {
+		if ast.IsGenerated(file) {
+			fp := pkg.Fset.File(file.Pos())
+			if fp == nil {
+				// skip files which cannot be located
+				continue
+			}
+			generatedFiles[fp.Name()] = true
+		}
+	}
+	return generatedFiles
+}
+
+// buildSSA runs the SSA pass which builds the SSA representation of the package. It handles gracefully any panic.
+func (gosec *Analyzer) buildSSA(pkg *packages.Package) (interface{}, error) {
+	defer func() {
+		if r := recover(); r != nil {
+			gosec.logger.Printf("Panic when running SSA analyser on package: %s", pkg.Name)
+		}
+	}()
+	ssaPass := &analysis.Pass{
+		Analyzer:          buildssa.Analyzer,
+		Fset:              pkg.Fset,
+		Files:             pkg.Syntax,
+		OtherFiles:        pkg.OtherFiles,
+		IgnoredFiles:      pkg.IgnoredFiles,
+		Pkg:               pkg.Types,
+		TypesInfo:         pkg.TypesInfo,
+		TypesSizes:        pkg.TypesSizes,
+		ResultOf:          nil,
+		Report:            nil,
+		ImportObjectFact:  nil,
+		ExportObjectFact:  nil,
+		ImportPackageFact: nil,
+		ExportPackageFact: nil,
+		AllObjectFacts:    nil,
+		AllPackageFacts:   nil,
+	}
+
+	return ssaPass.Analyzer.Run(ssaPass)
+}
+
 // ParseErrors parses the errors from given package
 func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
 	if len(pkg.Errors) == 0 {
@@ -232,13 +522,13 @@ func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
 		var line int
 		if len(parts) > 1 {
 			if line, err = strconv.Atoi(parts[1]); err != nil {
-				return fmt.Errorf("parsing line: %v", err)
+				return fmt.Errorf("parsing line: %w", err)
 			}
 		}
 		var column int
 		if len(parts) > 2 {
 			if column, err = strconv.Atoi(parts[2]); err != nil {
-				return fmt.Errorf("parsing column: %v", err)
+				return fmt.Errorf("parsing column: %w", err)
 			}
 		}
 		msg := strings.TrimSpace(pkgErr.Msg)
@@ -256,11 +546,11 @@ func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
 // AppendError appends an error to the file errors
 func (gosec *Analyzer) AppendError(file string, err error) {
 	// Do not report the error for empty packages (e.g. files excluded from build with a tag)
-	r := regexp.MustCompile(`no buildable Go source files in`)
-	if r.MatchString(err.Error()) {
+	var noGoErr *build.NoGoError
+	if errors.As(err, &noGoErr) {
 		return
 	}
-	errors := []Error{}
+	errors := make([]Error, 0)
 	if ferrs, ok := gosec.errors[file]; ok {
 		errors = ferrs
 	}
@@ -270,107 +560,191 @@ func (gosec *Analyzer) AppendError(file string, err error) {
 }
 
 // ignore a node (and sub-tree) if it is tagged with a nosec tag comment
-func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) {
-	if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec {
+func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo {
+	if gosec.ignoreNosec {
+		return nil
+	}
+	groups, ok := gosec.context.Comments[n]
+	if !ok {
+		return nil
+	}
 
-		// Checks if an alternative for #nosec is set and, if not, uses the default.
-		noSecDefaultTag := "#nosec"
-		noSecAlternativeTag, err := gosec.config.GetGlobal(NoSecAlternative)
-		if err != nil {
-			noSecAlternativeTag = noSecDefaultTag
+	// Checks if an alternative for #nosec is set and, if not, uses the default.
+	noSecDefaultTag, err := gosec.config.GetGlobal(Nosec)
+	if err != nil {
+		noSecDefaultTag = NoSecTag(string(Nosec))
+	} else {
+		noSecDefaultTag = NoSecTag(noSecDefaultTag)
+	}
+	noSecAlternativeTag, err := gosec.config.GetGlobal(NoSecAlternative)
+	if err != nil {
+		noSecAlternativeTag = noSecDefaultTag
+	} else {
+		noSecAlternativeTag = NoSecTag(noSecAlternativeTag)
+	}
+
+	for _, group := range groups {
+		found, args := findNoSecDirective(group, noSecDefaultTag, noSecAlternativeTag)
+		if !found {
+			continue
 		}
 
-		for _, group := range groups {
+		gosec.stats.NumNosec++
 
-			foundDefaultTag := strings.Contains(group.Text(), noSecDefaultTag)
-			foundAlternativeTag := strings.Contains(group.Text(), noSecAlternativeTag)
+		// Extract the directive and the justification.
+		justification := ""
+		commentParts := regexp.MustCompile(`-{2,}`).Split(args, 2)
+		directive := commentParts[0]
+		if len(commentParts) > 1 {
+			justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n"))
+		}
 
-			if foundDefaultTag || foundAlternativeTag {
-				gosec.stats.NumNosec++
+		// Pull out the specific rules that are listed to be ignored.
+		re := regexp.MustCompile(`(G\d{3})`)
+		matches := re.FindAllStringSubmatch(directive, -1)
 
-				// Pull out the specific rules that are listed to be ignored.
-				re := regexp.MustCompile(`(G\d{3})`)
-				matches := re.FindAllStringSubmatch(group.Text(), -1)
+		suppression := issue.SuppressionInfo{
+			Kind:          "inSource",
+			Justification: justification,
+		}
 
-				// If no specific rules were given, ignore everything.
-				if len(matches) == 0 {
-					return nil, true
-				}
+		// Find the rule IDs to ignore.
+		ignores := make(map[string]issue.SuppressionInfo)
+		for _, v := range matches {
+			ignores[v[1]] = suppression
+		}
 
-				// Find the rule IDs to ignore.
-				var ignores []string
-				for _, v := range matches {
-					ignores = append(ignores, v[1])
-				}
-				return ignores, false
-			}
+		// If no specific rules were given, ignore everything.
+		if len(matches) == 0 {
+			ignores[aliasOfAllRules] = suppression
 		}
+		return ignores
 	}
-	return nil, false
+	return nil
 }
 
-// Visit runs the gosec visitor logic over an AST created by parsing go code.
-// Rule methods added with AddRule will be invoked as necessary.
-func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
-	// If we've reached the end of this branch, pop off the ignores stack.
-	if n == nil {
-		if len(gosec.context.Ignores) > 0 {
-			gosec.context.Ignores = gosec.context.Ignores[1:]
+// findNoSecDirective checks if the comment group contains `#nosec` or `//gosec:disable` directive.
+// If found, it returns true and the directive's arguments.
+func findNoSecDirective(group *ast.CommentGroup, noSecDefaultTag, noSecAlternativeTag string) (bool, string) {
+	// Check if the comment grounp has a nosec comment.
+	for _, tag := range []string{noSecDefaultTag, noSecAlternativeTag} {
+		if found, args := findNoSecTag(group, tag); found {
+			return true, args
 		}
-		return gosec
-	}
-
-	// Get any new rule exclusions.
-	ignoredRules, ignoreAll := gosec.ignore(n)
-	if ignoreAll {
-		return nil
 	}
 
-	// Now create the union of exclusions.
-	ignores := map[string]bool{}
-	if len(gosec.context.Ignores) > 0 {
-		for k, v := range gosec.context.Ignores[0] {
-			ignores[k] = v
+	// Check if the comment group has a directive comment.
+	for _, c := range group.List {
+		match := directiveRegexp.FindStringSubmatch(c.Text)
+		if len(match) > 0 {
+			return true, match[0]
 		}
 	}
 
-	for _, v := range ignoredRules {
-		ignores[v] = true
+	return false, ""
+}
+
+func findNoSecTag(group *ast.CommentGroup, tag string) (bool, string) {
+	comment := strings.TrimSpace(group.Text())
+
+	if strings.HasPrefix(comment, tag) || regexp.MustCompile("\n *"+tag).MatchString(comment) {
+		// Discard what's in front of the nosec tag.
+		return true, strings.SplitN(comment, tag, 2)[1]
 	}
 
-	// Push the new set onto the stack.
-	gosec.context.Ignores = append([]map[string]bool{ignores}, gosec.context.Ignores...)
+	return false, ""
+}
 
-	// Track aliased and initialization imports
-	gosec.context.Imports.TrackImport(n)
+// Visit runs the gosec visitor logic over an AST created by parsing go code.
+// Rule methods added with AddRule will be invoked as necessary.
+func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
+	// Using ast.File instead of ast.ImportSpec, so that we can track all imports at once.
+	switch i := n.(type) {
+	case *ast.File:
+		gosec.context.Imports.TrackFile(i)
+	}
 
 	for _, rule := range gosec.ruleset.RegisteredFor(n) {
-		if _, ok := ignores[rule.ID()]; ok {
-			continue
-		}
 		issue, err := rule.Match(n, gosec.context)
 		if err != nil {
 			file, line := GetLocation(n, gosec.context)
 			file = path.Base(file)
 			gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
 		}
-		if issue != nil {
-			gosec.issues = append(gosec.issues, issue)
+		gosec.updateIssues(issue)
+	}
+	return gosec
+}
+
+func (gosec *Analyzer) updateIgnores() {
+	for n := range gosec.context.Comments {
+		gosec.updateIgnoredRulesForNode(n)
+	}
+}
+
+func (gosec *Analyzer) updateIgnoredRulesForNode(n ast.Node) {
+	ignoredRules := gosec.ignore(n)
+	if len(ignoredRules) > 0 {
+		if gosec.context.Ignores == nil {
+			gosec.context.Ignores = newIgnores()
+		}
+		line := issue.GetLine(gosec.context.FileSet.File(n.Pos()), n)
+		gosec.context.Ignores.add(
+			gosec.context.FileSet.File(n.Pos()).Name(),
+			line,
+			ignoredRules,
+		)
+	}
+}
+
+func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id string) ([]issue.SuppressionInfo, bool) {
+	ignoredRules := gosec.context.Ignores.get(file, line)
+
+	// Check if the rule was specifically suppressed at this location.
+	generalSuppressions, generalIgnored := ignoredRules[aliasOfAllRules]
+	ruleSuppressions, ruleIgnored := ignoredRules[id]
+	ignored := generalIgnored || ruleIgnored
+	suppressions := append(generalSuppressions, ruleSuppressions...)
+
+	// Track external suppressions of this rule.
+	if gosec.ruleset.IsRuleSuppressed(id) || gosec.analyzerSet.IsSuppressed(id) {
+		ignored = true
+		suppressions = append(suppressions, issue.SuppressionInfo{
+			Kind:          "external",
+			Justification: externalSuppressionJustification,
+		})
+	}
+	return suppressions, ignored
+}
+
+func (gosec *Analyzer) updateIssues(issue *issue.Issue) {
+	if issue != nil {
+		suppressions, ignored := gosec.getSuppressionsAtLineInFile(issue.File, issue.Line, issue.RuleID)
+		if gosec.showIgnored {
+			issue.NoSec = ignored
+		}
+		if !ignored || !gosec.showIgnored {
 			gosec.stats.NumFound++
 		}
+		if ignored && gosec.trackSuppressions {
+			issue.WithSuppressions(suppressions)
+			gosec.issues = append(gosec.issues, issue)
+		} else if !ignored || gosec.showIgnored || gosec.ignoreNosec {
+			gosec.issues = append(gosec.issues, issue)
+		}
 	}
-	return gosec
 }
 
 // Report returns the current issues discovered and the metrics about the scan
-func (gosec *Analyzer) Report() ([]*Issue, *Metrics, map[string][]Error) {
+func (gosec *Analyzer) Report() ([]*issue.Issue, *Metrics, map[string][]Error) {
 	return gosec.issues, gosec.stats, gosec.errors
 }
 
 // Reset clears state such as context, issues and metrics from the configured analyzer
 func (gosec *Analyzer) Reset() {
 	gosec.context = &Context{}
-	gosec.issues = make([]*Issue, 0, 16)
+	gosec.issues = make([]*issue.Issue, 0, 16)
 	gosec.stats = &Metrics{}
 	gosec.ruleset = NewRuleSet()
+	gosec.analyzerSet = analyzers.NewAnalyzerSet()
 }
diff --git a/analyzer_test.go b/analyzer_test.go
index 801473c403..62a2361ba7 100644
--- a/analyzer_test.go
+++ b/analyzer_test.go
@@ -1,23 +1,37 @@
+// (c) Copyright 2024 Mercedes-Benz Tech Innovation GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package gosec_test
 
 import (
 	"errors"
-	"io/ioutil"
+	"go/build"
 	"log"
-	"os"
+	"regexp"
 	"strings"
 
-	"github.com/securego/gosec/v2"
-	"github.com/securego/gosec/v2/rules"
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
 	"golang.org/x/tools/go/packages"
 
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/analyzers"
+	"github.com/securego/gosec/v2/rules"
 	"github.com/securego/gosec/v2/testutils"
 )
 
 var _ = Describe("Analyzer", func() {
-
 	var (
 		analyzer  *gosec.Analyzer
 		logger    *log.Logger
@@ -26,24 +40,21 @@ var _ = Describe("Analyzer", func() {
 	)
 	BeforeEach(func() {
 		logger, _ = testutils.NewLogger()
-		analyzer = gosec.NewAnalyzer(nil, tests, logger)
+		analyzer = gosec.NewAnalyzer(nil, tests, false, false, 1, logger)
 	})
 
 	Context("when processing a package", func() {
-
 		It("should not report an error if the package contains no Go files", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
-			dir, err := ioutil.TempDir("", "empty")
-			defer os.RemoveAll(dir)
-			Expect(err).ShouldNot(HaveOccurred())
-			err = analyzer.Process(buildTags, dir)
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
+			dir := GinkgoT().TempDir()
+			err := analyzer.Process(buildTags, dir)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(0))
+			Expect(errors).To(BeEmpty())
 		})
 
 		It("should report an error if the package fails to build", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
 			pkg := testutils.NewTestPackage()
 			defer pkg.Close()
 			pkg.AddFile("wonky.go", `func main(){ println("forgot the package")}`)
@@ -52,14 +63,14 @@ var _ = Describe("Analyzer", func() {
 			err = analyzer.Process(buildTags, pkg.Path)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
+			Expect(errors).To(HaveLen(1))
 			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(1))
+				Expect(ferr).To(HaveLen(1))
 			}
 		})
 
 		It("should be able to analyze multiple Go files", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
 			pkg := testutils.NewTestPackage()
 			defer pkg.Close()
 			pkg.AddFile("foo.go", `
@@ -80,8 +91,31 @@ var _ = Describe("Analyzer", func() {
 			Expect(metrics.NumFiles).To(Equal(2))
 		})
 
+		It("should be able to analyze multiple Go files concurrently", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, true, false, 32, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				package main
+				func main(){
+					bar()
+				}`)
+			pkg.AddFile("bar.go", `
+				package main
+				func bar(){
+					println("package has two files!")
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			_, metrics, _ := customAnalyzer.Report()
+			Expect(metrics.NumFiles).To(Equal(2))
+		})
+
 		It("should be able to analyze multiple Go packages", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
 			pkg1 := testutils.NewTestPackage()
 			pkg2 := testutils.NewTestPackage()
 			defer pkg1.Close()
@@ -107,7 +141,7 @@ var _ = Describe("Analyzer", func() {
 		It("should find errors when nosec is not in use", func() {
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			controlPackage := testutils.NewTestPackage()
 			defer controlPackage.Close()
@@ -118,11 +152,42 @@ var _ = Describe("Analyzer", func() {
 			Expect(err).ShouldNot(HaveOccurred())
 			controlIssues, _, _ := analyzer.Report()
 			Expect(controlIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should find errors when nosec is not in use", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("cipher.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			controlIssues, _, _ := analyzer.Report()
+			Expect(controlIssues).Should(HaveLen(sample.Errors))
+		})
 
+		It("should find errors when nosec is not in use", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("md4.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			controlIssues, _, _ := analyzer.Report()
+			Expect(controlIssues).Should(HaveLen(sample.Errors))
 		})
 
 		It("should report Go build errors and invalid files", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
 			pkg := testutils.NewTestPackage()
 			defer pkg.Close()
 			pkg.AddFile("foo.go", `
@@ -134,23 +199,29 @@ var _ = Describe("Analyzer", func() {
 			err = analyzer.Process(buildTags, pkg.Path)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
+			foundErr := false
 			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(1))
+				Expect(ferr).To(HaveLen(1))
+				match, err := regexp.MatchString(ferr[0].Err, `expected declaration, found '}'`)
+				if !match || err != nil {
+					continue
+				}
+				foundErr = true
 				Expect(ferr[0].Line).To(Equal(4))
 				Expect(ferr[0].Column).To(Equal(5))
 				Expect(ferr[0].Err).Should(MatchRegexp(`expected declaration, found '}'`))
 			}
+			Expect(foundErr).To(BeTrue())
 		})
 
-		It("should not report errors when a nosec comment is present", func() {
+		It("should not report errors when a nosec line comment is present", func() {
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
 			nosecPackage.AddFile("md5.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
@@ -160,15 +231,14 @@ var _ = Describe("Analyzer", func() {
 			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should not report errors when an exclude comment is present for the correct rule", func() {
-			// Rule for MD5 weak crypto usage
+		It("should not report errors when a disable directive is present", func() {
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G401", 1)
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1)
 			nosecPackage.AddFile("md5.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
@@ -178,237 +248,1411 @@ var _ = Describe("Analyzer", func() {
 			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should report errors when an exclude comment is present for a different rule", func() {
-			sample := testutils.SampleCodeG401[0]
+		It("should not report errors when a nosec line comment is present", func() {
+			sample := testutils.SampleCodeG405[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G301", 1)
-			nosecPackage.AddFile("md5.go", nosecSource)
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
 			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
 			nosecIssues, _, _ := analyzer.Report()
-			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
-			sample := testutils.SampleCodeG401[0]
+		It("should not report errors when a disable directive is present", func() {
+			sample := testutils.SampleCodeG405[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G301 G401", 1)
-			nosecPackage.AddFile("md5.go", nosecSource)
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
 			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			err = analyzer.Process(buildTags, nosecPackage.Path)
-			Expect(err).ShouldNot(HaveOccurred())
 			nosecIssues, _, _ := analyzer.Report()
 			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should pass the build tags", func() {
-			sample := testutils.SampleCodeBuildTag[0]
+		It("should not report errors when a nosec line comment is present", func() {
+			sample := testutils.SampleCodeG406[0]
 			source := sample.Code[0]
-			analyzer.LoadRules(rules.Generate().Builders())
-			pkg := testutils.NewTestPackage()
-			defer pkg.Close()
-			pkg.AddFile("tags.go", source)
-			tags := []string{"tag"}
-			err := analyzer.Process(tags, pkg.Path)
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should process an empty package with test file", func() {
-			analyzer.LoadRules(rules.Generate().Builders())
-			pkg := testutils.NewTestPackage()
-			defer pkg.Close()
-			pkg.AddFile("foo_test.go", `
-				package tests
-			    import "testing"
-			    func TestFoo(t *testing.T){
-			    }`)
-			err := pkg.Build()
+		It("should not report errors when a disable directive is present", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			err = analyzer.Process(buildTags, pkg.Path)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should be possible to overwrite nosec comments, and report issues", func() {
-			// Rule for MD5 weak crypto usage
+		It("should not report errors when a nosec block comment is present", func() {
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-
-			// overwrite nosec option
-			nosecIgnoreConfig := gosec.NewConfig()
-			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
-			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
-			customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() /* #nosec */", 1)
 			nosecPackage.AddFile("md5.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			nosecIssues, _, _ := customAnalyzer.Report()
-			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when a nosec block comment is present", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
 
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) /* #nosec */", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should be possible to use an alternative nosec tag", func() {
+		It("should not report errors when a nosec block comment is present", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() /* #nosec */", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
 			// Rule for MD5 weak crypto usage
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-
-			// overwrite nosec option
-			nosecIgnoreConfig := gosec.NewConfig()
-			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
-			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
-			customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #falsePositive", 1)
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G401", 1)
 			nosecPackage.AddFile("md5.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			nosecIssues, _, _ := customAnalyzer.Report()
-			Expect(nosecIssues).Should(HaveLen(0))
-
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should ignore vulnerabilities when the default tag is found", func() {
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
 			// Rule for MD5 weak crypto usage
 			sample := testutils.SampleCodeG401[0]
 			source := sample.Code[0]
-
-			// overwrite nosec option
-			nosecIgnoreConfig := gosec.NewConfig()
-			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
-			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
-			customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
 
 			nosecPackage := testutils.NewTestPackage()
 			defer nosecPackage.Close()
-			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G401", 1)
 			nosecPackage.AddFile("md5.go", nosecSource)
 			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			nosecIssues, _, _ := customAnalyzer.Report()
-			Expect(nosecIssues).Should(HaveLen(0))
-
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should be able to analyze Go test package", func() {
-			customAnalyzer := gosec.NewAnalyzer(nil, true, logger)
-			customAnalyzer.LoadRules(rules.Generate().Builders())
-			pkg := testutils.NewTestPackage()
-			defer pkg.Close()
-			pkg.AddFile("foo.go", `
-				package foo
-				func foo(){
-				}`)
-			pkg.AddFile("foo_test.go", `
-				package foo_test
-				import "testing"
-				func test() error {
-				  return nil
-				}
-				func TestFoo(t *testing.T){
-					test()
-				}`)
-			err := pkg.Build()
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec G405", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			err = customAnalyzer.Process(buildTags, pkg.Path)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			issues, _, _ := customAnalyzer.Report()
-			Expect(issues).Should(HaveLen(1))
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
-	})
-	It("should be able to analyze Cgo files", func() {
-		analyzer.LoadRules(rules.Generate().Builders())
-		sample := testutils.SampleCodeCgo[0]
-		source := sample.Code[0]
 
-		testPackage := testutils.NewTestPackage()
-		defer testPackage.Close()
-		testPackage.AddFile("main.go", source)
-		err := testPackage.Build()
-		Expect(err).ShouldNot(HaveOccurred())
-		err = analyzer.Process(buildTags, testPackage.Path)
-		Expect(err).ShouldNot(HaveOccurred())
-		issues, _, _ := analyzer.Report()
-		Expect(issues).Should(HaveLen(0))
-	})
-
-	Context("when parsing errors from a package", func() {
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
 
-		It("should return no error when the error list is empty", func() {
-			pkg := &packages.Package{}
-			err := analyzer.ParseErrors(pkg)
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G405", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-		})
-
-		It("should properly parse the errors", func() {
-			pkg := &packages.Package{
-				Errors: []packages.Error{
-					packages.Error{
-						Pos: "file:1:2",
-						Msg: "build error",
-					},
-				},
-			}
-			err := analyzer.ParseErrors(pkg)
+			err = analyzer.Process(buildTags, nosecPackage.Path)
 			Expect(err).ShouldNot(HaveOccurred())
-			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
-			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(1))
-				Expect(ferr[0].Line).To(Equal(1))
-				Expect(ferr[0].Column).To(Equal(2))
-				Expect(ferr[0].Err).Should(MatchRegexp(`build error`))
-			}
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
 		})
 
-		It("should properly parse the errors without line and column", func() {
-			pkg := &packages.Package{
-				Errors: []packages.Error{
-					packages.Error{
-						Pos: "file",
-						Msg: "build error",
-					},
-				},
-			}
-			err := analyzer.ParseErrors(pkg)
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
+			// Rule for MD4 deprecated weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec G406", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
 			Expect(err).ShouldNot(HaveOccurred())
-			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
-			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(1))
-				Expect(ferr[0].Line).To(Equal(0))
-				Expect(ferr[0].Column).To(Equal(0))
-				Expect(ferr[0].Err).Should(MatchRegexp(`build error`))
-			}
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for the correct rule", func() {
+			// Rule for MD4 deprecated weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G406", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when a nosec  block and line comment are present", func() {
+			sample := testutils.SampleCodeG101[23]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g101.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+		It("should not report errors when only a nosec  block is present", func() {
+			sample := testutils.SampleCodeG101[24]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g101.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+		It("should not report errors when a single line nosec  is present on a multi-line issue", func() {
+			sample := testutils.SampleCodeG112[3]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G112")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g112.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when a disable directive block and line comment are present", func() {
+			sample := testutils.SampleCodeG101[26]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g101.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+		It("should not report errors when only a disable directive block is present", func() {
+			sample := testutils.SampleCodeG101[27]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g101.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+		It("should not report errors when a single line nosec  is present on a multi-line issue", func() {
+			sample := testutils.SampleCodeG112[4]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G112")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("g112.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G301", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G301", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec G301", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G301", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec G301", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when an exclude comment is present for a different rule", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G301", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G301 G401", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G301 G401", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec G301 G405", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G301 G405", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec G301 G406", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G301 G406", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should pass the build tags", func() {
+			sample := testutils.SampleCodeBuildTag[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("tags.go", source)
+			tags := []string{"tag"}
+			err := analyzer.Process(tags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+		})
+
+		It("should process an empty package with test file", func() {
+			analyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo_test.go", `
+				package tests
+			    import "testing"
+			    func TestFoo(t *testing.T){
+			    }`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues", func() {
+			// Rule for MD5 weak crypto usage
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite disable directive, and report issues", func() {
+			// Rule for MD5 weak crypto usage
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite disable directive comments, and report issues", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues", func() {
+			// Rule for MD4 weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite disable directive comments, and report issues", func() {
+			// Rule for MD4 weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues but they should not be counted", func() {
+			// Rule for MD5 weak crypto usage
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "mynosec")
+			nosecIgnoreConfig.SetGlobal(gosec.ShowIgnored, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #mynosec", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, metrics, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+			Expect(metrics.NumFound).Should(Equal(0))
+			Expect(metrics.NumNosec).Should(Equal(1))
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues but they should not be counted", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "mynosec")
+			nosecIgnoreConfig.SetGlobal(gosec.ShowIgnored, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) // #mynosec", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, metrics, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+			Expect(metrics.NumFound).Should(Equal(0))
+			Expect(metrics.NumNosec).Should(Equal(1))
+		})
+
+		It("should be possible to overwrite nosec comments, and report issues but they should not be counted", func() {
+			// Rule for MD4 weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.Nosec, "mynosec")
+			nosecIgnoreConfig.SetGlobal(gosec.ShowIgnored, "true")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() // #mynosec", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, metrics, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+			Expect(metrics.NumFound).Should(Equal(0))
+			Expect(metrics.NumNosec).Should(Equal(1))
+		})
+
+		It("should not report errors when nosec tag is in front of a line", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//#nosec G401\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when disable directive is in front of a line", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//gosec:disable G401\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when nosec tag is in front of a line", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//Some description\n//#nosec G405\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when disable directive is in front of a line", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//Some description\n//gosec:disable G405\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when nosec tag is in front of a line", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//Some description\n//#nosec G406\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when disable directive is in front of a line", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//Some description\n//gosec:disable G406\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should report errors when nosec tag is not in front of a line", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//Another description #nosec G401\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when nosec tag is not in front of a line", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//Some description\n//Another description #nosec G405\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when nosec tag is not in front of a line", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//Some description\n//Another description #nosec G406\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should not report errors when rules are in front of nosec tag even rules are wrong", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//G301\n//#nosec\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when rules are in front of nosec tag even rules are wrong", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//G301\n//#nosec\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should not report errors when rules are in front of nosec tag even rules are wrong", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//G301\n//#nosec\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//#nosec\n//G301\n//#nosec\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "//gosec:disable G301\n//gosec:disable\nh := md5.New()", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//#nosec\n//G301\n//#nosec\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "//gosec:disable G301\n//gosec:disable\nc, e := des.NewCipher([]byte(\"mySecret\"))", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//#nosec\n//G301\n//#nosec\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should report errors when there are disable directives after a //gosec:disable WrongRuleList", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "//gosec:disable G301\n//gosec:disable\nh := md4.New()", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := analyzer.Report()
+			Expect(nosecIssues).Should(HaveLen(sample.Errors))
+		})
+
+		It("should be possible to use an alternative nosec tag", func() {
+			// Rule for MD5 weak crypto usage
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #falsePositive", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should be possible to use an alternative nosec tag", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) // #falsePositive", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should be possible to use an alternative nosec tag", func() {
+			// Rule for MD4 deprecated weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() // #falsePositive", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should ignore vulnerabilities when the default tag is found", func() {
+			// Rule for MD5 weak crypto usage
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should ignore vulnerabilities when the default tag is found", func() {
+			// Rule for DES weak crypto usage
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should ignore vulnerabilities when the default tag is found", func() {
+			// Rule for MD4 deprecated weak crypto usage
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+
+			// overwrite nosec option
+			nosecIgnoreConfig := gosec.NewConfig()
+			nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "falsePositive")
+			customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			nosecIssues, _, _ := customAnalyzer.Report()
+			Expect(nosecIssues).Should(BeEmpty())
+		})
+
+		It("should be able to analyze Go test package", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				package foo
+				func foo(){
+				}`)
+			pkg.AddFile("foo_test.go", `
+				package foo_test
+				import "testing"
+				func test() error {
+				  return nil
+				}
+				func TestFoo(t *testing.T){
+					test()
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := customAnalyzer.Report()
+			Expect(issues).Should(HaveLen(1))
+		})
+		It("should be able to scan generated files if NOT excluded when using the rules", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				package foo
+				// Code generated some-generator DO NOT EDIT.
+				func test() error {
+				  return nil
+				}
+				func TestFoo(t *testing.T){
+					test()
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := customAnalyzer.Report()
+			Expect(issues).Should(HaveLen(1))
+		})
+		It("should be able to skip generated files if excluded when using the rules", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, true, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				// Code generated some-generator DO NOT EDIT.
+				package foo
+				func test() error {
+				  return nil
+				}
+				func TestFoo(t *testing.T){
+					test()
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := customAnalyzer.Report()
+			Expect(issues).Should(BeEmpty())
+		})
+		It("should be able to scan generated files if NOT excluded when using the analyzes", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, false, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			customAnalyzer.LoadAnalyzers(analyzers.Generate(false).AnalyzersInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				package main
+				// Code generated some-generator DO NOT EDIT.
+        import (
+          "fmt"
+        )
+        func main() {
+          values := []string{}
+          fmt.Println(values[0])
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := customAnalyzer.Report()
+			Expect(issues).Should(HaveLen(1))
+		})
+		It("should be able to skip generated files if excluded when using the analyzes", func() {
+			customAnalyzer := gosec.NewAnalyzer(nil, true, true, false, 1, logger)
+			customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
+			customAnalyzer.LoadAnalyzers(analyzers.Generate(false).AnalyzersInfo())
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("foo.go", `
+				// Code generated some-generator DO NOT EDIT.
+				package main
+        import (
+          "fmt"
+        )
+        func main() {
+          values := []string{}
+          fmt.Println(values[0])
+				}`)
+			err := pkg.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = customAnalyzer.Process(buildTags, pkg.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := customAnalyzer.Report()
+			Expect(issues).Should(BeEmpty())
+		})
+	})
+	It("should be able to analyze Cgo files", func() {
+		analyzer.LoadRules(rules.Generate(false).RulesInfo())
+		sample := testutils.SampleCodeCgo[0]
+		source := sample.Code[0]
+
+		testPackage := testutils.NewTestPackage()
+		defer testPackage.Close()
+		testPackage.AddFile("main.go", source)
+		err := testPackage.Build()
+		Expect(err).ShouldNot(HaveOccurred())
+		err = analyzer.Process(buildTags, testPackage.Path)
+		Expect(err).ShouldNot(HaveOccurred())
+		issues, _, _ := analyzer.Report()
+		Expect(issues).Should(BeEmpty())
+	})
+
+	Context("when parsing errors from a package", func() {
+		It("should return no error when the error list is empty", func() {
+			pkg := &packages.Package{}
+			err := analyzer.ParseErrors(pkg)
+			Expect(err).ShouldNot(HaveOccurred())
+		})
+
+		It("should properly parse the errors", func() {
+			pkg := &packages.Package{
+				Errors: []packages.Error{
+					{
+						Pos: "file:1:2",
+						Msg: "build error",
+					},
+				},
+			}
+			err := analyzer.ParseErrors(pkg)
+			Expect(err).ShouldNot(HaveOccurred())
+			_, _, errors := analyzer.Report()
+			Expect(errors).To(HaveLen(1))
+			for _, ferr := range errors {
+				Expect(ferr).To(HaveLen(1))
+				Expect(ferr[0].Line).To(Equal(1))
+				Expect(ferr[0].Column).To(Equal(2))
+				Expect(ferr[0].Err).Should(MatchRegexp(`build error`))
+			}
+		})
+
+		It("should properly parse the errors without line and column", func() {
+			pkg := &packages.Package{
+				Errors: []packages.Error{
+					{
+						Pos: "file",
+						Msg: "build error",
+					},
+				},
+			}
+			err := analyzer.ParseErrors(pkg)
+			Expect(err).ShouldNot(HaveOccurred())
+			_, _, errors := analyzer.Report()
+			Expect(errors).To(HaveLen(1))
+			for _, ferr := range errors {
+				Expect(ferr).To(HaveLen(1))
+				Expect(ferr[0].Line).To(Equal(0))
+				Expect(ferr[0].Column).To(Equal(0))
+				Expect(ferr[0].Err).Should(MatchRegexp(`build error`))
+			}
 		})
 
 		It("should properly parse the errors without column", func() {
 			pkg := &packages.Package{
 				Errors: []packages.Error{
-					packages.Error{
+					{
 						Pos: "file",
 						Msg: "build error",
 					},
@@ -417,9 +1661,9 @@ var _ = Describe("Analyzer", func() {
 			err := analyzer.ParseErrors(pkg)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
+			Expect(errors).To(HaveLen(1))
 			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(1))
+				Expect(ferr).To(HaveLen(1))
 				Expect(ferr[0].Line).To(Equal(0))
 				Expect(ferr[0].Column).To(Equal(0))
 				Expect(ferr[0].Err).Should(MatchRegexp(`build error`))
@@ -429,7 +1673,7 @@ var _ = Describe("Analyzer", func() {
 		It("should return error when line cannot be parsed", func() {
 			pkg := &packages.Package{
 				Errors: []packages.Error{
-					packages.Error{
+					{
 						Pos: "file:line",
 						Msg: "build error",
 					},
@@ -442,7 +1686,7 @@ var _ = Describe("Analyzer", func() {
 		It("should return error when column cannot be parsed", func() {
 			pkg := &packages.Package{
 				Errors: []packages.Error{
-					packages.Error{
+					{
 						Pos: "file:1:column",
 						Msg: "build error",
 					},
@@ -455,11 +1699,11 @@ var _ = Describe("Analyzer", func() {
 		It("should append  error to the same file", func() {
 			pkg := &packages.Package{
 				Errors: []packages.Error{
-					packages.Error{
+					{
 						Pos: "file:1:2",
 						Msg: "error1",
 					},
-					packages.Error{
+					{
 						Pos: "file:3:4",
 						Msg: "error2",
 					},
@@ -468,9 +1712,9 @@ var _ = Describe("Analyzer", func() {
 			err := analyzer.ParseErrors(pkg)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
+			Expect(errors).To(HaveLen(1))
 			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(2))
+				Expect(ferr).To(HaveLen(2))
 				Expect(ferr[0].Line).To(Equal(1))
 				Expect(ferr[0].Column).To(Equal(2))
 				Expect(ferr[0].Err).Should(MatchRegexp(`error1`))
@@ -499,15 +1743,18 @@ var _ = Describe("Analyzer", func() {
 
 	Context("when appending errors", func() {
 		It("should skip error for non-buildable packages", func() {
-			analyzer.AppendError("test", errors.New(`loading file from package "pkg/test": no buildable Go source files in pkg/test`))
+			err := &build.NoGoError{
+				Dir: "pkg/test",
+			}
+			analyzer.AppendError("test", err)
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(0))
+			Expect(errors).To(BeEmpty())
 		})
 
 		It("should add a new error", func() {
 			pkg := &packages.Package{
 				Errors: []packages.Error{
-					packages.Error{
+					{
 						Pos: "file:1:2",
 						Msg: "build error",
 					},
@@ -517,10 +1764,492 @@ var _ = Describe("Analyzer", func() {
 			Expect(err).ShouldNot(HaveOccurred())
 			analyzer.AppendError("file", errors.New("file build error"))
 			_, _, errors := analyzer.Report()
-			Expect(len(errors)).To(Equal(1))
+			Expect(errors).To(HaveLen(1))
 			for _, ferr := range errors {
-				Expect(len(ferr)).To(Equal(2))
+				Expect(ferr).To(HaveLen(2))
 			}
 		})
 	})
+
+	Context("when tracking suppressions", func() {
+		BeforeEach(func() {
+			analyzer = gosec.NewAnalyzer(nil, tests, false, true, 1, logger)
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G401 -- Justification", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable G401 -- Justification", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec G405 -- Justification", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable G405 -- Justification", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec G406 -- Justification", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable G406 -- Justification", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG401[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //gosec:disable", 1)
+			nosecPackage.AddFile("md5.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //#nosec", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG405[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G405")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "c, e := des.NewCipher([]byte(\"mySecret\"))", "c, e := des.NewCipher([]byte(\"mySecret\")) //gosec:disable", 1)
+			nosecPackage.AddFile("cipher.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //#nosec", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the violation is suppressed without certain rules", func() {
+			sample := testutils.SampleCodeG406[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G406")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "h := md4.New()", "h := md4.New() //gosec:disable", 1)
+			nosecPackage.AddFile("md4.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
+		})
+
+		It("should not report an error if the rule is not included", func() {
+			sample := testutils.SampleCodeG101[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(false, "G401")).RulesInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("pwd.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			controlIssues, _, _ := analyzer.Report()
+			Expect(controlIssues).Should(HaveLen(sample.Errors))
+			Expect(controlIssues[0].Suppressions).To(HaveLen(1))
+			Expect(controlIssues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(controlIssues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should not report an error if the rule is excluded", func() {
+			sample := testutils.SampleCodeG101[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(true, "G101")).RulesInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("pwd.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).Should(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should not report an error if the analyzer is not included", func() {
+			sample := testutils.SampleCodeG407[0]
+			source := sample.Code[0]
+			analyzer.LoadAnalyzers(analyzers.Generate(true, analyzers.NewAnalyzerFilter(false, "G115")).AnalyzersInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("cipher.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			controlIssues, _, _ := analyzer.Report()
+			Expect(controlIssues).Should(HaveLen(sample.Errors))
+			Expect(controlIssues[0].Suppressions).To(HaveLen(1))
+			Expect(controlIssues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(controlIssues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should not report an error if the analyzer is excluded", func() {
+			sample := testutils.SampleCodeG407[0]
+			source := sample.Code[0]
+			analyzer.LoadAnalyzers(analyzers.Generate(true, analyzers.NewAnalyzerFilter(true, "G407")).AnalyzersInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("cipher.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).Should(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should not report an error if the analyzer is not included", func() {
+			sample := testutils.SampleCodeG602[0]
+			source := sample.Code[0]
+			analyzer.LoadAnalyzers(analyzers.Generate(true, analyzers.NewAnalyzerFilter(false, "G115")).AnalyzersInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("cipher.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			controlIssues, _, _ := analyzer.Report()
+			Expect(controlIssues).Should(HaveLen(sample.Errors))
+			Expect(controlIssues[0].Suppressions).To(HaveLen(1))
+			Expect(controlIssues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(controlIssues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should not report an error if the analyzer is excluded", func() {
+			sample := testutils.SampleCodeG602[0]
+			source := sample.Code[0]
+			analyzer.LoadAnalyzers(analyzers.Generate(true, analyzers.NewAnalyzerFilter(true, "G602")).AnalyzersInfo())
+
+			controlPackage := testutils.NewTestPackage()
+			defer controlPackage.Close()
+			controlPackage.AddFile("cipher.go", source)
+			err := controlPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, controlPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).Should(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("external"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
+		})
+
+		It("should track multiple suppressions if the violation is multiply suppressed", func() {
+			sample := testutils.SampleCodeG101[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(true, "G101")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source, "password := \"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\"", "password := \"f62e5bcda4fae4f82370da0c6f20697b8f8447ef\" //#nosec G101 -- Justification", 1)
+			nosecPackage.AddFile("pwd.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).Should(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(2))
+		})
+
+		It("should not report an error if the violation is suppressed on a struct filed", func() {
+			sample := testutils.SampleCodeG402[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G402")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source,
+				"TLSClientConfig: &tls.Config{InsecureSkipVerify: true}",
+				"TLSClientConfig: &tls.Config{InsecureSkipVerify: true} // #nosec G402", 1)
+			nosecPackage.AddFile("tls.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+		})
+
+		It("should not report an error if the violation is suppressed on a struct filed", func() {
+			sample := testutils.SampleCodeG402[0]
+			source := sample.Code[0]
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G402")).RulesInfo())
+
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecSource := strings.Replace(source,
+				"TLSClientConfig: &tls.Config{InsecureSkipVerify: true}",
+				"TLSClientConfig: &tls.Config{InsecureSkipVerify: true} //gosec:disable G402", 1)
+			nosecPackage.AddFile("tls.go", nosecSource)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(sample.Errors))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+		})
+
+		It("should not report an error if the violation is suppressed on multi-lien issue", func() {
+			source := `
+package main
+
+import (
+	"fmt"
+)
+
+const TokenLabel = `
+			source += "`" + `
+f62e5bcda4fae4f82370da0c6f20697b8f8447ef
+      ` + "`" + "//#nosec G101 -- false positive, this is not a private data" + `
+func main() {
+	fmt.Printf("Label: %s ", TokenLabel)
+}
+      `
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("pwd.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(1))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("false positive, this is not a private data"))
+		})
+
+		It("should not report an error if the violation is suppressed on multi-lien issue", func() {
+			source := `
+package main
+
+import (
+	"fmt"
+)
+
+const TokenLabel = `
+			source += "`" + `
+f62e5bcda4fae4f82370da0c6f20697b8f8447ef
+      ` + "`" + "//gosec:disable G101 -- false positive, this is not a private data" + `
+func main() {
+	fmt.Printf("Label: %s ", TokenLabel)
+}
+      `
+			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
+			nosecPackage := testutils.NewTestPackage()
+			defer nosecPackage.Close()
+			nosecPackage.AddFile("pwd.go", source)
+			err := nosecPackage.Build()
+			Expect(err).ShouldNot(HaveOccurred())
+			err = analyzer.Process(buildTags, nosecPackage.Path)
+			Expect(err).ShouldNot(HaveOccurred())
+			issues, _, _ := analyzer.Report()
+			Expect(issues).To(HaveLen(1))
+			Expect(issues[0].Suppressions).To(HaveLen(1))
+			Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
+			Expect(issues[0].Suppressions[0].Justification).To(Equal("false positive, this is not a private data"))
+		})
+	})
 })
diff --git a/analyzers/analyzers_set.go b/analyzers/analyzers_set.go
new file mode 100644
index 0000000000..e2fe51c926
--- /dev/null
+++ b/analyzers/analyzers_set.go
@@ -0,0 +1,38 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import "golang.org/x/tools/go/analysis"
+
+type AnalyzerSet struct {
+	Analyzers             []*analysis.Analyzer
+	AnalyzerSuppressedMap map[string]bool
+}
+
+// NewAnalyzerSet constructs a new AnalyzerSet
+func NewAnalyzerSet() *AnalyzerSet {
+	return &AnalyzerSet{nil, make(map[string]bool)}
+}
+
+// Register adds a trigger for the supplied analyzer
+func (a *AnalyzerSet) Register(analyzer *analysis.Analyzer, isSuppressed bool) {
+	a.Analyzers = append(a.Analyzers, analyzer)
+	a.AnalyzerSuppressedMap[analyzer.Name] = isSuppressed
+}
+
+// IsSuppressed will return whether the Analyzer is suppressed.
+func (a *AnalyzerSet) IsSuppressed(ruleID string) bool {
+	return a.AnalyzerSuppressedMap[ruleID]
+}
diff --git a/analyzers/analyzers_test.go b/analyzers/analyzers_test.go
new file mode 100644
index 0000000000..a77445ae6c
--- /dev/null
+++ b/analyzers/analyzers_test.go
@@ -0,0 +1,66 @@
+package analyzers_test
+
+import (
+	"fmt"
+	"log"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/analyzers"
+	"github.com/securego/gosec/v2/testutils"
+)
+
+var _ = Describe("gosec analyzers", func() {
+	var (
+		logger    *log.Logger
+		config    gosec.Config
+		analyzer  *gosec.Analyzer
+		runner    func(string, []testutils.CodeSample)
+		buildTags []string
+		tests     bool
+	)
+
+	BeforeEach(func() {
+		logger, _ = testutils.NewLogger()
+		config = gosec.NewConfig()
+		analyzer = gosec.NewAnalyzer(config, tests, false, false, 1, logger)
+		runner = func(analyzerId string, samples []testutils.CodeSample) {
+			for n, sample := range samples {
+				analyzer.Reset()
+				analyzer.SetConfig(sample.Config)
+				analyzer.LoadAnalyzers(analyzers.Generate(false, analyzers.NewAnalyzerFilter(false, analyzerId)).AnalyzersInfo())
+				pkg := testutils.NewTestPackage()
+				defer pkg.Close()
+				for i, code := range sample.Code {
+					pkg.AddFile(fmt.Sprintf("sample_%d_%d.go", n, i), code)
+				}
+				err := pkg.Build()
+				Expect(err).ShouldNot(HaveOccurred())
+				Expect(pkg.PrintErrors()).Should(BeZero())
+				err = analyzer.Process(buildTags, pkg.Path)
+				Expect(err).ShouldNot(HaveOccurred())
+				issues, _, _ := analyzer.Report()
+				if len(issues) != sample.Errors {
+					fmt.Println(sample.Code)
+				}
+				Expect(issues).Should(HaveLen(sample.Errors))
+			}
+		}
+	})
+
+	Context("report correct errors for all samples", func() {
+		It("should detect integer conversion overflow", func() {
+			runner("G115", testutils.SampleCodeG115)
+		})
+
+		It("should detect hardcoded nonce/IV", func() {
+			runner("G407", testutils.SampleCodeG407)
+		})
+
+		It("should detect out of bounds slice access", func() {
+			runner("G602", testutils.SampleCodeG602)
+		})
+	})
+})
diff --git a/analyzers/analyzerslist.go b/analyzers/analyzerslist.go
new file mode 100644
index 0000000000..8d222384aa
--- /dev/null
+++ b/analyzers/analyzerslist.go
@@ -0,0 +1,95 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import (
+	"golang.org/x/tools/go/analysis"
+)
+
+// AnalyzerDefinition contains the description of an analyzer and a mechanism to
+// create it.
+type AnalyzerDefinition struct {
+	ID          string
+	Description string
+	Create      AnalyzerBuilder
+}
+
+// AnalyzerBuilder is used to register an analyzer definition with the analyzer
+type AnalyzerBuilder func(id string, description string) *analysis.Analyzer
+
+// AnalyzerList contains a mapping of analyzer ID's to analyzer definitions and a mapping
+// of analyzer ID's to whether analyzers are suppressed.
+type AnalyzerList struct {
+	Analyzers          map[string]AnalyzerDefinition
+	AnalyzerSuppressed map[string]bool
+}
+
+// AnalyzersInfo returns all the create methods and the analyzer suppressed map for a
+// given list
+func (al *AnalyzerList) AnalyzersInfo() (map[string]AnalyzerDefinition, map[string]bool) {
+	builders := make(map[string]AnalyzerDefinition)
+	for _, def := range al.Analyzers {
+		builders[def.ID] = def
+	}
+	return builders, al.AnalyzerSuppressed
+}
+
+// AnalyzerFilter can be used to include or exclude an analyzer depending on the return
+// value of the function
+type AnalyzerFilter func(string) bool
+
+// NewAnalyzerFilter is a closure that will include/exclude the analyzer ID's based on
+// the supplied boolean value (false means don't remove, true means exclude).
+func NewAnalyzerFilter(action bool, analyzerIDs ...string) AnalyzerFilter {
+	analyzerlist := make(map[string]bool)
+	for _, analyzer := range analyzerIDs {
+		analyzerlist[analyzer] = true
+	}
+	return func(analyzer string) bool {
+		if _, found := analyzerlist[analyzer]; found {
+			return action
+		}
+		return !action
+	}
+}
+
+var defaultAnalyzers = []AnalyzerDefinition{
+	{"G115", "Type conversion which leads to integer overflow", newConversionOverflowAnalyzer},
+	{"G602", "Possible slice bounds out of range", newSliceBoundsAnalyzer},
+	{"G407", "Use of hardcoded IV/nonce for encryption", newHardCodedNonce},
+}
+
+// Generate the list of analyzers to use
+func Generate(trackSuppressions bool, filters ...AnalyzerFilter) *AnalyzerList {
+	analyzerMap := make(map[string]AnalyzerDefinition)
+	analyzerSuppressedMap := make(map[string]bool)
+
+	for _, analyzer := range defaultAnalyzers {
+		analyzerSuppressedMap[analyzer.ID] = false
+		addToAnalyzerList := true
+		for _, filter := range filters {
+			if filter(analyzer.ID) {
+				analyzerSuppressedMap[analyzer.ID] = true
+				if !trackSuppressions {
+					addToAnalyzerList = false
+				}
+			}
+		}
+		if addToAnalyzerList {
+			analyzerMap[analyzer.ID] = analyzer
+		}
+	}
+	return &AnalyzerList{Analyzers: analyzerMap, AnalyzerSuppressed: analyzerSuppressedMap}
+}
diff --git a/analyzers/anaylzers_suite_test.go b/analyzers/anaylzers_suite_test.go
new file mode 100644
index 0000000000..32d73016ae
--- /dev/null
+++ b/analyzers/anaylzers_suite_test.go
@@ -0,0 +1,13 @@
+package analyzers_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestAnalyzers(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Analyzers Suite")
+}
diff --git a/analyzers/conversion_overflow.go b/analyzers/conversion_overflow.go
new file mode 100644
index 0000000000..42e186710d
--- /dev/null
+++ b/analyzers/conversion_overflow.go
@@ -0,0 +1,559 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import (
+	"cmp"
+	"fmt"
+	"go/token"
+	"math"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
+	"golang.org/x/tools/go/ssa"
+
+	"github.com/securego/gosec/v2/issue"
+)
+
+type integer struct {
+	signed bool
+	size   int
+	min    int
+	max    uint
+}
+
+type rangeResult struct {
+	minValue             int
+	maxValue             uint
+	explicitPositiveVals []uint
+	explicitNegativeVals []int
+	isRangeCheck         bool
+	convertFound         bool
+}
+
+type branchResults struct {
+	minValue             *int
+	maxValue             *uint
+	explicitPositiveVals []uint
+	explicitNegativeVals []int
+	convertFound         bool
+}
+
+func newConversionOverflowAnalyzer(id string, description string) *analysis.Analyzer {
+	return &analysis.Analyzer{
+		Name:     id,
+		Doc:      description,
+		Run:      runConversionOverflow,
+		Requires: []*analysis.Analyzer{buildssa.Analyzer},
+	}
+}
+
+func runConversionOverflow(pass *analysis.Pass) (interface{}, error) {
+	ssaResult, err := getSSAResult(pass)
+	if err != nil {
+		return nil, fmt.Errorf("building ssa representation: %w", err)
+	}
+
+	issues := []*issue.Issue{}
+	for _, mcall := range ssaResult.SSA.SrcFuncs {
+		for _, block := range mcall.DomPreorder() {
+			for _, instr := range block.Instrs {
+				switch instr := instr.(type) {
+				case *ssa.Convert:
+					src := instr.X.Type().Underlying().String()
+					dst := instr.Type().Underlying().String()
+					if isIntOverflow(src, dst) {
+						if isSafeConversion(instr) {
+							continue
+						}
+						issue := newIssue(pass.Analyzer.Name,
+							fmt.Sprintf("integer overflow conversion %s -> %s", src, dst),
+							pass.Fset,
+							instr.Pos(),
+							issue.High,
+							issue.Medium,
+						)
+						issues = append(issues, issue)
+					}
+				}
+			}
+		}
+	}
+
+	if len(issues) > 0 {
+		return issues, nil
+	}
+	return nil, nil
+}
+
+func isIntOverflow(src string, dst string) bool {
+	srcInt, err := parseIntType(src)
+	if err != nil {
+		return false
+	}
+
+	dstInt, err := parseIntType(dst)
+	if err != nil {
+		return false
+	}
+
+	return srcInt.min < dstInt.min || srcInt.max > dstInt.max
+}
+
+func parseIntType(intType string) (integer, error) {
+	re := regexp.MustCompile(`^(?P<type>u?int)(?P<size>\d{1,2})?$`)
+	matches := re.FindStringSubmatch(intType)
+	if matches == nil {
+		return integer{}, fmt.Errorf("no integer type match found for %s", intType)
+	}
+
+	it := matches[re.SubexpIndex("type")]
+	is := matches[re.SubexpIndex("size")]
+
+	signed := it == "int"
+
+	// use default system int type in case size is not present in the type.
+	intSize := strconv.IntSize
+	if is != "" {
+		var err error
+		intSize, err = strconv.Atoi(is)
+		if err != nil {
+			return integer{}, fmt.Errorf("failed to parse the integer type size: %w", err)
+		}
+	}
+
+	if intSize != 8 && intSize != 16 && intSize != 32 && intSize != 64 && is != "" {
+		return integer{}, fmt.Errorf("invalid bit size: %d", intSize)
+	}
+
+	var minVal int
+	var maxVal uint
+
+	if signed {
+		shiftAmount := intSize - 1
+
+		// Perform a bounds check.
+		if shiftAmount < 0 {
+			return integer{}, fmt.Errorf("invalid shift amount: %d", shiftAmount)
+		}
+
+		maxVal = (1 << uint(shiftAmount)) - 1
+		minVal = -1 << (intSize - 1)
+
+	} else {
+		maxVal = (1 << uint(intSize)) - 1
+		minVal = 0
+	}
+
+	return integer{
+		signed: signed,
+		size:   intSize,
+		min:    minVal,
+		max:    maxVal,
+	}, nil
+}
+
+func isSafeConversion(instr *ssa.Convert) bool {
+	dstType := instr.Type().Underlying().String()
+
+	// Check for constant conversions.
+	if constVal, ok := instr.X.(*ssa.Const); ok {
+		if isConstantInRange(constVal, dstType) {
+			return true
+		}
+	}
+
+	// Check for string to integer conversions with specified bit size.
+	if isStringToIntConversion(instr, dstType) {
+		return true
+	}
+
+	// Check for explicit range checks.
+	if hasExplicitRangeCheck(instr, dstType) {
+		return true
+	}
+
+	return false
+}
+
+func isConstantInRange(constVal *ssa.Const, dstType string) bool {
+	value, err := strconv.ParseInt(constVal.Value.String(), 10, 64)
+	if err != nil {
+		return false
+	}
+
+	dstInt, err := parseIntType(dstType)
+	if err != nil {
+		return false
+	}
+
+	if dstInt.signed {
+		return value >= -(1<<(dstInt.size-1)) && value <= (1<<(dstInt.size-1))-1
+	}
+	return value >= 0 && value <= (1<<dstInt.size)-1
+}
+
+func isStringToIntConversion(instr *ssa.Convert, dstType string) bool {
+	// Traverse the SSA instructions to find the original variable.
+	original := instr.X
+	for {
+		switch v := original.(type) {
+		case *ssa.Call:
+			if v.Call.StaticCallee() != nil && (v.Call.StaticCallee().Name() == "ParseInt" || v.Call.StaticCallee().Name() == "ParseUint") {
+				if len(v.Call.Args) == 3 {
+					if bitSize, ok := v.Call.Args[2].(*ssa.Const); ok {
+						signed := v.Call.StaticCallee().Name() == "ParseInt"
+						bitSizeValue, err := strconv.Atoi(bitSize.Value.String())
+						if err != nil {
+							return false
+						}
+						dstInt, err := parseIntType(dstType)
+						if err != nil {
+							return false
+						}
+
+						// we're good if:
+						// - signs match and bit size is <= than destination
+						// - parsing unsigned and bit size is < than destination
+						isSafe := (bitSizeValue <= dstInt.size && signed == dstInt.signed) ||
+							(bitSizeValue < dstInt.size && !signed)
+						return isSafe
+					}
+				}
+			}
+			return false
+		case *ssa.Phi:
+			original = v.Edges[0]
+		case *ssa.Extract:
+			original = v.Tuple
+		default:
+			return false
+		}
+	}
+}
+
+func hasExplicitRangeCheck(instr *ssa.Convert, dstType string) bool {
+	dstInt, err := parseIntType(dstType)
+	if err != nil {
+		return false
+	}
+
+	srcInt, err := parseIntType(instr.X.Type().String())
+	if err != nil {
+		return false
+	}
+
+	minValue := srcInt.min
+	maxValue := srcInt.max
+	explicitPositiveVals := []uint{}
+	explicitNegativeVals := []int{}
+
+	if minValue > dstInt.min && maxValue < dstInt.max {
+		return true
+	}
+
+	visitedIfs := make(map[*ssa.If]bool)
+	for _, block := range instr.Parent().Blocks {
+		for _, blockInstr := range block.Instrs {
+			switch v := blockInstr.(type) {
+			case *ssa.If:
+				result := getResultRange(v, instr, visitedIfs)
+				if result.isRangeCheck {
+					minValue = max(minValue, result.minValue)
+					maxValue = min(maxValue, result.maxValue)
+					explicitPositiveVals = append(explicitPositiveVals, result.explicitPositiveVals...)
+					explicitNegativeVals = append(explicitNegativeVals, result.explicitNegativeVals...)
+				}
+			case *ssa.Call:
+				// These function return an int of a guaranteed size.
+				if v != instr.X {
+					continue
+				}
+				if fn, isBuiltin := v.Call.Value.(*ssa.Builtin); isBuiltin {
+					switch fn.Name() {
+					case "len", "cap":
+						minValue = 0
+					}
+				}
+			}
+
+			if explicitValsInRange(explicitPositiveVals, explicitNegativeVals, dstInt) {
+				return true
+			} else if minValue >= dstInt.min && maxValue <= dstInt.max {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// getResultRange is a recursive function that walks the branches of the if statement to find the range of the variable.
+func getResultRange(ifInstr *ssa.If, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) rangeResult {
+	if visitedIfs[ifInstr] {
+		return rangeResult{minValue: math.MinInt, maxValue: math.MaxUint}
+	}
+	visitedIfs[ifInstr] = true
+
+	cond := ifInstr.Cond
+	binOp, ok := cond.(*ssa.BinOp)
+	if !ok || !isRangeCheck(binOp, instr.X) {
+		return rangeResult{minValue: math.MinInt, maxValue: math.MaxUint}
+	}
+
+	result := rangeResult{
+		minValue:     math.MinInt,
+		maxValue:     math.MaxUint,
+		isRangeCheck: true,
+	}
+
+	thenBounds := walkBranchForConvert(ifInstr.Block().Succs[0], instr, visitedIfs)
+	elseBounds := walkBranchForConvert(ifInstr.Block().Succs[1], instr, visitedIfs)
+
+	updateResultFromBinOp(&result, binOp, instr, thenBounds.convertFound)
+
+	if thenBounds.convertFound {
+		result.convertFound = true
+		result.minValue = maxWithPtr(result.minValue, thenBounds.minValue)
+		result.maxValue = minWithPtr(result.maxValue, thenBounds.maxValue)
+	} else if elseBounds.convertFound {
+		result.convertFound = true
+		result.minValue = maxWithPtr(result.minValue, elseBounds.minValue)
+		result.maxValue = minWithPtr(result.maxValue, elseBounds.maxValue)
+	}
+
+	result.explicitPositiveVals = append(result.explicitPositiveVals, thenBounds.explicitPositiveVals...)
+	result.explicitNegativeVals = append(result.explicitNegativeVals, thenBounds.explicitNegativeVals...)
+	result.explicitPositiveVals = append(result.explicitPositiveVals, elseBounds.explicitPositiveVals...)
+	result.explicitNegativeVals = append(result.explicitNegativeVals, elseBounds.explicitNegativeVals...)
+
+	return result
+}
+
+// updateResultFromBinOp updates the rangeResult based on the BinOp instruction and the location of the Convert instruction.
+func updateResultFromBinOp(result *rangeResult, binOp *ssa.BinOp, instr *ssa.Convert, successPathConvert bool) {
+	x, y := binOp.X, binOp.Y
+	operandsFlipped := false
+
+	compareVal, op := getRealValueFromOperation(instr.X)
+
+	// Handle FieldAddr
+	if fieldAddr, ok := compareVal.(*ssa.FieldAddr); ok {
+		compareVal = fieldAddr
+	}
+
+	if !isSameOrRelated(x, compareVal) {
+		y = x
+		operandsFlipped = true
+	}
+
+	constVal, ok := y.(*ssa.Const)
+	if !ok {
+		return
+	}
+	// TODO: constVal.Value nil check avoids #1229 panic but seems to be hiding a bug in the code above or in x/tools/go/ssa.
+	if constVal.Value == nil {
+		// log.Fatalf("[gosec] constVal.Value is nil flipped=%t, constVal=%#v, binOp=%#v", operandsFlipped, constVal, binOp)
+		return
+	}
+	switch binOp.Op {
+	case token.LEQ, token.LSS:
+		updateMinMaxForLessOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert)
+	case token.GEQ, token.GTR:
+		updateMinMaxForGreaterOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert)
+	case token.EQL:
+		if !successPathConvert {
+			break
+		}
+		updateExplicitValues(result, constVal)
+	case token.NEQ:
+		if successPathConvert {
+			break
+		}
+		updateExplicitValues(result, constVal)
+	}
+
+	if op == "neg" {
+		minVal := result.minValue
+		maxVal := result.maxValue
+
+		if minVal >= 0 {
+			result.maxValue = uint(minVal)
+		}
+		if maxVal <= math.MaxInt {
+			result.minValue = int(maxVal)
+		}
+	}
+}
+
+func updateExplicitValues(result *rangeResult, constVal *ssa.Const) {
+	if strings.Contains(constVal.String(), "-") {
+		result.explicitNegativeVals = append(result.explicitNegativeVals, int(constVal.Int64()))
+	} else {
+		result.explicitPositiveVals = append(result.explicitPositiveVals, uint(constVal.Uint64()))
+	}
+}
+
+func updateMinMaxForLessOrEqual(result *rangeResult, constVal *ssa.Const, op token.Token, operandsFlipped bool, successPathConvert bool) {
+	// If the success path has a conversion and the operands are not flipped, then the constant value is the maximum value.
+	if successPathConvert && !operandsFlipped {
+		result.maxValue = uint(constVal.Uint64())
+		if op == token.LEQ {
+			result.maxValue--
+		}
+	} else {
+		result.minValue = int(constVal.Int64())
+		if op == token.GTR {
+			result.minValue++
+		}
+	}
+}
+
+func updateMinMaxForGreaterOrEqual(result *rangeResult, constVal *ssa.Const, op token.Token, operandsFlipped bool, successPathConvert bool) {
+	// If the success path has a conversion and the operands are not flipped, then the constant value is the minimum value.
+	if successPathConvert && !operandsFlipped {
+		result.minValue = int(constVal.Int64())
+		if op == token.GEQ {
+			result.minValue++
+		}
+	} else {
+		result.maxValue = uint(constVal.Uint64())
+		if op == token.LSS {
+			result.maxValue--
+		}
+	}
+}
+
+// walkBranchForConvert walks the branch of the if statement to find the range of the variable and where the conversion is.
+func walkBranchForConvert(block *ssa.BasicBlock, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) branchResults {
+	bounds := branchResults{}
+
+	for _, blockInstr := range block.Instrs {
+		switch v := blockInstr.(type) {
+		case *ssa.If:
+			result := getResultRange(v, instr, visitedIfs)
+			bounds.convertFound = bounds.convertFound || result.convertFound
+
+			if result.isRangeCheck {
+				bounds.minValue = toPtr(maxWithPtr(result.minValue, bounds.minValue))
+				bounds.maxValue = toPtr(minWithPtr(result.maxValue, bounds.maxValue))
+				bounds.explicitPositiveVals = append(bounds.explicitPositiveVals, result.explicitPositiveVals...)
+				bounds.explicitNegativeVals = append(bounds.explicitNegativeVals, result.explicitNegativeVals...)
+			}
+		case *ssa.Call:
+			if v == instr.X {
+				if fn, isBuiltin := v.Call.Value.(*ssa.Builtin); isBuiltin && (fn.Name() == "len" || fn.Name() == "cap") {
+					bounds.minValue = toPtr(0)
+				}
+			}
+		case *ssa.Convert:
+			if v == instr {
+				bounds.convertFound = true
+				return bounds
+			}
+		}
+	}
+
+	return bounds
+}
+
+func isRangeCheck(v ssa.Value, x ssa.Value) bool {
+	compareVal, _ := getRealValueFromOperation(x)
+
+	switch op := v.(type) {
+	case *ssa.BinOp:
+		switch op.Op {
+		case token.LSS, token.LEQ, token.GTR, token.GEQ, token.EQL, token.NEQ:
+			leftMatch := isSameOrRelated(op.X, compareVal)
+			rightMatch := isSameOrRelated(op.Y, compareVal)
+			return leftMatch || rightMatch
+		}
+	}
+	return false
+}
+
+func getRealValueFromOperation(v ssa.Value) (ssa.Value, string) {
+	switch v := v.(type) {
+	case *ssa.UnOp:
+		if v.Op == token.SUB {
+			val, _ := getRealValueFromOperation(v.X)
+			return val, "neg"
+		}
+		return getRealValueFromOperation(v.X)
+	case *ssa.FieldAddr:
+		return v, "field"
+	case *ssa.Alloc:
+		return v, "alloc"
+	}
+	return v, ""
+}
+
+func isSameOrRelated(a, b ssa.Value) bool {
+	aVal, _ := getRealValueFromOperation(a)
+	bVal, _ := getRealValueFromOperation(b)
+
+	if aVal == bVal {
+		return true
+	}
+
+	// Check if both are FieldAddr operations referring to the same field of the same struct
+	if aField, aOk := aVal.(*ssa.FieldAddr); aOk {
+		if bField, bOk := bVal.(*ssa.FieldAddr); bOk {
+			return aField.X == bField.X && aField.Field == bField.Field
+		}
+	}
+
+	return false
+}
+
+func explicitValsInRange(explicitPosVals []uint, explicitNegVals []int, dstInt integer) bool {
+	if len(explicitPosVals) == 0 && len(explicitNegVals) == 0 {
+		return false
+	}
+
+	for _, val := range explicitPosVals {
+		if val > dstInt.max {
+			return false
+		}
+	}
+
+	for _, val := range explicitNegVals {
+		if val < dstInt.min {
+			return false
+		}
+	}
+
+	return true
+}
+
+func minWithPtr[T cmp.Ordered](a T, b *T) T {
+	if b == nil {
+		return a
+	}
+	return min(a, *b)
+}
+
+func maxWithPtr[T cmp.Ordered](a T, b *T) T {
+	if b == nil {
+		return a
+	}
+	return max(a, *b)
+}
+
+func toPtr[T any](a T) *T {
+	return &a
+}
diff --git a/analyzers/conversion_overflow_test.go b/analyzers/conversion_overflow_test.go
new file mode 100644
index 0000000000..8a8483705d
--- /dev/null
+++ b/analyzers/conversion_overflow_test.go
@@ -0,0 +1,139 @@
+package analyzers
+
+import (
+	"math"
+	"strconv"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("ParseIntType", func() {
+	Context("with valid input", func() {
+		DescribeTable("should correctly parse and calculate bounds for",
+			func(intType string, expectedSigned bool, expectedSize int, expectedMin int, expectedMax uint) {
+				result, err := parseIntType(intType)
+				Expect(err).NotTo(HaveOccurred())
+				Expect(result.signed).To(Equal(expectedSigned))
+				Expect(result.size).To(Equal(expectedSize))
+				Expect(result.min).To(Equal(expectedMin))
+				Expect(result.max).To(Equal(expectedMax))
+			},
+			Entry("uint8", "uint8", false, 8, 0, uint(math.MaxUint8)),
+			Entry("int8", "int8", true, 8, math.MinInt8, uint(math.MaxInt8)),
+			Entry("uint16", "uint16", false, 16, 0, uint(math.MaxUint16)),
+			Entry("int16", "int16", true, 16, math.MinInt16, uint(math.MaxInt16)),
+			Entry("uint32", "uint32", false, 32, 0, uint(math.MaxUint32)),
+			Entry("int32", "int32", true, 32, math.MinInt32, uint(math.MaxInt32)),
+			Entry("uint64", "uint64", false, 64, 0, uint(math.MaxUint64)),
+			Entry("int64", "int64", true, 64, math.MinInt64, uint(math.MaxInt64)),
+		)
+
+		It("should use system's int size for 'int' and 'uint'", func() {
+			intResult, err := parseIntType("int")
+			Expect(err).NotTo(HaveOccurred())
+			Expect(intResult.size).To(Equal(strconv.IntSize))
+
+			uintResult, err := parseIntType("uint")
+			Expect(err).NotTo(HaveOccurred())
+			Expect(uintResult.size).To(Equal(strconv.IntSize))
+		})
+	})
+
+	Context("with invalid input", func() {
+		DescribeTable("should return an error for",
+			func(intType string, expectedErrorString string) {
+				_, err := parseIntType(intType)
+				Expect(err).To(HaveOccurred())
+				Expect(err.Error()).To(ContainSubstring(expectedErrorString))
+			},
+			Entry("empty string", "", "no integer type match found for "),
+			Entry("invalid type", "float64", "no integer type match found for float64"),
+			Entry("invalid size", "int65", "invalid bit size: 65"),
+			Entry("negative size", "int-8", "no integer type match found for int-8"),
+			Entry("non-numeric size", "intABC", "no integer type match found for intABC"),
+		)
+	})
+})
+
+var _ = Describe("IsIntOverflow", func() {
+	DescribeTable("should correctly identify overflow scenarios on a 64-bit system",
+		func(src string, dst string, expectedOverflow bool) {
+			result := isIntOverflow(src, dst)
+			Expect(result).To(Equal(expectedOverflow))
+		},
+		// Unsigned to Signed conversions
+		Entry("uint8 to int8", "uint8", "int8", true),
+		Entry("uint8 to int16", "uint8", "int16", false),
+		Entry("uint8 to int32", "uint8", "int32", false),
+		Entry("uint8 to int64", "uint8", "int64", false),
+		Entry("uint16 to int8", "uint16", "int8", true),
+		Entry("uint16 to int16", "uint16", "int16", true),
+		Entry("uint16 to int32", "uint16", "int32", false),
+		Entry("uint16 to int64", "uint16", "int64", false),
+		Entry("uint32 to int8", "uint32", "int8", true),
+		Entry("uint32 to int16", "uint32", "int16", true),
+		Entry("uint32 to int32", "uint32", "int32", true),
+		Entry("uint32 to int64", "uint32", "int64", false),
+		Entry("uint64 to int8", "uint64", "int8", true),
+		Entry("uint64 to int16", "uint64", "int16", true),
+		Entry("uint64 to int32", "uint64", "int32", true),
+		Entry("uint64 to int64", "uint64", "int64", true),
+
+		// Unsigned to Unsigned conversions
+		Entry("uint8 to uint16", "uint8", "uint16", false),
+		Entry("uint8 to uint32", "uint8", "uint32", false),
+		Entry("uint8 to uint64", "uint8", "uint64", false),
+		Entry("uint16 to uint8", "uint16", "uint8", true),
+		Entry("uint16 to uint32", "uint16", "uint32", false),
+		Entry("uint16 to uint64", "uint16", "uint64", false),
+		Entry("uint32 to uint8", "uint32", "uint8", true),
+		Entry("uint32 to uint16", "uint32", "uint16", true),
+		Entry("uint32 to uint64", "uint32", "uint64", false),
+		Entry("uint64 to uint8", "uint64", "uint8", true),
+		Entry("uint64 to uint16", "uint64", "uint16", true),
+		Entry("uint64 to uint32", "uint64", "uint32", true),
+
+		// Signed to Unsigned conversions
+		Entry("int8 to uint8", "int8", "uint8", true),
+		Entry("int8 to uint16", "int8", "uint16", true),
+		Entry("int8 to uint32", "int8", "uint32", true),
+		Entry("int8 to uint64", "int8", "uint64", true),
+		Entry("int16 to uint8", "int16", "uint8", true),
+		Entry("int16 to uint16", "int16", "uint16", true),
+		Entry("int16 to uint32", "int16", "uint32", true),
+		Entry("int16 to uint64", "int16", "uint64", true),
+		Entry("int32 to uint8", "int32", "uint8", true),
+		Entry("int32 to uint16", "int32", "uint16", true),
+		Entry("int32 to uint32", "int32", "uint32", true),
+		Entry("int32 to uint64", "int32", "uint64", true),
+		Entry("int64 to uint8", "int64", "uint8", true),
+		Entry("int64 to uint16", "int64", "uint16", true),
+		Entry("int64 to uint32", "int64", "uint32", true),
+		Entry("int64 to uint64", "int64", "uint64", true),
+
+		// Signed to Signed conversions
+		Entry("int8 to int16", "int8", "int16", false),
+		Entry("int8 to int32", "int8", "int32", false),
+		Entry("int8 to int64", "int8", "int64", false),
+		Entry("int16 to int8", "int16", "int8", true),
+		Entry("int16 to int32", "int16", "int32", false),
+		Entry("int16 to int64", "int16", "int64", false),
+		Entry("int32 to int8", "int32", "int8", true),
+		Entry("int32 to int16", "int32", "int16", true),
+		Entry("int32 to int64", "int32", "int64", false),
+		Entry("int64 to int8", "int64", "int8", true),
+		Entry("int64 to int16", "int64", "int16", true),
+		Entry("int64 to int32", "int64", "int32", true),
+
+		// Same type conversions (should never overflow)
+		Entry("uint8 to uint8", "uint8", "uint8", false),
+		Entry("uint16 to uint16", "uint16", "uint16", false),
+		Entry("uint32 to uint32", "uint32", "uint32", false),
+		Entry("uint64 to uint64", "uint64", "uint64", false),
+		Entry("int8 to int8", "int8", "int8", false),
+		Entry("int16 to int16", "int16", "int16", false),
+		Entry("int32 to int32", "int32", "int32", false),
+		Entry("int64 to int64", "int64", "int64", false),
+	)
+})
diff --git a/analyzers/hardcoded_nonce.go b/analyzers/hardcoded_nonce.go
new file mode 100644
index 0000000000..4501fb65aa
--- /dev/null
+++ b/analyzers/hardcoded_nonce.go
@@ -0,0 +1,251 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import (
+	"errors"
+	"fmt"
+	"go/token"
+	"strings"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
+	"golang.org/x/tools/go/ssa"
+
+	"github.com/securego/gosec/v2/issue"
+)
+
+const defaultIssueDescription = "Use of hardcoded IV/nonce for encryption"
+
+func newHardCodedNonce(id string, description string) *analysis.Analyzer {
+	return &analysis.Analyzer{
+		Name:     id,
+		Doc:      description,
+		Run:      runHardCodedNonce,
+		Requires: []*analysis.Analyzer{buildssa.Analyzer},
+	}
+}
+
+func runHardCodedNonce(pass *analysis.Pass) (interface{}, error) {
+	ssaResult, err := getSSAResult(pass)
+	if err != nil {
+		return nil, fmt.Errorf("building ssa representation: %w", err)
+	}
+
+	// Holds the function name as key, the number of arguments that the function accepts, and at which index of those accepted arguments is the nonce/IV
+	// Example "Test" 3, 1 -- means the function "Test" which accepts 3 arguments, and has the nonce arg as second argument
+	calls := map[string][]int{
+		"(crypto/cipher.AEAD).Seal":     {4, 1},
+		"crypto/cipher.NewCBCEncrypter": {2, 1},
+		"crypto/cipher.NewCFBEncrypter": {2, 1},
+		"crypto/cipher.NewCTR":          {2, 1},
+		"crypto/cipher.NewOFB":          {2, 1},
+	}
+	ssaPkgFunctions := ssaResult.SSA.SrcFuncs
+	args := getArgsFromTrackedFunctions(ssaPkgFunctions, calls)
+	if args == nil {
+		return nil, errors.New("no tracked functions found, resulting in no variables to track")
+	}
+	var issues []*issue.Issue
+	for _, arg := range args {
+		if arg == nil {
+			continue
+		}
+		i, err := raiseIssue(*arg, calls, ssaPkgFunctions, pass, "")
+		if err != nil {
+			return issues, fmt.Errorf("raising issue error: %w", err)
+		}
+		issues = append(issues, i...)
+	}
+	return issues, nil
+}
+
+func raiseIssue(val ssa.Value, funcsToTrack map[string][]int, ssaFuncs []*ssa.Function,
+	pass *analysis.Pass, issueDescription string,
+) ([]*issue.Issue, error) {
+	if issueDescription == "" {
+		issueDescription = defaultIssueDescription
+	}
+	var err error
+	var allIssues []*issue.Issue
+	var issues []*issue.Issue
+	switch valType := (val).(type) {
+	case *ssa.Slice:
+		issueDescription += " by passing hardcoded slice/array"
+		issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.High)
+		allIssues = append(allIssues, issues...)
+	case *ssa.UnOp:
+		// Check if it's a dereference operation (a.k.a pointer)
+		if valType.Op == token.MUL {
+			issueDescription += " by passing pointer which points to hardcoded variable"
+			issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.Low)
+			allIssues = append(allIssues, issues...)
+		}
+	// When the value assigned to a variable is a function call.
+	// It goes and check if this function contains call to crypto/rand.Read
+	// in it's body(Assuming that calling crypto/rand.Read in a function,
+	// is used for the generation of nonce/iv )
+	case *ssa.Call:
+		if callValue := valType.Call.Value; callValue != nil {
+			if calledFunction, ok := callValue.(*ssa.Function); ok {
+				if contains, funcErr := isFuncContainsCryptoRand(calledFunction); !contains && funcErr == nil {
+					issueDescription += " by passing a value from function which doesn't use crypto/rand"
+					issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.Medium)
+					allIssues = append(allIssues, issues...)
+				} else if funcErr != nil {
+					err = funcErr
+				}
+			}
+		}
+	// only checks from strings->[]byte
+	// might need to add additional types
+	case *ssa.Convert:
+		if valType.Type().String() == "[]byte" && valType.X.Type().String() == "string" {
+			issueDescription += " by passing converted string"
+			issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.High)
+			allIssues = append(allIssues, issues...)
+		}
+	case *ssa.Parameter:
+		// arg given to tracked function is wrapped in another function, example:
+		// func encrypt(..,nonce,...){
+		// 		aesgcm.Seal(nonce)
+		// }
+		// save parameter position, by checking the name of the variable used in
+		// tracked functions and comparing it with the name of the arg
+		if valType.Parent() != nil {
+			trackedFunctions := make(map[string][]int)
+			for index, funcArgs := range valType.Parent().Params {
+				if funcArgs.Name() == valType.Name() && funcArgs.Type() == valType.Type() {
+					trackedFunctions[valType.Parent().String()] = []int{len(valType.Parent().Params), index}
+				}
+			}
+			args := getArgsFromTrackedFunctions(ssaFuncs, trackedFunctions)
+
+			issueDescription += " by passing a parameter to a function and"
+			// recursively backtrack to where the origin of a variable passed to multiple functions is
+			for _, arg := range args {
+				if arg == nil {
+					continue
+				}
+				issues, err = raiseIssue(*arg, trackedFunctions, ssaFuncs, pass, issueDescription)
+				allIssues = append(allIssues, issues...)
+			}
+		}
+	}
+	return allIssues, err
+}
+
+// iterateThroughReferrers iterates through all places that use the `variable` argument and check if it's used in one of the tracked functions.
+func iterateThroughReferrers(variable ssa.Value, funcsToTrack map[string][]int,
+	analyzerID string, issueDescription string,
+	fileSet *token.FileSet, issueConfidence issue.Score,
+) ([]*issue.Issue, error) {
+	if funcsToTrack == nil || variable == nil || analyzerID == "" || issueDescription == "" || fileSet == nil {
+		return nil, errors.New("received a nil object")
+	}
+	var gosecIssues []*issue.Issue
+	refs := variable.Referrers()
+	if refs == nil {
+		return gosecIssues, nil
+	}
+	// Go through all functions that use the given arg variable
+	for _, ref := range *refs {
+		// Iterate through the functions we are interested
+		for trackedFunc := range funcsToTrack {
+
+			// Split the functions we are interested in, by the '.' because we will use the function name to do the comparison
+			// MIGHT GIVE SOME FALSE POSITIVES THIS WAY
+			trackedFuncParts := strings.Split(trackedFunc, ".")
+			trackedFuncPartsName := trackedFuncParts[len(trackedFuncParts)-1]
+			if strings.Contains(ref.String(), trackedFuncPartsName) {
+				gosecIssues = append(gosecIssues, newIssue(analyzerID, issueDescription, fileSet, ref.Pos(), issue.High, issueConfidence))
+			}
+		}
+	}
+	return gosecIssues, nil
+}
+
+// isFuncContainsCryptoRand checks whether a function contains a call to crypto/rand.Read in it's function body.
+func isFuncContainsCryptoRand(funcCall *ssa.Function) (bool, error) {
+	if funcCall == nil {
+		return false, errors.New("passed ssa.Function object is nil")
+	}
+	for _, block := range funcCall.Blocks {
+		for _, instr := range block.Instrs {
+			if call, ok := instr.(*ssa.Call); ok {
+				if calledFunction, ok := call.Call.Value.(*ssa.Function); ok {
+					if calledFunction.Pkg != nil && calledFunction.Pkg.Pkg.Path() == "crypto/rand" && calledFunction.Name() == "Read" {
+						return true, nil
+					}
+				}
+			}
+		}
+	}
+	return false, nil
+}
+
+func addToVarsMap(value ssa.Value, mapToAddTo map[string]*ssa.Value) {
+	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 {
+	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
+}
+
+func getArgsFromTrackedFunctions(ssaFuncs []*ssa.Function, trackedFunc map[string][]int) map[string]*ssa.Value {
+	values := make(map[string]*ssa.Value)
+	for _, pkgFunc := range ssaFuncs {
+		for _, funcBlock := range pkgFunc.Blocks {
+			for _, funcBlocInstr := range funcBlock.Instrs {
+				iterateTrackedFunctionsAndAddArgs(funcBlocInstr, trackedFunc, values)
+			}
+		}
+	}
+	return values
+}
+
+func iterateTrackedFunctionsAndAddArgs(funcBlocInstr ssa.Instruction, trackedFunc map[string][]int, values map[string]*ssa.Value) {
+	if funcCall, ok := (funcBlocInstr).(*ssa.Call); ok {
+		for trackedFuncName, trackedFuncArgsInfo := range trackedFunc {
+			// only process functions that have the same number of arguments as the ones we track
+			if len(funcCall.Call.Args) == trackedFuncArgsInfo[0] {
+				tmpArg := funcCall.Call.Args[trackedFuncArgsInfo[1]]
+				// check if the function is called from an object or directly from the package
+				if funcCall.Call.Method != nil {
+					if methodFullname := funcCall.Call.Method.FullName(); methodFullname == trackedFuncName {
+						if !isContainedInMap(tmpArg, values) {
+							addToVarsMap(tmpArg, values)
+						}
+					}
+				} else if funcCall.Call.Value.String() == trackedFuncName {
+					if !isContainedInMap(tmpArg, values) {
+						addToVarsMap(tmpArg, values)
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/analyzers/slice_bounds.go b/analyzers/slice_bounds.go
new file mode 100644
index 0000000000..968102f268
--- /dev/null
+++ b/analyzers/slice_bounds.go
@@ -0,0 +1,399 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import (
+	"errors"
+	"fmt"
+	"go/token"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
+	"golang.org/x/tools/go/ssa"
+
+	"github.com/securego/gosec/v2/issue"
+)
+
+type bound int
+
+const (
+	lowerUnbounded bound = iota
+	upperUnbounded
+	unbounded
+	upperBounded
+)
+
+const maxDepth = 20
+
+func newSliceBoundsAnalyzer(id string, description string) *analysis.Analyzer {
+	return &analysis.Analyzer{
+		Name:     id,
+		Doc:      description,
+		Run:      runSliceBounds,
+		Requires: []*analysis.Analyzer{buildssa.Analyzer},
+	}
+}
+
+func runSliceBounds(pass *analysis.Pass) (interface{}, error) {
+	ssaResult, err := getSSAResult(pass)
+	if err != nil {
+		return nil, err
+	}
+
+	issues := map[ssa.Instruction]*issue.Issue{}
+	ifs := map[ssa.If]*ssa.BinOp{}
+	for _, mcall := range ssaResult.SSA.SrcFuncs {
+		for _, block := range mcall.DomPreorder() {
+			for _, instr := range block.Instrs {
+				switch instr := instr.(type) {
+				case *ssa.Alloc:
+					sliceCap, err := extractSliceCapFromAlloc(instr.String())
+					if err != nil {
+						break
+					}
+					allocRefs := instr.Referrers()
+					if allocRefs == nil {
+						break
+					}
+					for _, instr := range *allocRefs {
+						if slice, ok := instr.(*ssa.Slice); ok {
+							if _, ok := slice.X.(*ssa.Alloc); ok {
+								if slice.Parent() != nil {
+									l, h := extractSliceBounds(slice)
+									newCap := computeSliceNewCap(l, h, sliceCap)
+									violations := []ssa.Instruction{}
+									trackSliceBounds(0, newCap, slice, &violations, ifs)
+									for _, s := range violations {
+										switch s := s.(type) {
+										case *ssa.Slice:
+											issue := newIssue(
+												pass.Analyzer.Name,
+												"slice bounds out of range",
+												pass.Fset,
+												s.Pos(),
+												issue.Low,
+												issue.High)
+											issues[s] = issue
+										case *ssa.IndexAddr:
+											issue := newIssue(
+												pass.Analyzer.Name,
+												"slice index out of range",
+												pass.Fset,
+												s.Pos(),
+												issue.Low,
+												issue.High)
+											issues[s] = issue
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	for ifref, binop := range ifs {
+		bound, value, err := extractBinOpBound(binop)
+		if err != nil {
+			continue
+		}
+		for i, block := range ifref.Block().Succs {
+			if i == 1 {
+				bound = invBound(bound)
+			}
+			var processBlock func(block *ssa.BasicBlock, depth int)
+			processBlock = func(block *ssa.BasicBlock, depth int) {
+				if depth == maxDepth {
+					return
+				}
+				depth++
+				for _, instr := range block.Instrs {
+					if _, ok := issues[instr]; ok {
+						switch bound {
+						case lowerUnbounded:
+							break
+						case upperUnbounded, unbounded:
+							delete(issues, instr)
+						case upperBounded:
+							switch tinstr := instr.(type) {
+							case *ssa.Slice:
+								lower, upper := extractSliceBounds(tinstr)
+								if isSliceInsideBounds(0, value, lower, upper) {
+									delete(issues, instr)
+								}
+							case *ssa.IndexAddr:
+								indexValue, err := extractIntValue(tinstr.Index.String())
+								if err != nil {
+									break
+								}
+								if isSliceIndexInsideBounds(0, value, indexValue) {
+									delete(issues, instr)
+								}
+							}
+						}
+					} else if nestedIfInstr, ok := instr.(*ssa.If); ok {
+						for _, nestedBlock := range nestedIfInstr.Block().Succs {
+							processBlock(nestedBlock, depth)
+						}
+					}
+				}
+			}
+
+			processBlock(block, 0)
+		}
+	}
+
+	foundIssues := []*issue.Issue{}
+	for _, issue := range issues {
+		foundIssues = append(foundIssues, issue)
+	}
+	if len(foundIssues) > 0 {
+		return foundIssues, nil
+	}
+	return nil, nil
+}
+
+func trackSliceBounds(depth int, sliceCap int, slice ssa.Node, violations *[]ssa.Instruction, ifs map[ssa.If]*ssa.BinOp) {
+	if depth == maxDepth {
+		return
+	}
+	depth++
+	if violations == nil {
+		violations = &[]ssa.Instruction{}
+	}
+	referrers := slice.Referrers()
+	if referrers != nil {
+		for _, refinstr := range *referrers {
+			switch refinstr := refinstr.(type) {
+			case *ssa.Slice:
+				checkAllSlicesBounds(depth, sliceCap, refinstr, violations, ifs)
+				switch refinstr.X.(type) {
+				case *ssa.Alloc, *ssa.Parameter:
+					l, h := extractSliceBounds(refinstr)
+					newCap := computeSliceNewCap(l, h, sliceCap)
+					trackSliceBounds(depth, newCap, refinstr, violations, ifs)
+				}
+			case *ssa.IndexAddr:
+				indexValue, err := extractIntValue(refinstr.Index.String())
+				if err == nil && !isSliceIndexInsideBounds(0, sliceCap, indexValue) {
+					*violations = append(*violations, refinstr)
+				}
+			case *ssa.Call:
+				if ifref, cond := extractSliceIfLenCondition(refinstr); ifref != nil && cond != nil {
+					ifs[*ifref] = cond
+				} else {
+					parPos := -1
+					for pos, arg := range refinstr.Call.Args {
+						if a, ok := arg.(*ssa.Slice); ok && a == slice {
+							parPos = pos
+						}
+					}
+					if fn, ok := refinstr.Call.Value.(*ssa.Function); ok {
+						if len(fn.Params) > parPos && parPos > -1 {
+							param := fn.Params[parPos]
+							trackSliceBounds(depth, sliceCap, param, violations, ifs)
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+func checkAllSlicesBounds(depth int, sliceCap int, slice *ssa.Slice, violations *[]ssa.Instruction, ifs map[ssa.If]*ssa.BinOp) {
+	if depth == maxDepth {
+		return
+	}
+	depth++
+	if violations == nil {
+		violations = &[]ssa.Instruction{}
+	}
+	sliceLow, sliceHigh := extractSliceBounds(slice)
+	if !isSliceInsideBounds(0, sliceCap, sliceLow, sliceHigh) {
+		*violations = append(*violations, slice)
+	}
+	switch slice.X.(type) {
+	case *ssa.Alloc, *ssa.Parameter, *ssa.Slice:
+		l, h := extractSliceBounds(slice)
+		newCap := computeSliceNewCap(l, h, sliceCap)
+		trackSliceBounds(depth, newCap, slice, violations, ifs)
+	}
+
+	references := slice.Referrers()
+	if references == nil {
+		return
+	}
+	for _, ref := range *references {
+		switch s := ref.(type) {
+		case *ssa.Slice:
+			checkAllSlicesBounds(depth, sliceCap, s, violations, ifs)
+			switch s.X.(type) {
+			case *ssa.Alloc, *ssa.Parameter:
+				l, h := extractSliceBounds(s)
+				newCap := computeSliceNewCap(l, h, sliceCap)
+				trackSliceBounds(depth, newCap, s, violations, ifs)
+			}
+		}
+	}
+}
+
+func extractSliceIfLenCondition(call *ssa.Call) (*ssa.If, *ssa.BinOp) {
+	if builtInLen, ok := call.Call.Value.(*ssa.Builtin); ok {
+		if builtInLen.Name() == "len" {
+			refs := call.Referrers()
+			if refs != nil {
+				for _, ref := range *refs {
+					if binop, ok := ref.(*ssa.BinOp); ok {
+						binoprefs := binop.Referrers()
+						for _, ref := range *binoprefs {
+							if ifref, ok := ref.(*ssa.If); ok {
+								return ifref, binop
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+
+func computeSliceNewCap(l, h, oldCap int) int {
+	if l == 0 && h == 0 {
+		return oldCap
+	}
+	if l > 0 && h == 0 {
+		return oldCap - l
+	}
+	if l == 0 && h > 0 {
+		return h
+	}
+	return h - l
+}
+
+func invBound(bound bound) bound {
+	switch bound {
+	case lowerUnbounded:
+		return upperUnbounded
+	case upperUnbounded:
+		return lowerUnbounded
+	case upperBounded:
+		return unbounded
+	case unbounded:
+		return upperBounded
+	default:
+		return unbounded
+	}
+}
+
+func extractBinOpBound(binop *ssa.BinOp) (bound, int, error) {
+	if binop.X != nil {
+		if x, ok := binop.X.(*ssa.Const); ok {
+			value, err := strconv.Atoi(x.Value.String())
+			if err != nil {
+				return lowerUnbounded, value, err
+			}
+			switch binop.Op {
+			case token.LSS, token.LEQ:
+				return upperUnbounded, value, nil
+			case token.GTR, token.GEQ:
+				return lowerUnbounded, value, nil
+			case token.EQL:
+				return upperBounded, value, nil
+			case token.NEQ:
+				return unbounded, value, nil
+			}
+		}
+	}
+	if binop.Y != nil {
+		if y, ok := binop.Y.(*ssa.Const); ok {
+			value, err := strconv.Atoi(y.Value.String())
+			if err != nil {
+				return lowerUnbounded, value, err
+			}
+			switch binop.Op {
+			case token.LSS, token.LEQ:
+				return lowerUnbounded, value, nil
+			case token.GTR, token.GEQ:
+				return upperUnbounded, value, nil
+			case token.EQL:
+				return upperBounded, value, nil
+			case token.NEQ:
+				return unbounded, value, nil
+			}
+		}
+	}
+	return lowerUnbounded, 0, fmt.Errorf("unable to extract constant from binop")
+}
+
+func isSliceIndexInsideBounds(l, h int, index int) bool {
+	return (l <= index && index < h)
+}
+
+func isSliceInsideBounds(l, h int, cl, ch int) bool {
+	return (l <= cl && h >= ch) && (l <= ch && h >= cl)
+}
+
+func extractSliceBounds(slice *ssa.Slice) (int, int) {
+	var low int
+	if slice.Low != nil {
+		l, err := extractIntValue(slice.Low.String())
+		if err == nil {
+			low = l
+		}
+	}
+	var high int
+	if slice.High != nil {
+		h, err := extractIntValue(slice.High.String())
+		if err == nil {
+			high = h
+		}
+	}
+	return low, high
+}
+
+func extractIntValue(value string) (int, error) {
+	parts := strings.Split(value, ":")
+	if len(parts) != 2 {
+		return 0, fmt.Errorf("invalid value: %s", value)
+	}
+	if parts[1] != "int" {
+		return 0, fmt.Errorf("invalid value: %s", value)
+	}
+	return strconv.Atoi(parts[0])
+}
+
+func extractSliceCapFromAlloc(instr string) (int, error) {
+	re := regexp.MustCompile(`new \[(\d+)\]*`)
+	var sliceCap int
+	matches := re.FindAllStringSubmatch(instr, -1)
+	if matches == nil {
+		return sliceCap, errors.New("no slice cap found")
+	}
+
+	if len(matches) > 0 {
+		m := matches[0]
+		if len(m) > 1 {
+			return strconv.Atoi(m[1])
+		}
+	}
+
+	return 0, errors.New("no slice cap found")
+}
diff --git a/analyzers/util.go b/analyzers/util.go
new file mode 100644
index 0000000000..57cc42bd03
--- /dev/null
+++ b/analyzers/util.go
@@ -0,0 +1,104 @@
+// (c) Copyright gosec's authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package analyzers
+
+import (
+	"fmt"
+	"go/token"
+	"log"
+	"os"
+	"strconv"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/buildssa"
+
+	"github.com/securego/gosec/v2/issue"
+)
+
+// SSAAnalyzerResult contains various information returned by the
+// SSA analysis along with some configuration
+type SSAAnalyzerResult struct {
+	Config map[string]interface{}
+	Logger *log.Logger
+	SSA    *buildssa.SSA
+}
+
+// BuildDefaultAnalyzers returns the default list of analyzers
+func BuildDefaultAnalyzers() []*analysis.Analyzer {
+	return []*analysis.Analyzer{
+		newConversionOverflowAnalyzer("G115", "Type conversion which leads to integer overflow"),
+		newSliceBoundsAnalyzer("G602", "Possible slice bounds out of range"),
+		newHardCodedNonce("G407", "Use of hardcoded IV/nonce for encryption"),
+	}
+}
+
+// getSSAResult retrieves the SSA result from analysis pass
+func getSSAResult(pass *analysis.Pass) (*SSAAnalyzerResult, error) {
+	result, ok := pass.ResultOf[buildssa.Analyzer]
+	if !ok {
+		return nil, fmt.Errorf("no SSA result found in the analysis pass")
+	}
+	ssaResult, ok := result.(*SSAAnalyzerResult)
+	if !ok {
+		return nil, fmt.Errorf("the analysis pass result is not of type SSA")
+	}
+	return ssaResult, nil
+}
+
+// newIssue creates a new gosec issue
+func newIssue(analyzerID string, desc string, fileSet *token.FileSet,
+	pos token.Pos, severity, confidence issue.Score,
+) *issue.Issue {
+	file := fileSet.File(pos)
+	// This can occur when there is a compilation issue into the code.
+	if file == nil {
+		return &issue.Issue{}
+	}
+	line := file.Line(pos)
+	col := file.Position(pos).Column
+
+	return &issue.Issue{
+		RuleID:     analyzerID,
+		File:       file.Name(),
+		Line:       strconv.Itoa(line),
+		Col:        strconv.Itoa(col),
+		Severity:   severity,
+		Confidence: confidence,
+		What:       desc,
+		Cwe:        issue.GetCweByRule(analyzerID),
+		Code:       issueCodeSnippet(fileSet, pos),
+	}
+}
+
+func issueCodeSnippet(fileSet *token.FileSet, pos token.Pos) string {
+	file := fileSet.File(pos)
+
+	start := (int64)(file.Line(pos))
+	if start-issue.SnippetOffset > 0 {
+		start = start - issue.SnippetOffset
+	}
+	end := (int64)(file.Line(pos))
+	end = end + issue.SnippetOffset
+
+	var code string
+	if file, err := os.Open(file.Name()); err == nil {
+		defer file.Close() // #nosec
+		code, err = issue.CodeSnippet(file, start, end)
+		if err != nil {
+			return err.Error()
+		}
+	}
+	return code
+}
diff --git a/autofix/ai.go b/autofix/ai.go
new file mode 100644
index 0000000000..3535f7e2df
--- /dev/null
+++ b/autofix/ai.go
@@ -0,0 +1,142 @@
+package autofix
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"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
+  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
+}
+
+// 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
+}
+
+// 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 {
+	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 {
+			issue.Autofix = val
+			continue
+		}
+
+		prompt := fmt.Sprintf(AIPrompt, issue.What)
+		resp, err := model.GenerateContent(ctx, prompt)
+		if err != nil {
+			return fmt.Errorf("generating autofix with gemini: %w", err)
+		}
+
+		if resp == "" {
+			return errors.New("no autofix returned by gemini")
+		}
+
+		issue.Autofix = resp
+		cachedAutofix[issue.What] = issue.Autofix
+	}
+	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 --git a/autofix/ai_test.go b/autofix/ai_test.go
new file mode 100644
index 0000000000..4fac6bf7b0
--- /dev/null
+++ b/autofix/ai_test.go
@@ -0,0 +1,109 @@
+package autofix
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"github.com/stretchr/testify/require"
+
+	"github.com/securego/gosec/v2/issue"
+)
+
+// MockGenAIClient is a mock of the GenAIClient interface
+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) {
+	args := m.Called(ctx, prompt)
+	return args.String(0), args.Error(1)
+}
+
+func TestGenerateSolutionByGemini_Success(t *testing.T) {
+	// Arrange
+	issues := []*issue.Issue{
+		{What: "Example issue 1"},
+	}
+
+	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()
+
+	// Act
+	err := generateSolutionByGemini(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)
+}
+
+func TestGenerateSolutionByGemini_NoCandidates(t *testing.T) {
+	// Arrange
+	issues := []*issue.Issue{
+		{What: "Example issue 2"},
+	}
+
+	mockClient := new(MockGenAIClient)
+	mockModel := new(MockGenAIGenerativeModel)
+	mockClient.On("GenerativeModel", GeminiModel).Return(mockModel).Once()
+	mockModel.On("GenerateContent", mock.Anything, mock.Anything).Return("", nil).Once()
+
+	// Act
+	err := generateSolutionByGemini(mockClient, issues)
+
+	// Assert
+	require.EqualError(t, err, "no autofix returned by gemini")
+	mock.AssertExpectationsForObjects(t, mockClient, mockModel)
+}
+
+func TestGenerateSolutionByGemini_APIError(t *testing.T) {
+	// Arrange
+	issues := []*issue.Issue{
+		{What: "Example issue 3"},
+	}
+
+	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()
+
+	// Act
+	err := generateSolutionByGemini(mockClient, issues)
+
+	// Assert
+	require.EqualError(t, err, "generating autofix with gemini: API error")
+	mock.AssertExpectationsForObjects(t, mockClient, mockModel)
+}
+
+func TestGenerateSolution_UnsupportedProvider(t *testing.T) {
+	// Arrange
+	issues := []*issue.Issue{
+		{What: "Example issue 4"},
+	}
+
+	// Act
+	err := GenerateSolution("unsupported-provider", "test-api-key", "", issues)
+
+	// Assert
+	require.EqualError(t, err, "ai provider not supported")
+}
diff --git a/call_list.go b/call_list.go
index 4b3fcf0573..4f2d6c54e4 100644
--- a/call_list.go
+++ b/call_list.go
@@ -47,7 +47,7 @@ func (c CallList) Add(selector, ident string) {
 }
 
 // Contains returns true if the package and function are
-/// members of this call list.
+// members of this call list.
 func (c CallList) Contains(selector, ident string) bool {
 	if idents, ok := c[selector]; ok {
 		_, found := idents[ident]
@@ -77,17 +77,26 @@ func (c CallList) ContainsPkgCallExpr(n ast.Node, ctx *Context, stripVendor bool
 		return nil
 	}
 
-	// Use only explicit path (optionally strip vendor path prefix) to reduce conflicts
-	path, ok := GetImportPath(selector, ctx)
-	if !ok {
-		return nil
+	// Selector can have two forms:
+	// 1. A short name if a module function is called (expr.Name).
+	// E.g., "big" if called function from math/big.
+	// 2. A full name if a structure function is called (TypeOf(expr)).
+	// E.g., "math/big.Rat" if called function of Rat structure from math/big.
+	if !strings.ContainsRune(selector, '.') {
+		// Use only explicit path (optionally strip vendor path prefix) to reduce conflicts
+		path, ok := GetImportPath(selector, ctx)
+		if !ok {
+			return nil
+		}
+		selector = path
 	}
+
 	if stripVendor {
-		if vendorIdx := strings.Index(path, vendorPath); vendorIdx >= 0 {
-			path = path[vendorIdx+len(vendorPath):]
+		if vendorIdx := strings.Index(selector, vendorPath); vendorIdx >= 0 {
+			selector = selector[vendorIdx+len(vendorPath):]
 		}
 	}
-	if !c.Contains(path, ident) {
+	if !c.Contains(selector, ident) {
 		return nil
 	}
 
diff --git a/call_list_test.go b/call_list_test.go
index f3668bd294..179b127041 100644
--- a/call_list_test.go
+++ b/call_list_test.go
@@ -3,16 +3,15 @@ package gosec_test
 import (
 	"go/ast"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
 	"github.com/securego/gosec/v2/testutils"
 )
 
 var _ = Describe("Call List", func() {
-	var (
-		calls gosec.CallList
-	)
+	var calls gosec.CallList
 	BeforeEach(func() {
 		calls = gosec.NewCallList()
 	})
@@ -22,7 +21,7 @@ var _ = Describe("Call List", func() {
 	})
 
 	It("should be possible to add a single call", func() {
-		Expect(calls).Should(HaveLen(0))
+		Expect(calls).Should(BeEmpty())
 		calls.Add("foo", "bar")
 		Expect(calls).Should(HaveLen(1))
 
@@ -33,7 +32,7 @@ var _ = Describe("Call List", func() {
 	})
 
 	It("should be possible to add multiple calls at once", func() {
-		Expect(calls).Should(HaveLen(0))
+		Expect(calls).Should(BeEmpty())
 		calls.AddAll("fmt", "Sprint", "Sprintf", "Printf", "Println")
 
 		expected := map[string]bool{
@@ -47,14 +46,14 @@ var _ = Describe("Call List", func() {
 	})
 
 	It("should be possible to add pointer call", func() {
-		Expect(calls).Should(HaveLen(0))
+		Expect(calls).Should(BeEmpty())
 		calls.Add("*bytes.Buffer", "WriteString")
 		actual := calls.ContainsPointer("*bytes.Buffer", "WriteString")
 		Expect(actual).Should(BeTrue())
 	})
 
 	It("should be possible to check pointer call", func() {
-		Expect(calls).Should(HaveLen(0))
+		Expect(calls).Should(BeEmpty())
 		calls.Add("bytes.Buffer", "WriteString")
 		actual := calls.ContainsPointer("*bytes.Buffer", "WriteString")
 		Expect(actual).Should(BeTrue())
@@ -95,6 +94,56 @@ var _ = Describe("Call List", func() {
 		Expect(matched).Should(Equal(1))
 	})
 
+	It("should match a package call expression", func() {
+		// Create file to be scanned
+		pkg := testutils.NewTestPackage()
+		defer pkg.Close()
+		pkg.AddFile("cipher.go", testutils.SampleCodeG405[0].Code[0])
+
+		ctx := pkg.CreateContext("cipher.go")
+
+		// Search for des.NewCipher()
+		calls.Add("crypto/des", "NewCipher")
+
+		// Stub out visitor and count number of matched call expr
+		matched := 0
+		v := testutils.NewMockVisitor()
+		v.Context = ctx
+		v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
+			if _, ok := n.(*ast.CallExpr); ok && calls.ContainsPkgCallExpr(n, ctx, false) != nil {
+				matched++
+			}
+			return true
+		}
+		ast.Walk(v, ctx.Root)
+		Expect(matched).Should(Equal(1))
+	})
+
+	It("should match a package call expression", func() {
+		// Create file to be scanned
+		pkg := testutils.NewTestPackage()
+		defer pkg.Close()
+		pkg.AddFile("md4.go", testutils.SampleCodeG406[0].Code[0])
+
+		ctx := pkg.CreateContext("md4.go")
+
+		// Search for md4.New()
+		calls.Add("golang.org/x/crypto/md4", "New")
+
+		// Stub out visitor and count number of matched call expr
+		matched := 0
+		v := testutils.NewMockVisitor()
+		v.Context = ctx
+		v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
+			if _, ok := n.(*ast.CallExpr); ok && calls.ContainsPkgCallExpr(n, ctx, false) != nil {
+				matched++
+			}
+			return true
+		}
+		ast.Walk(v, ctx.Root)
+		Expect(matched).Should(Equal(1))
+	})
+
 	It("should match a call expression", func() {
 		// Create file to be scanned
 		pkg := testutils.NewTestPackage()
diff --git a/cmd/gosec/main.go b/cmd/gosec/main.go
index e954e5392f..3154fabc46 100644
--- a/cmd/gosec/main.go
+++ b/cmd/gosec/main.go
@@ -17,14 +17,19 @@ package main
 import (
 	"flag"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"log"
 	"os"
+	"runtime"
 	"sort"
 	"strings"
 
 	"github.com/securego/gosec/v2"
-	"github.com/securego/gosec/v2/output"
+	"github.com/securego/gosec/v2/analyzers"
+	"github.com/securego/gosec/v2/autofix"
+	"github.com/securego/gosec/v2/cmd/vflag"
+	"github.com/securego/gosec/v2/issue"
+	"github.com/securego/gosec/v2/report"
 	"github.com/securego/gosec/v2/rules"
 )
 
@@ -55,6 +60,8 @@ USAGE:
 	$ gosec -exclude=G101 $GOPATH/src/github.com/example/project/...
 
 `
+	// Environment variable for AI API key.
+	aiAPIKeyEnv = "GOSEC_AI_API_KEY" // #nosec G101
 )
 
 type arrayFlags []string
@@ -72,12 +79,18 @@ var (
 	// #nosec flag
 	flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
 
+	// show ignored
+	flagShowIgnored = flag.Bool("show-ignored", false, "If enabled, ignored issues are printed")
+
 	// format output
 	flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
 
 	// #nosec alternative tag
 	flagAlternativeNoSec = flag.String("nosec-tag", "", "Set an alternative string for #nosec. Some examples: #dontanalyze, #falsepositive")
 
+	// flagEnableAudit enables audit mode
+	flagEnableAudit = flag.Bool("enable-audit", false, "Enable audit mode")
+
 	// output file
 	flagOutput = flag.String("out", "", "Set output file for results")
 
@@ -91,11 +104,13 @@ var (
 	flagRulesInclude = flag.String("include", "", "Comma separated list of rules IDs to include. (see rule list)")
 
 	// rules to explicitly exclude
-	flagRulesExclude = flag.String("exclude", "", "Comma separated list of rules IDs to exclude. (see rule list)")
+	flagRulesExclude = vflag.ValidatedFlag{}
+
+	// rules to explicitly exclude
+	flagExcludeGenerated = flag.Bool("exclude-generated", false, "Exclude generated files")
 
 	// log to file or stderr
 	flagLogfile = flag.String("log", "", "Log messages to file rather than stderr")
-
 	// sort the issues by severity
 	flagSortIssues = flag.Bool("sort", true, "Sort issues by severity")
 
@@ -108,6 +123,9 @@ var (
 	// fail by confidence
 	flagConfidence = flag.String("confidence", "low", "Filter out the issues with a lower confidence than the given value. Valid options are: low, medium, high")
 
+	// concurrency value
+	flagConcurrency = flag.Int("concurrency", runtime.NumCPU(), "Concurrency value")
+
 	// do not fail
 	flagNoFail = flag.Bool("no-fail", false, "Do not fail the scanning, even if issues were found")
 
@@ -117,7 +135,34 @@ var (
 	// print version and quit with exit code 0
 	flagVersion = flag.Bool("version", false, "Print version and quit with exit code 0")
 
-	// exlude the folders from scan
+	// stdout the results as well as write it in the output file
+	flagStdOut = flag.Bool("stdout", false, "Stdout the results as well as write it in the output file")
+
+	// print the text report with color, this is enabled by default
+	flagColor = flag.Bool("color", true, "Prints the text format report with colorization when it goes in the stdout")
+
+	// append ./... to the target dir.
+	flagRecursive = flag.Bool("r", false, "Appends \"./...\" to the target dir.")
+
+	// overrides the output format when stdout the results while saving them in the output file
+	flagVerbose = flag.String("verbose", "", "Overrides the output format when stdout the results while saving them in the output file.\nValid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
+
+	// output suppression information for auditing purposes
+	flagTrackSuppressions = flag.Bool("track-suppressions", false, "Output suppression information, including its kind and justification")
+
+	// flagTerse shows only the summary of scan discarding all the logs
+	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")
+
+	// 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
 
 	logger *log.Logger
@@ -132,15 +177,24 @@ func usage() {
 	fmt.Fprint(os.Stderr, "\n\nRULES:\n\n")
 
 	// sorted rule list for ease of reading
-	rl := rules.Generate()
-	keys := make([]string, 0, len(rl))
-	for key := range rl {
+	rl := rules.Generate(*flagTrackSuppressions)
+	al := analyzers.Generate(*flagTrackSuppressions)
+	keys := make([]string, 0, len(rl.Rules)+len(al.Analyzers))
+	for key := range rl.Rules {
+		keys = append(keys, key)
+	}
+	for key := range al.Analyzers {
 		keys = append(keys, key)
 	}
 	sort.Strings(keys)
 	for _, k := range keys {
-		v := rl[k]
-		fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, v.Description)
+		var description string
+		if rule, ok := rl.Rules[k]; ok {
+			description = rule.Description
+		} else if analyzer, ok := al.Analyzers[k]; ok {
+			description = analyzer.Description
+		}
+		fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, description)
 	}
 	fmt.Fprint(os.Stderr, "\n")
 }
@@ -161,9 +215,23 @@ func loadConfig(configFile string) (gosec.Config, error) {
 	if *flagIgnoreNoSec {
 		config.SetGlobal(gosec.Nosec, "true")
 	}
+	if *flagShowIgnored {
+		config.SetGlobal(gosec.ShowIgnored, "true")
+	}
 	if *flagAlternativeNoSec != "" {
 		config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
 	}
+	if *flagEnableAudit {
+		config.SetGlobal(gosec.Audit, "true")
+	}
+	// set global option IncludeRules, when flag set or global option IncludeRules  is nil
+	if v, _ := config.GetGlobal(gosec.IncludeRules); *flagRulesInclude != "" || v == "" {
+		config.SetGlobal(gosec.IncludeRules, *flagRulesInclude)
+	}
+	// set global option ExcludeRules, when flag set or global option ExcludeRules  is nil
+	if v, _ := config.GetGlobal(gosec.ExcludeRules); flagRulesExclude.String() != "" || v == "" {
+		config.SetGlobal(gosec.ExcludeRules, flagRulesExclude.String())
+	}
 	return config, nil
 }
 
@@ -184,59 +252,110 @@ func loadRules(include, exclude string) rules.RuleList {
 	} else {
 		logger.Println("Excluding rules: default")
 	}
-	return rules.Generate(filters...)
+	return rules.Generate(*flagTrackSuppressions, filters...)
+}
+
+func loadAnalyzers(include, exclude string) *analyzers.AnalyzerList {
+	var filters []analyzers.AnalyzerFilter
+	if include != "" {
+		logger.Printf("Including analyzers: %s", include)
+		including := strings.Split(include, ",")
+		filters = append(filters, analyzers.NewAnalyzerFilter(false, including...))
+	} else {
+		logger.Println("Including analyzers: default")
+	}
+
+	if exclude != "" {
+		logger.Printf("Excluding analyzers: %s", exclude)
+		excluding := strings.Split(exclude, ",")
+		filters = append(filters, analyzers.NewAnalyzerFilter(true, excluding...))
+	} else {
+		logger.Println("Excluding analyzers: default")
+	}
+	return analyzers.Generate(*flagTrackSuppressions, filters...)
 }
 
-func saveOutput(filename, format string, color bool, paths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
-	rootPaths := []string{}
+func getRootPaths(paths []string) []string {
+	rootPaths := make([]string, 0)
 	for _, path := range paths {
 		rootPath, err := gosec.RootPath(path)
 		if err != nil {
-			return fmt.Errorf("failed to get the root path of the projects: %s", err)
+			logger.Fatal(fmt.Errorf("failed to get the root path of the projects: %w", err))
 		}
 		rootPaths = append(rootPaths, rootPath)
 	}
-	if filename != "" {
-		outfile, err := os.Create(filename)
-		if err != nil {
-			return err
-		}
-		defer outfile.Close() // #nosec G307
-		err = output.CreateReport(outfile, format, color, rootPaths, issues, metrics, errors)
-		if err != nil {
-			return err
-		}
-	} else {
-		err := output.CreateReport(os.Stdout, format, color, rootPaths, issues, metrics, errors)
-		if err != nil {
-			return err
-		}
+	return rootPaths
+}
+
+// If verbose is defined it overwrites the defined format
+// Otherwise the actual format is used
+func getPrintedFormat(format string, verbose string) string {
+	if verbose != "" {
+		return verbose
+	}
+	return format
+}
+
+func printReport(format string, color bool, rootPaths []string, reportInfo *gosec.ReportInfo) error {
+	err := report.CreateReport(os.Stdout, format, color, rootPaths, reportInfo)
+	if err != nil {
+		return err
 	}
 	return nil
 }
 
-func convertToScore(severity string) (gosec.Score, error) {
-	severity = strings.ToLower(severity)
-	switch severity {
+func saveReport(filename, format string, rootPaths []string, reportInfo *gosec.ReportInfo) error {
+	outfile, err := os.Create(filename) // #nosec G304
+	if err != nil {
+		return err
+	}
+	defer outfile.Close() // #nosec G307
+	err = report.CreateReport(outfile, format, false, rootPaths, reportInfo)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func convertToScore(value string) (issue.Score, error) {
+	value = strings.ToLower(value)
+	switch value {
 	case "low":
-		return gosec.Low, nil
+		return issue.Low, nil
 	case "medium":
-		return gosec.Medium, nil
+		return issue.Medium, nil
 	case "high":
-		return gosec.High, nil
+		return issue.High, nil
 	default:
-		return gosec.Low, fmt.Errorf("provided severity '%s' not valid. Valid options: low, medium, high", severity)
+		return issue.Low, fmt.Errorf("provided value '%s' not valid. Valid options: low, medium, high", value)
 	}
 }
 
-func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) []*gosec.Issue {
-	result := []*gosec.Issue{}
+func filterIssues(issues []*issue.Issue, severity issue.Score, confidence issue.Score) ([]*issue.Issue, int) {
+	result := make([]*issue.Issue, 0)
+	trueIssues := 0
 	for _, issue := range issues {
 		if issue.Severity >= severity && issue.Confidence >= confidence {
 			result = append(result, issue)
+			if (!issue.NoSec || !*flagShowIgnored) && len(issue.Suppressions) == 0 {
+				trueIssues++
+			}
 		}
 	}
-	return result
+	return result, trueIssues
+}
+
+func exit(issues []*issue.Issue, errors map[string][]gosec.Error, noFail bool) {
+	nsi := 0
+	for _, issue := range issues {
+		if len(issue.Suppressions) == 0 {
+			nsi++
+		}
+	}
+	if (nsi > 0 || len(errors) > 0) && !noFail {
+		os.Exit(1)
+	}
+	os.Exit(0)
 }
 
 func main() {
@@ -252,11 +371,14 @@ func main() {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", "vendor")
 	}
-	err = flag.Set("exclude-dir", ".git")
+	err = flag.Set("exclude-dir", "\\.git/")
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", ".git")
+		fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", "\\.git/")
 	}
 
+	// set for exclude
+	flag.Var(&flagRulesExclude, "exclude", "Comma separated list of rules IDs to exclude. (see rule list)")
+
 	// Parse command line arguments
 	flag.Parse()
 
@@ -265,9 +387,9 @@ func main() {
 		os.Exit(0)
 	}
 
-	// Ensure at least one file was specified
-	if flag.NArg() == 0 {
-		fmt.Fprintf(os.Stderr, "\nError: FILE [FILE...] or './...' expected\n") // #nosec
+	// Ensure at least one file was specified or that the recursive -r flag was set.
+	if flag.NArg() == 0 && !*flagRecursive {
+		fmt.Fprintf(os.Stderr, "\nError: FILE [FILE...] or './...' or -r expected\n") // #nosec
 		flag.Usage()
 		os.Exit(1)
 	}
@@ -283,18 +405,12 @@ func main() {
 		}
 	}
 
-	if *flagQuiet {
-		logger = log.New(ioutil.Discard, "", 0)
+	if *flagQuiet || *flagTerse {
+		logger = log.New(io.Discard, "", 0)
 	} else {
 		logger = log.New(logWriter, "[gosec] ", log.LstdFlags)
 	}
 
-	// Color flag is allowed for text format
-	var color bool
-	if *flagFormat == "text" {
-		color = true
-	}
-
 	failSeverity, err := convertToScore(*flagSeverity)
 	if err != nil {
 		logger.Fatalf("Invalid severity value: %v", err)
@@ -312,24 +428,43 @@ func main() {
 	}
 
 	// Load enabled rule definitions
-	ruleDefinitions := loadRules(*flagRulesInclude, *flagRulesExclude)
-	if len(ruleDefinitions) == 0 {
-		logger.Fatal("No rules are configured")
+	excludeRules, err := config.GetGlobal(gosec.ExcludeRules)
+	if err != nil {
+		logger.Fatal(err)
+	}
+	includeRules, err := config.GetGlobal(gosec.IncludeRules)
+	if err != nil {
+		logger.Fatal(err)
+	}
+
+	ruleList := loadRules(includeRules, excludeRules)
+
+	analyzerList := loadAnalyzers(includeRules, excludeRules)
+
+	if len(ruleList.Rules) == 0 && len(analyzerList.Analyzers) == 0 {
+		logger.Fatal("No rules/analyzers are configured")
 	}
 
 	// Create the analyzer
-	analyzer := gosec.NewAnalyzer(config, *flagScanTests, logger)
-	analyzer.LoadRules(ruleDefinitions.Builders())
+	analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, *flagTrackSuppressions, *flagConcurrency, logger)
+	analyzer.LoadRules(ruleList.RulesInfo())
+	analyzer.LoadAnalyzers(analyzerList.AnalyzersInfo())
 
 	excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude)
 	var packages []string
-	for _, path := range flag.Args() {
+
+	paths := flag.Args()
+	if len(paths) == 0 {
+		paths = append(paths, "./...")
+	}
+	for _, path := range paths {
 		pcks, err := gosec.PackagePaths(path, excludedDirs)
 		if err != nil {
 			logger.Fatal(err)
 		}
 		packages = append(packages, pcks...)
 	}
+
 	if len(packages) == 0 {
 		logger.Fatal("No packages found")
 	}
@@ -352,9 +487,10 @@ func main() {
 	}
 
 	// Filter the issues by severity and confidence
-	issues = filterIssues(issues, failSeverity, failConfidence)
-	if metrics.NumFound != len(issues) {
-		metrics.NumFound = len(issues)
+	var trueIssues int
+	issues, trueIssues = filterIssues(issues, failSeverity, failConfidence)
+	if metrics.NumFound != trueIssues {
+		metrics.NumFound = trueIssues
 	}
 
 	// Exit quietly if nothing was found
@@ -363,15 +499,36 @@ func main() {
 	}
 
 	// Create output report
-	if err := saveOutput(*flagOutput, *flagFormat, color, flag.Args(), issues, metrics, errors); err != nil {
-		logger.Fatal(err)
+	rootPaths := getRootPaths(flag.Args())
+
+	reportInfo := gosec.NewReportInfo(issues, metrics, errors).WithVersion(Version)
+
+	// Call AI request to solve the issues
+	aiAPIKey := os.Getenv(aiAPIKeyEnv)
+	if aiAPIKey == "" {
+		aiAPIKey = *flagAiAPIKey
+	}
+	if *flagAiAPIProvider != "" && aiAPIKey != "" {
+		err := autofix.GenerateSolution(*flagAiAPIProvider, aiAPIKey, *flagAiEndpoint, issues)
+		if err != nil {
+			logger.Print(err)
+		}
+	}
+
+	if *flagOutput == "" || *flagStdOut {
+		fileFormat := getPrintedFormat(*flagFormat, *flagVerbose)
+		if err := printReport(fileFormat, *flagColor, rootPaths, reportInfo); err != nil {
+			logger.Fatal(err)
+		}
+	}
+	if *flagOutput != "" {
+		if err := saveReport(*flagOutput, *flagFormat, rootPaths, reportInfo); err != nil {
+			logger.Fatal(err)
+		}
 	}
 
 	// Finalize logging
 	logWriter.Close() // #nosec
 
-	// Do we have an issue? If so exit 1 unless NoFail is set
-	if (len(issues) > 0 || len(errors) > 0) && !*flagNoFail {
-		os.Exit(1)
-	}
+	exit(issues, errors, *flagNoFail)
 }
diff --git a/cmd/gosec/sort_issues.go b/cmd/gosec/sort_issues.go
index ef27425b93..c155cbf697 100644
--- a/cmd/gosec/sort_issues.go
+++ b/cmd/gosec/sort_issues.go
@@ -1,40 +1,28 @@
 package main
 
 import (
-	"sort"
+	"cmp"
+	"slices"
 	"strconv"
 	"strings"
 
-	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 // handle ranges
 func extractLineNumber(s string) int {
 	lineNumber, _ := strconv.Atoi(strings.Split(s, "-")[0])
 	return lineNumber
-
-}
-
-type sortBySeverity []*gosec.Issue
-
-func (s sortBySeverity) Len() int { return len(s) }
-
-func (s sortBySeverity) Less(i, j int) bool {
-	if s[i].Severity == s[j].Severity {
-		if s[i].What == s[j].What {
-			if s[i].File == s[j].File {
-				return extractLineNumber(s[i].Line) > extractLineNumber(s[j].Line)
-			}
-			return s[i].File > s[j].File
-		}
-		return s[i].What > s[j].What
-	}
-	return s[i].Severity > s[j].Severity
 }
 
-func (s sortBySeverity) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-
 // sortIssues sorts the issues by severity in descending order
-func sortIssues(issues []*gosec.Issue) {
-	sort.Sort(sortBySeverity(issues))
+func sortIssues(issues []*issue.Issue) {
+	slices.SortFunc(issues, func(i, j *issue.Issue) int {
+		return -cmp.Or(
+			cmp.Compare(i.Severity, j.Severity),
+			cmp.Compare(i.What, j.What),
+			cmp.Compare(i.File, j.File),
+			cmp.Compare(extractLineNumber(i.Line), extractLineNumber(j.Line)),
+		)
+	})
 }
diff --git a/cmd/gosec/sort_issues_test.go b/cmd/gosec/sort_issues_test.go
index 2b06689743..c7880b2f3b 100644
--- a/cmd/gosec/sort_issues_test.go
+++ b/cmd/gosec/sort_issues_test.go
@@ -3,24 +3,25 @@ package main
 import (
 	"testing"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	"github.com/securego/gosec/v2"
+
+	"github.com/securego/gosec/v2/issue"
 )
 
-var defaultIssue = gosec.Issue{
+var defaultIssue = issue.Issue{
 	File:       "/home/src/project/test.go",
 	Line:       "1",
 	Col:        "1",
 	RuleID:     "ruleID",
 	What:       "test",
-	Confidence: gosec.High,
-	Severity:   gosec.High,
+	Confidence: issue.High,
+	Severity:   issue.High,
 	Code:       "1: testcode",
-	Cwe:        gosec.GetCwe("G101"),
+	Cwe:        issue.GetCweByRule("G101"),
 }
 
-func createIssue() gosec.Issue {
+func createIssue() issue.Issue {
 	return defaultIssue
 }
 
@@ -29,8 +30,8 @@ func TestRules(t *testing.T) {
 	RunSpecs(t, "Sort issues Suite")
 }
 
-func firstIsGreater(less, greater *gosec.Issue) {
-	slice := []*gosec.Issue{less, greater}
+func firstIsGreater(less, greater *issue.Issue) {
+	slice := []*issue.Issue{less, greater}
 
 	sortIssues(slice)
 
@@ -38,16 +39,16 @@ func firstIsGreater(less, greater *gosec.Issue) {
 }
 
 var _ = Describe("Sorting by Severity", func() {
-	It("sortes by severity", func() {
+	It("sorts by severity", func() {
 		less := createIssue()
-		less.Severity = gosec.Low
+		less.Severity = issue.Low
 		greater := createIssue()
-		less.Severity = gosec.High
+		less.Severity = issue.High
 		firstIsGreater(&less, &greater)
 	})
 
-	Context("Serverity is same", func() {
-		It("sortes by What", func() {
+	Context("Severity is same", func() {
+		It("sorts by What", func() {
 			less := createIssue()
 			less.What = "test1"
 			greater := createIssue()
@@ -56,8 +57,8 @@ var _ = Describe("Sorting by Severity", func() {
 		})
 	})
 
-	Context("Serverity and What is same", func() {
-		It("sortes by File", func() {
+	Context("Severity and What is same", func() {
+		It("sorts by File", func() {
 			less := createIssue()
 			less.File = "test1"
 			greater := createIssue()
@@ -67,8 +68,8 @@ var _ = Describe("Sorting by Severity", func() {
 		})
 	})
 
-	Context("Serverity, What and File is same", func() {
-		It("sortes by line number", func() {
+	Context("Severity, What and File is same", func() {
+		It("sorts by line number", func() {
 			less := createIssue()
 			less.Line = "1"
 			greater := createIssue()
diff --git a/cmd/gosecutil/tools.go b/cmd/gosecutil/tools.go
index 23146c116b..9846ea28e0 100644
--- a/cmd/gosecutil/tools.go
+++ b/cmd/gosecutil/tools.go
@@ -26,11 +26,13 @@ import (
 	"strings"
 )
 
-type command func(args ...string)
-type utilities struct {
-	commands map[string]command
-	call     []string
-}
+type (
+	command   func(args ...string)
+	utilities struct {
+		commands map[string]command
+		call     []string
+	}
+)
 
 // Custom commands / utilities to run instead of default analyzer
 func newUtils() *utilities {
@@ -58,7 +60,6 @@ func (u *utilities) String() string {
 func (u *utilities) Set(opt string) error {
 	if _, ok := u.commands[opt]; !ok {
 		return fmt.Errorf("valid tools are: %s", u.String())
-
 	}
 	u.call = append(u.call, opt)
 	return nil
@@ -75,12 +76,12 @@ func (u *utilities) run(args ...string) {
 func shouldSkip(path string) bool {
 	st, e := os.Stat(path)
 	if e != nil {
-		// #nosec
+		//#nosec
 		fmt.Fprintf(os.Stderr, "Skipping: %s - %s\n", path, e)
 		return true
 	}
 	if st.IsDir() {
-		// #nosec
+		//#nosec
 		fmt.Fprintf(os.Stderr, "Skipping: %s - directory\n", path)
 		return true
 	}
@@ -98,12 +99,12 @@ func dumpAst(files ...string) {
 		fset := token.NewFileSet() // positions are relative to fset
 		f, err := parser.ParseFile(fset, arg, nil, 0)
 		if err != nil {
-			// #nosec
+			//#nosec
 			fmt.Fprintf(os.Stderr, "Unable to parse file %s\n", err)
 			continue
 		}
 
-		// Print the AST. #nosec
+		//#nosec -- Print the AST.
 		ast.Print(fset, f)
 	}
 }
@@ -121,7 +122,7 @@ func createContext(filename string) *context {
 	fileset := token.NewFileSet()
 	root, e := parser.ParseFile(fileset, filename, nil, parser.ParseComments)
 	if e != nil {
-		// #nosec
+		//#nosec
 		fmt.Fprintf(os.Stderr, "Unable to parse file: %s. Reason: %s\n", filename, e)
 		return nil
 	}
@@ -137,7 +138,7 @@ func createContext(filename string) *context {
 	config := types.Config{Importer: importer.Default()}
 	pkg, e := config.Check("main.go", fileset, []*ast.File{root}, info)
 	if e != nil {
-		// #nosec
+		//#nosec
 		fmt.Fprintf(os.Stderr, "Type check failed for file: %s. Reason: %s\n", filename, e)
 		return nil
 	}
@@ -162,7 +163,7 @@ func printObject(obj types.Object) {
 }
 
 func checkContext(ctx *context, file string) bool {
-	// #nosec
+	//#nosec
 	if ctx == nil {
 		fmt.Fprintln(os.Stderr, "Failed to create context for file: ", file)
 		return false
@@ -171,7 +172,6 @@ func checkContext(ctx *context, file string) bool {
 }
 
 func dumpCallObj(files ...string) {
-
 	for _, file := range files {
 		if shouldSkip(file) {
 			continue
@@ -184,9 +184,9 @@ func dumpCallObj(files ...string) {
 			var obj types.Object
 			switch node := n.(type) {
 			case *ast.Ident:
-				obj = context.info.ObjectOf(node) //context.info.Uses[node]
+				obj = context.info.ObjectOf(node) // context.info.Uses[node]
 			case *ast.SelectorExpr:
-				obj = context.info.ObjectOf(node.Sel) //context.info.Uses[node.Sel]
+				obj = context.info.ObjectOf(node.Sel) // context.info.Uses[node.Sel]
 			default:
 				obj = nil
 			}
diff --git a/cmd/tlsconfig/header_template.go b/cmd/tlsconfig/header_template.go
index c5ca67ef68..5e12498f1a 100644
--- a/cmd/tlsconfig/header_template.go
+++ b/cmd/tlsconfig/header_template.go
@@ -9,5 +9,6 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 `))
diff --git a/cmd/tlsconfig/rule_template.go b/cmd/tlsconfig/rule_template.go
index 952be29232..eee263d0e5 100644
--- a/cmd/tlsconfig/rule_template.go
+++ b/cmd/tlsconfig/rule_template.go
@@ -7,7 +7,7 @@ var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
 // DO NOT EDIT - generated by tlsconfig tool
 func New{{.Name}}TLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 	return &insecureConfigTLS{
-                MetaData: gosec.MetaData{ID: id},
+                MetaData: issue.MetaData{ID: id},
 		requiredType: "crypto/tls.Config",
 		MinVersion:   {{ .MinVersion }},
 		MaxVersion:   {{ .MaxVersion }},
@@ -15,6 +15,6 @@ func New{{.Name}}TLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node)
 {{range $cipherName := .Ciphers }} "{{$cipherName}}",
 {{end}}
 		},
-	}, []ast.Node{(*ast.CompositeLit)(nil)}
+	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
 }
 `))
diff --git a/cmd/tlsconfig/tls_version_go14.go b/cmd/tlsconfig/tls_version.go
similarity index 93%
rename from cmd/tlsconfig/tls_version_go14.go
rename to cmd/tlsconfig/tls_version.go
index a5c907c607..9c238c6a9a 100644
--- a/cmd/tlsconfig/tls_version_go14.go
+++ b/cmd/tlsconfig/tls_version.go
@@ -1,6 +1,3 @@
-// +build go1.14 !go1.11
-
-// main
 package main
 
 import (
diff --git a/cmd/tlsconfig/tls_version_go12_go13.go b/cmd/tlsconfig/tls_version_go12_go13.go
deleted file mode 100644
index ae29178479..0000000000
--- a/cmd/tlsconfig/tls_version_go12_go13.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// +build go1.12,!go1.14
-
-// This file can be removed once go1.13 is no longer supported
-
-package main
-
-import (
-	"crypto/tls"
-	"sort"
-)
-
-func mapTLSVersions(tlsVersions []string) []int {
-	var versions []int
-	for _, tlsVersion := range tlsVersions {
-		switch tlsVersion {
-		case "TLSv1.3":
-			versions = append(versions, tls.VersionTLS13)
-		case "TLSv1.2":
-			versions = append(versions, tls.VersionTLS12)
-		case "TLSv1.1":
-			versions = append(versions, tls.VersionTLS11)
-		case "TLSv1":
-			versions = append(versions, tls.VersionTLS10)
-		case "SSLv3":
-			// unsupported from go1.14
-			versions = append(versions, tls.VersionSSL30)
-		default:
-			continue
-		}
-	}
-	sort.Ints(versions)
-	return versions
-}
diff --git a/cmd/tlsconfig/tlsconfig.go b/cmd/tlsconfig/tlsconfig.go
index ece83ec4c2..16c7189a07 100644
--- a/cmd/tlsconfig/tlsconfig.go
+++ b/cmd/tlsconfig/tlsconfig.go
@@ -1,5 +1,3 @@
-// +build go1.12
-
 package main
 
 import (
@@ -9,13 +7,14 @@ import (
 	"flag"
 	"fmt"
 	"go/format"
-	"io/ioutil"
 	"log"
 	"net/http"
+	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/mozilla/tls-observatory/constants"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 )
 
 var (
@@ -47,8 +46,8 @@ type Configuration struct {
 	ECDHParamSize         float64  `json:"ecdh_param_size"`
 	HstsMinAge            float64  `json:"hsts_min_age"`
 	OldestClients         []string `json:"oldest_clients"`
-	OCSPStample           bool     `json:"ocsp_staple"`
-	ServerPreferedOrder   bool     `json:"server_preferred_order"`
+	OCSPStaple            bool     `json:"ocsp_staple"`
+	ServerPreferredOrder  bool     `json:"server_preferred_order"`
 	MaxCertLifespan       float64  `json:"maximum_certificate_lifespan"`
 }
 
@@ -65,11 +64,11 @@ type goTLSConfiguration struct {
 
 // getTLSConfFromURL retrieves the json containing the TLS configurations from the specified URL.
 func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
-	r, err := http.Get(url) // #nosec G107
+	r, err := http.Get(url) //#nosec G107
 	if err != nil {
 		return nil, err
 	}
-	defer r.Body.Close()
+	defer r.Body.Close() //#nosec G307
 
 	var sstls ServerSideTLSJson
 	err = json.NewDecoder(r.Body).Decode(&sstls)
@@ -81,7 +80,8 @@ func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
 }
 
 func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfiguration, error) {
-	cipherConf := goCipherConfiguration{Name: strings.Title(name)}
+	caser := cases.Title(language.English)
+	cipherConf := goCipherConfiguration{Name: caser.String(name)}
 	conf, ok := sstls.Configurations[name]
 	if !ok {
 		return cipherConf, fmt.Errorf("TLS configuration '%s' not found", name)
@@ -108,7 +108,7 @@ func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfigurat
 		cipherConf.MinVersion = fmt.Sprintf("0x%04x", versions[0])
 		cipherConf.MaxVersion = fmt.Sprintf("0x%04x", versions[len(versions)-1])
 	} else {
-		return cipherConf, fmt.Errorf("No TLS versions found for configuration '%s'", name)
+		return cipherConf, fmt.Errorf("no TLS versions found for configuration '%s'", name)
 	}
 	return cipherConf, nil
 }
@@ -121,27 +121,27 @@ func getGoTLSConf() (goTLSConfiguration, error) {
 		panic(msg)
 	}
 
-	tlsConfg := goTLSConfiguration{}
+	tlsConfig := goTLSConfiguration{}
 
 	modern, err := getGoCipherConfig("modern", *sstls)
 	if err != nil {
-		return tlsConfg, err
+		return tlsConfig, err
 	}
-	tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, modern)
+	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, modern)
 
 	intermediate, err := getGoCipherConfig("intermediate", *sstls)
 	if err != nil {
-		return tlsConfg, err
+		return tlsConfig, err
 	}
-	tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, intermediate)
+	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, intermediate)
 
 	old, err := getGoCipherConfig("old", *sstls)
 	if err != nil {
-		return tlsConfg, err
+		return tlsConfig, err
 	}
-	tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, old)
+	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, old)
 
-	return tlsConfg, nil
+	return tlsConfig, nil
 }
 
 func getCurrentDir() (string, error) {
@@ -187,7 +187,7 @@ func main() {
 	}
 
 	outputPath := filepath.Join(dir, *outputFile)
-	if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
+	if err := os.WriteFile(outputPath, src, 0o644); err != nil /*#nosec G306*/ {
 		log.Fatalf("Writing output: %s", err)
-	} // #nosec G306
+	}
 }
diff --git a/cmd/vflag/flag.go b/cmd/vflag/flag.go
new file mode 100644
index 0000000000..6830234190
--- /dev/null
+++ b/cmd/vflag/flag.go
@@ -0,0 +1,25 @@
+package vflag
+
+import (
+	"errors"
+	"strings"
+)
+
+// ValidatedFlag cli string type
+type ValidatedFlag struct {
+	Value string
+}
+
+func (f *ValidatedFlag) String() string {
+	return f.Value
+}
+
+// Set will be called for flag that is of validateFlag type
+func (f *ValidatedFlag) Set(value string) error {
+	if strings.Contains(value, "-") {
+		return errors.New("flag value cannot start with -")
+	}
+
+	f.Value = value
+	return nil
+}
diff --git a/config.go b/config.go
index 5b7f739368..fc355d8ff3 100644
--- a/config.go
+++ b/config.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 )
 
 const (
@@ -20,12 +19,25 @@ type GlobalOption string
 const (
 	// Nosec global option for #nosec directive
 	Nosec GlobalOption = "nosec"
+	// ShowIgnored defines whether nosec issues are counted as finding or not
+	ShowIgnored GlobalOption = "show-ignored"
 	// Audit global option which indicates that gosec runs in audit mode
 	Audit GlobalOption = "audit"
 	// NoSecAlternative global option alternative for #nosec directive
 	NoSecAlternative GlobalOption = "#nosec"
+	// ExcludeRules global option for some rules  should not be load
+	ExcludeRules GlobalOption = "exclude"
+	// IncludeRules global option for  should be load
+	IncludeRules GlobalOption = "include"
+	// SSA global option to enable go analysis framework with SSA support
+	SSA GlobalOption = "ssa"
 )
 
+// NoSecTag returns the tag used to disable gosec for a line of code.
+func NoSecTag(tag string) string {
+	return fmt.Sprintf("%s%s", "#", tag)
+}
+
 // Config is used to provide configuration and customization to each of the rules.
 type Config map[string]interface{}
 
@@ -56,9 +68,9 @@ func (c Config) convertGlobals() {
 
 // ReadFrom implements the io.ReaderFrom interface. This
 // should be used with io.Reader to load configuration from
-//file or from string etc.
+// file or from string etc.
 func (c Config) ReadFrom(r io.Reader) (int64, error) {
-	data, err := ioutil.ReadAll(r)
+	data, err := io.ReadAll(r)
 	if err != nil {
 		return int64(len(data)), err
 	}
@@ -83,7 +95,7 @@ func (c Config) WriteTo(w io.Writer) (int64, error) {
 func (c Config) Get(section string) (interface{}, error) {
 	settings, found := c[section]
 	if !found {
-		return nil, fmt.Errorf("Section %s not in configuration", section)
+		return nil, fmt.Errorf("section %s not in configuration", section)
 	}
 	return settings, nil
 }
diff --git a/config_test.go b/config_test.go
index 51f8e4e831..3b0b00367a 100644
--- a/config_test.go
+++ b/config_test.go
@@ -4,8 +4,9 @@ import (
 	"bytes"
 	"strings"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
 )
 
@@ -16,7 +17,6 @@ var _ = Describe("Configuration", func() {
 	})
 
 	Context("when loading from disk", func() {
-
 		It("should be possible to load configuration from a file", func() {
 			json := `{"G101": {}}`
 			buffer := bytes.NewBufferString(json)
@@ -35,7 +35,6 @@ var _ = Describe("Configuration", func() {
 			_, err = configuration.ReadFrom(emptyBuffer)
 			Expect(err).Should(HaveOccurred())
 		})
-
 	})
 
 	Context("when saving to disk", func() {
@@ -49,7 +48,6 @@ var _ = Describe("Configuration", func() {
 		})
 
 		It("should be possible to save configuration to file", func() {
-
 			configuration.Set("G101", map[string]string{
 				"mode": "strict",
 			})
@@ -59,12 +57,10 @@ var _ = Describe("Configuration", func() {
 			Expect(int(nbytes)).ShouldNot(BeZero())
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(buffer.String()).Should(Equal(`{"G101":{"mode":"strict"},"global":{}}`))
-
 		})
 	})
 
 	Context("when configuring rules", func() {
-
 		It("should be possible to get configuration for a rule", func() {
 			settings := map[string]string{
 				"ciphers": "AES256-GCM",
@@ -81,7 +77,7 @@ var _ = Describe("Configuration", func() {
 	Context("when using global configuration options", func() {
 		It("should have a default global section", func() {
 			settings, err := configuration.Get("global")
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			expectedType := make(map[gosec.GlobalOption]string)
 			Expect(settings).Should(BeAssignableToTypeOf(expectedType))
 		})
@@ -89,7 +85,7 @@ var _ = Describe("Configuration", func() {
 		It("should save global settings to correct section", func() {
 			configuration.SetGlobal(gosec.Nosec, "enabled")
 			settings, err := configuration.Get("global")
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			if globals, ok := settings.(map[gosec.GlobalOption]string); ok {
 				Expect(globals["nosec"]).Should(MatchRegexp("enabled"))
 			} else {
@@ -97,14 +93,14 @@ var _ = Describe("Configuration", func() {
 			}
 
 			setValue, err := configuration.GetGlobal(gosec.Nosec)
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			Expect(setValue).Should(MatchRegexp("enabled"))
 		})
 
 		It("should find global settings which are enabled", func() {
 			configuration.SetGlobal(gosec.Nosec, "enabled")
 			enabled, err := configuration.IsGlobalEnabled(gosec.Nosec)
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			Expect(enabled).Should(BeTrue())
 		})
 
@@ -117,10 +113,10 @@ var _ = Describe("Configuration", func() {
 			}`
 			cfg := gosec.NewConfig()
 			_, err := cfg.ReadFrom(strings.NewReader(config))
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 
 			value, err := cfg.GetGlobal(gosec.Nosec)
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			Expect(value).Should(Equal("enabled"))
 		})
 		It("should parse the global settings of other types from file", func() {
@@ -132,10 +128,10 @@ var _ = Describe("Configuration", func() {
 			}`
 			cfg := gosec.NewConfig()
 			_, err := cfg.ReadFrom(strings.NewReader(config))
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 
 			value, err := cfg.GetGlobal(gosec.Nosec)
-			Expect(err).Should(BeNil())
+			Expect(err).ShouldNot(HaveOccurred())
 			Expect(value).Should(Equal("true"))
 		})
 	})
diff --git a/cosign.pub b/cosign.pub
new file mode 100644
index 0000000000..c6fd55988b
--- /dev/null
+++ b/cosign.pub
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFphl7f2VuFRfsi4wqiLUCQ9xHQgV
+O2VMDNcvh+kxiymLXa+GkPzSKExFYIlVwfg13URvCiB+kFvITmLzuLiGQg==
+-----END PUBLIC KEY-----
diff --git a/cwe/cwe_suite_test.go b/cwe/cwe_suite_test.go
new file mode 100644
index 0000000000..df9047829f
--- /dev/null
+++ b/cwe/cwe_suite_test.go
@@ -0,0 +1,13 @@
+package cwe_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestCwe(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Cwe Suite")
+}
diff --git a/cwe/data.go b/cwe/data.go
new file mode 100644
index 0000000000..a9568ba4d5
--- /dev/null
+++ b/cwe/data.go
@@ -0,0 +1,150 @@
+package cwe
+
+const (
+	// Acronym is the acronym of CWE
+	Acronym = "CWE"
+	// Version the CWE version
+	Version = "4.4"
+	// ReleaseDateUtc the release Date of CWE Version
+	ReleaseDateUtc = "2021-03-15"
+	// Organization MITRE
+	Organization = "MITRE"
+	// Description the description of CWE
+	Description = "The MITRE Common Weakness Enumeration"
+	// InformationURI link to the published CWE PDF
+	InformationURI = "https://cwe.mitre.org/data/published/cwe_v" + Version + ".pdf/"
+	// DownloadURI link to the zipped XML of the CWE list
+	DownloadURI = "https://cwe.mitre.org/data/xml/cwec_v" + Version + ".xml.zip"
+)
+
+var idWeaknesses = map[string]*Weakness{
+	"22": {
+		ID:          "22",
+		Description: "The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.",
+		Name:        "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')",
+	},
+	"78": {
+		ID:          "78",
+		Description: "The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.",
+		Name:        "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')",
+	},
+	"79": {
+		ID:          "79",
+		Description: "The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.",
+		Name:        "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')",
+	},
+	"88": {
+		ID:          "88",
+		Description: "The software constructs a string for a command to executed by a separate component\nin another control sphere, but it does not properly delimit the\nintended arguments, options, or switches within that command string.",
+		Name:        "Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')",
+	},
+	"89": {
+		ID:          "89",
+		Description: "The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.",
+		Name:        "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')",
+	},
+	"118": {
+		ID:          "118",
+		Description: "The software does not restrict or incorrectly restricts operations within the boundaries of a resource that is accessed using an index or pointer, such as memory or files.",
+		Name:        "Incorrect Access of Indexable Resource ('Range Error')",
+	},
+	"190": {
+		ID:          "190",
+		Description: "The software performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.",
+		Name:        "Integer Overflow or Wraparound",
+	},
+	"200": {
+		ID:          "200",
+		Description: "The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.",
+		Name:        "Exposure of Sensitive Information to an Unauthorized Actor",
+	},
+	"242": {
+		ID:          "242",
+		Description: "The program calls a function that can never be guaranteed to work safely.",
+		Name:        "Use of Inherently Dangerous Function",
+	},
+	"276": {
+		ID:          "276",
+		Description: "During installation, installed file permissions are set to allow anyone to modify those files.",
+		Name:        "Incorrect Default Permissions",
+	},
+	"295": {
+		ID:          "295",
+		Description: "The software does not validate, or incorrectly validates, a certificate.",
+		Name:        "Improper Certificate Validation",
+	},
+	"310": {
+		ID:          "310",
+		Description: "Weaknesses in this category are related to the design and implementation of data confidentiality and integrity. Frequently these deal with the use of encoding techniques, encryption libraries, and hashing algorithms. The weaknesses in this category could lead to a degradation of the quality data if they are not addressed.",
+		Name:        "Cryptographic Issues",
+	},
+	"322": {
+		ID:          "322",
+		Description: "The software performs a key exchange with an actor without verifying the identity of that actor.",
+		Name:        "Key Exchange without Entity Authentication",
+	},
+	"326": {
+		ID:          "326",
+		Description: "The software stores or transmits sensitive data using an encryption scheme that is theoretically sound, but is not strong enough for the level of protection required.",
+		Name:        "Inadequate Encryption Strength",
+	},
+	"327": {
+		ID:          "327",
+		Description: "The use of a broken or risky cryptographic algorithm is an unnecessary risk that may result in the exposure of sensitive information.",
+		Name:        "Use of a Broken or Risky Cryptographic Algorithm",
+	},
+	"328": {
+		ID:          "328",
+		Description: "The product uses an algorithm that produces a digest (output value) that does not meet security expectations for a hash function that allows an adversary to reasonably determine the original input (preimage attack), find another input that can produce the same hash (2nd preimage attack), or find multiple inputs that evaluate to the same hash (birthday attack). ",
+		Name:        "Use of Weak Hash",
+	},
+	"338": {
+		ID:          "338",
+		Description: "The product uses a Pseudo-Random Number Generator (PRNG) in a security context, but the PRNG's algorithm is not cryptographically strong.",
+		Name:        "Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)",
+	},
+	"377": {
+		ID:          "377",
+		Description: "Creating and using insecure temporary files can leave application and system data vulnerable to attack.",
+		Name:        "Insecure Temporary File",
+	},
+	"400": {
+		ID:          "400",
+		Description: "The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.",
+		Name:        "Uncontrolled Resource Consumption",
+	},
+	"409": {
+		ID:          "409",
+		Description: "The software does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output.",
+		Name:        "Improper Handling of Highly Compressed Data (Data Amplification)",
+	},
+	"676": {
+		ID:          "676",
+		Description: "The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely.",
+		Name:        "Use of Potentially Dangerous Function",
+	},
+	"703": {
+		ID:          "703",
+		Description: "The software does not properly anticipate or handle exceptional conditions that rarely occur during normal operation of the software.",
+		Name:        "Improper Check or Handling of Exceptional Conditions",
+	},
+	"798": {
+		ID:          "798",
+		Description: "The software contains hard-coded credentials, such as a password or cryptographic key, which it uses for its own inbound authentication, outbound communication to external components, or encryption of internal data.",
+		Name:        "Use of Hard-coded Credentials",
+	},
+	"1204": {
+		ID:          "1204",
+		Description: "The product uses a cryptographic primitive that uses an Initialization Vector (IV), but the product does not generate IVs that are sufficiently unpredictable or unique according to the expected cryptographic requirements for that primitive.",
+		Name:        "Generation of Weak Initialization Vector (IV)",
+	},
+}
+
+// Get Retrieves a CWE weakness by it's id
+func Get(id string) *Weakness {
+	weakness, ok := idWeaknesses[id]
+	if ok && weakness != nil {
+		return weakness
+	}
+	return nil
+}
diff --git a/cwe/data_test.go b/cwe/data_test.go
new file mode 100644
index 0000000000..d708c3c750
--- /dev/null
+++ b/cwe/data_test.go
@@ -0,0 +1,22 @@
+package cwe_test
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2/cwe"
+)
+
+var _ = Describe("CWE data", func() {
+	BeforeEach(func() {
+	})
+	Context("when consulting cwe data", func() {
+		It("it should retrieves the weakness", func() {
+			weakness := cwe.Get("798")
+			Expect(weakness).ShouldNot(BeNil())
+			Expect(weakness.ID).ShouldNot(BeNil())
+			Expect(weakness.Name).ShouldNot(BeNil())
+			Expect(weakness.Description).ShouldNot(BeNil())
+		})
+	})
+})
diff --git a/cwe/types.go b/cwe/types.go
new file mode 100644
index 0000000000..562510a8b4
--- /dev/null
+++ b/cwe/types.go
@@ -0,0 +1,38 @@
+package cwe
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+// Weakness defines a CWE weakness based on http://cwe.mitre.org/data/xsd/cwe_schema_v6.4.xsd
+type Weakness struct {
+	ID          string
+	Name        string
+	Description string
+}
+
+// SprintURL format the CWE URL
+func (w *Weakness) SprintURL() string {
+	return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", w.ID)
+}
+
+// SprintID format the CWE ID
+func (w *Weakness) SprintID() string {
+	id := "0000"
+	if w != nil {
+		id = w.ID
+	}
+	return fmt.Sprintf("%s-%s", Acronym, id)
+}
+
+// MarshalJSON print only id and URL
+func (w *Weakness) MarshalJSON() ([]byte, error) {
+	return json.Marshal(&struct {
+		ID  string `json:"id"`
+		URL string `json:"url"`
+	}{
+		ID:  w.ID,
+		URL: w.SprintURL(),
+	})
+}
diff --git a/cwe/types_test.go b/cwe/types_test.go
new file mode 100644
index 0000000000..46b2a61e99
--- /dev/null
+++ b/cwe/types_test.go
@@ -0,0 +1,26 @@
+package cwe_test
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2/cwe"
+)
+
+var _ = Describe("CWE Types", func() {
+	BeforeEach(func() {
+	})
+	Context("when consulting cwe types", func() {
+		It("it should retrieves the information and download URIs", func() {
+			Expect(cwe.InformationURI).To(Equal("https://cwe.mitre.org/data/published/cwe_v4.4.pdf/"))
+			Expect(cwe.DownloadURI).To(Equal("https://cwe.mitre.org/data/xml/cwec_v4.4.xml.zip"))
+		})
+
+		It("it should retrieves the weakness ID and URL", func() {
+			weakness := &cwe.Weakness{ID: "798"}
+			Expect(weakness).ShouldNot(BeNil())
+			Expect(weakness.SprintID()).To(Equal("CWE-798"))
+			Expect(weakness.SprintURL()).To(Equal("https://cwe.mitre.org/data/definitions/798.html"))
+		})
+	})
+})
diff --git a/entrypoint.sh b/entrypoint.sh
index 4dc0467297..bc6ad6a241 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -1,7 +1,11 @@
 #!/usr/bin/env bash
 
-# Expand the arguments into an array of strings. This is requires because the GitHub action
+# Expand the arguments into an array of strings. This is required because the GitHub action
 # provides all arguments concatenated as a single string.
 ARGS=("$@")
 
+if [[ ! -z "${GITHUB_AUTHENTICATION_TOKEN}" ]]; then
+  git config --global --add url."https://x-access-token:${GITHUB_AUTHENTICATION_TOKEN}@github.com/".insteadOf "https://github.com/"
+fi
+
 /bin/gosec ${ARGS[*]}
diff --git a/errors.go b/errors.go
index a27aa5821c..2f66727046 100644
--- a/errors.go
+++ b/errors.go
@@ -20,7 +20,7 @@ func NewError(line, column int, err string) *Error {
 	}
 }
 
-// sortErros sorts the golang erros by line
+// sortErrors sorts the golang errors by line
 func sortErrors(allErrors map[string][]Error) {
 	for _, errors := range allErrors {
 		sort.Slice(errors, func(i, j int) bool {
diff --git a/flag_test.go b/flag_test.go
new file mode 100644
index 0000000000..bebb32c037
--- /dev/null
+++ b/flag_test.go
@@ -0,0 +1,45 @@
+package gosec_test
+
+import (
+	"flag"
+	"os"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2/cmd/vflag"
+)
+
+var _ = Describe("Cli", func() {
+	Context("vflag test", func() {
+		It("value must be empty as parameter value contains invalid character", func() {
+			os.Args = []string{"gosec", "-flag1=-incorrect"}
+			f := vflag.ValidatedFlag{}
+			flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
+			flag.Var(&f, "falg1", "")
+			flag.CommandLine.Init("flag1", flag.ContinueOnError)
+			flag.Parse()
+			Expect(flag.Parsed()).Should(BeTrue())
+			Expect(f.Value).Should(Equal(``))
+		})
+		It("value must be empty as parameter value contains invalid character without equal sign", func() {
+			os.Args = []string{"gosec", "-test2= -incorrect"}
+			f := vflag.ValidatedFlag{}
+			flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
+			flag.Var(&f, "test2", "")
+			flag.CommandLine.Init("test2", flag.ContinueOnError)
+			flag.Parse()
+			Expect(flag.Parsed()).Should(BeTrue())
+			Expect(f.Value).Should(Equal(``))
+		})
+		It("value must not be empty as parameter value contains valid character", func() {
+			os.Args = []string{"gosec", "-test3=correct"}
+			f := vflag.ValidatedFlag{}
+			flag.Var(&f, "test3", "")
+			flag.CommandLine.Init("test3", flag.ContinueOnError)
+			flag.Parse()
+			Expect(flag.Parsed()).Should(BeTrue())
+			Expect(f.Value).Should(Equal(`correct`))
+		})
+	})
+})
diff --git a/go.mod b/go.mod
index 98d8d277b9..5e15b5fa6d 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,59 @@
 module github.com/securego/gosec/v2
 
 require (
-	github.com/gookit/color v1.3.8
-	github.com/lib/pq v1.10.0 // indirect
-	github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880
-	github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
-	github.com/onsi/ginkgo v1.15.2
-	github.com/onsi/gomega v1.11.0
-	golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
-	golang.org/x/mod v0.4.1 // indirect
-	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
-	golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46 // indirect
-	golang.org/x/text v0.3.5 // indirect
-	golang.org/x/tools v0.1.0
-	gopkg.in/yaml.v2 v2.4.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/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.40.0
+	golang.org/x/text v0.27.0
+	golang.org/x/tools v0.35.0
+	google.golang.org/api v0.243.0
+	gopkg.in/yaml.v3 v3.0.1
 )
 
-go 1.14
+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
+	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/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/pmezard/go-difflib v1.0.0 // indirect
+	github.com/stretchr/objx v0.5.2 // 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.uber.org/automaxprocs v1.6.0 // indirect
+	golang.org/x/mod v0.26.0 // indirect
+	golang.org/x/net v0.42.0 // indirect
+	golang.org/x/oauth2 v0.30.0 // indirect
+	golang.org/x/sync v0.16.0 // indirect
+	golang.org/x/sys v0.34.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-20250715232539-7130f93afb79 // indirect
+	google.golang.org/grpc v1.73.0 // indirect
+	google.golang.org/protobuf v1.36.6 // indirect
+)
+
+go 1.23.0
diff --git a/go.sum b/go.sum
index 6898da512f..b7f27207d4 100644
--- a/go.sum
+++ b/go.sum
@@ -13,14 +13,26 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
 cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
 cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
 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/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/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=
@@ -54,6 +66,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
+github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -88,9 +102,9 @@ github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -100,9 +114,16 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -133,19 +154,22 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 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=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -155,15 +179,25 @@ 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/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=
 github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
 github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
+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/gookit/color v1.3.8 h1:w2WcSwaCa1ojRWO60Mm4GJUJomBNKR9G+x9DwaaCL1c=
-github.com/gookit/color v1.3.8/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
+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/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=
@@ -206,17 +240,19 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
 github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
-github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -243,29 +279,22 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8=
-github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880 h1:DXaIt8v4XXkFoVZXkG/PjLS5Rz3I2yoflOQrnuGgJeA=
-github.com/mozilla/tls-observatory v0.0.0-20210209181001-cf43108d6880/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
+github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5 h1:0KqC6/sLy7fDpBdybhVkkv4Yz+PmB7c9Dz9z3dLW804=
+github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
 github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc=
-github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
-github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
 github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 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 v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.15.2 h1:l77YT15o814C2qVL47NOyjV/6RbaP7kKdrvZnxQ3Org=
-github.com/onsi/ginkgo v1.15.2/go.mod h1:Dd6YFfwBW84ETqqtL0CPyPXillHgY6XhQH3uuCCTr/o=
+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/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug=
-github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg=
+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/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=
@@ -275,6 +304,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
@@ -292,6 +323,8 @@ github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -312,13 +345,15 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 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/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=
@@ -328,6 +363,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
 github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
@@ -335,7 +372,6 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
@@ -345,9 +381,27 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 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.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=
+go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
+go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
@@ -363,8 +417,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.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
+golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 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=
@@ -376,6 +430,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
+golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
+golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -396,8 +452,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.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
+golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
 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=
@@ -427,19 +483,18 @@ golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
+golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 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=
@@ -449,7 +504,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.0.0-20201020160332-67f06af15bc9/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/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=
@@ -467,13 +523,10 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -493,25 +546,25 @@ 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.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46 h1:V066+OYJ66oTjnhm4Yrn7SXIwSCiDQJxpBxmvqb1N1c=
-golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
+golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
+golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 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.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 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=
@@ -558,14 +611,11 @@ 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.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
+golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
 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=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 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=
@@ -582,6 +632,8 @@ 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.243.0 h1:sw+ESIJ4BVnlJcWu9S+p2Z6Qq1PjG77T8IJ1xtp4jZQ=
+google.golang.org/api v0.243.0/go.mod h1:GE4QtYfaybx1KmeHMdBnNnyLzBZCVihGBXAmJu/uUr8=
 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=
@@ -620,6 +672,12 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
+google.golang.org/genproto/googleapis/api v0.0.0-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-20250715232539-7130f93afb79 h1:1ZwqphdOdWYXsUHgMpU/101nCtf/kSp9hOrcvFsnl10=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250715232539-7130f93afb79/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
 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=
@@ -634,6 +692,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/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=
@@ -643,20 +703,21 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 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 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 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=
 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=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -667,11 +728,10 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.6/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/gosec_suite_test.go b/gosec_suite_test.go
index 7475c356a8..e63a63d004 100644
--- a/gosec_suite_test.go
+++ b/gosec_suite_test.go
@@ -1,10 +1,10 @@
 package gosec_test
 
 import (
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
 	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
 )
 
 func TestGosec(t *testing.T) {
diff --git a/helpers.go b/helpers.go
index 83dfa293a5..33e2b4a0d8 100644
--- a/helpers.go
+++ b/helpers.go
@@ -15,12 +15,15 @@
 package gosec
 
 import (
+	"bytes"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"go/ast"
 	"go/token"
 	"go/types"
 	"os"
+	"os/exec"
 	"os/user"
 	"path/filepath"
 	"regexp"
@@ -29,15 +32,18 @@ import (
 	"strings"
 )
 
+// 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
 // initialization only imports.
 //
 // Usage:
-// 	node, matched := MatchCallByPackage(n, ctx, "math/rand", "Read")
 //
+//	node, matched := MatchCallByPackage(n, ctx, "math/rand", "Read")
 func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*ast.CallExpr, bool) {
-	importedName, found := GetImportedName(pkg, c)
+	importedNames, found := GetImportedNames(pkg, c)
 	if !found {
 		return nil, false
 	}
@@ -47,7 +53,10 @@ func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*a
 		if err != nil {
 			return nil, false
 		}
-		if packageName == importedName {
+		for _, in := range importedNames {
+			if packageName != in {
+				continue
+			}
 			for _, name := range names {
 				if callName == name {
 					return callExpr, true
@@ -74,7 +83,7 @@ func GetInt(n ast.Node) (int64, error) {
 	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.INT {
 		return strconv.ParseInt(node.Value, 0, 64)
 	}
-	return 0, fmt.Errorf("Unexpected AST node type: %T", n)
+	return 0, fmt.Errorf("unexpected AST node type: %T", n)
 }
 
 // GetFloat will read and return a float value from an ast.BasicLit
@@ -82,7 +91,7 @@ func GetFloat(n ast.Node) (float64, error) {
 	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.FLOAT {
 		return strconv.ParseFloat(node.Value, 64)
 	}
-	return 0.0, fmt.Errorf("Unexpected AST node type: %T", n)
+	return 0.0, fmt.Errorf("unexpected AST node type: %T", n)
 }
 
 // GetChar will read and return a char value from an ast.BasicLit
@@ -90,7 +99,41 @@ func GetChar(n ast.Node) (byte, error) {
 	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.CHAR {
 		return node.Value[0], nil
 	}
-	return 0, fmt.Errorf("Unexpected AST node type: %T", n)
+	return 0, fmt.Errorf("unexpected AST node type: %T", n)
+}
+
+// GetStringRecursive will recursively walk down a tree of *ast.BinaryExpr. It will then concat the results, and return.
+// Unlike the other getters, it does _not_ raise an error for unknown ast.Node types. At the base, the recursion will hit a non-BinaryExpr type,
+// either BasicLit or other, so it's not an error case. It will only error if `strconv.Unquote` errors. This matters, because there's
+// currently functionality that relies on error values being returned by GetString if and when it hits a non-basiclit string node type,
+// hence for cases where recursion is needed, we use this separate function, so that we can still be backwards compatible.
+//
+// This was added to handle a SQL injection concatenation case where the injected value is infixed between two strings, not at the start or end. See example below
+//
+// Do note that this will omit non-string values. So for example, if you were to use this node:
+// ```go
+// q := "SELECT * FROM foo WHERE name = '" + os.Args[0] + "' AND 1=1" // will result in "SELECT * FROM foo WHERE ” AND 1=1"
+
+func GetStringRecursive(n ast.Node) (string, error) {
+	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.STRING {
+		return strconv.Unquote(node.Value)
+	}
+
+	if expr, ok := n.(*ast.BinaryExpr); ok {
+		x, err := GetStringRecursive(expr.X)
+		if err != nil {
+			return "", err
+		}
+
+		y, err := GetStringRecursive(expr.Y)
+		if err != nil {
+			return "", err
+		}
+
+		return x + y, nil
+	}
+
+	return "", nil
 }
 
 // GetString will read and return a string value from an ast.BasicLit
@@ -98,7 +141,8 @@ func GetString(n ast.Node) (string, error) {
 	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.STRING {
 		return strconv.Unquote(node.Value)
 	}
-	return "", fmt.Errorf("Unexpected AST node type: %T", n)
+
+	return "", fmt.Errorf("unexpected AST node type: %T", n)
 }
 
 // GetCallObject returns the object and call expression and associated
@@ -145,7 +189,7 @@ func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
 			case *ast.CallExpr:
 				switch call := expr.Fun.(type) {
 				case *ast.Ident:
-					if call.Name == "new" {
+					if call.Name == "new" && len(expr.Args) > 0 {
 						t := ctx.Info.TypeOf(expr.Args[0])
 						if t != nil {
 							return t.String(), fn.Sel.Name, nil
@@ -168,7 +212,6 @@ func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
 							}
 						}
 					}
-
 				}
 			}
 		case *ast.Ident:
@@ -180,7 +223,7 @@ func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
 }
 
 // GetCallStringArgsValues returns the values of strings arguments if they can be resolved
-func GetCallStringArgsValues(n ast.Node, ctx *Context) []string {
+func GetCallStringArgsValues(n ast.Node, _ *Context) []string {
 	values := []string{}
 	switch node := n.(type) {
 	case *ast.CallExpr:
@@ -199,32 +242,42 @@ func GetCallStringArgsValues(n ast.Node, ctx *Context) []string {
 	return values
 }
 
-// GetIdentStringValues return the string values of an Ident if they can be resolved
-func GetIdentStringValues(ident *ast.Ident) []string {
+func getIdentStringValues(ident *ast.Ident, stringFinder func(ast.Node) (string, error)) []string {
 	values := []string{}
 	obj := ident.Obj
 	if obj != nil {
 		switch decl := obj.Decl.(type) {
 		case *ast.ValueSpec:
 			for _, v := range decl.Values {
-				value, err := GetString(v)
+				value, err := stringFinder(v)
 				if err == nil {
 					values = append(values, value)
 				}
 			}
 		case *ast.AssignStmt:
 			for _, v := range decl.Rhs {
-				value, err := GetString(v)
+				value, err := stringFinder(v)
 				if err == nil {
 					values = append(values, value)
 				}
 			}
 		}
-
 	}
 	return values
 }
 
+// GetIdentStringValuesRecursive returns the string of values of an Ident if they can be resolved
+// The difference between this and GetIdentStringValues is that it will attempt to resolve the strings recursively,
+// if it is passed a *ast.BinaryExpr. See GetStringRecursive for details
+func GetIdentStringValuesRecursive(ident *ast.Ident) []string {
+	return getIdentStringValues(ident, GetStringRecursive)
+}
+
+// GetIdentStringValues return the string values of an Ident if they can be resolved
+func GetIdentStringValues(ident *ast.Ident) []string {
+	return getIdentStringValues(ident, GetString)
+}
+
 // GetBinaryExprOperands returns all operands of a binary expression by traversing
 // the expression tree
 func GetBinaryExprOperands(be *ast.BinaryExpr) []ast.Node {
@@ -246,32 +299,26 @@ func GetBinaryExprOperands(be *ast.BinaryExpr) []ast.Node {
 	return result
 }
 
-// GetImportedName returns the name used for the package within the
-// code. It will resolve aliases and ignores initialization only imports.
-func GetImportedName(path string, ctx *Context) (string, bool) {
-	importName, imported := ctx.Imports.Imported[path]
-	if !imported {
-		return "", false
-	}
-
-	if _, initonly := ctx.Imports.InitOnly[path]; initonly {
-		return "", false
-	}
-
-	if alias, ok := ctx.Imports.Aliased[path]; ok {
-		importName = alias
-	}
-	return importName, true
+// GetImportedNames returns the name(s)/alias(es) used for the package within
+// the code. It ignores initialization-only imports.
+func GetImportedNames(path string, ctx *Context) (names []string, found bool) {
+	importNames, imported := ctx.Imports.Imported[path]
+	return importNames, imported
 }
 
 // GetImportPath resolves the full import path of an identifier based on
-// the imports in the current context.
+// the imports in the current context(including aliases).
 func GetImportPath(name string, ctx *Context) (string, bool) {
 	for path := range ctx.Imports.Imported {
-		if imported, ok := GetImportedName(path, ctx); ok && imported == name {
-			return path, true
+		if imported, ok := GetImportedNames(path, ctx); ok {
+			for _, n := range imported {
+				if n == name {
+					return path, true
+				}
+			}
 		}
 	}
+
 	return "", false
 }
 
@@ -298,7 +345,7 @@ func Gopath() []string {
 }
 
 // Getenv returns the values of the environment variable, otherwise
-//returns the default if variable is not set
+// returns the default if variable is not set
 func Getenv(key, userDefault string) string {
 	if val := os.Getenv(key); val != "" {
 		return val
@@ -306,7 +353,7 @@ func Getenv(key, userDefault string) string {
 	return userDefault
 }
 
-// GetPkgRelativePath returns the Go relative relative path derived
+// GetPkgRelativePath returns the Go relative path derived
 // form the given path
 func GetPkgRelativePath(path string) (string, error) {
 	abspath, err := filepath.Abs(path)
@@ -404,7 +451,7 @@ func PackagePaths(root string, excludes []*regexp.Regexp) ([]string, error) {
 	err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
 		if filepath.Ext(path) == ".go" {
 			path = filepath.Dir(path)
-			if isExcluded(path, excludes) {
+			if isExcluded(filepath.ToSlash(path), excludes) {
 				return nil
 			}
 			paths[path] = true
@@ -439,7 +486,7 @@ func isExcluded(str string, excludes []*regexp.Regexp) bool {
 func ExcludedDirsRegExp(excludedDirs []string) []*regexp.Regexp {
 	var exps []*regexp.Regexp
 	for _, excludedDir := range excludedDirs {
-		str := fmt.Sprintf(`([\\/])?%s([\\/])?`, excludedDir)
+		str := fmt.Sprintf(`([\\/])?%s([\\/])?`, strings.ReplaceAll(filepath.ToSlash(excludedDir), "/", `\/`))
 		r := regexp.MustCompile(str)
 		exps = append(exps, r)
 	}
@@ -448,8 +495,61 @@ func ExcludedDirsRegExp(excludedDirs []string) []*regexp.Regexp {
 
 // RootPath returns the absolute root path of a scan
 func RootPath(root string) (string, error) {
-	if strings.HasSuffix(root, "...") {
-		root = root[0 : len(root)-3]
-	}
+	root = strings.TrimSuffix(root, "...")
 	return filepath.Abs(root)
 }
+
+// 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(envGoModVersion); ok {
+		return parseGoVersion(strings.TrimPrefix(env, "go"))
+	}
+
+	goVersion, err := goModVersion()
+	if err != nil {
+		return parseGoVersion(strings.TrimPrefix(runtime.Version(), "go"))
+	}
+
+	return parseGoVersion(goVersion)
+}
+
+type goListOutput struct {
+	GoVersion string `json:"GoVersion"`
+}
+
+func goModVersion() (string, error) {
+	cmd := exec.Command("go", "list", "-m", "-json")
+
+	raw, err := cmd.CombinedOutput()
+	if err != nil {
+		return "", fmt.Errorf("command go list: %w: %s", err, string(raw))
+	}
+
+	var v goListOutput
+	err = json.NewDecoder(bytes.NewBuffer(raw)).Decode(&v)
+	if err != nil {
+		return "", fmt.Errorf("unmarshaling error: %w: %s", err, string(raw))
+	}
+
+	return v.GoVersion, nil
+}
+
+// parseGoVersion parses Go version.
+// example:
+// - 1.19rc2
+// - 1.19beta2
+// - 1.19.4
+// - 1.19
+func parseGoVersion(version string) (int, int, int) {
+	exp := regexp.MustCompile(`(\d+).(\d+)(?:.(\d+))?.*`)
+	parts := exp.FindStringSubmatch(version)
+	if len(parts) <= 1 {
+		return 0, 0, 0
+	}
+
+	major, _ := strconv.Atoi(parts[1])
+	minor, _ := strconv.Atoi(parts[2])
+	build, _ := strconv.Atoi(parts[3])
+
+	return major, minor, build
+}
diff --git a/helpers_test.go b/helpers_test.go
index 0f32c3869f..3349057574 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -2,29 +2,23 @@ package gosec_test
 
 import (
 	"go/ast"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"regexp"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
 	"github.com/securego/gosec/v2/testutils"
 )
 
 var _ = Describe("Helpers", func() {
-	Context("when listing pacakge paths", func() {
+	Context("when listing package paths", func() {
 		var dir string
 		JustBeforeEach(func() {
-			var err error
-			dir, err = ioutil.TempDir("", "gosec")
-			Expect(err).ShouldNot(HaveOccurred())
-			_, err = ioutil.TempFile(dir, "test*.go")
-			Expect(err).ShouldNot(HaveOccurred())
-		})
-		AfterEach(func() {
-			err := os.RemoveAll(dir)
+			dir = GinkgoT().TempDir()
+			_, err := os.MkdirTemp(dir, "test*.go")
 			Expect(err).ShouldNot(HaveOccurred())
 		})
 		It("should return the root directory as package path", func() {
@@ -32,14 +26,14 @@ var _ = Describe("Helpers", func() {
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(paths).Should(Equal([]string{dir}))
 		})
-		It("should return the package package path", func() {
+		It("should return the package path", func() {
 			paths, err := gosec.PackagePaths(dir+"/...", nil)
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(paths).Should(Equal([]string{dir}))
 		})
 		It("should exclude folder", func() {
 			nested := dir + "/vendor"
-			err := os.Mkdir(nested, 0755)
+			err := os.Mkdir(nested, 0o755)
 			Expect(err).ShouldNot(HaveOccurred())
 			_, err = os.Create(nested + "/test.go")
 			Expect(err).ShouldNot(HaveOccurred())
@@ -49,6 +43,18 @@ var _ = Describe("Helpers", func() {
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(paths).Should(Equal([]string{dir}))
 		})
+		It("should exclude folder with subpath", func() {
+			nested := dir + "/pkg/generated"
+			err := os.MkdirAll(nested, 0o755)
+			Expect(err).ShouldNot(HaveOccurred())
+			_, err = os.Create(nested + "/test.go")
+			Expect(err).ShouldNot(HaveOccurred())
+			exclude, err := regexp.Compile(`([\\/])?/pkg\/generated([\\/])?`)
+			Expect(err).ShouldNot(HaveOccurred())
+			paths, err := gosec.PackagePaths(dir+"/...", []*regexp.Regexp{exclude})
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(paths).Should(Equal([]string{dir}))
+		})
 		It("should be empty when folder does not exist", func() {
 			nested := dir + "/test"
 			paths, err := gosec.PackagePaths(nested+"/...", nil)
@@ -66,7 +72,7 @@ var _ = Describe("Helpers", func() {
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(root).Should(Equal(filepath.Join(cwd, base)))
 		})
-		It("should retrun the absolute path from ellipsis path", func() {
+		It("should return the absolute path from ellipsis path", func() {
 			base := "test"
 			cwd, err := os.Getwd()
 			Expect(err).ShouldNot(HaveOccurred())
@@ -79,18 +85,29 @@ var _ = Describe("Helpers", func() {
 	Context("when excluding the dirs", func() {
 		It("should create a proper regexp", func() {
 			r := gosec.ExcludedDirsRegExp([]string{"test"})
-			Expect(len(r)).Should(Equal(1))
+			Expect(r).Should(HaveLen(1))
 			match := r[0].MatchString("/home/go/src/project/test/pkg")
 			Expect(match).Should(BeTrue())
 			match = r[0].MatchString("/home/go/src/project/vendor/pkg")
 			Expect(match).Should(BeFalse())
 		})
 
+		It("should create a proper regexp for dir with subdir", func() {
+			r := gosec.ExcludedDirsRegExp([]string{`test/generated`})
+			Expect(r).Should(HaveLen(1))
+			match := r[0].MatchString("/home/go/src/project/test/generated")
+			Expect(match).Should(BeTrue())
+			match = r[0].MatchString("/home/go/src/project/test/pkg")
+			Expect(match).Should(BeFalse())
+			match = r[0].MatchString("/home/go/src/project/vendor/pkg")
+			Expect(match).Should(BeFalse())
+		})
+
 		It("should create no regexp when dir list is empty", func() {
 			r := gosec.ExcludedDirsRegExp(nil)
-			Expect(len(r)).Should(Equal(0))
+			Expect(r).Should(BeEmpty())
 			r = gosec.ExcludedDirsRegExp([]string{})
-			Expect(len(r)).Should(Equal(0))
+			Expect(r).Should(BeEmpty())
 		})
 	})
 
@@ -228,6 +245,38 @@ var _ = Describe("Helpers", func() {
 
 			Expect(result).Should(HaveKeyWithValue("fmt", "Println"))
 		})
+
+		It("should return the type and call name when built-in new function is overridden", func() {
+			pkg := testutils.NewTestPackage()
+			defer pkg.Close()
+			pkg.AddFile("main.go", `
+      package main
+
+      type S struct{ F int }
+
+      func (f S) Fun() {}
+
+      func new() S { return S{} }
+
+      func main() {
+	      new().Fun()
+      }
+			`)
+			ctx := pkg.CreateContext("main.go")
+			result := map[string]string{}
+			visitor := testutils.NewMockVisitor()
+			visitor.Context = ctx
+			visitor.Callback = func(n ast.Node, ctx *gosec.Context) bool {
+				typeName, call, err := gosec.GetCallInfo(n, ctx)
+				if err == nil {
+					result[typeName] = call
+				}
+				return true
+			}
+			ast.Walk(visitor, ctx.Root)
+
+			Expect(result).Should(HaveKeyWithValue("main", "new"))
+		})
 	})
 	Context("when getting binary expression operands", func() {
 		It("should return all operands of a binary expression", func() {
@@ -258,7 +307,7 @@ var _ = Describe("Helpers", func() {
 			ast.Walk(visitor, ctx.Root)
 
 			operands := gosec.GetBinaryExprOperands(be)
-			Expect(len(operands)).Should(Equal(2))
+			Expect(operands).Should(HaveLen(2))
 		})
 		It("should return all operands of complex binary expression", func() {
 			pkg := testutils.NewTestPackage()
@@ -290,7 +339,7 @@ var _ = Describe("Helpers", func() {
 			ast.Walk(visitor, ctx.Root)
 
 			operands := gosec.GetBinaryExprOperands(be)
-			Expect(len(operands)).Should(Equal(4))
+			Expect(operands).Should(HaveLen(4))
 		})
 	})
 })
diff --git a/import_tracker.go b/import_tracker.go
index cbb8c5518c..0d9ebfe16a 100644
--- a/import_tracker.go
+++ b/import_tracker.go
@@ -15,61 +15,64 @@ package gosec
 import (
 	"go/ast"
 	"go/types"
+	"regexp"
 	"strings"
 )
 
+var versioningPackagePattern = regexp.MustCompile(`v[0-9]+$`)
+
 // ImportTracker is used to normalize the packages that have been imported
 // by a source file. It is able to differentiate between plain imports, aliased
 // imports and init only imports.
 type ImportTracker struct {
-	Imported map[string]string
-	Aliased  map[string]string
-	InitOnly map[string]bool
+	// Imported is a map of Imported with their associated names/aliases.
+	Imported map[string][]string
 }
 
 // NewImportTracker creates an empty Import tracker instance
 func NewImportTracker() *ImportTracker {
 	return &ImportTracker{
-		make(map[string]string),
-		make(map[string]string),
-		make(map[string]bool),
+		Imported: make(map[string][]string),
 	}
 }
 
 // TrackFile track all the imports used by the supplied file
 func (t *ImportTracker) TrackFile(file *ast.File) {
 	for _, imp := range file.Imports {
-		path := strings.Trim(imp.Path.Value, `"`)
-		parts := strings.Split(path, "/")
-		if len(parts) > 0 {
-			name := parts[len(parts)-1]
-			t.Imported[path] = name
-		}
+		t.TrackImport(imp)
 	}
 }
 
 // TrackPackages tracks all the imports used by the supplied packages
 func (t *ImportTracker) TrackPackages(pkgs ...*types.Package) {
 	for _, pkg := range pkgs {
-		t.Imported[pkg.Path()] = pkg.Name()
+		t.Imported[pkg.Path()] = []string{pkg.Name()}
 	}
 }
 
-// TrackImport tracks imports and handles the 'unsafe' import
-func (t *ImportTracker) TrackImport(n ast.Node) {
-	if imported, ok := n.(*ast.ImportSpec); ok {
-		path := strings.Trim(imported.Path.Value, `"`)
-		if imported.Name != nil {
-			if imported.Name.Name == "_" {
-				// Initialization only import
-				t.InitOnly[path] = true
-			} else {
-				// Aliased import
-				t.Aliased[path] = imported.Name.Name
-			}
-		}
-		if path == "unsafe" {
-			t.Imported[path] = path
+// TrackImport tracks imports.
+func (t *ImportTracker) TrackImport(imported *ast.ImportSpec) {
+	importPath := strings.Trim(imported.Path.Value, `"`)
+	if imported.Name != nil {
+		if imported.Name.Name != "_" {
+			// Aliased import
+			t.Imported[importPath] = append(t.Imported[importPath], imported.Name.String())
 		}
+	} else {
+		t.Imported[importPath] = append(t.Imported[importPath], importName(importPath))
+	}
+}
+
+func importName(importPath string) string {
+	parts := strings.Split(importPath, "/")
+	name := importPath
+	if len(parts) > 0 {
+		name = parts[len(parts)-1]
+	}
+	// If the last segment of the path is version information, consider the second to last segment as the package name.
+	// (e.g., `math/rand/v2` would be `rand`)
+	if len(parts) > 1 && versioningPackagePattern.MatchString(name) {
+		name = parts[len(parts)-2]
 	}
+	return name
 }
diff --git a/import_tracker_test.go b/import_tracker_test.go
index aaae4aa1f2..f6ad13a7eb 100644
--- a/import_tracker_test.go
+++ b/import_tracker_test.go
@@ -1,11 +1,11 @@
 package gosec_test
 
 import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
 	"github.com/securego/gosec/v2/testutils"
-
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
 )
 
 var _ = Describe("Import Tracker", func() {
@@ -28,7 +28,7 @@ var _ = Describe("Import Tracker", func() {
 			files := pkgs[0].Syntax
 			Expect(files).Should(HaveLen(1))
 			tracker.TrackFile(files[0])
-			Expect(tracker.Imported).Should(Equal(map[string]string{"fmt": "fmt"}))
+			Expect(tracker.Imported).Should(Equal(map[string][]string{"fmt": {"fmt"}}))
 		})
 		It("should parse the named imports from file", func() {
 			tracker := gosec.NewImportTracker()
@@ -48,7 +48,7 @@ var _ = Describe("Import Tracker", func() {
 			files := pkgs[0].Syntax
 			Expect(files).Should(HaveLen(1))
 			tracker.TrackFile(files[0])
-			Expect(tracker.Imported).Should(Equal(map[string]string{"fmt": "fmt"}))
+			Expect(tracker.Imported).Should(Equal(map[string][]string{"fmt": {"fm"}}))
 		})
 	})
 })
diff --git a/install.sh b/install.sh
index 37bed0a2e3..2b6403cb25 100644
--- a/install.sh
+++ b/install.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 set -e
-# Code generated by godownloader on 2020-03-02T13:35:13Z. DO NOT EDIT.
+# Code generated by godownloader. DO NOT EDIT.
 #
 
 usage() {
@@ -63,8 +63,11 @@ execute() {
 get_binaries() {
   case "$PLATFORM" in
     darwin/amd64) BINARIES="gosec" ;;
+    darwin/arm64) BINARIES="gosec" ;;
     linux/amd64) BINARIES="gosec" ;;
+    linux/arm64) BINARIES="gosec" ;;
     windows/amd64) BINARIES="gosec" ;;
+    windows/arm64) BINARIES="gosec" ;;
     *)
       log_crit "platform $PLATFORM is not supported.  Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
       exit 1
@@ -277,11 +280,13 @@ http_copy() {
 github_release() {
   owner_repo=$1
   version=$2
-  test -z "$version" && version="latest"
-  giturl="https://github.com/${owner_repo}/releases/${version}"
+  giturl="https://api.github.com/repos/${owner_repo}/releases/tags/${version}"
+  if [ -z "${version}" ]; then
+    giturl="https://api.github.com/repos/${owner_repo}/releases/latest"
+  fi
   json=$(http_copy "$giturl" "Accept:application/json")
   test -z "$json" && return 1
-  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name": *"//' | sed 's/".*//')
   test -z "$version" && return 1
   echo "$version"
 }
diff --git a/issue.go b/issue/issue.go
similarity index 50%
rename from issue.go
rename to issue/issue.go
index aa58c3434b..b7c804e455 100644
--- a/issue.go
+++ b/issue/issue.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package gosec
+package issue
 
 import (
 	"bufio"
@@ -23,6 +23,8 @@ import (
 	"go/token"
 	"os"
 	"strconv"
+
+	"github.com/securego/gosec/v2/cwe"
 )
 
 // Score type used by severity and confidence values
@@ -41,66 +43,83 @@ const (
 // the beginning and after the end of a code snippet
 const SnippetOffset = 1
 
-// Cwe id and url
-type Cwe struct {
-	ID  string
-	URL string
-}
-
-// GetCwe creates a cwe object for a given RuleID
-func GetCwe(id string) Cwe {
-	return Cwe{ID: id, URL: fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", id)}
+// GetCweByRule retrieves a cwe weakness for a given RuleID
+func GetCweByRule(id string) *cwe.Weakness {
+	cweID, ok := ruleToCWE[id]
+	if ok && cweID != "" {
+		return cwe.Get(cweID)
+	}
+	return nil
 }
 
-// IssueToCWE maps gosec rules to CWEs
-var IssueToCWE = map[string]Cwe{
-	"G101": GetCwe("798"),
-	"G102": GetCwe("200"),
-	"G103": GetCwe("242"),
-	"G104": GetCwe("703"),
-	"G106": GetCwe("322"),
-	"G107": GetCwe("88"),
-	"G108": GetCwe("200"),
-	"G109": GetCwe("190"),
-	"G110": GetCwe("409"),
-	"G201": GetCwe("89"),
-	"G202": GetCwe("89"),
-	"G203": GetCwe("79"),
-	"G204": GetCwe("78"),
-	"G301": GetCwe("276"),
-	"G302": GetCwe("276"),
-	"G303": GetCwe("377"),
-	"G304": GetCwe("22"),
-	"G305": GetCwe("22"),
-	"G306": GetCwe("276"),
-	"G307": GetCwe("703"),
-	"G401": GetCwe("326"),
-	"G402": GetCwe("295"),
-	"G403": GetCwe("310"),
-	"G404": GetCwe("338"),
-	"G501": GetCwe("327"),
-	"G502": GetCwe("327"),
-	"G503": GetCwe("327"),
-	"G504": GetCwe("327"),
-	"G505": GetCwe("327"),
-	"G601": GetCwe("118"),
+// ruleToCWE maps gosec rules to CWEs
+var ruleToCWE = map[string]string{
+	"G101": "798",
+	"G102": "200",
+	"G103": "242",
+	"G104": "703",
+	"G106": "322",
+	"G107": "88",
+	"G108": "200",
+	"G109": "190",
+	"G110": "409",
+	"G111": "22",
+	"G112": "400",
+	"G114": "676",
+	"G115": "190",
+	"G201": "89",
+	"G202": "89",
+	"G203": "79",
+	"G204": "78",
+	"G301": "276",
+	"G302": "276",
+	"G303": "377",
+	"G304": "22",
+	"G305": "22",
+	"G306": "276",
+	"G401": "328",
+	"G402": "295",
+	"G403": "310",
+	"G404": "338",
+	"G405": "327",
+	"G406": "328",
+	"G407": "1204",
+	"G501": "327",
+	"G502": "327",
+	"G503": "327",
+	"G504": "327",
+	"G505": "327",
+	"G506": "327",
+	"G507": "327",
+	"G601": "118",
+	"G602": "118",
 }
 
 // Issue is returned by a gosec rule if it discovers an issue with the scanned code.
 type Issue struct {
-	Severity   Score  `json:"severity"`   // issue severity (how problematic it is)
-	Confidence Score  `json:"confidence"` // issue confidence (how sure we are we found it)
-	Cwe        Cwe    `json:"cwe"`        // Cwe associated with RuleID
-	RuleID     string `json:"rule_id"`    // Human readable explanation
-	What       string `json:"details"`    // Human readable explanation
-	File       string `json:"file"`       // File name we found it in
-	Code       string `json:"code"`       // Impacted code line
-	Line       string `json:"line"`       // Line number in file
-	Col        string `json:"column"`     // Column number in line
+	Severity     Score             `json:"severity"`          // issue severity (how problematic it is)
+	Confidence   Score             `json:"confidence"`        // issue confidence (how sure we are we found it)
+	Cwe          *cwe.Weakness     `json:"cwe"`               // Cwe associated with RuleID
+	RuleID       string            `json:"rule_id"`           // Human readable explanation
+	What         string            `json:"details"`           // Human readable explanation
+	File         string            `json:"file"`              // File name we found it in
+	Code         string            `json:"code"`              // Impacted code line
+	Line         string            `json:"line"`              // Line number in file
+	Col          string            `json:"column"`            // Column number in line
+	NoSec        bool              `json:"nosec"`             // true if the issue is nosec
+	Suppressions []SuppressionInfo `json:"suppressions"`      // Suppression info of the issue
+	Autofix      string            `json:"autofix,omitempty"` // Proposed auto fix the issue
+}
+
+// SuppressionInfo object is to record the kind and the justification that used
+// to suppress violations.
+type SuppressionInfo struct {
+	Kind          string `json:"kind"`
+	Justification string `json:"justification"`
 }
 
 // FileLocation point out the file path and line number in file
-func (i Issue) FileLocation() string {
+func (i *Issue) FileLocation() string {
 	return fmt.Sprintf("%s:%s", i.File, i.Line)
 }
 
@@ -131,11 +150,8 @@ func (c Score) String() string {
 	return "UNDEFINED"
 }
 
-// codeSnippet extracts a code snippet based on the ast reference
-func codeSnippet(file *os.File, start int64, end int64, n ast.Node) (string, error) {
-	if n == nil {
-		return "", fmt.Errorf("invalid AST node provided")
-	}
+// CodeSnippet extracts a code snippet based on the ast reference
+func CodeSnippet(file *os.File, start int64, end int64) (string, error) {
 	var pos int64
 	var buf bytes.Buffer
 	scanner := bufio.NewScanner(file)
@@ -165,23 +181,21 @@ func codeSnippetEndLine(node ast.Node, fobj *token.File) int64 {
 	return e + SnippetOffset
 }
 
-// NewIssue creates a new Issue
-func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score, confidence Score) *Issue {
-	fobj := ctx.FileSet.File(node.Pos())
+// New creates a new Issue
+func New(fobj *token.File, node ast.Node, ruleID, desc string, severity, confidence Score) *Issue {
 	name := fobj.Name()
-	start, end := fobj.Line(node.Pos()), fobj.Line(node.End())
-	line := strconv.Itoa(start)
-	if start != end {
-		line = fmt.Sprintf("%d-%d", start, end)
-	}
+	line := GetLine(fobj, node)
 	col := strconv.Itoa(fobj.Position(node.Pos()).Column)
 
 	var code string
-	if file, err := os.Open(fobj.Name()); err == nil {
+	if node == nil {
+		code = "invalid AST node provided"
+	}
+	if file, err := os.Open(fobj.Name()); err == nil && node != nil {
 		defer file.Close() // #nosec
 		s := codeSnippetStartLine(node, fobj)
 		e := codeSnippetEndLine(node, fobj)
-		code, err = codeSnippet(file, s, e, node)
+		code, err = CodeSnippet(file, s, e)
 		if err != nil {
 			code = err.Error()
 		}
@@ -196,6 +210,22 @@ func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score,
 		Confidence: confidence,
 		Severity:   severity,
 		Code:       code,
-		Cwe:        IssueToCWE[ruleID],
+		Cwe:        GetCweByRule(ruleID),
+	}
+}
+
+// WithSuppressions set the suppressions of the issue
+func (i *Issue) WithSuppressions(suppressions []SuppressionInfo) *Issue {
+	i.Suppressions = suppressions
+	return i
+}
+
+// GetLine returns the line number of a given ast.Node
+func GetLine(fobj *token.File, node ast.Node) string {
+	start, end := fobj.Line(node.Pos()), fobj.Line(node.End())
+	line := strconv.Itoa(start)
+	if start != end {
+		line = fmt.Sprintf("%d-%d", start, end)
 	}
+	return line
 }
diff --git a/issue_test.go b/issue/issue_test.go
similarity index 88%
rename from issue_test.go
rename to issue/issue_test.go
index b7bfe198f5..ed19c81bcc 100644
--- a/issue_test.go
+++ b/issue/issue_test.go
@@ -1,17 +1,18 @@
-package gosec_test
+package issue_test
 
 import (
 	"go/ast"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 	"github.com/securego/gosec/v2/rules"
 	"github.com/securego/gosec/v2/testutils"
 )
 
 var _ = Describe("Issue", func() {
-
 	Context("when creating a new issue", func() {
 		It("should create a code snippet from the specified ast.Node", func() {
 			var target *ast.BasicLit
@@ -37,12 +38,13 @@ var _ = Describe("Issue", func() {
 			ast.Walk(v, ctx.Root)
 			Expect(target).ShouldNot(BeNil())
 
-			issue := gosec.NewIssue(ctx, target, "TEST", "", gosec.High, gosec.High)
+			fobj := ctx.GetFileAtNodePos(target)
+			issue := issue.New(fobj, target, "TEST", "", issue.High, issue.High)
 			Expect(issue).ShouldNot(BeNil())
 			Expect(issue.Code).Should(MatchRegexp(`"bar"`))
 			Expect(issue.Line).Should(Equal("2"))
 			Expect(issue.Col).Should(Equal("16"))
-			Expect(issue.Cwe.ID).Should(Equal(""))
+			Expect(issue.Cwe).Should(BeNil())
 		})
 
 		It("should return an error if specific context is not able to be obtained", func() {
@@ -77,13 +79,13 @@ var _ = Describe("Issue", func() {
 			ast.Walk(v, ctx.Root)
 			Expect(target).ShouldNot(BeNil())
 
-			// Use hardcodeded rule to check assignment
+			// Use hardcoded rule to check assignment
 			cfg := gosec.NewConfig()
 			rule, _ := rules.NewHardcodedCredentials("TEST", cfg)
-			issue, err := rule.Match(target, ctx)
+			foundIssue, err := rule.Match(target, ctx)
 			Expect(err).ShouldNot(HaveOccurred())
-			Expect(issue).ShouldNot(BeNil())
-			Expect(issue.FileLocation()).Should(MatchRegexp("foo.go:5"))
+			Expect(foundIssue).ShouldNot(BeNil())
+			Expect(foundIssue.FileLocation()).Should(MatchRegexp("foo.go:5"))
 		})
 
 		It("should provide accurate line and file information", func() {
@@ -134,7 +136,5 @@ func main() {
 		It("should maintain the provided confidence score", func() {
 			Skip("Not implemented")
 		})
-
 	})
-
 })
diff --git a/output/formatter.go b/output/formatter.go
deleted file mode 100644
index 8aa55e8d94..0000000000
--- a/output/formatter.go
+++ /dev/null
@@ -1,438 +0,0 @@
-// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package output
-
-import (
-	"bufio"
-	"bytes"
-	"encoding/csv"
-	"encoding/json"
-	"encoding/xml"
-	"fmt"
-	htmlTemplate "html/template"
-	"io"
-	"strconv"
-	"strings"
-	plainTemplate "text/template"
-
-	color "github.com/gookit/color"
-	"github.com/securego/gosec/v2"
-	"gopkg.in/yaml.v2"
-)
-
-// ReportFormat enumerates the output format for reported issues
-type ReportFormat int
-
-const (
-	// ReportText is the default format that writes to stdout
-	ReportText ReportFormat = iota // Plain text format
-
-	// ReportJSON set the output format to json
-	ReportJSON // Json format
-
-	// ReportCSV set the output format to csv
-	ReportCSV // CSV format
-
-	// ReportJUnitXML set the output format to junit xml
-	ReportJUnitXML // JUnit XML format
-
-	// ReportSARIF set the output format to SARIF
-	ReportSARIF // SARIF format
-
-	//SonarqubeEffortMinutes effort to fix in minutes
-	SonarqubeEffortMinutes = 5
-)
-
-var text = `Results:
-{{range $filePath,$fileErrors := .Errors}}
-Golang errors in file: [{{ $filePath }}]:
-{{range $index, $error := $fileErrors}}
-  > [line {{$error.Line}} : column {{$error.Column}}] - {{$error.Err}}
-{{end}}
-{{end}}
-{{ range $index, $issue := .Issues }}
-[{{ highlight $issue.FileLocation $issue.Severity }}] - {{ $issue.RuleID }} (CWE-{{ $issue.Cwe.ID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
-{{ printCode $issue }}
-
-{{ end }}
-{{ notice "Summary:" }}
-   Files: {{.Stats.NumFiles}}
-   Lines: {{.Stats.NumLines}}
-   Nosec: {{.Stats.NumNosec}}
-  Issues: {{ if eq .Stats.NumFound 0 }}
-	{{- success .Stats.NumFound }}
-	{{- else }}
-	{{- danger .Stats.NumFound }}
-	{{- end }}
-
-`
-
-type reportInfo struct {
-	Errors map[string][]gosec.Error `json:"Golang errors"`
-	Issues []*gosec.Issue
-	Stats  *gosec.Metrics
-}
-
-// CreateReport generates a report based for the supplied issues and metrics given
-// the specified format. The formats currently accepted are: json, yaml, csv, junit-xml, html, sonarqube, golint and text.
-func CreateReport(w io.Writer, format string, enableColor bool, rootPaths []string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
-	data := &reportInfo{
-		Errors: errors,
-		Issues: issues,
-		Stats:  metrics,
-	}
-	var err error
-	switch format {
-	case "json":
-		err = reportJSON(w, data)
-	case "yaml":
-		err = reportYAML(w, data)
-	case "csv":
-		err = reportCSV(w, data)
-	case "junit-xml":
-		err = reportJUnitXML(w, data)
-	case "html":
-		err = reportFromHTMLTemplate(w, html, data)
-	case "text":
-		err = reportFromPlaintextTemplate(w, text, enableColor, data)
-	case "sonarqube":
-		err = reportSonarqube(rootPaths, w, data)
-	case "golint":
-		err = reportGolint(w, data)
-	case "sarif":
-		err = reportSARIFTemplate(rootPaths, w, data)
-	default:
-		err = reportFromPlaintextTemplate(w, text, enableColor, data)
-	}
-	return err
-}
-
-func reportSonarqube(rootPaths []string, w io.Writer, data *reportInfo) error {
-	si, err := convertToSonarIssues(rootPaths, data)
-	if err != nil {
-		return err
-	}
-	raw, err := json.MarshalIndent(si, "", "\t")
-	if err != nil {
-		return err
-	}
-	_, err = w.Write(raw)
-	return err
-}
-
-func convertToSonarIssues(rootPaths []string, data *reportInfo) (*sonarIssues, error) {
-	si := &sonarIssues{[]sonarIssue{}}
-	for _, issue := range data.Issues {
-		var sonarFilePath string
-		for _, rootPath := range rootPaths {
-			if strings.HasPrefix(issue.File, rootPath) {
-				sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
-			}
-		}
-
-		if sonarFilePath == "" {
-			continue
-		}
-
-		lines := strings.Split(issue.Line, "-")
-		startLine, err := strconv.Atoi(lines[0])
-		if err != nil {
-			return si, err
-		}
-		endLine := startLine
-		if len(lines) > 1 {
-			endLine, err = strconv.Atoi(lines[1])
-			if err != nil {
-				return si, err
-			}
-		}
-
-		s := sonarIssue{
-			EngineID: "gosec",
-			RuleID:   issue.RuleID,
-			PrimaryLocation: location{
-				Message:   issue.What,
-				FilePath:  sonarFilePath,
-				TextRange: textRange{StartLine: startLine, EndLine: endLine},
-			},
-			Type:          "VULNERABILITY",
-			Severity:      getSonarSeverity(issue.Severity.String()),
-			EffortMinutes: SonarqubeEffortMinutes,
-			Cwe:           issue.Cwe,
-		}
-		si.SonarIssues = append(si.SonarIssues, s)
-	}
-	return si, nil
-}
-
-func convertToSarifReport(rootPaths []string, data *reportInfo) (*sarifReport, error) {
-	sr := buildSarifReport()
-
-	type rule struct {
-		index int
-		rule  *sarifRule
-	}
-
-	rules := make([]*sarifRule, 0)
-	rulesIndices := make(map[string]rule)
-	lastRuleIndex := -1
-
-	results := []*sarifResult{}
-
-	for _, issue := range data.Issues {
-		r, ok := rulesIndices[issue.RuleID]
-		if !ok {
-			lastRuleIndex++
-			r = rule{index: lastRuleIndex, rule: buildSarifRule(issue)}
-			rulesIndices[issue.RuleID] = r
-			rules = append(rules, r.rule)
-		}
-
-		location, err := buildSarifLocation(issue, rootPaths)
-		if err != nil {
-			return nil, err
-		}
-
-		result := &sarifResult{
-			RuleID:    r.rule.ID,
-			RuleIndex: r.index,
-			Level:     getSarifLevel(issue.Severity.String()),
-			Message: &sarifMessage{
-				Text: issue.What,
-			},
-			Locations: []*sarifLocation{location},
-		}
-
-		results = append(results, result)
-	}
-
-	tool := &sarifTool{
-		Driver: &sarifDriver{
-			Name:           "gosec",
-			Version:        "2.1.0",
-			InformationURI: "https://github.com/securego/gosec/",
-			Rules:          rules,
-		},
-	}
-
-	run := &sarifRun{
-		Tool:    tool,
-		Results: results,
-	}
-
-	sr.Runs = append(sr.Runs, run)
-
-	return sr, nil
-}
-
-func reportJSON(w io.Writer, data *reportInfo) error {
-	raw, err := json.MarshalIndent(data, "", "\t")
-	if err != nil {
-		return err
-	}
-
-	_, err = w.Write(raw)
-	return err
-}
-
-func reportYAML(w io.Writer, data *reportInfo) error {
-	raw, err := yaml.Marshal(data)
-	if err != nil {
-		return err
-	}
-	_, err = w.Write(raw)
-	return err
-}
-
-func reportCSV(w io.Writer, data *reportInfo) error {
-	out := csv.NewWriter(w)
-	defer out.Flush()
-	for _, issue := range data.Issues {
-		err := out.Write([]string{
-			issue.File,
-			issue.Line,
-			issue.What,
-			issue.Severity.String(),
-			issue.Confidence.String(),
-			issue.Code,
-			fmt.Sprintf("CWE-%s", issue.Cwe.ID),
-		})
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func reportGolint(w io.Writer, data *reportInfo) error {
-	// Output Sample:
-	// /tmp/main.go:11:14: [CWE-310] RSA keys should be at least 2048 bits (Rule:G403, Severity:MEDIUM, Confidence:HIGH)
-
-	for _, issue := range data.Issues {
-		what := issue.What
-		if issue.Cwe.ID != "" {
-			what = fmt.Sprintf("[CWE-%s] %s", issue.Cwe.ID, issue.What)
-		}
-
-		// issue.Line uses "start-end" format for multiple line detection.
-		lines := strings.Split(issue.Line, "-")
-		start := lines[0]
-
-		_, err := fmt.Fprintf(w, "%s:%s:%s: %s (Rule:%s, Severity:%s, Confidence:%s)\n",
-			issue.File,
-			start,
-			issue.Col,
-			what,
-			issue.RuleID,
-			issue.Severity.String(),
-			issue.Confidence.String(),
-		)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func reportJUnitXML(w io.Writer, data *reportInfo) error {
-	junitXMLStruct := createJUnitXMLStruct(data)
-	raw, err := xml.MarshalIndent(junitXMLStruct, "", "\t")
-	if err != nil {
-		return err
-	}
-
-	xmlHeader := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-	raw = append(xmlHeader, raw...)
-	_, err = w.Write(raw)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func reportSARIFTemplate(rootPaths []string, w io.Writer, data *reportInfo) error {
-	sr, err := convertToSarifReport(rootPaths, data)
-	if err != nil {
-		return err
-	}
-	raw, err := json.MarshalIndent(sr, "", "\t")
-	if err != nil {
-		return err
-	}
-
-	_, err = w.Write(raw)
-	return err
-}
-
-func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, enableColor bool, data *reportInfo) error {
-	t, e := plainTemplate.
-		New("gosec").
-		Funcs(plainTextFuncMap(enableColor)).
-		Parse(reportTemplate)
-	if e != nil {
-		return e
-	}
-
-	return t.Execute(w, data)
-}
-
-func reportFromHTMLTemplate(w io.Writer, reportTemplate string, data *reportInfo) error {
-	t, e := htmlTemplate.New("gosec").Parse(reportTemplate)
-	if e != nil {
-		return e
-	}
-
-	return t.Execute(w, data)
-}
-
-func plainTextFuncMap(enableColor bool) plainTemplate.FuncMap {
-	if enableColor {
-		return plainTemplate.FuncMap{
-			"highlight": highlight,
-			"danger":    color.Danger.Render,
-			"notice":    color.Notice.Render,
-			"success":   color.Success.Render,
-			"printCode": printCodeSnippet,
-		}
-	}
-
-	// by default those functions return the given content untouched
-	return plainTemplate.FuncMap{
-		"highlight": func(t string, s gosec.Score) string {
-			return t
-		},
-		"danger":    fmt.Sprint,
-		"notice":    fmt.Sprint,
-		"success":   fmt.Sprint,
-		"printCode": printCodeSnippet,
-	}
-}
-
-var (
-	errorTheme   = color.New(color.FgLightWhite, color.BgRed)
-	warningTheme = color.New(color.FgBlack, color.BgYellow)
-	defaultTheme = color.New(color.FgWhite, color.BgBlack)
-)
-
-// highlight returns content t colored based on Score
-func highlight(t string, s gosec.Score) string {
-	switch s {
-	case gosec.High:
-		return errorTheme.Sprint(t)
-	case gosec.Medium:
-		return warningTheme.Sprint(t)
-	default:
-		return defaultTheme.Sprint(t)
-	}
-}
-
-// printCodeSnippet prints the code snippet from the issue by adding a marker to the affected line
-func printCodeSnippet(issue *gosec.Issue) string {
-	start, end := parseLine(issue.Line)
-	scanner := bufio.NewScanner(strings.NewReader(issue.Code))
-	var buf bytes.Buffer
-	line := start
-	for scanner.Scan() {
-		codeLine := scanner.Text()
-		if strings.HasPrefix(codeLine, strconv.Itoa(line)) && line <= end {
-			codeLine = "  > " + codeLine + "\n"
-			line++
-		} else {
-			codeLine = "    " + codeLine + "\n"
-		}
-		buf.WriteString(codeLine)
-	}
-	return buf.String()
-}
-
-// parseLine extract the start and the end line numbers from a issue line
-func parseLine(line string) (int, int) {
-	parts := strings.Split(line, "-")
-	start := parts[0]
-	end := start
-	if len(parts) > 1 {
-		end = parts[1]
-	}
-	s, err := strconv.Atoi(start)
-	if err != nil {
-		return -1, -1
-	}
-	e, err := strconv.Atoi(end)
-	if err != nil {
-		return -1, -1
-	}
-	return s, e
-}
diff --git a/output/junit_xml_format.go b/output/junit_xml_format.go
deleted file mode 100644
index de11e49532..0000000000
--- a/output/junit_xml_format.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package output
-
-import (
-	"encoding/xml"
-	htmlLib "html"
-	"strconv"
-
-	"github.com/securego/gosec/v2"
-)
-
-type junitXMLReport struct {
-	XMLName    xml.Name    `xml:"testsuites"`
-	Testsuites []testsuite `xml:"testsuite"`
-}
-
-type testsuite struct {
-	XMLName   xml.Name   `xml:"testsuite"`
-	Name      string     `xml:"name,attr"`
-	Tests     int        `xml:"tests,attr"`
-	Testcases []testcase `xml:"testcase"`
-}
-
-type testcase struct {
-	XMLName xml.Name `xml:"testcase"`
-	Name    string   `xml:"name,attr"`
-	Failure failure  `xml:"failure"`
-}
-
-type failure struct {
-	XMLName xml.Name `xml:"failure"`
-	Message string   `xml:"message,attr"`
-	Text    string   `xml:",innerxml"`
-}
-
-func generatePlaintext(issue *gosec.Issue) string {
-	return "Results:\n" +
-		"[" + issue.File + ":" + issue.Line + "] - " +
-		issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
-		", Severity: " + strconv.Itoa(int(issue.Severity)) +
-		", CWE: " + issue.Cwe.ID + ")\n" + "> " + htmlLib.EscapeString(issue.Code)
-}
-
-func createJUnitXMLStruct(data *reportInfo) junitXMLReport {
-	var xmlReport junitXMLReport
-	testsuites := map[string]int{}
-
-	for _, issue := range data.Issues {
-		index, ok := testsuites[issue.What]
-		if !ok {
-			xmlReport.Testsuites = append(xmlReport.Testsuites, testsuite{
-				Name: issue.What,
-			})
-			index = len(xmlReport.Testsuites) - 1
-			testsuites[issue.What] = index
-		}
-		testcase := testcase{
-			Name: issue.File,
-			Failure: failure{
-				Message: "Found 1 vulnerability. See stacktrace for details.",
-				Text:    generatePlaintext(issue),
-			},
-		}
-
-		xmlReport.Testsuites[index].Testcases = append(xmlReport.Testsuites[index].Testcases, testcase)
-		xmlReport.Testsuites[index].Tests++
-	}
-
-	return xmlReport
-}
diff --git a/output/sarif_format.go b/output/sarif_format.go
deleted file mode 100644
index 16624bef44..0000000000
--- a/output/sarif_format.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package output
-
-import (
-	"fmt"
-	"strconv"
-	"strings"
-
-	"github.com/securego/gosec/v2"
-)
-
-type sarifLevel string
-
-const (
-	sarifNone    = sarifLevel("none")
-	sarifNote    = sarifLevel("note")
-	sarifWarning = sarifLevel("warning")
-	sarifError   = sarifLevel("error")
-)
-
-type sarifProperties struct {
-	Tags []string `json:"tags"`
-}
-
-type sarifRule struct {
-	ID                   string              `json:"id"`
-	Name                 string              `json:"name"`
-	ShortDescription     *sarifMessage       `json:"shortDescription"`
-	FullDescription      *sarifMessage       `json:"fullDescription"`
-	Help                 *sarifMessage       `json:"help"`
-	Properties           *sarifProperties    `json:"properties"`
-	DefaultConfiguration *sarifConfiguration `json:"defaultConfiguration"`
-}
-
-type sarifConfiguration struct {
-	Level sarifLevel `json:"level"`
-}
-
-type sarifArtifactLocation struct {
-	URI string `json:"uri"`
-}
-
-type sarifRegion struct {
-	StartLine   uint64 `json:"startLine"`
-	EndLine     uint64 `json:"endLine"`
-	StartColumn uint64 `json:"startColumn"`
-	EndColumn   uint64 `json:"endColumn"`
-}
-
-type sarifPhysicalLocation struct {
-	ArtifactLocation *sarifArtifactLocation `json:"artifactLocation"`
-	Region           *sarifRegion           `json:"region"`
-}
-
-type sarifLocation struct {
-	PhysicalLocation *sarifPhysicalLocation `json:"physicalLocation"`
-}
-
-type sarifMessage struct {
-	Text string `json:"text"`
-}
-
-type sarifResult struct {
-	RuleID    string           `json:"ruleId"`
-	RuleIndex int              `json:"ruleIndex"`
-	Level     sarifLevel       `json:"level"`
-	Message   *sarifMessage    `json:"message"`
-	Locations []*sarifLocation `json:"locations"`
-}
-
-type sarifDriver struct {
-	Name           string       `json:"name"`
-	Version        string       `json:"version"`
-	InformationURI string       `json:"informationUri"`
-	Rules          []*sarifRule `json:"rules,omitempty"`
-}
-
-type sarifTool struct {
-	Driver *sarifDriver `json:"driver"`
-}
-
-type sarifRun struct {
-	Tool    *sarifTool     `json:"tool"`
-	Results []*sarifResult `json:"results"`
-}
-
-type sarifReport struct {
-	Schema  string      `json:"$schema"`
-	Version string      `json:"version"`
-	Runs    []*sarifRun `json:"runs"`
-}
-
-// buildSarifReport return SARIF report struct
-func buildSarifReport() *sarifReport {
-	return &sarifReport{
-		Version: "2.1.0",
-		Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
-		Runs:    []*sarifRun{},
-	}
-}
-
-// buildSarifRule return SARIF rule field struct
-func buildSarifRule(issue *gosec.Issue) *sarifRule {
-	return &sarifRule{
-		ID:   fmt.Sprintf("%s (CWE-%s)", issue.RuleID, issue.Cwe.ID),
-		Name: issue.What,
-		ShortDescription: &sarifMessage{
-			Text: issue.What,
-		},
-		FullDescription: &sarifMessage{
-			Text: issue.What,
-		},
-		Help: &sarifMessage{
-			Text: fmt.Sprintf("%s\nSeverity: %s\nConfidence: %s\nCWE: %s", issue.What, issue.Severity.String(), issue.Confidence.String(), issue.Cwe.URL),
-		},
-		Properties: &sarifProperties{
-			Tags: []string{fmt.Sprintf("CWE-%s", issue.Cwe.ID), issue.Severity.String()},
-		},
-		DefaultConfiguration: &sarifConfiguration{
-			Level: getSarifLevel(issue.Severity.String()),
-		},
-	}
-}
-
-// buildSarifLocation return SARIF location struct
-func buildSarifLocation(issue *gosec.Issue, rootPaths []string) (*sarifLocation, error) {
-	var filePath string
-
-	lines := strings.Split(issue.Line, "-")
-	startLine, err := strconv.ParseUint(lines[0], 10, 64)
-	if err != nil {
-		return nil, err
-	}
-	endLine := startLine
-	if len(lines) > 1 {
-		endLine, err = strconv.ParseUint(lines[1], 10, 64)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	col, err := strconv.ParseUint(issue.Col, 10, 64)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, rootPath := range rootPaths {
-		if strings.HasPrefix(issue.File, rootPath) {
-			filePath = strings.Replace(issue.File, rootPath+"/", "", 1)
-		}
-	}
-
-	location := &sarifLocation{
-		PhysicalLocation: &sarifPhysicalLocation{
-			ArtifactLocation: &sarifArtifactLocation{
-				URI: filePath,
-			},
-			Region: &sarifRegion{
-				StartLine:   startLine,
-				EndLine:     endLine,
-				StartColumn: col,
-				EndColumn:   col,
-			},
-		},
-	}
-
-	return location, nil
-}
-
-// From https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127839
-// * "warning": The rule specified by ruleId was evaluated and a problem was found.
-// * "error": The rule specified by ruleId was evaluated and a serious problem was found.
-// * "note": The rule specified by ruleId was evaluated and a minor problem or an opportunity to improve the code was found.
-func getSarifLevel(s string) sarifLevel {
-	switch s {
-	case "LOW":
-		return sarifWarning
-	case "MEDIUM":
-		return sarifError
-	case "HIGH":
-		return sarifError
-	default:
-		return sarifNote
-	}
-}
diff --git a/output/sonarqube_format.go b/output/sonarqube_format.go
deleted file mode 100644
index dc7cb0e508..0000000000
--- a/output/sonarqube_format.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package output
-
-import "github.com/securego/gosec/v2"
-
-type textRange struct {
-	StartLine   int `json:"startLine"`
-	EndLine     int `json:"endLine"`
-	StartColumn int `json:"startColumn,omitempty"`
-	EtartColumn int `json:"endColumn,omitempty"`
-}
-type location struct {
-	Message   string    `json:"message"`
-	FilePath  string    `json:"filePath"`
-	TextRange textRange `json:"textRange,omitempty"`
-}
-
-type sonarIssue struct {
-	EngineID           string     `json:"engineId"`
-	RuleID             string     `json:"ruleId"`
-	Cwe                gosec.Cwe  `json:"cwe"`
-	PrimaryLocation    location   `json:"primaryLocation"`
-	Type               string     `json:"type"`
-	Severity           string     `json:"severity"`
-	EffortMinutes      int        `json:"effortMinutes"`
-	SecondaryLocations []location `json:"secondaryLocations,omitempty"`
-}
-
-type sonarIssues struct {
-	SonarIssues []sonarIssue `json:"issues"`
-}
-
-func getSonarSeverity(s string) string {
-	switch s {
-	case "LOW":
-		return "MINOR"
-	case "MEDIUM":
-		return "MAJOR"
-	case "HIGH":
-		return "BLOCKER"
-	default:
-		return "INFO"
-	}
-}
diff --git a/output/template.go b/output/template.go
deleted file mode 100644
index c69f586582..0000000000
--- a/output/template.go
+++ /dev/null
@@ -1,396 +0,0 @@
-// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package output
-
-const html = `
-<!doctype html>
-<html lang="en">
-<head>
-  <meta charset="utf-8">
-  <title>Go AST Scanner</title>
-  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.2.1/css/bulma.min.css" integrity="sha256-DRcOKg8NK1KkSkcymcGmxOtS/lAn0lHWJXRa15gMHHk=" crossorigin="anonymous"/>
-  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js" integrity="sha256-cLWs9L+cjZg8CjGHMpJqUgKKouPlmoMP/0wIdPtaPGs=" crossorigin="anonymous"></script>
-  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js" integrity="sha256-JIW8lNqN2EtqC6ggNZYnAdKMJXRQfkPMvdRt+b0/Jxc=" crossorigin="anonymous"></script>
-  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.17.0/babel.min.js" integrity="sha256-1IWWLlCKFGFj/cjryvC7GDF5wRYnf9tSvNVVEj8Bm+o=" crossorigin="anonymous"></script>
-  <style>
-    div.issue div.tag, div.panel-block input[type="checkbox"] {
-      margin-right: 0.5em;
-    }
-    
-    label.disabled {
-      text-decoration: line-through;
-    }
-    
-    nav.panel select {
-      width: 100%;
-    }
-
-    .break-word {
-      word-wrap: break-word;
-    }
-  </style>
-</head>
-<body>
-  <section class="section">
-    <div class="container">
-      <div id="content"></div>
-    </div>
-  </section>
-  <script>
-    var data = {{ . }};
-  </script>
-  <script type="text/babel">
-    var IssueTag = React.createClass({
-      render: function() {
-        var level = ""
-        if (this.props.level === "HIGH") {
-          level = "is-danger";
-        }
-        if (this.props.level === "MEDIUM") {
-          level = "is-warning";
-        }
-        return (
-          <div className="tag { level }">
-            { this.props.label }: { this.props.level }
-          </div>
-        );
-      }
-    });
-    
-    var Issue = React.createClass({
-      render: function() {
-        return (
-          <div className="issue box">
-            <div className="is-pulled-right">
-              <IssueTag label="Severity" level={ this.props.data.severity }/>
-              <IssueTag label="Confidence" level={ this.props.data.confidence }/>
-            </div>
-            <p>
-              <strong className="break-word">
-                { this.props.data.file } (line { this.props.data.line })
-              </strong>
-              <br/>
-              { this.props.data.details }
-            </p>
-            <figure className="highlight">
-              <pre>
-                <code className="golang hljs">
-                  { this.props.data.code }
-                </code>
-              </pre>
-            </figure>
-          </div>
-        );
-      }
-    });
-    
-    var Stats = React.createClass({
-      render: function() {
-        return (
-          <p className="help">
-            Scanned { this.props.data.Stats.files.toLocaleString() } files
-            with { this.props.data.Stats.lines.toLocaleString() } lines of code.
-          </p>
-        );
-      }
-    });
-    
-    var Issues = React.createClass({
-      render: function() {
-        if (this.props.data.Stats.files === 0) {
-          return (
-            <div className="notification">
-              No source files found. Do you even Go?
-            </div>
-          );
-        }
-    
-        if (this.props.data.Issues.length === 0) {
-          return (
-            <div>
-              <div className="notification">
-                Awesome! No issues found!
-              </div>
-              <Stats data={ this.props.data } />
-            </div>
-          );
-        }
-    
-        var issues = this.props.data.Issues
-          .filter(function(issue) {
-            return this.props.severity.includes(issue.severity);
-          }.bind(this))
-          .filter(function(issue) {
-            return this.props.confidence.includes(issue.confidence);
-          }.bind(this))
-          .filter(function(issue) {
-            if (this.props.issueType) {
-              return issue.details.toLowerCase().startsWith(this.props.issueType.toLowerCase());
-            } else {
-              return true
-            }
-          }.bind(this))
-          .map(function(issue) {
-            return (<Issue data={issue} />);
-          }.bind(this));
-    
-        if (issues.length === 0) {
-          return (
-            <div>
-              <div className="notification">
-                No issues matched given filters
-                (of total { this.props.data.Issues.length } issues).
-              </div>
-              <Stats data={ this.props.data } />
-            </div>
-          );
-        }
-    
-        return (
-          <div className="issues">
-            { issues }
-            <Stats data={ this.props.data } />
-          </div>
-        );
-      }
-    });
-    
-    var LevelSelector = React.createClass({
-      handleChange: function(level) {
-        return function(e) {
-          var updated = this.props.selected
-            .filter(function(item) { return item != level; });
-          if (e.target.checked) {
-            updated.push(level);
-          }
-          this.props.onChange(updated);
-        }.bind(this);
-      },
-      render: function() {
-        var highDisabled = !this.props.available.includes("HIGH");
-        var mediumDisabled = !this.props.available.includes("MEDIUM");
-        var lowDisabled = !this.props.available.includes("LOW");
-        var on = "", off = "disabled";
-        var HIGH = "HIGH", MEDIUM = "MEDIUM", LOW = "LOW";
-        return (
-          <span>
-            <label className="label checkbox { (highDisabled ? off : on )}">
-              <input
-                type="checkbox"
-                checked={ this.props.selected.includes(HIGH) }
-                disabled={ highDisabled }
-                onChange={ this.handleChange(HIGH) }/>
-              High
-            </label>
-            <label className="label checkbox {( mediumDisabled ? off : on )}">
-              <input
-                type="checkbox"
-                checked={ this.props.selected.includes(MEDIUM) }
-                disabled={ mediumDisabled }
-                onChange={ this.handleChange(MEDIUM) }/>
-              Medium
-            </label>
-            <label className="label checkbox {( lowDisabled ? off : on )}">
-              <input
-                type="checkbox"
-                checked={ this.props.selected.includes(LOW) }
-                disabled={ lowDisabled }
-                onChange={ this.handleChange(LOW) }/>
-              Low
-            </label>
-          </span>
-        );
-      }
-    });
-    
-    var Navigation = React.createClass({
-      updateSeverity: function(vals) {
-        this.props.onSeverity(vals);
-      },
-      updateConfidence: function(vals) {
-        this.props.onConfidence(vals);
-      },
-      updateIssueType: function(e) {
-        if (e.target.value == "all") {
-          this.props.onIssueType(null);
-        } else {
-          this.props.onIssueType(e.target.value);
-        }
-      },
-      render: function() {
-        var issueTypes = this.props.allIssueTypes
-          .map(function(it) {
-            var matches = this.props.issueType == it
-            return (
-              <option value={ it } selected={ matches }>
-                { it }
-              </option>
-            );
-          }.bind(this));
-        return (
-          <nav className="panel">
-            <div className="panel-heading">
-              Filters
-            </div>
-            <div className="panel-block">
-              <strong>
-                Severity
-              </strong>
-            </div>
-            <div className="panel-block">
-              <LevelSelector 
-                selected={ this.props.severity }
-                available={ this.props.allSeverities }
-                onChange={ this.updateSeverity } />
-            </div>
-            <div className="panel-block">
-              <strong>
-                Confidence
-              </strong>
-            </div>
-            <div className="panel-block">
-              <LevelSelector
-                selected={ this.props.confidence }
-                available={ this.props.allConfidences }
-                onChange={ this.updateConfidence } />
-            </div>
-            <div className="panel-block">
-              <strong>
-                Issue Type
-              </strong>
-            </div>
-            <div className="panel-block">
-              <select onChange={ this.updateIssueType }>
-                <option value="all" selected={ !this.props.issueType }>
-                  (all)
-                </option>
-                { issueTypes }
-              </select>
-            </div>
-          </nav>
-        );
-      }
-    });
-    var IssueBrowser = React.createClass({
-      getInitialState: function() {
-        return {};
-      },
-      componentWillMount: function() {
-        this.updateIssues(this.props.data);
-      },
-      handleSeverity: function(val) {
-        this.updateIssueTypes(this.props.data.Issues, val, this.state.confidence);
-        this.setState({severity: val});
-      },
-      handleConfidence: function(val) {
-        this.updateIssueTypes(this.props.data.Issues, this.state.severity, val);
-        this.setState({confidence: val});
-      },
-      handleIssueType: function(val) {
-        this.setState({issueType: val});
-      },
-      updateIssues: function(data) {
-        if (!data) {
-          this.setState({data: data});
-          return;
-        }
-        var allSeverities = data.Issues
-          .map(function(issue) {
-            return issue.severity
-          })
-          .sort()
-          .filter(function(item, pos, ary) {
-            return !pos || item != ary[pos - 1];
-          });
-        var allConfidences = data.Issues
-          .map(function(issue) {
-            return issue.confidence
-          })
-          .sort()
-          .filter(function(item, pos, ary) {
-            return !pos || item != ary[pos - 1];
-          });
-        var selectedSeverities = allSeverities;
-        var selectedConfidences = allConfidences;
-        this.updateIssueTypes(data.Issues, selectedSeverities, selectedConfidences);
-        this.setState({
-          data: data,
-          severity: selectedSeverities,
-          allSeverities: allSeverities,
-          confidence: selectedConfidences,
-          allConfidences: allConfidences,
-          issueType: null
-        });
-      },
-      updateIssueTypes: function(issues, severities, confidences) {
-        var allTypes = issues
-          .filter(function(issue) {
-            return severities.includes(issue.severity);
-          })
-          .filter(function(issue) {
-            return confidences.includes(issue.confidence);
-          })
-          .map(function(issue) {
-            return issue.details;
-          })
-          .sort()
-          .filter(function(item, pos, ary) {
-            return !pos || item != ary[pos - 1];
-          });
-    
-        if (this.state.issueType && !allTypes.includes(this.state.issueType)) {
-          this.setState({issueType: null});
-        }
-        
-        this.setState({allIssueTypes: allTypes});
-      },
-      render: function() {
-        return (
-          <div className="content">
-            <div className="columns">
-              <div className="column is-one-quarter">
-                <Navigation
-                  severity={ this.state.severity } 
-                  confidence={ this.state.confidence }
-                  issueType={ this.state.issueType }
-                  allSeverities={ this.state.allSeverities } 
-                  allConfidences={ this.state.allConfidences }
-                  allIssueTypes={ this.state.allIssueTypes }
-                  onSeverity={ this.handleSeverity } 
-                  onConfidence={ this.handleConfidence } 
-                  onIssueType={ this.handleIssueType }
-                />
-              </div>
-              <div className="column is-three-quarters">
-                <Issues
-                  data={ this.props.data }
-                  severity={ this.state.severity }
-                  confidence={ this.state.confidence }
-                  issueType={ this.state.issueType }
-                />
-              </div>
-            </div>
-          </div>
-        );
-      }
-    });
-
-    ReactDOM.render(
-      <IssueBrowser data={ data } />,
-      document.getElementById("content")
-    );
-  </script>
-</body>
-</html>`
diff --git a/perf-diff.sh b/perf-diff.sh
new file mode 100755
index 0000000000..cf3084cbf6
--- /dev/null
+++ b/perf-diff.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+BIN="gosec"
+BUILD_DIR="/tmp/securego"
+
+# Scan the current folder and measure the duration.
+function scan() {
+  local scan_cmd=$1
+  s=$(date +%s%3N)
+  $scan_cmd -quiet ./...
+  e=$(date +%s%3N)
+  res=$(expr $e - $s)
+  echo $res
+}
+
+# Build the master reference version.
+mkdir -p ${BUILD_DIR}
+git clone --quiet https://github.com/securego/gosec.git ${BUILD_DIR} >/dev/null
+make -C ${BUILD_DIR} >/dev/null
+
+# Scan once with the main reference.
+duration_master=$(scan "${BUILD_DIR}/${BIN}")
+echo "gosec reference time: ${duration_master}ms"
+
+# Build the current version.
+make -C . >/dev/null
+
+# Scan once with the current version.
+duration=$(scan "./${BIN}")
+echo "gosec time: ${duration}ms"
+
+# Compute the difference of the execution time.
+diff=$(($duration - $duration_master))
+if [[ diff -lt 0 ]]; then
+  diff=$(($diff * -1))
+fi
+echo "diff: ${diff}ms"
+perf=$((100 - ($duration * 100) / $duration_master))
+echo "perf diff: ${perf}%"
+
+# Fail the build if there is a performance degradation of more than 10%.
+if [[ $perf -lt -10 ]]; then
+  exit 1
+fi
diff --git a/renovate.json b/renovate.json
index 92327e12d5..58ee1e0ea8 100644
--- a/renovate.json
+++ b/renovate.json
@@ -1,7 +1,25 @@
 {
+  "dependencyDashboard": true,
+  "dependencyDashboardTitle" : "Renovate(bot) : dependency dashboard",
+  "vulnerabilityAlerts": {
+    "enabled": true
+  },
   "extends": [
-    "config:semverAllMonthly",
-    ":enableVulnerabilityAlertsWithLabel(vulnerablity)",
-    ":docker"
-  ]
+    ":preserveSemverRanges",
+    "group:all",
+    "schedule:weekly"
+  ],
+  "lockFileMaintenance": {
+    "commitMessageAction": "Update",
+    "enabled": true,
+    "extends": [
+      "group:all",
+      "schedule:weekly"
+    ]
+  },
+  "postUpdateOptions": [
+    "gomodTidy",
+    "gomodUpdateImportPaths"
+  ],
+  "separateMajorMinor": false
 }
diff --git a/report.go b/report.go
new file mode 100644
index 0000000000..4fdeea5206
--- /dev/null
+++ b/report.go
@@ -0,0 +1,28 @@
+package gosec
+
+import (
+	"github.com/securego/gosec/v2/issue"
+)
+
+// ReportInfo this is report information
+type ReportInfo struct {
+	Errors       map[string][]Error `json:"Golang errors"`
+	Issues       []*issue.Issue
+	Stats        *Metrics
+	GosecVersion string
+}
+
+// NewReportInfo instantiate a ReportInfo
+func NewReportInfo(issues []*issue.Issue, metrics *Metrics, errors map[string][]Error) *ReportInfo {
+	return &ReportInfo{
+		Errors: errors,
+		Issues: issues,
+		Stats:  metrics,
+	}
+}
+
+// WithVersion defines the version of gosec used to generate the report
+func (r *ReportInfo) WithVersion(version string) *ReportInfo {
+	r.GosecVersion = version
+	return r
+}
diff --git a/report/csv/writer.go b/report/csv/writer.go
new file mode 100644
index 0000000000..40bb6a1b1e
--- /dev/null
+++ b/report/csv/writer.go
@@ -0,0 +1,29 @@
+package csv
+
+import (
+	"encoding/csv"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in csv format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	out := csv.NewWriter(w)
+	defer out.Flush()
+	for _, issue := range data.Issues {
+		err := out.Write([]string{
+			issue.File,
+			issue.Line,
+			issue.What,
+			issue.Severity.String(),
+			issue.Confidence.String(),
+			issue.Code,
+			issue.Cwe.SprintID(),
+		})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/report/formatter.go b/report/formatter.go
new file mode 100644
index 0000000000..3e83dc6432
--- /dev/null
+++ b/report/formatter.go
@@ -0,0 +1,93 @@
+// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package report
+
+import (
+	"io"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+	"github.com/securego/gosec/v2/report/csv"
+	"github.com/securego/gosec/v2/report/golint"
+	"github.com/securego/gosec/v2/report/html"
+	"github.com/securego/gosec/v2/report/json"
+	"github.com/securego/gosec/v2/report/junit"
+	"github.com/securego/gosec/v2/report/sarif"
+	"github.com/securego/gosec/v2/report/sonar"
+	"github.com/securego/gosec/v2/report/text"
+	"github.com/securego/gosec/v2/report/yaml"
+)
+
+// Format enumerates the output format for reported issues
+type Format int
+
+const (
+	// ReportText is the default format that writes to stdout
+	ReportText Format = iota // Plain text format
+
+	// ReportJSON set the output format to json
+	ReportJSON // Json format
+
+	// ReportCSV set the output format to csv
+	ReportCSV // CSV format
+
+	// ReportJUnitXML set the output format to junit xml
+	ReportJUnitXML // JUnit XML format
+
+	// ReportSARIF set the output format to SARIF
+	ReportSARIF // SARIF format
+)
+
+// CreateReport generates a report based for the supplied issues and metrics given
+// the specified format. The formats currently accepted are: json, yaml, csv, junit-xml, html, sonarqube, golint and text.
+func CreateReport(w io.Writer, format string, enableColor bool, rootPaths []string, data *gosec.ReportInfo) error {
+	var err error
+	if format != "json" && format != "sarif" {
+		data.Issues = filterOutSuppressedIssues(data.Issues)
+	}
+	switch format {
+	case "json":
+		err = json.WriteReport(w, data)
+	case "yaml":
+		err = yaml.WriteReport(w, data)
+	case "csv":
+		err = csv.WriteReport(w, data)
+	case "junit-xml":
+		err = junit.WriteReport(w, data)
+	case "html":
+		err = html.WriteReport(w, data)
+	case "text":
+		err = text.WriteReport(w, data, enableColor)
+	case "sonarqube":
+		err = sonar.WriteReport(w, data, rootPaths)
+	case "golint":
+		err = golint.WriteReport(w, data)
+	case "sarif":
+		err = sarif.WriteReport(w, data, rootPaths)
+	default:
+		err = text.WriteReport(w, data, enableColor)
+	}
+	return err
+}
+
+func filterOutSuppressedIssues(issues []*issue.Issue) []*issue.Issue {
+	nonSuppressedIssues := []*issue.Issue{}
+	for _, issue := range issues {
+		if len(issue.Suppressions) == 0 {
+			nonSuppressedIssues = append(nonSuppressedIssues, issue)
+		}
+	}
+	return nonSuppressedIssues
+}
diff --git a/output/formatter_suite_test.go b/report/formatter_suite_test.go
similarity index 76%
rename from output/formatter_suite_test.go
rename to report/formatter_suite_test.go
index 62b1404b17..a7a9ba4d9d 100644
--- a/output/formatter_suite_test.go
+++ b/report/formatter_suite_test.go
@@ -1,10 +1,10 @@
-package output
+package report
 
 import (
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
 	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
 )
 
 func TestRules(t *testing.T) {
diff --git a/output/formatter_test.go b/report/formatter_test.go
similarity index 52%
rename from output/formatter_test.go
rename to report/formatter_test.go
index 9ac12eb211..ad4d9fc8ab 100644
--- a/output/formatter_test.go
+++ b/report/formatter_test.go
@@ -1,4 +1,4 @@
-package output
+package report
 
 import (
 	"bytes"
@@ -6,49 +6,54 @@ import (
 	"fmt"
 	"strings"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+	"gopkg.in/yaml.v3"
+
 	"github.com/securego/gosec/v2"
-	"gopkg.in/yaml.v2"
+	"github.com/securego/gosec/v2/cwe"
+	"github.com/securego/gosec/v2/issue"
+	"github.com/securego/gosec/v2/report/junit"
+	"github.com/securego/gosec/v2/report/sonar"
 )
 
-func createIssueWithFileWhat(file, what string) *gosec.Issue {
-	issue := createIssue("i1", gosec.GetCwe("G101"))
+func createIssueWithFileWhat(file, what string) *issue.Issue {
+	issue := createIssue("i1", issue.GetCweByRule("G101"))
 	issue.File = file
 	issue.What = what
 	return &issue
 }
 
-func createIssue(ruleID string, cwe gosec.Cwe) gosec.Issue {
-	return gosec.Issue{
+func createIssue(ruleID string, weakness *cwe.Weakness) issue.Issue {
+	return issue.Issue{
 		File:       "/home/src/project/test.go",
 		Line:       "1",
 		Col:        "1",
 		RuleID:     ruleID,
 		What:       "test",
-		Confidence: gosec.High,
-		Severity:   gosec.High,
+		Confidence: issue.High,
+		Severity:   issue.High,
 		Code:       "1: testcode",
-		Cwe:        cwe,
+		Cwe:        weakness,
 	}
 }
 
-func createReportInfo(rule string, cwe gosec.Cwe) reportInfo {
-	issue := createIssue(rule, cwe)
+func createReportInfo(rule string, weakness *cwe.Weakness) gosec.ReportInfo {
+	newissue := createIssue(rule, weakness)
 	metrics := gosec.Metrics{}
-	return reportInfo{
+	return gosec.ReportInfo{
 		Errors: map[string][]gosec.Error{},
-		Issues: []*gosec.Issue{
-			&issue,
+		Issues: []*issue.Issue{
+			&newissue,
 		},
 		Stats: &metrics,
 	}
 }
 
 func stripString(str string) string {
-	ret := strings.Replace(str, "\n", "", -1)
-	ret = strings.Replace(ret, " ", "", -1)
-	ret = strings.Replace(ret, "\t", "", -1)
+	ret := strings.ReplaceAll(str, "\n", "")
+	ret = strings.ReplaceAll(ret, " ", "")
+	ret = strings.ReplaceAll(ret, "\t", "")
 	return ret
 }
 
@@ -57,10 +62,10 @@ var _ = Describe("Formatter", func() {
 	})
 	Context("when converting to Sonarqube issues", func() {
 		It("it should parse the report info", func() {
-			data := &reportInfo{
+			data := &gosec.ReportInfo{
 				Errors: map[string][]gosec.Error{},
-				Issues: []*gosec.Issue{
-					&gosec.Issue{
+				Issues: []*issue.Issue{
+					{
 						Severity:   2,
 						Confidence: 0,
 						RuleID:     "test",
@@ -77,38 +82,38 @@ var _ = Describe("Formatter", func() {
 					NumFound: 0,
 				},
 			}
-			want := &sonarIssues{
-				SonarIssues: []sonarIssue{
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
 					{
 						EngineID: "gosec",
 						RuleID:   "test",
-						PrimaryLocation: location{
+						PrimaryLocation: &sonar.Location{
 							Message:  "test",
 							FilePath: "test.go",
-							TextRange: textRange{
+							TextRange: &sonar.TextRange{
 								StartLine: 1,
 								EndLine:   2,
 							},
 						},
 						Type:          "VULNERABILITY",
 						Severity:      "BLOCKER",
-						EffortMinutes: SonarqubeEffortMinutes,
+						EffortMinutes: sonar.EffortMinutes,
 					},
 				},
 			}
 
 			rootPath := "/home/src/project"
 
-			issues, err := convertToSonarIssues([]string{rootPath}, data)
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(*issues).To(Equal(*want))
 		})
 
 		It("it should parse the report info with files in subfolders", func() {
-			data := &reportInfo{
+			data := &gosec.ReportInfo{
 				Errors: map[string][]gosec.Error{},
-				Issues: []*gosec.Issue{
-					&gosec.Issue{
+				Issues: []*issue.Issue{
+					{
 						Severity:   2,
 						Confidence: 0,
 						RuleID:     "test",
@@ -125,37 +130,37 @@ var _ = Describe("Formatter", func() {
 					NumFound: 0,
 				},
 			}
-			want := &sonarIssues{
-				SonarIssues: []sonarIssue{
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
 					{
 						EngineID: "gosec",
 						RuleID:   "test",
-						PrimaryLocation: location{
+						PrimaryLocation: &sonar.Location{
 							Message:  "test",
 							FilePath: "subfolder/test.go",
-							TextRange: textRange{
+							TextRange: &sonar.TextRange{
 								StartLine: 1,
 								EndLine:   2,
 							},
 						},
 						Type:          "VULNERABILITY",
 						Severity:      "BLOCKER",
-						EffortMinutes: SonarqubeEffortMinutes,
+						EffortMinutes: sonar.EffortMinutes,
 					},
 				},
 			}
 
 			rootPath := "/home/src/project"
 
-			issues, err := convertToSonarIssues([]string{rootPath}, data)
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(*issues).To(Equal(*want))
 		})
 		It("it should not parse the report info for files from other projects", func() {
-			data := &reportInfo{
+			data := &gosec.ReportInfo{
 				Errors: map[string][]gosec.Error{},
-				Issues: []*gosec.Issue{
-					&gosec.Issue{
+				Issues: []*issue.Issue{
+					{
 						Severity:   2,
 						Confidence: 0,
 						RuleID:     "test",
@@ -172,22 +177,22 @@ var _ = Describe("Formatter", func() {
 					NumFound: 0,
 				},
 			}
-			want := &sonarIssues{
-				SonarIssues: []sonarIssue{},
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{},
 			}
 
 			rootPath := "/home/src/project2"
 
-			issues, err := convertToSonarIssues([]string{rootPath}, data)
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(*issues).To(Equal(*want))
 		})
 
-		It("it should parse the report info for multiple projects projects", func() {
-			data := &reportInfo{
+		It("it should parse the report info for multiple projects", func() {
+			data := &gosec.ReportInfo{
 				Errors: map[string][]gosec.Error{},
-				Issues: []*gosec.Issue{
-					&gosec.Issue{
+				Issues: []*issue.Issue{
+					{
 						Severity:   2,
 						Confidence: 0,
 						RuleID:     "test",
@@ -196,7 +201,7 @@ var _ = Describe("Formatter", func() {
 						Code:       "",
 						Line:       "1-2",
 					},
-					&gosec.Issue{
+					{
 						Severity:   2,
 						Confidence: 0,
 						RuleID:     "test",
@@ -213,44 +218,44 @@ var _ = Describe("Formatter", func() {
 					NumFound: 0,
 				},
 			}
-			want := &sonarIssues{
-				SonarIssues: []sonarIssue{
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
 					{
 						EngineID: "gosec",
 						RuleID:   "test",
-						PrimaryLocation: location{
+						PrimaryLocation: &sonar.Location{
 							Message:  "test",
 							FilePath: "test-project1.go",
-							TextRange: textRange{
+							TextRange: &sonar.TextRange{
 								StartLine: 1,
 								EndLine:   2,
 							},
 						},
 						Type:          "VULNERABILITY",
 						Severity:      "BLOCKER",
-						EffortMinutes: SonarqubeEffortMinutes,
+						EffortMinutes: sonar.EffortMinutes,
 					},
 					{
 						EngineID: "gosec",
 						RuleID:   "test",
-						PrimaryLocation: location{
+						PrimaryLocation: &sonar.Location{
 							Message:  "test",
 							FilePath: "test-project2.go",
-							TextRange: textRange{
+							TextRange: &sonar.TextRange{
 								StartLine: 1,
 								EndLine:   2,
 							},
 						},
 						Type:          "VULNERABILITY",
 						Severity:      "BLOCKER",
-						EffortMinutes: SonarqubeEffortMinutes,
+						EffortMinutes: sonar.EffortMinutes,
 					},
 				},
 			}
 
 			rootPaths := []string{"/home/src/project1", "/home/src/project2"}
 
-			issues, err := convertToSonarIssues(rootPaths, data)
+			issues, err := sonar.GenerateReport(rootPaths, data)
 			Expect(err).ShouldNot(HaveOccurred())
 			Expect(*issues).To(Equal(*want))
 		})
@@ -258,9 +263,9 @@ var _ = Describe("Formatter", func() {
 
 	Context("When using junit", func() {
 		It("preserves order of issues", func() {
-			issues := []*gosec.Issue{createIssueWithFileWhat("i1", "1"), createIssueWithFileWhat("i2", "2"), createIssueWithFileWhat("i3", "1")}
+			issues := []*issue.Issue{createIssueWithFileWhat("i1", "1"), createIssueWithFileWhat("i2", "2"), createIssueWithFileWhat("i3", "1")}
 
-			junitReport := createJUnitXMLStruct(&reportInfo{Issues: issues})
+			junitReport := junit.GenerateReport(&gosec.ReportInfo{Issues: issues})
 
 			testSuite := junitReport.Testsuites[0]
 
@@ -269,49 +274,81 @@ var _ = Describe("Formatter", func() {
 
 			testSuite = junitReport.Testsuites[1]
 			Expect(testSuite.Testcases[0].Name).To(Equal(issues[1].File))
-
 		})
 	})
 	Context("When using different report formats", func() {
-
-		grules := []string{"G101", "G102", "G103", "G104", "G106",
-			"G107", "G109", "G110", "G201", "G202", "G203", "G204",
-			"G301", "G302", "G303", "G304", "G305", "G401", "G402",
-			"G403", "G404", "G501", "G502", "G503", "G504", "G505"}
+		grules := []string{
+			"G101",
+			"G102",
+			"G103",
+			"G104",
+			"G106",
+			"G107",
+			"G109",
+			"G110",
+			"G111",
+			"G112",
+			"G201",
+			"G202",
+			"G203",
+			"G204",
+			"G301",
+			"G302",
+			"G303",
+			"G304",
+			"G305",
+			"G401",
+			"G402",
+			"G403",
+			"G404",
+			"G405",
+			"G406",
+			"G407",
+			"G501",
+			"G502",
+			"G503",
+			"G504",
+			"G505",
+			"G506",
+			"G507",
+			"G601",
+		}
 
 		It("csv formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				buf := new(bytes.Buffer)
-				err := CreateReport(buf, "csv", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err := CreateReport(buf, "csv", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				pattern := "/home/src/project/test.go,1,test,HIGH,HIGH,1: testcode,CWE-%s\n"
 				expect := fmt.Sprintf(pattern, cwe.ID)
-				Expect(string(buf.String())).To(Equal(expect))
+				Expect(buf.String()).To(Equal(expect))
 			}
 		})
 		It("xml formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				buf := new(bytes.Buffer)
-				err := CreateReport(buf, "xml", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{NumFiles: 0, NumLines: 0, NumNosec: 0, NumFound: 0}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{NumFiles: 0, NumLines: 0, NumNosec: 0, NumFound: 0}, errors).WithVersion("v2.7.0")
+				err := CreateReport(buf, "xml", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
-				pattern := "Results:\n\n\n[/home/src/project/test.go:1] - %s (CWE-%s): test (Confidence: HIGH, Severity: HIGH)\n  > 1: testcode\n\n\n\nSummary:\n   Files: 0\n   Lines: 0\n   Nosec: 0\n  Issues: 0\n\n"
+				pattern := "Results:\n\n\n[/home/src/project/test.go:1] - %s (CWE-%s): test (Confidence: HIGH, Severity: HIGH)\n  > 1: testcode\n\nAutofix: \n\nSummary:\n  Gosec  : v2.7.0\n  Files  : 0\n  Lines  : 0\n  Nosec  : 0\n  Issues : 0\n\n"
 				expect := fmt.Sprintf(pattern, rule, cwe.ID)
-				Expect(string(buf.String())).To(Equal(expect))
+				Expect(buf.String()).To(Equal(expect))
 			}
 		})
 		It("json formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				data := createReportInfo(rule, cwe)
 
@@ -320,7 +357,8 @@ var _ = Describe("Formatter", func() {
 				err := enc.Encode(data)
 				Expect(err).ShouldNot(HaveOccurred())
 				buf := new(bytes.Buffer)
-				err = CreateReport(buf, "json", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err = CreateReport(buf, "json", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				result := stripString(buf.String())
 				expectation := stripString(expect.String())
@@ -329,9 +367,9 @@ var _ = Describe("Formatter", func() {
 		})
 		It("html formatted report should  contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				data := createReportInfo(rule, cwe)
 
@@ -340,7 +378,8 @@ var _ = Describe("Formatter", func() {
 				err := enc.Encode(data)
 				Expect(err).ShouldNot(HaveOccurred())
 				buf := new(bytes.Buffer)
-				err = CreateReport(buf, "html", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err = CreateReport(buf, "html", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				result := stripString(buf.String())
 				expectation := stripString(expect.String())
@@ -349,9 +388,9 @@ var _ = Describe("Formatter", func() {
 		})
 		It("yaml formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				data := createReportInfo(rule, cwe)
 
@@ -360,7 +399,8 @@ var _ = Describe("Formatter", func() {
 				err := enc.Encode(data)
 				Expect(err).ShouldNot(HaveOccurred())
 				buf := new(bytes.Buffer)
-				err = CreateReport(buf, "yaml", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err = CreateReport(buf, "yaml", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				result := stripString(buf.String())
 				expectation := stripString(expect.String())
@@ -369,9 +409,9 @@ var _ = Describe("Formatter", func() {
 		})
 		It("junit-xml formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				data := createReportInfo(rule, cwe)
 
@@ -380,7 +420,8 @@ var _ = Describe("Formatter", func() {
 				err := enc.Encode(data)
 				Expect(err).ShouldNot(HaveOccurred())
 				buf := new(bytes.Buffer)
-				err = CreateReport(buf, "junit-xml", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err = CreateReport(buf, "junit-xml", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				expectation := stripString(fmt.Sprintf("[/home/src/project/test.go:1] - test (Confidence: 2, Severity: 2, CWE: %s)", cwe.ID))
 				result := stripString(buf.String())
@@ -389,9 +430,9 @@ var _ = Describe("Formatter", func() {
 		})
 		It("text formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				data := createReportInfo(rule, cwe)
 
@@ -400,20 +441,22 @@ var _ = Describe("Formatter", func() {
 				err := enc.Encode(data)
 				Expect(err).ShouldNot(HaveOccurred())
 				buf := new(bytes.Buffer)
-				err = CreateReport(buf, "text", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err = CreateReport(buf, "text", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				expectation := stripString(fmt.Sprintf("[/home/src/project/test.go:1] - %s (CWE-%s): test (Confidence: HIGH, Severity: HIGH)", rule, cwe.ID))
 				result := stripString(buf.String())
 				Expect(result).To(ContainSubstring(expectation))
 			}
 		})
-		It("sonarqube formatted report should contain the CWE mapping", func() {
+		It("sonarqube formatted report shouldn't contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 				buf := new(bytes.Buffer)
-				err := CreateReport(buf, "sonarqube", false, []string{"/home/src/project"}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err := CreateReport(buf, "sonarqube", false, []string{"/home/src/project"}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 
 				result := stripString(buf.String())
@@ -424,41 +467,104 @@ var _ = Describe("Formatter", func() {
 				Expect(err).ShouldNot(HaveOccurred())
 
 				expectation := stripString(expect.String())
-				Expect(result).To(ContainSubstring(expectation))
+				Expect(result).ShouldNot(ContainSubstring(expectation))
 			}
 		})
 		It("golint formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				buf := new(bytes.Buffer)
-				err := CreateReport(buf, "golint", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors)
+				err := CreateReport(buf, "golint", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 				pattern := "/home/src/project/test.go:1:1: [CWE-%s] test (Rule:%s, Severity:HIGH, Confidence:HIGH)\n"
 				expect := fmt.Sprintf(pattern, cwe.ID, rule)
-				Expect(string(buf.String())).To(Equal(expect))
+				Expect(buf.String()).To(Equal(expect))
 			}
 		})
 		It("sarif formatted report should contain the CWE mapping", func() {
 			for _, rule := range grules {
-				cwe := gosec.IssueToCWE[rule]
-				issue := createIssue(rule, cwe)
-				error := map[string][]gosec.Error{}
+				cwe := issue.GetCweByRule(rule)
+				newissue := createIssue(rule, cwe)
+				errors := map[string][]gosec.Error{}
 
 				buf := new(bytes.Buffer)
-				err := CreateReport(buf, "sarif", false, []string{}, []*gosec.Issue{&issue}, &gosec.Metrics{}, error)
+				reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, errors).WithVersion("v2.7.0")
+				err := CreateReport(buf, "sarif", false, []string{}, reportInfo)
 				Expect(err).ShouldNot(HaveOccurred())
 
 				result := stripString(buf.String())
 
-				pattern := "rules\":[{\"id\":\"%s(CWE-%s)\""
-				expect := fmt.Sprintf(pattern, rule, cwe.ID)
+				ruleIDPattern := "\"id\":\"%s\""
+				expectedRule := fmt.Sprintf(ruleIDPattern, rule)
+				Expect(err).ShouldNot(HaveOccurred())
+
+				Expect(result).To(ContainSubstring(expectedRule))
+
+				cweURIPattern := "\"helpUri\":\"https://cwe.mitre.org/data/definitions/%s.html\""
+				expectedCweURI := fmt.Sprintf(cweURIPattern, cwe.ID)
+				Expect(err).ShouldNot(HaveOccurred())
+
+				Expect(result).To(ContainSubstring(expectedCweURI))
+
+				cweIDPattern := "\"id\":\"%s\""
+				expectedCweID := fmt.Sprintf(cweIDPattern, cwe.ID)
 				Expect(err).ShouldNot(HaveOccurred())
 
-				Expect(result).To(ContainSubstring(expect))
+				Expect(result).To(ContainSubstring(expectedCweID))
 			}
 		})
 	})
+
+	Context("When converting suppressed issues", func() {
+		ruleID := "G101"
+		cwe := issue.GetCweByRule(ruleID)
+		suppressions := []issue.SuppressionInfo{
+			{
+				Kind:          "kind",
+				Justification: "justification",
+			},
+		}
+		suppressedIssue := createIssue(ruleID, cwe)
+		suppressedIssue.WithSuppressions(suppressions)
+
+		It("text formatted report should contain the suppressed issues", func() {
+			errors := map[string][]gosec.Error{}
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&suppressedIssue}, &gosec.Metrics{}, errors)
+
+			buf := new(bytes.Buffer)
+			err := CreateReport(buf, "text", false, []string{}, reportInfo)
+			Expect(err).ShouldNot(HaveOccurred())
+
+			result := stripString(buf.String())
+			Expect(result).To(ContainSubstring("Results:Summary"))
+		})
+
+		It("sarif formatted report should contain the suppressed issues", func() {
+			errors := map[string][]gosec.Error{}
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&suppressedIssue}, &gosec.Metrics{}, errors)
+
+			buf := new(bytes.Buffer)
+			err := CreateReport(buf, "sarif", false, []string{}, reportInfo)
+			Expect(err).ShouldNot(HaveOccurred())
+
+			result := stripString(buf.String())
+			Expect(result).To(ContainSubstring(`"results":[{`))
+		})
+
+		It("json formatted report should contain the suppressed issues", func() {
+			errors := map[string][]gosec.Error{}
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&suppressedIssue}, &gosec.Metrics{}, errors)
+
+			buf := new(bytes.Buffer)
+			err := CreateReport(buf, "json", false, []string{}, reportInfo)
+			Expect(err).ShouldNot(HaveOccurred())
+
+			result := stripString(buf.String())
+			Expect(result).To(ContainSubstring(`"Issues":[{`))
+		})
+	})
 })
diff --git a/report/golint/writer.go b/report/golint/writer.go
new file mode 100644
index 0000000000..3f1434ca32
--- /dev/null
+++ b/report/golint/writer.go
@@ -0,0 +1,40 @@
+package golint
+
+import (
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in golint format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	// Output Sample:
+	// /tmp/main.go:11:14: [CWE-310] RSA keys should be at least 2048 bits (Rule:G403, Severity:MEDIUM, Confidence:HIGH)
+
+	for _, issue := range data.Issues {
+		what := issue.What
+		if issue.Cwe != nil && issue.Cwe.ID != "" {
+			what = fmt.Sprintf("[%s] %s", issue.Cwe.SprintID(), issue.What)
+		}
+
+		// issue.Line uses "start-end" format for multiple line detection.
+		lines := strings.Split(issue.Line, "-")
+		start := lines[0]
+
+		_, err := fmt.Fprintf(w, "%s:%s:%s: %s (Rule:%s, Severity:%s, Confidence:%s)\n",
+			issue.File,
+			start,
+			issue.Col,
+			what,
+			issue.RuleID,
+			issue.Severity.String(),
+			issue.Confidence.String(),
+		)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/report/html/template.html b/report/html/template.html
new file mode 100644
index 0000000000..dc8dd5c309
--- /dev/null
+++ b/report/html/template.html
@@ -0,0 +1,440 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <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.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.28.2/babel.min.js" integrity="sha512-AEhA/tSQr2Jwvfb/CmMAIVkOmPdIB5EBZXiHs3TXmarNdhcwX7ufSddMMVh2dVD2/8M33nWExg2KK33MLen09Q==" crossorigin="anonymous"></script>
+  <style>
+  .field-label {
+    min-width: 80px;
+  }
+  .break-word {
+    word-wrap: break-word;
+  }
+  .help {
+    white-space: pre-wrap;
+  }
+  .tag {
+    width: 80px;
+  }
+  .summary-first {
+    padding: .75rem .75rem .1rem .75rem;
+  }
+  .summary-last {
+    padding: .1rem .75rem .75rem .75rem;
+  }
+  .summary {
+    padding: .1rem .75rem ;
+  }
+  </style>
+</head>
+<body>
+  <section class="section">
+    <div class="container">
+      <div id="content"></div>
+    </div>
+  </section>
+  <script>
+    var data = {{ . }};
+  </script>
+  <script type="text/babel">
+    var IssueTag = React.createClass({
+      render: function() {
+        var level = "tag"
+        if (this.props.level === "HIGH") {
+          level += " is-danger";
+        } else if (this.props.level === "MEDIUM") {
+          level += " is-warning";
+        } else if (this.props.level === "LOW") {
+          level += " is-info";
+        } else if (this.props.level === "WAIVED") {
+          level += " is-success";
+        }
+        level +=" is-rounded";
+        return (
+          <div className="control">
+            <div className="tags has-addons">
+              <span className="tag is-dark is-rounded">{ this.props.label }</span>
+              <span className={ level }>{ this.props.level }</span>
+            </div>
+          </div>
+        );
+      }
+    });
+    var Highlight = React.createClass({
+      componentDidMount: function(){
+        var current = ReactDOM.findDOMNode(this);
+        hljs.highlightElement(current);
+      },
+      render: function() {
+        return (
+          <pre className="go"><code >{ this.props.code }</code></pre>
+        );
+      }
+    });
+    var Issue = React.createClass({
+      render: function() {
+        return (
+          <div className="issue box">
+          <div className="columns">
+              <div className="column is-three-quarters">
+                <strong className="break-word">{ this.props.data.file } (line { this.props.data.line })</strong>
+                <p>{this.props.data.rule_id} (CWE-{this.props.data.cwe.id}): { this.props.data.details }</p>
+              </div>
+              <div className="column is-one-quarter">
+                <div className="field is-grouped is-grouped-multiline">
+                  {this.props.data.nosec && <IssueTag label="NoSec" level="WAIVED"/>}
+                  <IssueTag label="Severity" level={ this.props.data.severity }/>
+                  <IssueTag label="Confidence" level={ this.props.data.confidence }/>
+                </div>
+              </div>
+            </div>
+            <div className="highlight">
+              <Highlight key={ this.props.data.file + this.props.data.line } code={ this.props.data.code }/>
+            </div>
+          </div>
+        );
+      }
+    });
+    var Stats = React.createClass({
+      render: function() {
+        return (
+          <p className="help is-pulled-right">
+            Gosec {this.props.data.GosecVersion} scanned { this.props.data.Stats.files.toLocaleString() } files
+            with { this.props.data.Stats.lines.toLocaleString() } lines of code.
+            { this.props.data.Stats.nosec ? '\n' + this.props.data.Stats.nosec.toLocaleString() + ' false positives (nosec) have been waived.' : ''}
+          </p>
+        );
+      }
+    });
+    var Issues = React.createClass({
+      render: function() {
+        if (this.props.data.Stats.files === 0) {
+          return (
+            <div className="notification">
+              No source files found. Do you even Go?
+            </div>
+          );
+        }
+        if (this.props.data.Issues.length === 0) {
+          return (
+            <div>
+              <div className="notification">
+                Awesome! No issues found!
+              </div>
+              <Stats data={ this.props.data } />
+            </div>
+          );
+        }
+        var issues = this.props.data.Issues
+          .filter(function(issue) {
+            return this.props.severity.includes(issue.severity);
+          }.bind(this))
+          .filter(function(issue) {
+            return this.props.confidence.includes(issue.confidence);
+          }.bind(this))
+          .filter(function(issue) {
+            if (this.props.issueType) {
+              return issue.details.toLowerCase().startsWith(this.props.issueType.toLowerCase());
+            } else {
+              return true
+            }
+          }.bind(this))
+          .map(function(issue) {
+            return (<Issue data={issue} />);
+          }.bind(this));
+        if (issues.length === 0) {
+          return (
+            <div>
+              <div className="notification">
+                No issues matched given filters
+                (of total { this.props.data.Issues.length } issues).
+              </div>
+              <Stats data={ this.props.data } />
+            </div>
+          );
+        }
+        return (
+          <div className="issues">
+            { issues }
+            <Stats data={ this.props.data } />
+          </div>
+        );
+      }
+    });
+    var LevelSelector = React.createClass({
+      handleChange: function(level) {
+        return function(e) {
+          var updated = this.props.selected
+            .filter(function(item) { return item != level; });
+          if (e.target.checked) {
+            updated.push(level);
+          }
+          this.props.onChange(updated);
+        }.bind(this);
+      },
+      render: function() {
+        var HIGH = "HIGH", MEDIUM = "MEDIUM", LOW = "LOW";
+        var highDisabled = !this.props.available.includes(HIGH);
+        var mediumDisabled = !this.props.available.includes(MEDIUM);
+        var lowDisabled = !this.props.available.includes(LOW);
+        return (
+          <div className="field">
+            <div className="control">
+              <label className="checkbox" disabled={ highDisabled }>
+                <input
+                  type="checkbox"
+                  checked={ this.props.selected.includes(HIGH) }
+                  disabled={ highDisabled }
+                  onChange={ this.handleChange(HIGH) }/> High
+              </label>
+            </div>
+            <div className="control">
+              <label className="checkbox" disabled={ mediumDisabled }>
+                <input
+                  type="checkbox"
+                  checked={ this.props.selected.includes(MEDIUM) }
+                  disabled={ mediumDisabled }
+                  onChange={ this.handleChange(MEDIUM) }/> Medium
+              </label>
+            </div>
+            <div className="control">
+              <label className="checkbox" disabled={ lowDisabled }>
+                <input
+                  type="checkbox"
+                  checked={ this.props.selected.includes(LOW) }
+                  disabled={ lowDisabled }
+                  onChange={ this.handleChange(LOW) }/> Low
+              </label>
+            </div>
+          </div>
+        );
+      }
+    });
+    var Navigation = React.createClass({
+      updateSeverity: function(vals) {
+        this.props.onSeverity(vals);
+      },
+      updateConfidence: function(vals) {
+        this.props.onConfidence(vals);
+      },
+      updateIssueType: function(e) {
+        if (e.target.value == "all") {
+          this.props.onIssueType(null);
+        } else {
+          this.props.onIssueType(e.target.value);
+        }
+      },
+      render: function() {
+        var issueTypes = this.props.allIssueTypes
+          .map(function(it) {
+            var matches = this.props.issueType == it
+            return (
+              <option value={ it } selected={ matches }>
+                { it }
+              </option>
+            );
+          }.bind(this));
+        return (
+          <div>
+              <nav className="panel">
+                <div className="panel-heading">Filters</div>
+                <div className="panel-block">
+                  <div className="field is-horizontal">
+                    <div className="field-label is-normal">
+                      <label className="label is-pulled-left">Severity</label>
+                    </div>
+                    <div className="field-body">
+                      <LevelSelector selected={ this.props.severity } available={ this.props.allSeverities } onChange={ this.updateSeverity } />
+                    </div>
+                 </div>
+                </div>
+                <div className="panel-block">
+                  <div className="field is-horizontal">
+                    <div className="field-label is-normal">
+                      <label className="label is-pulled-left">Confidence</label>
+                    </div>
+                    <div className="field-body">
+                      <LevelSelector selected={ this.props.confidence } available={ this.props.allConfidences } onChange={ this.updateConfidence } />
+                    </div>
+                  </div>
+                </div>
+                <div className="panel-block">
+                  <div className="field is-horizontal">
+                    <div className="field-label is-normal">
+                      <label className="label is-pulled-left">Issue type</label>
+                    </div>
+                    <div className="field-body">
+                      <div className="field">
+                        <div className="control">
+                          <div className="select is-fullwidth">
+                            <select onChange={ this.updateIssueType }>
+                              <option value="all" selected={ !this.props.issueType }>
+                                (all)
+                              </option>
+                              { issueTypes }
+                            </select>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </nav>
+              <nav className="panel">
+                <div className="panel-heading">Summary</div>
+                  <div className="panel-block">
+                      <div className="columns is-multiline">
+                          <div className="column is-half summary-first">
+                              <label className="label is-pulled-left">Gosec: </label>
+                          </div>
+                          <div className="column is-half summary-first">
+                              {this.props.data.GosecVersion}
+                          </div>
+                          <div className="column is-half summary">
+                              <label className="label is-pulled-left">Files: </label>
+                          </div>
+                          <div className="column is-half summary">
+                              {this.props.data.Stats.files.toLocaleString()}
+                          </div>
+                          <div className="column is-half summary">
+                              <label className="label is-pulled-left">Lines: </label>
+                          </div>
+                          <div className="column is-half summary">
+                              {this.props.data.Stats.lines.toLocaleString()}
+                          </div>
+                          <div className="column is-half summary">
+                              <label className="label is-pulled-left">Nosec: </label>
+                          </div>
+                          <div className="column is-half summary">
+                              {this.props.data.Stats.nosec.toLocaleString()}
+                          </div>
+                          <div className="column is-half summary-last">
+                              <label className="label is-pulled-left">Issues: </label>
+                          </div>
+                          <div className="column is-half summary-last">
+                              {this.props.data.Stats.found.toLocaleString()}
+                          </div>
+                      </div>
+                  </div>
+              </nav>
+          </div>
+        );
+      }
+    });
+    var IssueBrowser = React.createClass({
+      getInitialState: function() {
+        return {};
+      },
+      componentWillMount: function() {
+        this.updateIssues(this.props.data);
+      },
+      handleSeverity: function(val) {
+        this.updateIssueTypes(this.props.data.Issues, val, this.state.confidence);
+        this.setState({severity: val});
+      },
+      handleConfidence: function(val) {
+        this.updateIssueTypes(this.props.data.Issues, this.state.severity, val);
+        this.setState({confidence: val});
+      },
+      handleIssueType: function(val) {
+        this.setState({issueType: val});
+      },
+      updateIssues: function(data) {
+        if (!data) {
+          this.setState({data: data});
+          return;
+        }
+        var allSeverities = data.Issues
+          .map(function(issue) {
+            return issue.severity
+          })
+          .sort()
+          .filter(function(item, pos, ary) {
+            return !pos || item != ary[pos - 1];
+          });
+        var allConfidences = data.Issues
+          .map(function(issue) {
+            return issue.confidence
+          })
+          .sort()
+          .filter(function(item, pos, ary) {
+            return !pos || item != ary[pos - 1];
+          });
+        var selectedSeverities = allSeverities;
+        var selectedConfidences = allConfidences;
+        this.updateIssueTypes(data.Issues, selectedSeverities, selectedConfidences);
+        this.setState({
+          data: data,
+          severity: selectedSeverities,
+          allSeverities: allSeverities,
+          confidence: selectedConfidences,
+          allConfidences: allConfidences,
+          issueType: null
+        });
+      },
+      updateIssueTypes: function(issues, severities, confidences) {
+        var allTypes = issues
+          .filter(function(issue) {
+            return severities.includes(issue.severity);
+          })
+          .filter(function(issue) {
+            return confidences.includes(issue.confidence);
+          })
+          .map(function(issue) {
+            return issue.details;
+          })
+          .sort()
+          .filter(function(item, pos, ary) {
+            return !pos || item != ary[pos - 1];
+          });
+        if (this.state.issueType && !allTypes.includes(this.state.issueType)) {
+          this.setState({issueType: null});
+        }
+        this.setState({allIssueTypes: allTypes});
+      },
+      render: function() {
+        return (
+          <div className="content">
+            <div className="columns">
+              <div className="column is-one-quarter">
+                <Navigation
+                  data={ this.props.data }
+                  severity={ this.state.severity }
+                  confidence={ this.state.confidence }
+                  issueType={ this.state.issueType }
+                  allSeverities={ this.state.allSeverities }
+                  allConfidences={ this.state.allConfidences }
+                  allIssueTypes={ this.state.allIssueTypes }
+                  onSeverity={ this.handleSeverity }
+                  onConfidence={ this.handleConfidence }
+                  onIssueType={ this.handleIssueType }
+                />
+              </div>
+              <div className="column is-three-quarters">
+                <Issues
+                  data={ this.props.data }
+                  severity={ this.state.severity }
+                  confidence={ this.state.confidence }
+                  issueType={ this.state.issueType }
+                />
+              </div>
+            </div>
+          </div>
+        );
+      }
+    });
+    ReactDOM.render(
+      <IssueBrowser data={ data } />,
+      document.getElementById("content")
+    );
+  </script>
+</body>
+</html>
diff --git a/report/html/writer.go b/report/html/writer.go
new file mode 100644
index 0000000000..9ddd5c450c
--- /dev/null
+++ b/report/html/writer.go
@@ -0,0 +1,22 @@
+package html
+
+import (
+	_ "embed"
+	"html/template"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+//go:embed template.html
+var templateContent string
+
+// WriteReport write a report in html format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	t, e := template.New("gosec").Parse(templateContent)
+	if e != nil {
+		return e
+	}
+
+	return t.Execute(w, data)
+}
diff --git a/report/json/writer.go b/report/json/writer.go
new file mode 100644
index 0000000000..7c20df8095
--- /dev/null
+++ b/report/json/writer.go
@@ -0,0 +1,19 @@
+package json
+
+import (
+	"encoding/json"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in json format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	raw, err := json.MarshalIndent(data, "", "\t")
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Write(raw)
+	return err
+}
diff --git a/report/junit/builder.go b/report/junit/builder.go
new file mode 100644
index 0000000000..1b1e816ee6
--- /dev/null
+++ b/report/junit/builder.go
@@ -0,0 +1,24 @@
+package junit
+
+// NewTestsuite instantiate a Testsuite
+func NewTestsuite(name string) *Testsuite {
+	return &Testsuite{
+		Name: name,
+	}
+}
+
+// NewFailure instantiate a Failure
+func NewFailure(message string, text string) *Failure {
+	return &Failure{
+		Message: message,
+		Text:    text,
+	}
+}
+
+// NewTestcase instantiate a Testcase
+func NewTestcase(name string, failure *Failure) *Testcase {
+	return &Testcase{
+		Name:    name,
+		Failure: failure,
+	}
+}
diff --git a/report/junit/formatter.go b/report/junit/formatter.go
new file mode 100644
index 0000000000..3b974fd7e1
--- /dev/null
+++ b/report/junit/formatter.go
@@ -0,0 +1,44 @@
+package junit
+
+import (
+	"html"
+	"strconv"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+func generatePlaintext(issue *issue.Issue) string {
+	cweID := "CWE"
+	if issue.Cwe != nil {
+		cweID = issue.Cwe.ID
+	}
+	return "Results:\n" +
+		"[" + issue.File + ":" + issue.Line + "] - " +
+		issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
+		", Severity: " + strconv.Itoa(int(issue.Severity)) +
+		", CWE: " + cweID + ")\n" + "> " + html.EscapeString(issue.Code) +
+		"\n Autofix: " + issue.Autofix
+}
+
+// GenerateReport Convert a gosec report to a JUnit Report
+func GenerateReport(data *gosec.ReportInfo) Report {
+	var xmlReport Report
+	testsuites := map[string]int{}
+
+	for _, issue := range data.Issues {
+		index, ok := testsuites[issue.What]
+		if !ok {
+			xmlReport.Testsuites = append(xmlReport.Testsuites, NewTestsuite(issue.What))
+			index = len(xmlReport.Testsuites) - 1
+			testsuites[issue.What] = index
+		}
+		failure := NewFailure("Found 1 vulnerability. See stacktrace for details.", generatePlaintext(issue))
+		testcase := NewTestcase(issue.File, failure)
+
+		xmlReport.Testsuites[index].Testcases = append(xmlReport.Testsuites[index].Testcases, testcase)
+		xmlReport.Testsuites[index].Tests++
+	}
+
+	return xmlReport
+}
diff --git a/report/junit/types.go b/report/junit/types.go
new file mode 100644
index 0000000000..bac3218b9a
--- /dev/null
+++ b/report/junit/types.go
@@ -0,0 +1,33 @@
+package junit
+
+import (
+	"encoding/xml"
+)
+
+// Report defines a JUnit XML report
+type Report struct {
+	XMLName    xml.Name     `xml:"testsuites"`
+	Testsuites []*Testsuite `xml:"testsuite"`
+}
+
+// Testsuite defines a JUnit testsuite
+type Testsuite struct {
+	XMLName   xml.Name    `xml:"testsuite"`
+	Name      string      `xml:"name,attr"`
+	Tests     int         `xml:"tests,attr"`
+	Testcases []*Testcase `xml:"testcase"`
+}
+
+// Testcase defines a JUnit testcase
+type Testcase struct {
+	XMLName xml.Name `xml:"testcase"`
+	Name    string   `xml:"name,attr"`
+	Failure *Failure `xml:"failure"`
+}
+
+// Failure defines a JUnit failure
+type Failure struct {
+	XMLName xml.Name `xml:"failure"`
+	Message string   `xml:"message,attr"`
+	Text    string   `xml:",innerxml"`
+}
diff --git a/report/junit/writer.go b/report/junit/writer.go
new file mode 100644
index 0000000000..f16ed129ce
--- /dev/null
+++ b/report/junit/writer.go
@@ -0,0 +1,26 @@
+package junit
+
+import (
+	"encoding/xml"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in JUnit format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	junitXMLStruct := GenerateReport(data)
+	raw, err := xml.MarshalIndent(junitXMLStruct, "", "\t")
+	if err != nil {
+		return err
+	}
+
+	xmlHeader := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+	raw = append(xmlHeader, raw...)
+	_, err = w.Write(raw)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/report/sarif/builder.go b/report/sarif/builder.go
new file mode 100644
index 0000000000..40663ced46
--- /dev/null
+++ b/report/sarif/builder.go
@@ -0,0 +1,220 @@
+package sarif
+
+// NewReport instantiate a SARIF Report
+func NewReport(version string, schema string) *Report {
+	return &Report{
+		Version: version,
+		Schema:  schema,
+	}
+}
+
+// WithRuns defines runs for the current report
+func (r *Report) WithRuns(runs ...*Run) *Report {
+	r.Runs = runs
+	return r
+}
+
+// NewMultiformatMessageString instantiate a MultiformatMessageString
+func NewMultiformatMessageString(text string) *MultiformatMessageString {
+	return &MultiformatMessageString{
+		Text: text,
+	}
+}
+
+// NewRun instantiate a Run
+func NewRun(tool *Tool) *Run {
+	return &Run{
+		Tool: tool,
+	}
+}
+
+// WithTaxonomies set the taxonomies for the current run
+func (r *Run) WithTaxonomies(taxonomies ...*ToolComponent) *Run {
+	r.Taxonomies = taxonomies
+	return r
+}
+
+// WithResults set the results for the current run
+func (r *Run) WithResults(results ...*Result) *Run {
+	r.Results = results
+	return r
+}
+
+// NewArtifactLocation instantiate an ArtifactLocation
+func NewArtifactLocation(uri string) *ArtifactLocation {
+	return &ArtifactLocation{
+		URI: uri,
+	}
+}
+
+// NewRegion instantiate a Region
+func NewRegion(startLine int, endLine int, startColumn int, endColumn int, sourceLanguage string) *Region {
+	return &Region{
+		StartLine:      startLine,
+		EndLine:        endLine,
+		StartColumn:    startColumn,
+		EndColumn:      endColumn,
+		SourceLanguage: sourceLanguage,
+	}
+}
+
+// WithSnippet defines the Snippet for the current Region
+func (r *Region) WithSnippet(snippet *ArtifactContent) *Region {
+	r.Snippet = snippet
+	return r
+}
+
+// NewArtifactContent instantiate an ArtifactContent
+func NewArtifactContent(text string) *ArtifactContent {
+	return &ArtifactContent{
+		Text: text,
+	}
+}
+
+// NewTool instantiate a Tool
+func NewTool(driver *ToolComponent) *Tool {
+	return &Tool{
+		Driver: driver,
+	}
+}
+
+// NewResult instantiate a Result
+func NewResult(ruleID string, ruleIndex int, level Level, message string, suppressions []*Suppression, autofix string) *Result {
+	result := &Result{
+		RuleID:       ruleID,
+		RuleIndex:    ruleIndex,
+		Level:        level,
+		Message:      NewMessage(message),
+		Suppressions: suppressions,
+	}
+	if len(autofix) > 0 {
+		result.Fixes = []*Fix{
+			{
+				Description: &Message{
+					Markdown: autofix,
+				},
+			},
+		}
+	}
+	return result
+}
+
+// NewMessage instantiate a Message
+func NewMessage(text string) *Message {
+	return &Message{
+		Text: text,
+	}
+}
+
+// WithLocations define the current result's locations
+func (r *Result) WithLocations(locations ...*Location) *Result {
+	r.Locations = locations
+	return r
+}
+
+// NewLocation instantiate a Location
+func NewLocation(physicalLocation *PhysicalLocation) *Location {
+	return &Location{
+		PhysicalLocation: physicalLocation,
+	}
+}
+
+// NewPhysicalLocation instantiate a PhysicalLocation
+func NewPhysicalLocation(artifactLocation *ArtifactLocation, region *Region) *PhysicalLocation {
+	return &PhysicalLocation{
+		ArtifactLocation: artifactLocation,
+		Region:           region,
+	}
+}
+
+// NewToolComponent instantiate a ToolComponent
+func NewToolComponent(name string, version string, informationURI string) *ToolComponent {
+	return &ToolComponent{
+		Name:           name,
+		Version:        version,
+		InformationURI: informationURI,
+		GUID:           uuid3(name),
+	}
+}
+
+// WithLanguage set Language for the current ToolComponent
+func (t *ToolComponent) WithLanguage(language string) *ToolComponent {
+	t.Language = language
+	return t
+}
+
+// WithSemanticVersion set SemanticVersion for the current ToolComponent
+func (t *ToolComponent) WithSemanticVersion(semanticVersion string) *ToolComponent {
+	t.SemanticVersion = semanticVersion
+	return t
+}
+
+// WithReleaseDateUtc set releaseDateUtc for the current ToolComponent
+func (t *ToolComponent) WithReleaseDateUtc(releaseDateUtc string) *ToolComponent {
+	t.ReleaseDateUtc = releaseDateUtc
+	return t
+}
+
+// WithDownloadURI set downloadURI for the current ToolComponent
+func (t *ToolComponent) WithDownloadURI(downloadURI string) *ToolComponent {
+	t.DownloadURI = downloadURI
+	return t
+}
+
+// WithOrganization set organization for the current ToolComponent
+func (t *ToolComponent) WithOrganization(organization string) *ToolComponent {
+	t.Organization = organization
+	return t
+}
+
+// WithShortDescription set shortDescription for the current ToolComponent
+func (t *ToolComponent) WithShortDescription(shortDescription *MultiformatMessageString) *ToolComponent {
+	t.ShortDescription = shortDescription
+	return t
+}
+
+// WithIsComprehensive set isComprehensive for the current ToolComponent
+func (t *ToolComponent) WithIsComprehensive(isComprehensive bool) *ToolComponent {
+	t.IsComprehensive = isComprehensive
+	return t
+}
+
+// WithMinimumRequiredLocalizedDataSemanticVersion set MinimumRequiredLocalizedDataSemanticVersion for the current ToolComponent
+func (t *ToolComponent) WithMinimumRequiredLocalizedDataSemanticVersion(minimumRequiredLocalizedDataSemanticVersion string) *ToolComponent {
+	t.MinimumRequiredLocalizedDataSemanticVersion = minimumRequiredLocalizedDataSemanticVersion
+	return t
+}
+
+// WithTaxa set taxa for the current ToolComponent
+func (t *ToolComponent) WithTaxa(taxa ...*ReportingDescriptor) *ToolComponent {
+	t.Taxa = taxa
+	return t
+}
+
+// WithSupportedTaxonomies set the supported taxonomies for the current ToolComponent
+func (t *ToolComponent) WithSupportedTaxonomies(supportedTaxonomies ...*ToolComponentReference) *ToolComponent {
+	t.SupportedTaxonomies = supportedTaxonomies
+	return t
+}
+
+// WithRules set the rules for the current ToolComponent
+func (t *ToolComponent) WithRules(rules ...*ReportingDescriptor) *ToolComponent {
+	t.Rules = rules
+	return t
+}
+
+// NewToolComponentReference instantiate a ToolComponentReference
+func NewToolComponentReference(name string) *ToolComponentReference {
+	return &ToolComponentReference{
+		Name: name,
+		GUID: uuid3(name),
+	}
+}
+
+// NewSuppression instantiate a Suppression
+func NewSuppression(kind string, justification string) *Suppression {
+	return &Suppression{
+		Kind:          kind,
+		Justification: justification,
+	}
+}
diff --git a/report/sarif/data.go b/report/sarif/data.go
new file mode 100644
index 0000000000..8efaf48be7
--- /dev/null
+++ b/report/sarif/data.go
@@ -0,0 +1,22 @@
+package sarif
+
+// Level SARIF level
+// From https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127839
+type Level string
+
+const (
+	// None : The concept of “severity” does not apply to this result because the kind
+	// property (§3.27.9) has a value other than "fail".
+	None = Level("none")
+	// Note : The rule specified by ruleId was evaluated and a minor problem or an opportunity
+	// to improve the code was found.
+	Note = Level("note")
+	// Warning : The rule specified by ruleId was evaluated and a problem was found.
+	Warning = Level("warning")
+	// Error : The rule specified by ruleId was evaluated and a serious problem was found.
+	Error = Level("error")
+	// Version : SARIF Schema version
+	Version = "2.1.0"
+	// Schema : SARIF Schema URL
+	Schema = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json"
+)
diff --git a/report/sarif/formatter.go b/report/sarif/formatter.go
new file mode 100644
index 0000000000..f47c250c72
--- /dev/null
+++ b/report/sarif/formatter.go
@@ -0,0 +1,252 @@
+package sarif
+
+import (
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+
+	"github.com/google/uuid"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/cwe"
+	"github.com/securego/gosec/v2/issue"
+)
+
+// GenerateReport converts a gosec report into a SARIF report
+func GenerateReport(rootPaths []string, data *gosec.ReportInfo) (*Report, error) {
+	rules := []*ReportingDescriptor{}
+
+	results := []*Result{}
+	cweTaxa := []*ReportingDescriptor{}
+	weaknesses := map[string]*cwe.Weakness{}
+
+	for _, issue := range data.Issues {
+		if issue.Cwe != nil {
+			_, ok := weaknesses[issue.Cwe.ID]
+			if !ok {
+				weakness := cwe.Get(issue.Cwe.ID)
+				weaknesses[issue.Cwe.ID] = weakness
+				cweTaxon := parseSarifTaxon(weakness)
+				cweTaxa = append(cweTaxa, cweTaxon)
+			}
+		}
+
+		rule := parseSarifRule(issue)
+		var ruleIndex int
+		rules, ruleIndex = addRuleInOrder(rules, rule)
+
+		location, err := parseSarifLocation(issue, rootPaths)
+		if err != nil {
+			return nil, err
+		}
+
+		result := NewResult(
+			issue.RuleID,
+			ruleIndex,
+			getSarifLevel(issue.Severity.String()),
+			issue.What,
+			buildSarifSuppressions(issue.Suppressions),
+			issue.Autofix,
+		).WithLocations(location)
+
+		results = append(results, result)
+	}
+
+	sort.SliceStable(cweTaxa, func(i, j int) bool { return cweTaxa[i].ID < cweTaxa[j].ID })
+
+	tool := NewTool(buildSarifDriver(rules, data.GosecVersion))
+
+	cweTaxonomy := buildCWETaxonomy(cweTaxa)
+
+	run := NewRun(tool).
+		WithTaxonomies(cweTaxonomy).
+		WithResults(results...)
+
+	return NewReport(Version, Schema).
+		WithRuns(run), nil
+}
+
+// addRuleInOrder inserts a rule into the rules slice keeping the rules IDs order, it returns the new rules
+// slice and the position where the rule was inserted
+func addRuleInOrder(rules []*ReportingDescriptor, rule *ReportingDescriptor) ([]*ReportingDescriptor, int) {
+	position := 0
+	for i, r := range rules {
+		if r.ID < rule.ID {
+			continue
+		}
+		if r.ID == rule.ID {
+			return rules, i
+		}
+		position = i
+		break
+	}
+	rules = append(rules, nil)
+	copy(rules[position+1:], rules[position:])
+	rules[position] = rule
+	return rules, position
+}
+
+// parseSarifRule return SARIF rule field struct
+func parseSarifRule(i *issue.Issue) *ReportingDescriptor {
+	cwe := issue.GetCweByRule(i.RuleID)
+	name := i.RuleID
+	if cwe != nil {
+		name = cwe.Name
+	}
+	return &ReportingDescriptor{
+		ID:               i.RuleID,
+		Name:             name,
+		ShortDescription: NewMultiformatMessageString(i.What),
+		FullDescription:  NewMultiformatMessageString(i.What),
+		Help: NewMultiformatMessageString(fmt.Sprintf("%s\nSeverity: %s\nConfidence: %s\n",
+			i.What, i.Severity.String(), i.Confidence.String())),
+		Properties: &PropertyBag{
+			"tags":      []string{"security", i.Severity.String()},
+			"precision": strings.ToLower(i.Confidence.String()),
+		},
+		DefaultConfiguration: &ReportingConfiguration{
+			Level: getSarifLevel(i.Severity.String()),
+		},
+		Relationships: []*ReportingDescriptorRelationship{
+			buildSarifReportingDescriptorRelationship(i.Cwe),
+		},
+	}
+}
+
+func buildSarifReportingDescriptorRelationship(weakness *cwe.Weakness) *ReportingDescriptorRelationship {
+	if weakness == nil {
+		return nil
+	}
+	return &ReportingDescriptorRelationship{
+		Target: &ReportingDescriptorReference{
+			ID:            weakness.ID,
+			GUID:          uuid3(weakness.SprintID()),
+			ToolComponent: NewToolComponentReference(cwe.Acronym),
+		},
+		Kinds: []string{"superset"},
+	}
+}
+
+func buildCWETaxonomy(taxa []*ReportingDescriptor) *ToolComponent {
+	return NewToolComponent(cwe.Acronym, cwe.Version, cwe.InformationURI).
+		WithReleaseDateUtc(cwe.ReleaseDateUtc).
+		WithDownloadURI(cwe.DownloadURI).
+		WithOrganization(cwe.Organization).
+		WithShortDescription(NewMultiformatMessageString(cwe.Description)).
+		WithIsComprehensive(true).
+		WithLanguage("en").
+		WithMinimumRequiredLocalizedDataSemanticVersion(cwe.Version).
+		WithTaxa(taxa...)
+}
+
+func parseSarifTaxon(weakness *cwe.Weakness) *ReportingDescriptor {
+	return &ReportingDescriptor{
+		ID:               weakness.ID,
+		GUID:             uuid3(weakness.SprintID()),
+		HelpURI:          weakness.SprintURL(),
+		FullDescription:  NewMultiformatMessageString(weakness.Description),
+		ShortDescription: NewMultiformatMessageString(weakness.Name),
+	}
+}
+
+func parseSemanticVersion(version string) string {
+	if len(version) == 0 {
+		return "devel"
+	}
+	if strings.HasPrefix(version, "v") {
+		return version[1:]
+	}
+	return version
+}
+
+func buildSarifDriver(rules []*ReportingDescriptor, gosecVersion string) *ToolComponent {
+	semanticVersion := parseSemanticVersion(gosecVersion)
+	return NewToolComponent("gosec", gosecVersion, "https://github.com/securego/gosec/").
+		WithSemanticVersion(semanticVersion).
+		WithSupportedTaxonomies(NewToolComponentReference(cwe.Acronym)).
+		WithRules(rules...)
+}
+
+func uuid3(value string) string {
+	return uuid.NewMD5(uuid.Nil, []byte(value)).String()
+}
+
+// parseSarifLocation return SARIF location struct
+func parseSarifLocation(i *issue.Issue, rootPaths []string) (*Location, error) {
+	region, err := parseSarifRegion(i)
+	if err != nil {
+		return nil, err
+	}
+	artifactLocation := parseSarifArtifactLocation(i, rootPaths)
+	return NewLocation(NewPhysicalLocation(artifactLocation, region)), nil
+}
+
+func parseSarifArtifactLocation(i *issue.Issue, rootPaths []string) *ArtifactLocation {
+	var filePath string
+	for _, rootPath := range rootPaths {
+		if strings.HasPrefix(i.File, rootPath) {
+			filePath = strings.Replace(i.File, rootPath+"/", "", 1)
+		}
+	}
+	return NewArtifactLocation(filePath)
+}
+
+func parseSarifRegion(i *issue.Issue) (*Region, error) {
+	lines := strings.Split(i.Line, "-")
+	startLine, err := strconv.Atoi(lines[0])
+	if err != nil {
+		return nil, err
+	}
+	endLine := startLine
+	if len(lines) > 1 {
+		endLine, err = strconv.Atoi(lines[1])
+		if err != nil {
+			return nil, err
+		}
+	}
+	col, err := strconv.Atoi(i.Col)
+	if err != nil {
+		return nil, err
+	}
+	var code string
+	line := startLine
+	codeLines := strings.Split(i.Code, "\n")
+	for _, codeLine := range codeLines {
+		lineStart := fmt.Sprintf("%d:", line)
+		if strings.HasPrefix(codeLine, lineStart) {
+			code += strings.TrimSpace(
+				strings.TrimPrefix(codeLine, lineStart))
+			if endLine > startLine {
+				code += "\n"
+			}
+			line++
+			if line > endLine {
+				break
+			}
+		}
+	}
+	snippet := NewArtifactContent(code)
+	return NewRegion(startLine, endLine, col, col, "go").WithSnippet(snippet), nil
+}
+
+func getSarifLevel(s string) Level {
+	switch s {
+	case "LOW":
+		return Warning
+	case "MEDIUM":
+		return Error
+	case "HIGH":
+		return Error
+	default:
+		return Note
+	}
+}
+
+func buildSarifSuppressions(suppressions []issue.SuppressionInfo) []*Suppression {
+	var sarifSuppressionList []*Suppression
+	for _, s := range suppressions {
+		sarifSuppressionList = append(sarifSuppressionList, NewSuppression(s.Kind, s.Justification))
+	}
+	return sarifSuppressionList
+}
diff --git a/report/sarif/sarif_suite_test.go b/report/sarif/sarif_suite_test.go
new file mode 100644
index 0000000000..2dc885d760
--- /dev/null
+++ b/report/sarif/sarif_suite_test.go
@@ -0,0 +1,13 @@
+package sarif_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestRules(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Sarif Formatters Suite")
+}
diff --git a/report/sarif/sarif_test.go b/report/sarif/sarif_test.go
new file mode 100644
index 0000000000..66567d31d5
--- /dev/null
+++ b/report/sarif/sarif_test.go
@@ -0,0 +1,176 @@
+package sarif_test
+
+import (
+	"bytes"
+	"regexp"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+	"github.com/securego/gosec/v2/report/sarif"
+)
+
+var _ = Describe("Sarif Formatter", func() {
+	BeforeEach(func() {
+	})
+	Context("when converting to Sarif issues", func() {
+		It("sarif formatted report should contain the result", func() {
+			buf := new(bytes.Buffer)
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{}, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
+			err := sarif.WriteReport(buf, reportInfo, []string{})
+			result := buf.String()
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(result).To(ContainSubstring("\"results\": ["))
+		})
+
+		It("sarif formatted report should contain the suppressed results", func() {
+			ruleID := "G101"
+			cwe := issue.GetCweByRule(ruleID)
+			suppressedIssue := issue.Issue{
+				File:       "/home/src/project/test.go",
+				Line:       "1",
+				Col:        "1",
+				RuleID:     ruleID,
+				What:       "test",
+				Confidence: issue.High,
+				Severity:   issue.High,
+				Code:       "1: testcode",
+				Cwe:        cwe,
+				Suppressions: []issue.SuppressionInfo{
+					{
+						Kind:          "kind",
+						Justification: "justification",
+					},
+				},
+			}
+
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&suppressedIssue}, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
+			buf := new(bytes.Buffer)
+			err := sarif.WriteReport(buf, reportInfo, []string{})
+			result := buf.String()
+			Expect(err).ShouldNot(HaveOccurred())
+
+			hasResults, _ := regexp.MatchString(`"results": \[(\s*){`, result)
+			Expect(hasResults).To(BeTrue())
+
+			hasSuppressions, _ := regexp.MatchString(`"suppressions": \[(\s*){`, result)
+			Expect(hasSuppressions).To(BeTrue())
+		})
+		It("sarif formatted report should contain the formatted one line code snippet", func() {
+			ruleID := "G101"
+			cwe := issue.GetCweByRule(ruleID)
+			code := "68: \t\t}\n69: \t\tvar data = template.HTML(v.TmplFile)\n70: \t\tisTmpl := true\n"
+			expectedCode := "var data = template.HTML(v.TmplFile)"
+			newissue := issue.Issue{
+				File:       "/home/src/project/test.go",
+				Line:       "69",
+				Col:        "14",
+				RuleID:     ruleID,
+				What:       "test",
+				Confidence: issue.High,
+				Severity:   issue.High,
+				Code:       code,
+				Cwe:        cwe,
+				Suppressions: []issue.SuppressionInfo{
+					{
+						Kind:          "kind",
+						Justification: "justification",
+					},
+				},
+			}
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
+			sarifReport, err := sarif.GenerateReport([]string{}, reportInfo)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(sarifReport.Runs[0].Results[0].Locations[0].PhysicalLocation.Region.Snippet.Text).Should(Equal(expectedCode))
+		})
+		It("sarif formatted report should contain the formatted multiple line code snippet", func() {
+			ruleID := "G101"
+			cwe := issue.GetCweByRule(ruleID)
+			code := "68: }\n69: var data = template.HTML(v.TmplFile)\n70: isTmpl := true\n"
+			expectedCode := "var data = template.HTML(v.TmplFile)\nisTmpl := true\n"
+			newissue := issue.Issue{
+				File:       "/home/src/project/test.go",
+				Line:       "69-70",
+				Col:        "14",
+				RuleID:     ruleID,
+				What:       "test",
+				Confidence: issue.High,
+				Severity:   issue.High,
+				Code:       code,
+				Cwe:        cwe,
+				Suppressions: []issue.SuppressionInfo{
+					{
+						Kind:          "kind",
+						Justification: "justification",
+					},
+				},
+			}
+			reportInfo := gosec.NewReportInfo([]*issue.Issue{&newissue}, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
+			sarifReport, err := sarif.GenerateReport([]string{}, reportInfo)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(sarifReport.Runs[0].Results[0].Locations[0].PhysicalLocation.Region.Snippet.Text).Should(Equal(expectedCode))
+		})
+		It("sarif formatted report should have proper rule index", func() {
+			rules := []string{"G404", "G101", "G102", "G103"}
+			issues := []*issue.Issue{}
+			for _, rule := range rules {
+				cwe := issue.GetCweByRule(rule)
+				newissue := issue.Issue{
+					File:       "/home/src/project/test.go",
+					Line:       "69-70",
+					Col:        "14",
+					RuleID:     rule,
+					What:       "test",
+					Confidence: issue.High,
+					Severity:   issue.High,
+					Cwe:        cwe,
+					Suppressions: []issue.SuppressionInfo{
+						{
+							Kind:          "kind",
+							Justification: "justification",
+						},
+					},
+				}
+				issues = append(issues, &newissue)
+
+			}
+			dupRules := []string{"G102", "G404"}
+			for _, rule := range dupRules {
+				cwe := issue.GetCweByRule(rule)
+				newissue := issue.Issue{
+					File:       "/home/src/project/test.go",
+					Line:       "69-70",
+					Col:        "14",
+					RuleID:     rule,
+					What:       "test",
+					Confidence: issue.High,
+					Severity:   issue.High,
+					Cwe:        cwe,
+					Suppressions: []issue.SuppressionInfo{
+						{
+							Kind:          "kind",
+							Justification: "justification",
+						},
+					},
+				}
+				issues = append(issues, &newissue)
+			}
+			reportInfo := gosec.NewReportInfo(issues, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
+
+			sarifReport, err := sarif.GenerateReport([]string{}, reportInfo)
+
+			Expect(err).ShouldNot(HaveOccurred())
+			resultRuleIndexes := map[string]int{}
+			for _, result := range sarifReport.Runs[0].Results {
+				resultRuleIndexes[result.RuleID] = result.RuleIndex
+			}
+			driverRuleIndexes := map[string]int{}
+			for ruleIndex, rule := range sarifReport.Runs[0].Tool.Driver.Rules {
+				driverRuleIndexes[rule.ID] = ruleIndex
+			}
+			Expect(resultRuleIndexes).Should(Equal(driverRuleIndexes))
+		})
+	})
+})
diff --git a/report/sarif/types.go b/report/sarif/types.go
new file mode 100644
index 0000000000..a341c39a95
--- /dev/null
+++ b/report/sarif/types.go
@@ -0,0 +1,1471 @@
+// Code generated by schema-generate. DO NOT EDIT.
+
+package sarif
+
+// Address A physical or virtual address, or a range of addresses, in an 'addressable region' (memory or a binary file).
+type Address struct {
+
+	// The address expressed as a byte offset from the start of the addressable region.
+	AbsoluteAddress int `json:"absoluteAddress,omitempty"`
+
+	// A human-readable fully qualified name that is associated with the address.
+	FullyQualifiedName string `json:"fullyQualifiedName,omitempty"`
+
+	// The index within run.addresses of the cached object for this address.
+	Index int `json:"index,omitempty"`
+
+	// An open-ended string that identifies the address kind. 'data', 'function', 'header','instruction', 'module', 'page', 'section', 'segment', 'stack', 'stackFrame', 'table' are well-known values.
+	Kind string `json:"kind,omitempty"`
+
+	// The number of bytes in this range of addresses.
+	Length int `json:"length,omitempty"`
+
+	// A name that is associated with the address, e.g., '.text'.
+	Name string `json:"name,omitempty"`
+
+	// The byte offset of this address from the absolute or relative address of the parent object.
+	OffsetFromParent int `json:"offsetFromParent,omitempty"`
+
+	// The index within run.addresses of the parent object.
+	ParentIndex int `json:"parentIndex,omitempty"`
+
+	// Key/value pairs that provide additional information about the address.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The address expressed as a byte offset from the absolute address of the top-most parent object.
+	RelativeAddress int `json:"relativeAddress,omitempty"`
+}
+
+// Artifact A single artifact. In some cases, this artifact might be nested within another artifact.
+type Artifact struct {
+
+	// The contents of the artifact.
+	Contents *ArtifactContent `json:"contents,omitempty"`
+
+	// A short description of the artifact.
+	Description *Message `json:"description,omitempty"`
+
+	// Specifies the encoding for an artifact object that refers to a text file.
+	Encoding string `json:"encoding,omitempty"`
+
+	// A dictionary, each of whose keys is the name of a hash function and each of whose values is the hashed value of the artifact produced by the specified hash function.
+	Hashes map[string]string `json:"hashes,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the artifact was most recently modified. See "Date/time properties" in the SARIF spec for the required format.
+	LastModifiedTimeUtc string `json:"lastModifiedTimeUtc,omitempty"`
+
+	// The length of the artifact in bytes.
+	Length int `json:"length,omitempty"`
+
+	// The location of the artifact.
+	Location *ArtifactLocation `json:"location,omitempty"`
+
+	// The MIME type (RFC 2045) of the artifact.
+	MimeType string `json:"mimeType,omitempty"`
+
+	// The offset in bytes of the artifact within its containing artifact.
+	Offset int `json:"offset,omitempty"`
+
+	// Identifies the index of the immediate parent of the artifact, if this artifact is nested.
+	ParentIndex int `json:"parentIndex,omitempty"`
+
+	// Key/value pairs that provide additional information about the artifact.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The role or roles played by the artifact in the analysis.
+	Roles []interface{} `json:"roles,omitempty"`
+
+	// Specifies the source language for any artifact object that refers to a text file that contains source code.
+	SourceLanguage string `json:"sourceLanguage,omitempty"`
+}
+
+// ArtifactChange A change to a single artifact.
+type ArtifactChange struct {
+
+	// The location of the artifact to change.
+	ArtifactLocation *ArtifactLocation `json:"artifactLocation"`
+
+	// Key/value pairs that provide additional information about the change.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of replacement objects, each of which represents the replacement of a single region in a single artifact specified by 'artifactLocation'.
+	Replacements []*Replacement `json:"replacements"`
+}
+
+// ArtifactContent Represents the contents of an artifact.
+type ArtifactContent struct {
+
+	// MIME Base64-encoded content from a binary artifact, or from a text artifact in its original encoding.
+	Binary string `json:"binary,omitempty"`
+
+	// Key/value pairs that provide additional information about the artifact content.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An alternate rendered representation of the artifact (e.g., a decompiled representation of a binary region).
+	Rendered *MultiformatMessageString `json:"rendered,omitempty"`
+
+	// UTF-8-encoded content from a text artifact.
+	Text string `json:"text,omitempty"`
+}
+
+// ArtifactLocation Specifies the location of an artifact.
+type ArtifactLocation struct {
+
+	// A short description of the artifact location.
+	Description *Message `json:"description,omitempty"`
+
+	// The index within the run artifacts array of the artifact object associated with the artifact location.
+	Index int `json:"index,omitempty"`
+
+	// Key/value pairs that provide additional information about the artifact location.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A string containing a valid relative or absolute URI.
+	URI string `json:"uri,omitempty"`
+
+	// A string which indirectly specifies the absolute URI with respect to which a relative URI in the "uri" property is interpreted.
+	UriBaseID string `json:"uriBaseId,omitempty"`
+}
+
+// Attachment An artifact relevant to a result.
+type Attachment struct {
+
+	// The location of the attachment.
+	ArtifactLocation *ArtifactLocation `json:"artifactLocation"`
+
+	// A message describing the role played by the attachment.
+	Description *Message `json:"description,omitempty"`
+
+	// Key/value pairs that provide additional information about the attachment.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of rectangles specifying areas of interest within the image.
+	Rectangles []*Rectangle `json:"rectangles,omitempty"`
+
+	// An array of regions of interest within the attachment.
+	Regions []*Region `json:"regions,omitempty"`
+}
+
+// CodeFlow A set of threadFlows which together describe a pattern of code execution relevant to detecting a result.
+type CodeFlow struct {
+
+	// A message relevant to the code flow.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the code flow.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of one or more unique threadFlow objects, each of which describes the progress of a program through a thread of execution.
+	ThreadFlows []*ThreadFlow `json:"threadFlows"`
+}
+
+// ConfigurationOverride Information about how a specific rule or notification was reconfigured at runtime.
+type ConfigurationOverride struct {
+
+	// Specifies how the rule or notification was configured during the scan.
+	Configuration *ReportingConfiguration `json:"configuration"`
+
+	// A reference used to locate the descriptor whose configuration was overridden.
+	Descriptor *ReportingDescriptorReference `json:"descriptor"`
+
+	// Key/value pairs that provide additional information about the configuration override.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Conversion Describes how a converter transformed the output of a static analysis tool from the analysis tool's native output format into the SARIF format.
+type Conversion struct {
+
+	// The locations of the analysis tool's per-run log files.
+	AnalysisToolLogFiles []*ArtifactLocation `json:"analysisToolLogFiles,omitempty"`
+
+	// An invocation object that describes the invocation of the converter.
+	Invocation *Invocation `json:"invocation,omitempty"`
+
+	// Key/value pairs that provide additional information about the conversion.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A tool object that describes the converter.
+	Tool *Tool `json:"tool"`
+}
+
+// Edge Represents a directed edge in a graph.
+type Edge struct {
+
+	// A string that uniquely identifies the edge within its graph.
+	ID string `json:"id"`
+
+	// A short description of the edge.
+	Label *Message `json:"label,omitempty"`
+
+	// Key/value pairs that provide additional information about the edge.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// Identifies the source node (the node at which the edge starts).
+	SourceNodeID string `json:"sourceNodeId"`
+
+	// Identifies the target node (the node at which the edge ends).
+	TargetNodeID string `json:"targetNodeId"`
+}
+
+// EdgeTraversal Represents the traversal of a single edge during a graph traversal.
+type EdgeTraversal struct {
+
+	// Identifies the edge being traversed.
+	EdgeID string `json:"edgeId"`
+
+	// The values of relevant expressions after the edge has been traversed.
+	FinalState map[string]*MultiformatMessageString `json:"finalState,omitempty"`
+
+	// A message to display to the user as the edge is traversed.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the edge traversal.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The number of edge traversals necessary to return from a nested graph.
+	StepOverEdgeCount int `json:"stepOverEdgeCount,omitempty"`
+}
+
+// Exception Describes a runtime exception encountered during the execution of an analysis tool.
+type Exception struct {
+
+	// An array of exception objects each of which is considered a cause of this exception.
+	InnerExceptions []*Exception `json:"innerExceptions,omitempty"`
+
+	// A string that identifies the kind of exception, for example, the fully qualified type name of an object that was thrown, or the symbolic name of a signal.
+	Kind string `json:"kind,omitempty"`
+
+	// A message that describes the exception.
+	Message string `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the exception.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The sequence of function calls leading to the exception.
+	Stack *Stack `json:"stack,omitempty"`
+}
+
+// ExternalProperties The top-level element of an external property file.
+type ExternalProperties struct {
+
+	// Addresses that will be merged with a separate run.
+	Addresses []*Address `json:"addresses,omitempty"`
+
+	// An array of artifact objects that will be merged with a separate run.
+	Artifacts []*Artifact `json:"artifacts,omitempty"`
+
+	// A conversion object that will be merged with a separate run.
+	Conversion *Conversion `json:"conversion,omitempty"`
+
+	// The analysis tool object that will be merged with a separate run.
+	Driver *ToolComponent `json:"driver,omitempty"`
+
+	// Tool extensions that will be merged with a separate run.
+	Extensions []*ToolComponent `json:"extensions,omitempty"`
+
+	// Key/value pairs that provide additional information that will be merged with a separate run.
+	ExternalizedProperties *PropertyBag `json:"externalizedProperties,omitempty"`
+
+	// An array of graph objects that will be merged with a separate run.
+	Graphs []*Graph `json:"graphs,omitempty"`
+
+	// A stable, unique identifier for this external properties object, in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// Describes the invocation of the analysis tool that will be merged with a separate run.
+	Invocations []*Invocation `json:"invocations,omitempty"`
+
+	// An array of logical locations such as namespaces, types or functions that will be merged with a separate run.
+	LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"`
+
+	// Tool policies that will be merged with a separate run.
+	Policies []*ToolComponent `json:"policies,omitempty"`
+
+	// Key/value pairs that provide additional information about the external properties.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of result objects that will be merged with a separate run.
+	Results []*Result `json:"results,omitempty"`
+
+	// A stable, unique identifier for the run associated with this external properties object, in the form of a GUID.
+	RunGUID string `json:"runGuid,omitempty"`
+
+	// The URI of the JSON schema corresponding to the version of the external property file format.
+	Schema string `json:"schema,omitempty"`
+
+	// Tool taxonomies that will be merged with a separate run.
+	Taxonomies []*ToolComponent `json:"taxonomies,omitempty"`
+
+	// An array of threadFlowLocation objects that will be merged with a separate run.
+	ThreadFlowLocations []*ThreadFlowLocation `json:"threadFlowLocations,omitempty"`
+
+	// Tool translations that will be merged with a separate run.
+	Translations []*ToolComponent `json:"translations,omitempty"`
+
+	// The SARIF format version of this external properties object.
+	Version interface{} `json:"version,omitempty"`
+
+	// Requests that will be merged with a separate run.
+	WebRequests []*WebRequest `json:"webRequests,omitempty"`
+
+	// Responses that will be merged with a separate run.
+	WebResponses []*WebResponse `json:"webResponses,omitempty"`
+}
+
+// ExternalPropertyFileReference Contains information that enables a SARIF consumer to locate the external property file that contains the value of an externalized property associated with the run.
+type ExternalPropertyFileReference struct {
+
+	// A stable, unique identifier for the external property file in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// A non-negative integer specifying the number of items contained in the external property file.
+	ItemCount int `json:"itemCount,omitempty"`
+
+	// The location of the external property file.
+	Location *ArtifactLocation `json:"location,omitempty"`
+
+	// Key/value pairs that provide additional information about the external property file.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// ExternalPropertyFileReferences References to external property files that should be inlined with the content of a root log file.
+type ExternalPropertyFileReferences struct {
+
+	// An array of external property files containing run.addresses arrays to be merged with the root log file.
+	Addresses []*ExternalPropertyFileReference `json:"addresses,omitempty"`
+
+	// An array of external property files containing run.artifacts arrays to be merged with the root log file.
+	Artifacts []*ExternalPropertyFileReference `json:"artifacts,omitempty"`
+
+	// An external property file containing a run.conversion object to be merged with the root log file.
+	Conversion *ExternalPropertyFileReference `json:"conversion,omitempty"`
+
+	// An external property file containing a run.driver object to be merged with the root log file.
+	Driver *ExternalPropertyFileReference `json:"driver,omitempty"`
+
+	// An array of external property files containing run.extensions arrays to be merged with the root log file.
+	Extensions []*ExternalPropertyFileReference `json:"extensions,omitempty"`
+
+	// An external property file containing a run.properties object to be merged with the root log file.
+	ExternalizedProperties *ExternalPropertyFileReference `json:"externalizedProperties,omitempty"`
+
+	// An array of external property files containing a run.graphs object to be merged with the root log file.
+	Graphs []*ExternalPropertyFileReference `json:"graphs,omitempty"`
+
+	// An array of external property files containing run.invocations arrays to be merged with the root log file.
+	Invocations []*ExternalPropertyFileReference `json:"invocations,omitempty"`
+
+	// An array of external property files containing run.logicalLocations arrays to be merged with the root log file.
+	LogicalLocations []*ExternalPropertyFileReference `json:"logicalLocations,omitempty"`
+
+	// An array of external property files containing run.policies arrays to be merged with the root log file.
+	Policies []*ExternalPropertyFileReference `json:"policies,omitempty"`
+
+	// Key/value pairs that provide additional information about the external property files.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of external property files containing run.results arrays to be merged with the root log file.
+	Results []*ExternalPropertyFileReference `json:"results,omitempty"`
+
+	// An array of external property files containing run.taxonomies arrays to be merged with the root log file.
+	Taxonomies []*ExternalPropertyFileReference `json:"taxonomies,omitempty"`
+
+	// An array of external property files containing run.threadFlowLocations arrays to be merged with the root log file.
+	ThreadFlowLocations []*ExternalPropertyFileReference `json:"threadFlowLocations,omitempty"`
+
+	// An array of external property files containing run.translations arrays to be merged with the root log file.
+	Translations []*ExternalPropertyFileReference `json:"translations,omitempty"`
+
+	// An array of external property files containing run.requests arrays to be merged with the root log file.
+	WebRequests []*ExternalPropertyFileReference `json:"webRequests,omitempty"`
+
+	// An array of external property files containing run.responses arrays to be merged with the root log file.
+	WebResponses []*ExternalPropertyFileReference `json:"webResponses,omitempty"`
+}
+
+// Fix A proposed fix for the problem represented by a result object. A fix specifies a set of artifacts to modify. For each artifact, it specifies a set of bytes to remove, and provides a set of new bytes to replace them.
+type Fix struct {
+
+	// One or more artifact changes that comprise a fix for a result.
+	ArtifactChanges []*ArtifactChange `json:"artifactChanges"`
+
+	// A message that describes the proposed fix, enabling viewers to present the proposed change to an end user.
+	Description *Message `json:"description,omitempty"`
+
+	// Key/value pairs that provide additional information about the fix.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Graph A network of nodes and directed edges that describes some aspect of the structure of the code (for example, a call graph).
+type Graph struct {
+
+	// A description of the graph.
+	Description *Message `json:"description,omitempty"`
+
+	// An array of edge objects representing the edges of the graph.
+	Edges []*Edge `json:"edges,omitempty"`
+
+	// An array of node objects representing the nodes of the graph.
+	Nodes []*Node `json:"nodes,omitempty"`
+
+	// Key/value pairs that provide additional information about the graph.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// GraphTraversal Represents a path through a graph.
+type GraphTraversal struct {
+
+	// A description of this graph traversal.
+	Description *Message `json:"description,omitempty"`
+
+	// The sequences of edges traversed by this graph traversal.
+	EdgeTraversals []*EdgeTraversal `json:"edgeTraversals,omitempty"`
+
+	// Values of relevant expressions at the start of the graph traversal that remain constant for the graph traversal.
+	ImmutableState map[string]*MultiformatMessageString `json:"immutableState,omitempty"`
+
+	// Values of relevant expressions at the start of the graph traversal that may change during graph traversal.
+	InitialState map[string]*MultiformatMessageString `json:"initialState,omitempty"`
+
+	// Key/value pairs that provide additional information about the graph traversal.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The index within the result.graphs to be associated with the result.
+	ResultGraphIndex int `json:"resultGraphIndex,omitempty"`
+
+	// The index within the run.graphs to be associated with the result.
+	RunGraphIndex int `json:"runGraphIndex,omitempty"`
+}
+
+// Invocation The runtime environment of the analysis tool run.
+type Invocation struct {
+
+	// The account under which the invocation occurred.
+	Account string `json:"account,omitempty"`
+
+	// An array of strings, containing in order the command line arguments passed to the tool from the operating system.
+	Arguments []string `json:"arguments,omitempty"`
+
+	// The command line used to invoke the tool.
+	CommandLine string `json:"commandLine,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the invocation ended. See "Date/time properties" in the SARIF spec for the required format.
+	EndTimeUtc string `json:"endTimeUtc,omitempty"`
+
+	// The environment variables associated with the analysis tool process, expressed as key/value pairs.
+	EnvironmentVariables map[string]string `json:"environmentVariables,omitempty"`
+
+	// An absolute URI specifying the location of the executable that was invoked.
+	ExecutableLocation *ArtifactLocation `json:"executableLocation,omitempty"`
+
+	// Specifies whether the tool's execution completed successfully.
+	ExecutionSuccessful bool `json:"executionSuccessful"`
+
+	// The process exit code.
+	ExitCode int `json:"exitCode,omitempty"`
+
+	// The reason for the process exit.
+	ExitCodeDescription string `json:"exitCodeDescription,omitempty"`
+
+	// The name of the signal that caused the process to exit.
+	ExitSignalName string `json:"exitSignalName,omitempty"`
+
+	// The numeric value of the signal that caused the process to exit.
+	ExitSignalNumber int `json:"exitSignalNumber,omitempty"`
+
+	// The machine on which the invocation occurred.
+	Machine string `json:"machine,omitempty"`
+
+	// An array of configurationOverride objects that describe notifications related runtime overrides.
+	NotificationConfigurationOverrides []*ConfigurationOverride `json:"notificationConfigurationOverrides,omitempty"`
+
+	// The id of the process in which the invocation occurred.
+	ProcessId int `json:"processId,omitempty"`
+
+	// The reason given by the operating system that the process failed to start.
+	ProcessStartFailureMessage string `json:"processStartFailureMessage,omitempty"`
+
+	// Key/value pairs that provide additional information about the invocation.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The locations of any response files specified on the tool's command line.
+	ResponseFiles []*ArtifactLocation `json:"responseFiles,omitempty"`
+
+	// An array of configurationOverride objects that describe rules related runtime overrides.
+	RuleConfigurationOverrides []*ConfigurationOverride `json:"ruleConfigurationOverrides,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the invocation started. See "Date/time properties" in the SARIF spec for the required format.
+	StartTimeUtc string `json:"startTimeUtc,omitempty"`
+
+	// A file containing the standard error stream from the process that was invoked.
+	Stderr *ArtifactLocation `json:"stderr,omitempty"`
+
+	// A file containing the standard input stream to the process that was invoked.
+	Stdin *ArtifactLocation `json:"stdin,omitempty"`
+
+	// A file containing the standard output stream from the process that was invoked.
+	Stdout *ArtifactLocation `json:"stdout,omitempty"`
+
+	// A file containing the interleaved standard output and standard error stream from the process that was invoked.
+	StdoutStderr *ArtifactLocation `json:"stdoutStderr,omitempty"`
+
+	// A list of conditions detected by the tool that are relevant to the tool's configuration.
+	ToolConfigurationNotifications []*Notification `json:"toolConfigurationNotifications,omitempty"`
+
+	// A list of runtime conditions detected by the tool during the analysis.
+	ToolExecutionNotifications []*Notification `json:"toolExecutionNotifications,omitempty"`
+
+	// The working directory for the invocation.
+	WorkingDirectory *ArtifactLocation `json:"workingDirectory,omitempty"`
+}
+
+// Location A location within a programming artifact.
+type Location struct {
+
+	// A set of regions relevant to the location.
+	Annotations []*Region `json:"annotations,omitempty"`
+
+	// Value that distinguishes this location from all other locations within a single result object.
+	Id int `json:"id,omitempty"`
+
+	// The logical locations associated with the result.
+	LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"`
+
+	// A message relevant to the location.
+	Message *Message `json:"message,omitempty"`
+
+	// Identifies the artifact and region.
+	PhysicalLocation *PhysicalLocation `json:"physicalLocation,omitempty"`
+
+	// Key/value pairs that provide additional information about the location.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of objects that describe relationships between this location and others.
+	Relationships []*LocationRelationship `json:"relationships,omitempty"`
+}
+
+// LocationRelationship Information about the relation of one location to another.
+type LocationRelationship struct {
+
+	// A description of the location relationship.
+	Description *Message `json:"description,omitempty"`
+
+	// A set of distinct strings that categorize the relationship. Well-known kinds include 'includes', 'isIncludedBy' and 'relevant'.
+	Kinds []string `json:"kinds,omitempty"`
+
+	// Key/value pairs that provide additional information about the location relationship.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A reference to the related location.
+	Target int `json:"target"`
+}
+
+// LogicalLocation A logical location of a construct that produced a result.
+type LogicalLocation struct {
+
+	// The machine-readable name for the logical location, such as a mangled function name provided by a C++ compiler that encodes calling convention, return type and other details along with the function name.
+	DecoratedName string `json:"decoratedName,omitempty"`
+
+	// The human-readable fully qualified name of the logical location.
+	FullyQualifiedName string `json:"fullyQualifiedName,omitempty"`
+
+	// The index within the logical locations array.
+	Index int `json:"index,omitempty"`
+
+	// The type of construct this logical location component refers to. Should be one of 'function', 'member', 'module', 'namespace', 'parameter', 'resource', 'returnType', 'type', 'variable', 'object', 'array', 'property', 'value', 'element', 'text', 'attribute', 'comment', 'declaration', 'dtd' or 'processingInstruction', if any of those accurately describe the construct.
+	Kind string `json:"kind,omitempty"`
+
+	// Identifies the construct in which the result occurred. For example, this property might contain the name of a class or a method.
+	Name string `json:"name,omitempty"`
+
+	// Identifies the index of the immediate parent of the construct in which the result was detected. For example, this property might point to a logical location that represents the namespace that holds a type.
+	ParentIndex int `json:"parentIndex,omitempty"`
+
+	// Key/value pairs that provide additional information about the logical location.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Message Encapsulates a message intended to be read by the end user.
+type Message struct {
+
+	// An array of strings to substitute into the message string.
+	Arguments []string `json:"arguments,omitempty"`
+
+	// The identifier for this message.
+	ID string `json:"id,omitempty"`
+
+	// A Markdown message string.
+	Markdown string `json:"markdown,omitempty"`
+
+	// Key/value pairs that provide additional information about the message.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A plain text message string.
+	Text string `json:"text,omitempty"`
+}
+
+// MultiformatMessageString A message string or message format string rendered in multiple formats.
+type MultiformatMessageString struct {
+
+	// A Markdown message string or format string.
+	Markdown string `json:"markdown,omitempty"`
+
+	// Key/value pairs that provide additional information about the message.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A plain text message string or format string.
+	Text string `json:"text"`
+}
+
+// Node Represents a node in a graph.
+type Node struct {
+
+	// Array of child nodes.
+	Children []*Node `json:"children,omitempty"`
+
+	// A string that uniquely identifies the node within its graph.
+	ID string `json:"id"`
+
+	// A short description of the node.
+	Label *Message `json:"label,omitempty"`
+
+	// A code location associated with the node.
+	Location *Location `json:"location,omitempty"`
+
+	// Key/value pairs that provide additional information about the node.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Notification Describes a condition relevant to the tool itself, as opposed to being relevant to a target being analyzed by the tool.
+type Notification struct {
+
+	// A reference used to locate the rule descriptor associated with this notification.
+	AssociatedRule *ReportingDescriptorReference `json:"associatedRule,omitempty"`
+
+	// A reference used to locate the descriptor relevant to this notification.
+	Descriptor *ReportingDescriptorReference `json:"descriptor,omitempty"`
+
+	// The runtime exception, if any, relevant to this notification.
+	Exception *Exception `json:"exception,omitempty"`
+
+	// A value specifying the severity level of the notification.
+	Level interface{} `json:"level,omitempty"`
+
+	// The locations relevant to this notification.
+	Locations []*Location `json:"locations,omitempty"`
+
+	// A message that describes the condition that was encountered.
+	Message *Message `json:"message"`
+
+	// Key/value pairs that provide additional information about the notification.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The thread identifier of the code that generated the notification.
+	ThreadID int `json:"threadId,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the analysis tool generated the notification.
+	TimeUtc string `json:"timeUtc,omitempty"`
+}
+
+// PhysicalLocation A physical location relevant to a result. Specifies a reference to a programming artifact together with a range of bytes or characters within that artifact.
+type PhysicalLocation struct {
+
+	// The address of the location.
+	Address *Address `json:"address,omitempty"`
+
+	// The location of the artifact.
+	ArtifactLocation *ArtifactLocation `json:"artifactLocation,omitempty"`
+
+	// Specifies a portion of the artifact that encloses the region. Allows a viewer to display additional context around the region.
+	ContextRegion *Region `json:"contextRegion,omitempty"`
+
+	// Key/value pairs that provide additional information about the physical location.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// Specifies a portion of the artifact.
+	Region *Region `json:"region,omitempty"`
+}
+
+// PropertyBag Key/value pairs that provide additional information about the object.
+type PropertyBag map[string]interface{}
+
+// Rectangle An area within an image.
+type Rectangle struct {
+
+	// The Y coordinate of the bottom edge of the rectangle, measured in the image's natural units.
+	Bottom float64 `json:"bottom,omitempty"`
+
+	// The X coordinate of the left edge of the rectangle, measured in the image's natural units.
+	Left float64 `json:"left,omitempty"`
+
+	// A message relevant to the rectangle.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the rectangle.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The X coordinate of the right edge of the rectangle, measured in the image's natural units.
+	Right float64 `json:"right,omitempty"`
+
+	// The Y coordinate of the top edge of the rectangle, measured in the image's natural units.
+	Top float64 `json:"top,omitempty"`
+}
+
+// Region A region within an artifact where a result was detected.
+type Region struct {
+
+	// The length of the region in bytes.
+	ByteLength int `json:"byteLength,omitempty"`
+
+	// The zero-based offset from the beginning of the artifact of the first byte in the region.
+	ByteOffset int `json:"byteOffset,omitempty"`
+
+	// The length of the region in characters.
+	CharLength int `json:"charLength,omitempty"`
+
+	// The zero-based offset from the beginning of the artifact of the first character in the region.
+	CharOffset int `json:"charOffset,omitempty"`
+
+	// The column number of the character following the end of the region.
+	EndColumn int `json:"endColumn,omitempty"`
+
+	// The line number of the last character in the region.
+	EndLine int `json:"endLine,omitempty"`
+
+	// A message relevant to the region.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the region.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The portion of the artifact contents within the specified region.
+	Snippet *ArtifactContent `json:"snippet,omitempty"`
+
+	// Specifies the source language, if any, of the portion of the artifact specified by the region object.
+	SourceLanguage string `json:"sourceLanguage,omitempty"`
+
+	// The column number of the first character in the region.
+	StartColumn int `json:"startColumn,omitempty"`
+
+	// The line number of the first character in the region.
+	StartLine int `json:"startLine,omitempty"`
+}
+
+// Replacement The replacement of a single region of an artifact.
+type Replacement struct {
+
+	// The region of the artifact to delete.
+	DeletedRegion *Region `json:"deletedRegion"`
+
+	// The content to insert at the location specified by the 'deletedRegion' property.
+	InsertedContent *ArtifactContent `json:"insertedContent,omitempty"`
+
+	// Key/value pairs that provide additional information about the replacement.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// ReportingConfiguration Information about a rule or notification that can be configured at runtime.
+type ReportingConfiguration struct {
+
+	// Specifies whether the report may be produced during the scan.
+	Enabled bool `json:"enabled,omitempty"`
+
+	// Specifies the failure level for the report.
+	Level interface{} `json:"level,omitempty"`
+
+	// Contains configuration information specific to a report.
+	Parameters *PropertyBag `json:"parameters,omitempty"`
+
+	// Key/value pairs that provide additional information about the reporting configuration.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// Specifies the relative priority of the report. Used for analysis output only.
+	Rank float64 `json:"rank,omitempty"`
+}
+
+// ReportingDescriptor Metadata that describes a specific report produced by the tool, as part of the analysis it provides or its runtime reporting.
+type ReportingDescriptor struct {
+
+	// Default reporting configuration information.
+	DefaultConfiguration *ReportingConfiguration `json:"defaultConfiguration,omitempty"`
+
+	// An array of unique identifies in the form of a GUID by which this report was known in some previous version of the analysis tool.
+	DeprecatedGuids []string `json:"deprecatedGuids,omitempty"`
+
+	// An array of stable, opaque identifiers by which this report was known in some previous version of the analysis tool.
+	DeprecatedIds []string `json:"deprecatedIds,omitempty"`
+
+	// An array of readable identifiers by which this report was known in some previous version of the analysis tool.
+	DeprecatedNames []string `json:"deprecatedNames,omitempty"`
+
+	// A description of the report. Should, as far as possible, provide details sufficient to enable resolution of any problem indicated by the result.
+	FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"`
+
+	// A unique identifier for the reporting descriptor in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// Provides the primary documentation for the report, useful when there is no online documentation.
+	Help *MultiformatMessageString `json:"help,omitempty"`
+
+	// A URI where the primary documentation for the report can be found.
+	HelpURI string `json:"helpUri,omitempty"`
+
+	// A stable, opaque identifier for the report.
+	ID string `json:"id"`
+
+	// A set of name/value pairs with arbitrary names. Each value is a multiformatMessageString object, which holds message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.
+	MessageStrings map[string]*MultiformatMessageString `json:"messageStrings,omitempty"`
+
+	// A report identifier that is understandable to an end user.
+	Name string `json:"name,omitempty"`
+
+	// Key/value pairs that provide additional information about the report.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of objects that describe relationships between this reporting descriptor and others.
+	Relationships []*ReportingDescriptorRelationship `json:"relationships,omitempty"`
+
+	// A concise description of the report. Should be a single sentence that is understandable when visible space is limited to a single line of text.
+	ShortDescription *MultiformatMessageString `json:"shortDescription,omitempty"`
+}
+
+// ReportingDescriptorReference Information about how to locate a relevant reporting descriptor.
+type ReportingDescriptorReference struct {
+
+	// A guid that uniquely identifies the descriptor.
+	GUID string `json:"guid,omitempty"`
+
+	// The id of the descriptor.
+	ID string `json:"id,omitempty"`
+
+	// The index into an array of descriptors in toolComponent.ruleDescriptors, toolComponent.notificationDescriptors, or toolComponent.taxonomyDescriptors, depending on context.
+	Index int `json:"index,omitempty"`
+
+	// Key/value pairs that provide additional information about the reporting descriptor reference.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A reference used to locate the toolComponent associated with the descriptor.
+	ToolComponent *ToolComponentReference `json:"toolComponent,omitempty"`
+}
+
+// ReportingDescriptorRelationship Information about the relation of one reporting descriptor to another.
+type ReportingDescriptorRelationship struct {
+
+	// A description of the reporting descriptor relationship.
+	Description *Message `json:"description,omitempty"`
+
+	// A set of distinct strings that categorize the relationship. Well-known kinds include 'canPrecede', 'canFollow', 'willPrecede', 'willFollow', 'superset', 'subset', 'equal', 'disjoint', 'relevant', and 'incomparable'.
+	Kinds []string `json:"kinds,omitempty"`
+
+	// Key/value pairs that provide additional information about the reporting descriptor reference.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A reference to the related reporting descriptor.
+	Target *ReportingDescriptorReference `json:"target"`
+}
+
+// Result A result produced by an analysis tool.
+type Result struct {
+
+	// Identifies the artifact that the analysis tool was instructed to scan. This need not be the same as the artifact where the result actually occurred.
+	AnalysisTarget *ArtifactLocation `json:"analysisTarget,omitempty"`
+
+	// A set of artifacts relevant to the result.
+	Attachments []*Attachment `json:"attachments,omitempty"`
+
+	// The state of a result relative to a baseline of a previous run.
+	BaselineState interface{} `json:"baselineState,omitempty"`
+
+	// An array of 'codeFlow' objects relevant to the result.
+	CodeFlows []*CodeFlow `json:"codeFlows,omitempty"`
+
+	// A stable, unique identifier for the equivalence class of logically identical results to which this result belongs, in the form of a GUID.
+	CorrelationGUID string `json:"correlationGuid,omitempty"`
+
+	// A set of strings each of which individually defines a stable, unique identity for the result.
+	Fingerprints map[string]string `json:"fingerprints,omitempty"`
+
+	// An array of 'fix' objects, each of which represents a proposed fix to the problem indicated by the result.
+	Fixes []*Fix `json:"fixes,omitempty"`
+
+	// An array of one or more unique 'graphTraversal' objects.
+	GraphTraversals []*GraphTraversal `json:"graphTraversals,omitempty"`
+
+	// An array of zero or more unique graph objects associated with the result.
+	Graphs []*Graph `json:"graphs,omitempty"`
+
+	// A stable, unique identifier for the result in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// An absolute URI at which the result can be viewed.
+	HostedViewerURI string `json:"hostedViewerUri,omitempty"`
+
+	// A value that categorizes results by evaluation state.
+	Kind interface{} `json:"kind,omitempty"`
+
+	// A value specifying the severity level of the result.
+	Level interface{} `json:"level,omitempty"`
+
+	// The set of locations where the result was detected. Specify only one location unless the problem indicated by the result can only be corrected by making a change at every specified location.
+	Locations []*Location `json:"locations,omitempty"`
+
+	// A message that describes the result. The first sentence of the message only will be displayed when visible space is limited.
+	Message *Message `json:"message"`
+
+	// A positive integer specifying the number of times this logically unique result was observed in this run.
+	OccurrenceCount int `json:"occurrenceCount,omitempty"`
+
+	// A set of strings that contribute to the stable, unique identity of the result.
+	PartialFingerprints map[string]string `json:"partialFingerprints,omitempty"`
+
+	// Key/value pairs that provide additional information about the result.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// Information about how and when the result was detected.
+	Provenance *ResultProvenance `json:"provenance,omitempty"`
+
+	// A number representing the priority or importance of the result.
+	Rank float64 `json:"rank,omitempty"`
+
+	// A set of locations relevant to this result.
+	RelatedLocations []*Location `json:"relatedLocations,omitempty"`
+
+	// A reference used to locate the rule descriptor relevant to this result.
+	Rule *ReportingDescriptorReference `json:"rule,omitempty"`
+
+	// The stable, unique identifier of the rule, if any, to which this result is relevant.
+	RuleID string `json:"ruleId,omitempty"`
+
+	// The index within the tool component rules array of the rule object associated with this result.
+	RuleIndex int `json:"ruleIndex,omitempty"`
+
+	// An array of 'stack' objects relevant to the result.
+	Stacks []*Stack `json:"stacks,omitempty"`
+
+	// A set of suppressions relevant to this result.
+	Suppressions []*Suppression `json:"suppressions,omitempty"`
+
+	// An array of references to taxonomy reporting descriptors that are applicable to the result.
+	Taxa []*ReportingDescriptorReference `json:"taxa,omitempty"`
+
+	// A web request associated with this result.
+	WebRequest *WebRequest `json:"webRequest,omitempty"`
+
+	// A web response associated with this result.
+	WebResponse *WebResponse `json:"webResponse,omitempty"`
+
+	// The URIs of the work items associated with this result.
+	WorkItemUris []string `json:"workItemUris,omitempty"`
+}
+
+// ResultProvenance Contains information about how and when a result was detected.
+type ResultProvenance struct {
+
+	// An array of physicalLocation objects which specify the portions of an analysis tool's output that a converter transformed into the result.
+	ConversionSources []*PhysicalLocation `json:"conversionSources,omitempty"`
+
+	// A GUID-valued string equal to the automationDetails.guid property of the run in which the result was first detected.
+	FirstDetectionRunGUID string `json:"firstDetectionRunGuid,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the result was first detected. See "Date/time properties" in the SARIF spec for the required format.
+	FirstDetectionTimeUtc string `json:"firstDetectionTimeUtc,omitempty"`
+
+	// The index within the run.invocations array of the invocation object which describes the tool invocation that detected the result.
+	InvocationIndex int `json:"invocationIndex,omitempty"`
+
+	// A GUID-valued string equal to the automationDetails.guid property of the run in which the result was most recently detected.
+	LastDetectionRunGUID string `json:"lastDetectionRunGuid,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which the result was most recently detected. See "Date/time properties" in the SARIF spec for the required format.
+	LastDetectionTimeUtc string `json:"lastDetectionTimeUtc,omitempty"`
+
+	// Key/value pairs that provide additional information about the result.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Run Describes a single run of an analysis tool, and contains the reported output of that run.
+type Run struct {
+
+	// Addresses associated with this run instance, if any.
+	Addresses []*Address `json:"addresses,omitempty"`
+
+	// An array of artifact objects relevant to the run.
+	Artifacts []*Artifact `json:"artifacts,omitempty"`
+
+	// Automation details that describe this run.
+	AutomationDetails *RunAutomationDetails `json:"automationDetails,omitempty"`
+
+	// The 'guid' property of a previous SARIF 'run' that comprises the baseline that was used to compute result 'baselineState' properties for the run.
+	BaselineGUID string `json:"baselineGuid,omitempty"`
+
+	// Specifies the unit in which the tool measures columns.
+	ColumnKind interface{} `json:"columnKind,omitempty"`
+
+	// A conversion object that describes how a converter transformed an analysis tool's native reporting format into the SARIF format.
+	Conversion *Conversion `json:"conversion,omitempty"`
+
+	// Specifies the default encoding for any artifact object that refers to a text file.
+	DefaultEncoding string `json:"defaultEncoding,omitempty"`
+
+	// Specifies the default source language for any artifact object that refers to a text file that contains source code.
+	DefaultSourceLanguage string `json:"defaultSourceLanguage,omitempty"`
+
+	// References to external property files that should be inlined with the content of a root log file.
+	ExternalPropertyFileReferences *ExternalPropertyFileReferences `json:"externalPropertyFileReferences,omitempty"`
+
+	// An array of zero or more unique graph objects associated with the run.
+	Graphs []*Graph `json:"graphs,omitempty"`
+
+	// Describes the invocation of the analysis tool.
+	Invocations []*Invocation `json:"invocations,omitempty"`
+
+	// The language of the messages emitted into the log file during this run (expressed as an ISO 639-1 two-letter lowercase culture code) and an optional region (expressed as an ISO 3166-1 two-letter uppercase subculture code associated with a country or region). The casing is recommended but not required (in order for this data to conform to RFC5646).
+	Language string `json:"language,omitempty"`
+
+	// An array of logical locations such as namespaces, types or functions.
+	LogicalLocations []*LogicalLocation `json:"logicalLocations,omitempty"`
+
+	// An ordered list of character sequences that were treated as line breaks when computing region information for the run.
+	NewlineSequences []string `json:"newlineSequences,omitempty"`
+
+	// The artifact location specified by each uriBaseId symbol on the machine where the tool originally ran.
+	OriginalUriBaseIds map[string]*ArtifactLocation `json:"originalUriBaseIds,omitempty"`
+
+	// Contains configurations that may potentially override both reportingDescriptor.defaultConfiguration (the tool's default severities) and invocation.configurationOverrides (severities established at run-time from the command line).
+	Policies []*ToolComponent `json:"policies,omitempty"`
+
+	// Key/value pairs that provide additional information about the run.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// An array of strings used to replace sensitive information in a redaction-aware property.
+	RedactionTokens []string `json:"redactionTokens,omitempty"`
+
+	// The set of results contained in an SARIF log. The results array can be omitted when a run is solely exporting rules metadata. It must be present (but may be empty) if a log file represents an actual scan.
+	Results []*Result `json:"results"`
+
+	// Automation details that describe the aggregate of runs to which this run belongs.
+	RunAggregates []*RunAutomationDetails `json:"runAggregates,omitempty"`
+
+	// A specialLocations object that defines locations of special significance to SARIF consumers.
+	SpecialLocations *SpecialLocations `json:"specialLocations,omitempty"`
+
+	// An array of toolComponent objects relevant to a taxonomy in which results are categorized.
+	Taxonomies []*ToolComponent `json:"taxonomies,omitempty"`
+
+	// An array of threadFlowLocation objects cached at run level.
+	ThreadFlowLocations []*ThreadFlowLocation `json:"threadFlowLocations,omitempty"`
+
+	// Information about the tool or tool pipeline that generated the results in this run. A run can only contain results produced by a single tool or tool pipeline. A run can aggregate results from multiple log files, as long as context around the tool run (tool command-line arguments and the like) is identical for all aggregated files.
+	Tool *Tool `json:"tool"`
+
+	// The set of available translations of the localized data provided by the tool.
+	Translations []*ToolComponent `json:"translations,omitempty"`
+
+	// Specifies the revision in version control of the artifacts that were scanned.
+	VersionControlProvenance []*VersionControlDetails `json:"versionControlProvenance,omitempty"`
+
+	// An array of request objects cached at run level.
+	WebRequests []*WebRequest `json:"webRequests,omitempty"`
+
+	// An array of response objects cached at run level.
+	WebResponses []*WebResponse `json:"webResponses,omitempty"`
+}
+
+// RunAutomationDetails Information that describes a run's identity and role within an engineering system process.
+type RunAutomationDetails struct {
+
+	// A stable, unique identifier for the equivalence class of runs to which this object's containing run object belongs in the form of a GUID.
+	CorrelationGUID string `json:"correlationGuid,omitempty"`
+
+	// A description of the identity and role played within the engineering system by this object's containing run object.
+	Description *Message `json:"description,omitempty"`
+
+	// A stable, unique identifier for this object's containing run object in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// A hierarchical string that uniquely identifies this object's containing run object.
+	ID string `json:"id,omitempty"`
+
+	// Key/value pairs that provide additional information about the run automation details.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// SpecialLocations Defines locations of special significance to SARIF consumers.
+type SpecialLocations struct {
+
+	// Provides a suggestion to SARIF consumers to display file paths relative to the specified location.
+	DisplayBase *ArtifactLocation `json:"displayBase,omitempty"`
+
+	// Key/value pairs that provide additional information about the special locations.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// Stack A call stack that is relevant to a result.
+type Stack struct {
+
+	// An array of stack frames that represents a sequence of calls, rendered in reverse chronological order, that comprise the call stack.
+	Frames []*StackFrame `json:"frames"`
+
+	// A message relevant to this call stack.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the stack.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// StackFrame A function call within a stack trace.
+type StackFrame struct {
+
+	// The location to which this stack frame refers.
+	Location *Location `json:"location,omitempty"`
+
+	// The name of the module that contains the code of this stack frame.
+	Module string `json:"module,omitempty"`
+
+	// The parameters of the call that is executing.
+	Parameters []string `json:"parameters,omitempty"`
+
+	// Key/value pairs that provide additional information about the stack frame.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The thread identifier of the stack frame.
+	ThreadID int `json:"threadId,omitempty"`
+}
+
+// Report Static Analysis Results Format (SARIF) Version 2.1.0 JSON Schema: a standard format for the output of static analysis tools.
+type Report struct {
+
+	// References to external property files that share data between runs.
+	InlineExternalProperties []*ExternalProperties `json:"inlineExternalProperties,omitempty"`
+
+	// Key/value pairs that provide additional information about the log file.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The set of runs contained in this log file.
+	Runs []*Run `json:"runs"`
+
+	// The URI of the JSON schema corresponding to the version.
+	Schema string `json:"$schema,omitempty"`
+
+	// The SARIF format version of this log file.
+	Version interface{} `json:"version"`
+}
+
+// Suppression A suppression that is relevant to a result.
+type Suppression struct {
+
+	// A stable, unique identifier for the suprression in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// A string representing the justification for the suppression.
+	Justification string `json:"justification,omitempty"`
+
+	// A string that indicates where the suppression is persisted.
+	Kind interface{} `json:"kind"`
+
+	// Identifies the location associated with the suppression.
+	Location *Location `json:"location,omitempty"`
+
+	// Key/value pairs that provide additional information about the suppression.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A string that indicates the review status of the suppression.
+	Status interface{} `json:"status,omitempty"`
+}
+
+// ThreadFlow Describes a sequence of code locations that specify a path through a single thread of execution such as an operating system or fiber.
+type ThreadFlow struct {
+
+	// An string that uniquely identifies the threadFlow within the codeFlow in which it occurs.
+	ID string `json:"id,omitempty"`
+
+	// Values of relevant expressions at the start of the thread flow that remain constant.
+	ImmutableState map[string]*MultiformatMessageString `json:"immutableState,omitempty"`
+
+	// Values of relevant expressions at the start of the thread flow that may change during thread flow execution.
+	InitialState map[string]*MultiformatMessageString `json:"initialState,omitempty"`
+
+	// A temporally ordered array of 'threadFlowLocation' objects, each of which describes a location visited by the tool while producing the result.
+	Locations []*ThreadFlowLocation `json:"locations"`
+
+	// A message relevant to the thread flow.
+	Message *Message `json:"message,omitempty"`
+
+	// Key/value pairs that provide additional information about the thread flow.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// ThreadFlowLocation A location visited by an analysis tool while simulating or monitoring the execution of a program.
+type ThreadFlowLocation struct {
+
+	// An integer representing the temporal order in which execution reached this location.
+	ExecutionOrder int `json:"executionOrder,omitempty"`
+
+	// The Coordinated Universal Time (UTC) date and time at which this location was executed.
+	ExecutionTimeUtc string `json:"executionTimeUtc,omitempty"`
+
+	// Specifies the importance of this location in understanding the code flow in which it occurs. The order from most to least important is "essential", "important", "unimportant". Default: "important".
+	Importance interface{} `json:"importance,omitempty"`
+
+	// The index within the run threadFlowLocations array.
+	Index int `json:"index,omitempty"`
+
+	// A set of distinct strings that categorize the thread flow location. Well-known kinds include 'acquire', 'release', 'enter', 'exit', 'call', 'return', 'branch', 'implicit', 'false', 'true', 'caution', 'danger', 'unknown', 'unreachable', 'taint', 'function', 'handler', 'lock', 'memory', 'resource', 'scope' and 'value'.
+	Kinds []string `json:"kinds,omitempty"`
+
+	// The code location.
+	Location *Location `json:"location,omitempty"`
+
+	// The name of the module that contains the code that is executing.
+	Module string `json:"module,omitempty"`
+
+	// An integer representing a containment hierarchy within the thread flow.
+	NestingLevel int `json:"nestingLevel,omitempty"`
+
+	// Key/value pairs that provide additional information about the threadflow location.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The call stack leading to this location.
+	Stack *Stack `json:"stack,omitempty"`
+
+	// A dictionary, each of whose keys specifies a variable or expression, the associated value of which represents the variable or expression value. For an annotation of kind 'continuation', for example, this dictionary might hold the current assumed values of a set of global variables.
+	State map[string]*MultiformatMessageString `json:"state,omitempty"`
+
+	// An array of references to rule or taxonomy reporting descriptors that are applicable to the thread flow location.
+	Taxa []*ReportingDescriptorReference `json:"taxa,omitempty"`
+
+	// A web request associated with this thread flow location.
+	WebRequest *WebRequest `json:"webRequest,omitempty"`
+
+	// A web response associated with this thread flow location.
+	WebResponse *WebResponse `json:"webResponse,omitempty"`
+}
+
+// Tool The analysis tool that was run.
+type Tool struct {
+
+	// The analysis tool that was run.
+	Driver *ToolComponent `json:"driver"`
+
+	// Tool extensions that contributed to or reconfigured the analysis tool that was run.
+	Extensions []*ToolComponent `json:"extensions,omitempty"`
+
+	// Key/value pairs that provide additional information about the tool.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// ToolComponent A component, such as a plug-in or the driver, of the analysis tool that was run.
+type ToolComponent struct {
+
+	// The component which is strongly associated with this component. For a translation, this refers to the component which has been translated. For an extension, this is the driver that provides the extension's plugin model.
+	AssociatedComponent *ToolComponentReference `json:"associatedComponent,omitempty"`
+
+	// The kinds of data contained in this object.
+	Contents []interface{} `json:"contents,omitempty"`
+
+	// The binary version of the tool component's primary executable file expressed as four non-negative integers separated by a period (for operating systems that express file versions in this way).
+	DottedQuadFileVersion string `json:"dottedQuadFileVersion,omitempty"`
+
+	// The absolute URI from which the tool component can be downloaded.
+	DownloadURI string `json:"downloadUri,omitempty"`
+
+	// A comprehensive description of the tool component.
+	FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"`
+
+	// The name of the tool component along with its version and any other useful identifying information, such as its locale.
+	FullName string `json:"fullName,omitempty"`
+
+	// A dictionary, each of whose keys is a resource identifier and each of whose values is a multiformatMessageString object, which holds message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.
+	GlobalMessageStrings map[string]*MultiformatMessageString `json:"globalMessageStrings,omitempty"`
+
+	// A unique identifier for the tool component in the form of a GUID.
+	GUID string `json:"guid,omitempty"`
+
+	// The absolute URI at which information about this version of the tool component can be found.
+	InformationURI string `json:"informationUri,omitempty"`
+
+	// Specifies whether this object contains a complete definition of the localizable and/or non-localizable data for this component, as opposed to including only data that is relevant to the results persisted to this log file.
+	IsComprehensive bool `json:"isComprehensive,omitempty"`
+
+	// The language of the messages emitted into the log file during this run (expressed as an ISO 639-1 two-letter lowercase language code) and an optional region (expressed as an ISO 3166-1 two-letter uppercase subculture code associated with a country or region). The casing is recommended but not required (in order for this data to conform to RFC5646).
+	Language string `json:"language,omitempty"`
+
+	// The semantic version of the localized strings defined in this component; maintained by components that provide translations.
+	LocalizedDataSemanticVersion string `json:"localizedDataSemanticVersion,omitempty"`
+
+	// An array of the artifactLocation objects associated with the tool component.
+	Locations []*ArtifactLocation `json:"locations,omitempty"`
+
+	// The minimum value of localizedDataSemanticVersion required in translations consumed by this component; used by components that consume translations.
+	MinimumRequiredLocalizedDataSemanticVersion string `json:"minimumRequiredLocalizedDataSemanticVersion,omitempty"`
+
+	// The name of the tool component.
+	Name string `json:"name"`
+
+	// An array of reportingDescriptor objects relevant to the notifications related to the configuration and runtime execution of the tool component.
+	Notifications []*ReportingDescriptor `json:"notifications,omitempty"`
+
+	// The organization or company that produced the tool component.
+	Organization string `json:"organization,omitempty"`
+
+	// A product suite to which the tool component belongs.
+	Product string `json:"product,omitempty"`
+
+	// A localizable string containing the name of the suite of products to which the tool component belongs.
+	ProductSuite string `json:"productSuite,omitempty"`
+
+	// Key/value pairs that provide additional information about the tool component.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A string specifying the UTC date (and optionally, the time) of the component's release.
+	ReleaseDateUtc string `json:"releaseDateUtc,omitempty"`
+
+	// An array of reportingDescriptor objects relevant to the analysis performed by the tool component.
+	Rules []*ReportingDescriptor `json:"rules,omitempty"`
+
+	// The tool component version in the format specified by Semantic Versioning 2.0.
+	SemanticVersion string `json:"semanticVersion,omitempty"`
+
+	// A brief description of the tool component.
+	ShortDescription *MultiformatMessageString `json:"shortDescription,omitempty"`
+
+	// An array of toolComponentReference objects to declare the taxonomies supported by the tool component.
+	SupportedTaxonomies []*ToolComponentReference `json:"supportedTaxonomies,omitempty"`
+
+	// An array of reportingDescriptor objects relevant to the definitions of both standalone and tool-defined taxonomies.
+	Taxa []*ReportingDescriptor `json:"taxa,omitempty"`
+
+	// Translation metadata, required for a translation, not populated by other component types.
+	TranslationMetadata *TranslationMetadata `json:"translationMetadata,omitempty"`
+
+	// The tool component version, in whatever format the component natively provides.
+	Version string `json:"version,omitempty"`
+}
+
+// ToolComponentReference Identifies a particular toolComponent object, either the driver or an extension.
+type ToolComponentReference struct {
+
+	// The 'guid' property of the referenced toolComponent.
+	GUID string `json:"guid,omitempty"`
+
+	// An index into the referenced toolComponent in tool.extensions.
+	Index int `json:"index,omitempty"`
+
+	// The 'name' property of the referenced toolComponent.
+	Name string `json:"name,omitempty"`
+
+	// Key/value pairs that provide additional information about the toolComponentReference.
+	Properties *PropertyBag `json:"properties,omitempty"`
+}
+
+// TranslationMetadata Provides additional metadata related to translation.
+type TranslationMetadata struct {
+
+	// The absolute URI from which the translation metadata can be downloaded.
+	DownloadURI string `json:"downloadUri,omitempty"`
+
+	// A comprehensive description of the translation metadata.
+	FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"`
+
+	// The full name associated with the translation metadata.
+	FullName string `json:"fullName,omitempty"`
+
+	// The absolute URI from which information related to the translation metadata can be downloaded.
+	InformationURI string `json:"informationUri,omitempty"`
+
+	// The name associated with the translation metadata.
+	Name string `json:"name"`
+
+	// Key/value pairs that provide additional information about the translation metadata.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// A brief description of the translation metadata.
+	ShortDescription *MultiformatMessageString `json:"shortDescription,omitempty"`
+}
+
+// VersionControlDetails Specifies the information necessary to retrieve a desired revision from a version control system.
+type VersionControlDetails struct {
+
+	// A Coordinated Universal Time (UTC) date and time that can be used to synchronize an enlistment to the state of the repository at that time.
+	AsOfTimeUtc string `json:"asOfTimeUtc,omitempty"`
+
+	// The name of a branch containing the revision.
+	Branch string `json:"branch,omitempty"`
+
+	// The location in the local file system to which the root of the repository was mapped at the time of the analysis.
+	MappedTo *ArtifactLocation `json:"mappedTo,omitempty"`
+
+	// Key/value pairs that provide additional information about the version control details.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The absolute URI of the repository.
+	RepositoryURI string `json:"repositoryUri"`
+
+	// A string that uniquely and permanently identifies the revision within the repository.
+	RevisionID string `json:"revisionId,omitempty"`
+
+	// A tag that has been applied to the revision.
+	RevisionTag string `json:"revisionTag,omitempty"`
+}
+
+// WebRequest Describes an HTTP request.
+type WebRequest struct {
+
+	// The body of the request.
+	Body *ArtifactContent `json:"body,omitempty"`
+
+	// The request headers.
+	Headers map[string]string `json:"headers,omitempty"`
+
+	// The index within the run.webRequests array of the request object associated with this result.
+	Index int `json:"index,omitempty"`
+
+	// The HTTP method. Well-known values are 'GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'.
+	Method string `json:"method,omitempty"`
+
+	// The request parameters.
+	Parameters map[string]string `json:"parameters,omitempty"`
+
+	// Key/value pairs that provide additional information about the request.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The request protocol. Example: 'http'.
+	Protocol string `json:"protocol,omitempty"`
+
+	// The target of the request.
+	Target string `json:"target,omitempty"`
+
+	// The request version. Example: '1.1'.
+	Version string `json:"version,omitempty"`
+}
+
+// WebResponse Describes the response to an HTTP request.
+type WebResponse struct {
+
+	// The body of the response.
+	Body *ArtifactContent `json:"body,omitempty"`
+
+	// The response headers.
+	Headers map[string]string `json:"headers,omitempty"`
+
+	// The index within the run.webResponses array of the response object associated with this result.
+	Index int `json:"index,omitempty"`
+
+	// Specifies whether a response was received from the server.
+	NoResponseReceived bool `json:"noResponseReceived,omitempty"`
+
+	// Key/value pairs that provide additional information about the response.
+	Properties *PropertyBag `json:"properties,omitempty"`
+
+	// The response protocol. Example: 'http'.
+	Protocol string `json:"protocol,omitempty"`
+
+	// The response reason. Example: 'Not found'.
+	ReasonPhrase string `json:"reasonPhrase,omitempty"`
+
+	// The response status code. Example: 451.
+	StatusCode int `json:"statusCode,omitempty"`
+
+	// The response version. Example: '1.1'.
+	Version string `json:"version,omitempty"`
+}
diff --git a/report/sarif/writer.go b/report/sarif/writer.go
new file mode 100644
index 0000000000..402b1e377a
--- /dev/null
+++ b/report/sarif/writer.go
@@ -0,0 +1,23 @@
+package sarif
+
+import (
+	"encoding/json"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in SARIF format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo, rootPaths []string) error {
+	sr, err := GenerateReport(rootPaths, data)
+	if err != nil {
+		return err
+	}
+	raw, err := json.MarshalIndent(sr, "", "\t")
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Write(raw)
+	return err
+}
diff --git a/report/sonar/builder.go b/report/sonar/builder.go
new file mode 100644
index 0000000000..a55c8ca586
--- /dev/null
+++ b/report/sonar/builder.go
@@ -0,0 +1,30 @@
+package sonar
+
+// NewLocation instantiate a Location
+func NewLocation(message string, filePath string, textRange *TextRange) *Location {
+	return &Location{
+		Message:   message,
+		FilePath:  filePath,
+		TextRange: textRange,
+	}
+}
+
+// NewTextRange instantiate a TextRange
+func NewTextRange(startLine int, endLine int) *TextRange {
+	return &TextRange{
+		StartLine: startLine,
+		EndLine:   endLine,
+	}
+}
+
+// NewIssue instantiate an Issue
+func NewIssue(engineID string, ruleID string, primaryLocation *Location, issueType string, severity string, effortMinutes int) *Issue {
+	return &Issue{
+		EngineID:        engineID,
+		RuleID:          ruleID,
+		PrimaryLocation: primaryLocation,
+		Type:            issueType,
+		Severity:        severity,
+		EffortMinutes:   effortMinutes,
+	}
+}
diff --git a/report/sonar/formatter.go b/report/sonar/formatter.go
new file mode 100644
index 0000000000..d635d9d132
--- /dev/null
+++ b/report/sonar/formatter.go
@@ -0,0 +1,77 @@
+package sonar
+
+import (
+	"strconv"
+	"strings"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+const (
+	// EffortMinutes effort to fix in minutes
+	EffortMinutes = 5
+)
+
+// GenerateReport Convert a gosec report to a Sonar Report
+func GenerateReport(rootPaths []string, data *gosec.ReportInfo) (*Report, error) {
+	si := &Report{Issues: []*Issue{}}
+	for _, issue := range data.Issues {
+		sonarFilePath := parseFilePath(issue, rootPaths)
+
+		if sonarFilePath == "" {
+			continue
+		}
+
+		textRange, err := parseTextRange(issue)
+		if err != nil {
+			return si, err
+		}
+
+		primaryLocation := NewLocation(issue.What, sonarFilePath, textRange)
+		severity := getSonarSeverity(issue.Severity.String())
+
+		s := NewIssue("gosec", issue.RuleID, primaryLocation, "VULNERABILITY", severity, EffortMinutes)
+		si.Issues = append(si.Issues, s)
+	}
+	return si, nil
+}
+
+func parseFilePath(issue *issue.Issue, rootPaths []string) string {
+	var sonarFilePath string
+	for _, rootPath := range rootPaths {
+		if strings.HasPrefix(issue.File, rootPath) {
+			sonarFilePath = strings.Replace(issue.File, rootPath+"/", "", 1)
+		}
+	}
+	return sonarFilePath
+}
+
+func parseTextRange(issue *issue.Issue) (*TextRange, error) {
+	lines := strings.Split(issue.Line, "-")
+	startLine, err := strconv.Atoi(lines[0])
+	if err != nil {
+		return nil, err
+	}
+	endLine := startLine
+	if len(lines) > 1 {
+		endLine, err = strconv.Atoi(lines[1])
+		if err != nil {
+			return nil, err
+		}
+	}
+	return NewTextRange(startLine, endLine), nil
+}
+
+func getSonarSeverity(s string) string {
+	switch s {
+	case "LOW":
+		return "MINOR"
+	case "MEDIUM":
+		return "MAJOR"
+	case "HIGH":
+		return "BLOCKER"
+	default:
+		return "INFO"
+	}
+}
diff --git a/report/sonar/sonar_suite_test.go b/report/sonar/sonar_suite_test.go
new file mode 100644
index 0000000000..d505cec620
--- /dev/null
+++ b/report/sonar/sonar_suite_test.go
@@ -0,0 +1,13 @@
+package sonar_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+func TestRules(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Sonar Formatters Suite")
+}
diff --git a/report/sonar/sonar_test.go b/report/sonar/sonar_test.go
new file mode 100644
index 0000000000..1db9ed5e8a
--- /dev/null
+++ b/report/sonar/sonar_test.go
@@ -0,0 +1,215 @@
+package sonar_test
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+	"github.com/securego/gosec/v2/report/sonar"
+)
+
+var _ = Describe("Sonar Formatter", func() {
+	BeforeEach(func() {
+	})
+	Context("when converting to Sonarqube issues", func() {
+		It("it should parse the report info", func() {
+			data := &gosec.ReportInfo{
+				Errors: map[string][]gosec.Error{},
+				Issues: []*issue.Issue{
+					{
+						Severity:   2,
+						Confidence: 0,
+						RuleID:     "test",
+						What:       "test",
+						File:       "/home/src/project/test.go",
+						Code:       "",
+						Line:       "1-2",
+					},
+				},
+				Stats: &gosec.Metrics{
+					NumFiles: 0,
+					NumLines: 0,
+					NumNosec: 0,
+					NumFound: 0,
+				},
+			}
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
+					{
+						EngineID: "gosec",
+						RuleID:   "test",
+						PrimaryLocation: &sonar.Location{
+							Message:  "test",
+							FilePath: "test.go",
+							TextRange: &sonar.TextRange{
+								StartLine: 1,
+								EndLine:   2,
+							},
+						},
+						Type:          "VULNERABILITY",
+						Severity:      "BLOCKER",
+						EffortMinutes: sonar.EffortMinutes,
+					},
+				},
+			}
+
+			rootPath := "/home/src/project"
+
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(*issues).To(Equal(*want))
+		})
+
+		It("it should parse the report info with files in subfolders", func() {
+			data := &gosec.ReportInfo{
+				Errors: map[string][]gosec.Error{},
+				Issues: []*issue.Issue{
+					{
+						Severity:   2,
+						Confidence: 0,
+						RuleID:     "test",
+						What:       "test",
+						File:       "/home/src/project/subfolder/test.go",
+						Code:       "",
+						Line:       "1-2",
+					},
+				},
+				Stats: &gosec.Metrics{
+					NumFiles: 0,
+					NumLines: 0,
+					NumNosec: 0,
+					NumFound: 0,
+				},
+			}
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
+					{
+						EngineID: "gosec",
+						RuleID:   "test",
+						PrimaryLocation: &sonar.Location{
+							Message:  "test",
+							FilePath: "subfolder/test.go",
+							TextRange: &sonar.TextRange{
+								StartLine: 1,
+								EndLine:   2,
+							},
+						},
+						Type:          "VULNERABILITY",
+						Severity:      "BLOCKER",
+						EffortMinutes: sonar.EffortMinutes,
+					},
+				},
+			}
+
+			rootPath := "/home/src/project"
+
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(*issues).To(Equal(*want))
+		})
+		It("it should not parse the report info for files from other projects", func() {
+			data := &gosec.ReportInfo{
+				Errors: map[string][]gosec.Error{},
+				Issues: []*issue.Issue{
+					{
+						Severity:   2,
+						Confidence: 0,
+						RuleID:     "test",
+						What:       "test",
+						File:       "/home/src/project1/test.go",
+						Code:       "",
+						Line:       "1-2",
+					},
+				},
+				Stats: &gosec.Metrics{
+					NumFiles: 0,
+					NumLines: 0,
+					NumNosec: 0,
+					NumFound: 0,
+				},
+			}
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{},
+			}
+
+			rootPath := "/home/src/project2"
+
+			issues, err := sonar.GenerateReport([]string{rootPath}, data)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(*issues).To(Equal(*want))
+		})
+
+		It("it should parse the report info for multiple projects", func() {
+			data := &gosec.ReportInfo{
+				Errors: map[string][]gosec.Error{},
+				Issues: []*issue.Issue{
+					{
+						Severity:   2,
+						Confidence: 0,
+						RuleID:     "test",
+						What:       "test",
+						File:       "/home/src/project1/test-project1.go",
+						Code:       "",
+						Line:       "1-2",
+					},
+					{
+						Severity:   2,
+						Confidence: 0,
+						RuleID:     "test",
+						What:       "test",
+						File:       "/home/src/project2/test-project2.go",
+						Code:       "",
+						Line:       "1-2",
+					},
+				},
+				Stats: &gosec.Metrics{
+					NumFiles: 0,
+					NumLines: 0,
+					NumNosec: 0,
+					NumFound: 0,
+				},
+			}
+			want := &sonar.Report{
+				Issues: []*sonar.Issue{
+					{
+						EngineID: "gosec",
+						RuleID:   "test",
+						PrimaryLocation: &sonar.Location{
+							Message:  "test",
+							FilePath: "test-project1.go",
+							TextRange: &sonar.TextRange{
+								StartLine: 1,
+								EndLine:   2,
+							},
+						},
+						Type:          "VULNERABILITY",
+						Severity:      "BLOCKER",
+						EffortMinutes: sonar.EffortMinutes,
+					},
+					{
+						EngineID: "gosec",
+						RuleID:   "test",
+						PrimaryLocation: &sonar.Location{
+							Message:  "test",
+							FilePath: "test-project2.go",
+							TextRange: &sonar.TextRange{
+								StartLine: 1,
+								EndLine:   2,
+							},
+						},
+						Type:          "VULNERABILITY",
+						Severity:      "BLOCKER",
+						EffortMinutes: sonar.EffortMinutes,
+					},
+				},
+			}
+
+			rootPaths := []string{"/home/src/project1", "/home/src/project2"}
+
+			issues, err := sonar.GenerateReport(rootPaths, data)
+			Expect(err).ShouldNot(HaveOccurred())
+			Expect(*issues).To(Equal(*want))
+		})
+	})
+})
diff --git a/report/sonar/types.go b/report/sonar/types.go
new file mode 100644
index 0000000000..c29eb7e059
--- /dev/null
+++ b/report/sonar/types.go
@@ -0,0 +1,32 @@
+package sonar
+
+// TextRange defines the text range of an issue's location
+type TextRange struct {
+	StartLine   int `json:"startLine"`
+	EndLine     int `json:"endLine"`
+	StartColumn int `json:"startColumn,omitempty"`
+	EtartColumn int `json:"endColumn,omitempty"`
+}
+
+// Location defines a sonar issue's location
+type Location struct {
+	Message   string     `json:"message"`
+	FilePath  string     `json:"filePath"`
+	TextRange *TextRange `json:"textRange,omitempty"`
+}
+
+// Issue defines a sonar issue
+type Issue struct {
+	EngineID           string      `json:"engineId"`
+	RuleID             string      `json:"ruleId"`
+	PrimaryLocation    *Location   `json:"primaryLocation"`
+	Type               string      `json:"type"`
+	Severity           string      `json:"severity"`
+	EffortMinutes      int         `json:"effortMinutes"`
+	SecondaryLocations []*Location `json:"secondaryLocations,omitempty"`
+}
+
+// Report defines a sonar report
+type Report struct {
+	Issues []*Issue `json:"issues"`
+}
diff --git a/report/sonar/writer.go b/report/sonar/writer.go
new file mode 100644
index 0000000000..bf5ea5328a
--- /dev/null
+++ b/report/sonar/writer.go
@@ -0,0 +1,22 @@
+package sonar
+
+import (
+	"encoding/json"
+	"io"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in sonar format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo, rootPaths []string) error {
+	si, err := GenerateReport(rootPaths, data)
+	if err != nil {
+		return err
+	}
+	raw, err := json.MarshalIndent(si, "", "\t")
+	if err != nil {
+		return err
+	}
+	_, err = w.Write(raw)
+	return err
+}
diff --git a/report/text/template.txt b/report/text/template.txt
new file mode 100644
index 0000000000..94a8c52272
--- /dev/null
+++ b/report/text/template.txt
@@ -0,0 +1,23 @@
+Results:
+{{range $filePath,$fileErrors := .Errors}}
+Golang errors in file: [{{ $filePath }}]:
+{{range $index, $error := $fileErrors}}
+  > [line {{$error.Line}} : column {{$error.Column}}] - {{$error.Err}}
+{{end}}
+{{end}}
+{{ range $index, $issue := .Issues }}
+[{{ highlight $issue.FileLocation $issue.Severity $issue.NoSec }}] - {{ $issue.RuleID }}{{ if $issue.NoSec }} ({{- success "NoSec" -}}){{ end }} ({{ if $issue.Cwe }}{{$issue.Cwe.SprintID}}{{ else }}{{"CWE"}}{{ end }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
+{{ printCode $issue }}
+{{ "Autofix" }}: {{ $issue.Autofix }}
+{{ end }}
+{{ notice "Summary:" }}
+  Gosec  : {{.GosecVersion}}
+  Files  : {{.Stats.NumFiles}}
+  Lines  : {{.Stats.NumLines}}
+  Nosec  : {{.Stats.NumNosec}}
+  Issues : {{ if eq .Stats.NumFound 0 }}
+	{{- success .Stats.NumFound }}
+	{{- else }}
+	{{- danger .Stats.NumFound }}
+	{{- end }}
+
diff --git a/report/text/writer.go b/report/text/writer.go
new file mode 100644
index 0000000000..d839a43c32
--- /dev/null
+++ b/report/text/writer.go
@@ -0,0 +1,115 @@
+package text
+
+import (
+	"bufio"
+	"bytes"
+	_ "embed" // use go embed to import template
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+	"text/template"
+
+	"github.com/gookit/color"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+var (
+	errorTheme   = color.New(color.FgLightWhite, color.BgRed)
+	warningTheme = color.New(color.FgBlack, color.BgYellow)
+	defaultTheme = color.New(color.FgWhite, color.BgBlack)
+
+	//go:embed template.txt
+	templateContent string
+)
+
+// WriteReport write a (colorized) report in text format
+func WriteReport(w io.Writer, data *gosec.ReportInfo, enableColor bool) error {
+	t, e := template.
+		New("gosec").
+		Funcs(plainTextFuncMap(enableColor)).
+		Parse(templateContent)
+	if e != nil {
+		return e
+	}
+
+	return t.Execute(w, data)
+}
+
+func plainTextFuncMap(enableColor bool) template.FuncMap {
+	if enableColor {
+		return template.FuncMap{
+			"highlight": highlight,
+			"danger":    color.Danger.Render,
+			"notice":    color.Notice.Render,
+			"success":   color.Success.Render,
+			"printCode": printCodeSnippet,
+		}
+	}
+
+	// by default those functions return the given content untouched
+	return template.FuncMap{
+		"highlight": func(t string, s issue.Score, ignored bool) string {
+			return t
+		},
+		"danger":    fmt.Sprint,
+		"notice":    fmt.Sprint,
+		"success":   fmt.Sprint,
+		"printCode": printCodeSnippet,
+	}
+}
+
+// highlight returns content t colored based on Score
+func highlight(t string, s issue.Score, ignored bool) string {
+	if ignored {
+		return defaultTheme.Sprint(t)
+	}
+	switch s {
+	case issue.High:
+		return errorTheme.Sprint(t)
+	case issue.Medium:
+		return warningTheme.Sprint(t)
+	default:
+		return defaultTheme.Sprint(t)
+	}
+}
+
+// printCodeSnippet prints the code snippet from the issue by adding a marker to the affected line
+func printCodeSnippet(issue *issue.Issue) string {
+	start, end := parseLine(issue.Line)
+	scanner := bufio.NewScanner(strings.NewReader(issue.Code))
+	var buf bytes.Buffer
+	line := start
+	for scanner.Scan() {
+		codeLine := scanner.Text()
+		if strings.HasPrefix(codeLine, strconv.Itoa(line)) && line <= end {
+			codeLine = "  > " + codeLine + "\n"
+			line++
+		} else {
+			codeLine = "    " + codeLine + "\n"
+		}
+		buf.WriteString(codeLine)
+	}
+	return buf.String()
+}
+
+// parseLine extract the start and the end line numbers from a issue line
+func parseLine(line string) (int, int) {
+	parts := strings.Split(line, "-")
+	start := parts[0]
+	end := start
+	if len(parts) > 1 {
+		end = parts[1]
+	}
+	s, err := strconv.Atoi(start)
+	if err != nil {
+		return -1, -1
+	}
+	e, err := strconv.Atoi(end)
+	if err != nil {
+		return -1, -1
+	}
+	return s, e
+}
diff --git a/report/yaml/writer.go b/report/yaml/writer.go
new file mode 100644
index 0000000000..54fe9682cf
--- /dev/null
+++ b/report/yaml/writer.go
@@ -0,0 +1,19 @@
+package yaml
+
+import (
+	"io"
+
+	"gopkg.in/yaml.v3"
+
+	"github.com/securego/gosec/v2"
+)
+
+// WriteReport write a report in yaml format to the output writer
+func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
+	raw, err := yaml.Marshal(data)
+	if err != nil {
+		return err
+	}
+	_, err = w.Write(raw)
+	return err
+}
diff --git a/resolve.go b/resolve.go
index cdc287e8e5..a201b8d32b 100644
--- a/resolve.go
+++ b/resolve.go
@@ -66,7 +66,7 @@ func resolveBinExpr(n *ast.BinaryExpr, c *Context) bool {
 	return (TryResolve(n.X, c) && TryResolve(n.Y, c))
 }
 
-func resolveCallExpr(n *ast.CallExpr, c *Context) bool {
+func resolveCallExpr(_ *ast.CallExpr, _ *Context) bool {
 	// TODO(tkelsey): next step, full function resolution
 	return false
 }
diff --git a/resolve_test.go b/resolve_test.go
index 6e6c261e1e..9ca1f1147f 100644
--- a/resolve_test.go
+++ b/resolve_test.go
@@ -3,8 +3,9 @@ package gosec_test
 import (
 	"go/ast"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
 	"github.com/securego/gosec/v2/testutils"
 )
@@ -336,6 +337,5 @@ var _ = Describe("Resolve ast node to concrete value", func() {
 			Expect(value).ShouldNot(BeNil())
 			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
 		})
-
 	})
 })
diff --git a/rule.go b/rule.go
index fbba089bbe..490a25da02 100644
--- a/rule.go
+++ b/rule.go
@@ -15,45 +15,58 @@ package gosec
 import (
 	"go/ast"
 	"reflect"
+
+	"github.com/securego/gosec/v2/issue"
 )
 
 // The Rule interface used by all rules supported by gosec.
 type Rule interface {
 	ID() string
-	Match(ast.Node, *Context) (*Issue, error)
+	Match(ast.Node, *Context) (*issue.Issue, error)
 }
 
 // RuleBuilder is used to register a rule definition with the analyzer
 type RuleBuilder func(id string, c Config) (Rule, []ast.Node)
 
-// A RuleSet maps lists of rules to the type of AST node they should be run on.
+// A RuleSet contains a mapping of lists of rules to the type of AST node they
+// should be run on and a mapping of rule ID's to whether the rule are
+// suppressed.
 // The analyzer will only invoke rules contained in the list associated with the
 // type of AST node it is currently visiting.
-type RuleSet map[reflect.Type][]Rule
+type RuleSet struct {
+	Rules             map[reflect.Type][]Rule
+	RuleSuppressedMap map[string]bool
+}
 
 // NewRuleSet constructs a new RuleSet
 func NewRuleSet() RuleSet {
-	return make(RuleSet)
+	return RuleSet{make(map[reflect.Type][]Rule), make(map[string]bool)}
 }
 
-// Register adds a trigger for the supplied rule for the the
+// Register adds a trigger for the supplied rule for the
 // specified ast nodes.
-func (r RuleSet) Register(rule Rule, nodes ...ast.Node) {
+func (r RuleSet) Register(rule Rule, isSuppressed bool, nodes ...ast.Node) {
 	for _, n := range nodes {
 		t := reflect.TypeOf(n)
-		if rules, ok := r[t]; ok {
-			r[t] = append(rules, rule)
+		if rules, ok := r.Rules[t]; ok {
+			r.Rules[t] = append(rules, rule)
 		} else {
-			r[t] = []Rule{rule}
+			r.Rules[t] = []Rule{rule}
 		}
 	}
+	r.RuleSuppressedMap[rule.ID()] = isSuppressed
 }
 
 // RegisteredFor will return all rules that are registered for a
 // specified ast node.
 func (r RuleSet) RegisteredFor(n ast.Node) []Rule {
-	if rules, found := r[reflect.TypeOf(n)]; found {
+	if rules, found := r.Rules[reflect.TypeOf(n)]; found {
 		return rules
 	}
 	return []Rule{}
 }
+
+// IsRuleSuppressed will return whether the rule is suppressed.
+func (r RuleSet) IsRuleSuppressed(ruleID string) bool {
+	return r.RuleSuppressedMap[ruleID]
+}
diff --git a/rule_test.go b/rule_test.go
index 67cb4e29a2..9830268a60 100644
--- a/rule_test.go
+++ b/rule_test.go
@@ -4,13 +4,15 @@ import (
 	"fmt"
 	"go/ast"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
+
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type mockrule struct {
-	issue    *gosec.Issue
+	issue    *issue.Issue
 	err      error
 	callback func(n ast.Node, ctx *gosec.Context) bool
 }
@@ -19,7 +21,7 @@ func (m *mockrule) ID() string {
 	return "MOCK"
 }
 
-func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	if m.callback(n, ctx) {
 		return m.issue, nil
 	}
@@ -27,9 +29,7 @@ func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
 }
 
 var _ = Describe("Rule", func() {
-
 	Context("when using a ruleset", func() {
-
 		var (
 			ruleset        gosec.RuleSet
 			dummyErrorRule gosec.Rule
@@ -44,9 +44,9 @@ var _ = Describe("Rule", func() {
 				callback: func(n ast.Node, ctx *gosec.Context) bool { return false },
 			}
 			dummyIssueRule = &mockrule{
-				issue: &gosec.Issue{
-					Severity:   gosec.High,
-					Confidence: gosec.High,
+				issue: &issue.Issue{
+					Severity:   issue.High,
+					Confidence: issue.High,
 					What:       `Some explanation of the thing`,
 					File:       "main.go",
 					Code:       `#include <stdio.h> int main(){ puts("hello world"); }`,
@@ -61,29 +61,36 @@ var _ = Describe("Rule", func() {
 			registeredNodeB := (*ast.AssignStmt)(nil)
 			unregisteredNode := (*ast.BinaryExpr)(nil)
 
-			ruleset.Register(dummyIssueRule, registeredNodeA, registeredNodeB)
+			ruleset.Register(dummyIssueRule, false, registeredNodeA, registeredNodeB)
 			Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
 			Expect(ruleset.RegisteredFor(registeredNodeA)).Should(ContainElement(dummyIssueRule))
 			Expect(ruleset.RegisteredFor(registeredNodeB)).Should(ContainElement(dummyIssueRule))
-
+			Expect(ruleset.IsRuleSuppressed(dummyIssueRule.ID())).Should(BeFalse())
 		})
 
 		It("should not register a rule when no ast.Nodes are specified", func() {
-			ruleset.Register(dummyErrorRule)
-			Expect(ruleset).Should(BeEmpty())
+			ruleset.Register(dummyErrorRule, false)
+			Expect(ruleset.Rules).Should(BeEmpty())
 		})
 
 		It("should be possible to retrieve a list of rules for a given node type", func() {
 			registeredNode := (*ast.CallExpr)(nil)
 			unregisteredNode := (*ast.AssignStmt)(nil)
-			ruleset.Register(dummyErrorRule, registeredNode)
-			ruleset.Register(dummyIssueRule, registeredNode)
+			ruleset.Register(dummyErrorRule, false, registeredNode)
+			ruleset.Register(dummyIssueRule, false, registeredNode)
 			Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
 			Expect(ruleset.RegisteredFor(registeredNode)).Should(HaveLen(2))
 			Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyErrorRule))
 			Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyIssueRule))
 		})
 
+		It("should register a suppressed rule", func() {
+			registeredNode := (*ast.CallExpr)(nil)
+			unregisteredNode := (*ast.AssignStmt)(nil)
+			ruleset.Register(dummyIssueRule, true, registeredNode)
+			Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyIssueRule))
+			Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
+			Expect(ruleset.IsRuleSuppressed(dummyIssueRule.ID())).Should(BeTrue())
+		})
 	})
-
 })
diff --git a/rules/archive.go b/rules/archive.go
index 92c7e4481c..987047435b 100644
--- a/rules/archive.go
+++ b/rules/archive.go
@@ -5,10 +5,11 @@ import (
 	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type archive struct {
-	gosec.MetaData
+	issue.MetaData
 	calls    gosec.CallList
 	argTypes []string
 }
@@ -18,7 +19,7 @@ func (a *archive) ID() string {
 }
 
 // Match inspects AST nodes to determine if the filepath.Joins uses any argument derived from type zip.File or tar.Header
-func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (a *archive) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if node := a.calls.ContainsPkgCallExpr(n, c, false); node != nil {
 		for _, arg := range node.Args {
 			var argType types.Type
@@ -38,7 +39,7 @@ func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 			if argType != nil {
 				for _, t := range a.argTypes {
 					if argType.String() == t {
-						return gosec.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil
+						return c.NewIssue(n, a.ID(), a.What, a.Severity, a.Confidence), nil
 					}
 				}
 			}
@@ -48,17 +49,17 @@ func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 }
 
 // NewArchive creates a new rule which detects the file traversal when extracting zip/tar archives
-func NewArchive(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewArchive(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
 	calls.Add("path/filepath", "Join")
 	calls.Add("path", "Join")
 	return &archive{
 		calls:    calls,
 		argTypes: []string{"*archive/zip.File", "*archive/tar.Header"},
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       "File traversal when extracting zip/tar archive",
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
diff --git a/rules/bad_defer.go b/rules/bad_defer.go
deleted file mode 100644
index b33a0477c4..0000000000
--- a/rules/bad_defer.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package rules
-
-import (
-	"fmt"
-	"go/ast"
-	"strings"
-
-	"github.com/securego/gosec/v2"
-)
-
-type deferType struct {
-	typ     string
-	methods []string
-}
-
-type badDefer struct {
-	gosec.MetaData
-	types []deferType
-}
-
-func (r *badDefer) ID() string {
-	return r.MetaData.ID
-}
-
-func normalize(typ string) string {
-	return strings.TrimPrefix(typ, "*")
-}
-
-func contains(methods []string, method string) bool {
-	for _, m := range methods {
-		if m == method {
-			return true
-		}
-	}
-	return false
-}
-
-func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
-	if deferStmt, ok := n.(*ast.DeferStmt); ok {
-		for _, deferTyp := range r.types {
-			if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil {
-				if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) {
-					return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence), nil
-				}
-			}
-		}
-
-	}
-
-	return nil, nil
-}
-
-// NewDeferredClosing detects unsafe defer of error returning methods
-func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	return &badDefer{
-		types: []deferType{
-			{
-				typ:     "os.File",
-				methods: []string{"Close"},
-			},
-		},
-		MetaData: gosec.MetaData{
-			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
-			What:       "Deferring unsafe method %q on type %q",
-		},
-	}, []ast.Node{(*ast.DeferStmt)(nil)}
-}
diff --git a/rules/bind.go b/rules/bind.go
index 8f6af067ad..fef760c808 100644
--- a/rules/bind.go
+++ b/rules/bind.go
@@ -19,11 +19,12 @@ import (
 	"regexp"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 // Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
 type bindsToAllNetworkInterfaces struct {
-	gosec.MetaData
+	issue.MetaData
 	calls   gosec.CallList
 	pattern *regexp.Regexp
 }
@@ -32,7 +33,7 @@ func (r *bindsToAllNetworkInterfaces) ID() string {
 	return r.MetaData.ID
 }
 
-func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	callExpr := r.calls.ContainsPkgCallExpr(n, c, false)
 	if callExpr == nil {
 		return nil, nil
@@ -42,14 +43,14 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose
 		if bl, ok := arg.(*ast.BasicLit); ok {
 			if arg, err := gosec.GetString(bl); err == nil {
 				if r.pattern.MatchString(arg) {
-					return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 				}
 			}
 		} else if ident, ok := arg.(*ast.Ident); ok {
 			values := gosec.GetIdentStringValues(ident)
 			for _, value := range values {
 				if r.pattern.MatchString(value) {
-					return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 				}
 			}
 		}
@@ -57,7 +58,7 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose
 		values := gosec.GetCallStringArgsValues(callExpr.Args[0], c)
 		for _, value := range values {
 			if r.pattern.MatchString(value) {
-				return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+				return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 			}
 		}
 	}
@@ -66,17 +67,17 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose
 
 // NewBindsToAllNetworkInterfaces detects socket connections that are setup to
 // listen on all network interfaces.
-func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewBindsToAllNetworkInterfaces(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
 	calls.Add("net", "Listen")
 	calls.Add("crypto/tls", "Listen")
 	return &bindsToAllNetworkInterfaces{
 		calls:   calls,
 		pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       "Binds to all network interfaces",
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
diff --git a/rules/blocklist.go b/rules/blocklist.go
index afd4ee56b7..a4376b19ad 100644
--- a/rules/blocklist.go
+++ b/rules/blocklist.go
@@ -19,27 +19,28 @@ import (
 	"strings"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type blocklistedImport struct {
-	gosec.MetaData
+	issue.MetaData
 	Blocklisted map[string]string
 }
 
 func unquote(original string) string {
-	copy := strings.TrimSpace(original)
-	copy = strings.TrimLeft(copy, `"`)
-	return strings.TrimRight(copy, `"`)
+	cleaned := strings.TrimSpace(original)
+	cleaned = strings.TrimLeft(cleaned, `"`)
+	return strings.TrimRight(cleaned, `"`)
 }
 
 func (r *blocklistedImport) ID() string {
 	return r.MetaData.ID
 }
 
-func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if node, ok := n.(*ast.ImportSpec); ok {
 		if description, ok := r.Blocklisted[unquote(node.Path.Value)]; ok {
-			return gosec.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil
+			return c.NewIssue(node, r.ID(), description, r.Severity, r.Confidence), nil
 		}
 	}
 	return nil, nil
@@ -47,12 +48,12 @@ func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, e
 
 // NewBlocklistedImports reports when a blocklisted import is being used.
 // Typically when a deprecated technology is being used.
-func NewBlocklistedImports(id string, conf gosec.Config, blocklist map[string]string) (gosec.Rule, []ast.Node) {
+func NewBlocklistedImports(id string, _ gosec.Config, blocklist map[string]string) (gosec.Rule, []ast.Node) {
 	return &blocklistedImport{
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 		},
 		Blocklisted: blocklist,
 	}, []ast.Node{(*ast.ImportSpec)(nil)}
@@ -92,3 +93,17 @@ func NewBlocklistedImportSHA1(id string, conf gosec.Config) (gosec.Rule, []ast.N
 		"crypto/sha1": "Blocklisted import crypto/sha1: weak cryptographic primitive",
 	})
 }
+
+// NewBlocklistedImportMD4 fails if MD4 is imported
+func NewBlocklistedImportMD4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+	return NewBlocklistedImports(id, conf, map[string]string{
+		"golang.org/x/crypto/md4": "Blocklisted import golang.org/x/crypto/md4: deprecated and weak cryptographic primitive",
+	})
+}
+
+// NewBlocklistedImportRIPEMD160 fails if RIPEMD160 is imported
+func NewBlocklistedImportRIPEMD160(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+	return NewBlocklistedImports(id, conf, map[string]string{
+		"golang.org/x/crypto/ripemd160": "Blocklisted import golang.org/x/crypto/ripemd160: deprecated and weak cryptographic primitive",
+	})
+}
diff --git a/rules/decompression-bomb.go b/rules/decompression_bomb.go
similarity index 91%
rename from rules/decompression-bomb.go
rename to rules/decompression_bomb.go
index 02256faa98..7e57f1a5b1 100644
--- a/rules/decompression-bomb.go
+++ b/rules/decompression_bomb.go
@@ -19,10 +19,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type decompressionBombCheck struct {
-	gosec.MetaData
+	issue.MetaData
 	readerCalls gosec.CallList
 	copyCalls   gosec.CallList
 }
@@ -40,7 +41,7 @@ func containsReaderCall(node ast.Node, ctx *gosec.Context, list gosec.CallList)
 	return list.Contains(s, idt)
 }
 
-func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	var readerVarObj map[*ast.Object]struct{}
 
 	// To check multiple lines, ctx.PassedValues is used to store temporary data.
@@ -72,7 +73,7 @@ func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gose
 			if idt, ok := n.Args[1].(*ast.Ident); ok {
 				if _, ok := readerVarObj[idt.Obj]; ok {
 					// Detect io.Copy(x, r)
-					return gosec.NewIssue(ctx, n, d.ID(), d.What, d.Severity, d.Confidence), nil
+					return ctx.NewIssue(n, d.ID(), d.What, d.Severity, d.Confidence), nil
 				}
 			}
 		}
@@ -82,7 +83,7 @@ func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gose
 }
 
 // NewDecompressionBombCheck detects if there is potential DoS vulnerability via decompression bomb
-func NewDecompressionBombCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewDecompressionBombCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	readerCalls := gosec.NewCallList()
 	readerCalls.Add("compress/gzip", "NewReader")
 	readerCalls.AddAll("compress/zlib", "NewReader", "NewReaderDict")
@@ -98,10 +99,10 @@ func NewDecompressionBombCheck(id string, conf gosec.Config) (gosec.Rule, []ast.
 	copyCalls.Add("io", "CopyBuffer")
 
 	return &decompressionBombCheck{
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.Medium,
+			Severity:   issue.Medium,
+			Confidence: issue.Medium,
 			What:       "Potential DoS vulnerability via decompression bomb",
 		},
 		readerCalls: readerCalls,
diff --git a/rules/directory_traversal.go b/rules/directory_traversal.go
new file mode 100644
index 0000000000..47bcb2dc4a
--- /dev/null
+++ b/rules/directory_traversal.go
@@ -0,0 +1,65 @@
+package rules
+
+import (
+	"go/ast"
+	"regexp"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+type traversal struct {
+	pattern *regexp.Regexp
+	issue.MetaData
+}
+
+func (r *traversal) ID() string {
+	return r.MetaData.ID
+}
+
+func (r *traversal) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
+	switch node := n.(type) {
+	case *ast.CallExpr:
+		return r.matchCallExpr(node, ctx)
+	}
+	return nil, nil
+}
+
+func (r *traversal) matchCallExpr(assign *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) {
+	for _, i := range assign.Args {
+		if basiclit, ok1 := i.(*ast.BasicLit); ok1 {
+			if fun, ok2 := assign.Fun.(*ast.SelectorExpr); ok2 {
+				if x, ok3 := fun.X.(*ast.Ident); ok3 {
+					str := x.Name + "." + fun.Sel.Name + "(" + basiclit.Value + ")"
+					if r.pattern.MatchString(str) {
+						return ctx.NewIssue(assign, r.ID(), r.What, r.Severity, r.Confidence), nil
+					}
+				}
+			}
+		}
+	}
+	return nil, nil
+}
+
+// NewDirectoryTraversal attempts to find the use of http.Dir("/")
+func NewDirectoryTraversal(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+	pattern := `http\.Dir\("\/"\)|http\.Dir\('\/'\)`
+	if val, ok := conf[id]; ok {
+		conf := val.(map[string]interface{})
+		if configPattern, ok := conf["pattern"]; ok {
+			if cfgPattern, ok := configPattern.(string); ok {
+				pattern = cfgPattern
+			}
+		}
+	}
+
+	return &traversal{
+		pattern: regexp.MustCompile(pattern),
+		MetaData: issue.MetaData{
+			ID:         id,
+			What:       "Potential directory traversal",
+			Confidence: issue.Medium,
+			Severity:   issue.Medium,
+		},
+	}, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/rules/errors.go b/rules/errors.go
index f16f91d046..2786426557 100644
--- a/rules/errors.go
+++ b/rules/errors.go
@@ -19,10 +19,11 @@ import (
 	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type noErrorCheck struct {
-	gosec.MetaData
+	issue.MetaData
 	whitelist gosec.CallList
 }
 
@@ -49,7 +50,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int {
 	return -1
 }
 
-func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	switch stmt := n.(type) {
 	case *ast.AssignStmt:
 		cfg := ctx.Config
@@ -61,7 +62,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro
 						return nil, nil
 					}
 					if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
-						return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+						return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 					}
 				}
 			}
@@ -70,7 +71,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro
 		if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
 			pos := returnsError(callExpr, ctx)
 			if pos >= 0 {
-				return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+				return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 			}
 		}
 	}
@@ -86,8 +87,10 @@ func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 	whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln")
 	whitelist.AddAll("strings.Builder", "Write", "WriteByte", "WriteRune", "WriteString")
 	whitelist.Add("io.PipeWriter", "CloseWithError")
+	whitelist.Add("hash.Hash", "Write")
+	whitelist.Add("os", "Unsetenv")
 
-	if configured, ok := conf["G104"]; ok {
+	if configured, ok := conf[id]; ok {
 		if whitelisted, ok := configured.(map[string]interface{}); ok {
 			for pkg, funcs := range whitelisted {
 				if funcs, ok := funcs.([]interface{}); ok {
@@ -98,11 +101,11 @@ func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 	}
 
 	return &noErrorCheck{
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Low,
-			Confidence: gosec.High,
-			What:       "Errors unhandled.",
+			Severity:   issue.Low,
+			Confidence: issue.High,
+			What:       "Errors unhandled",
 		},
 		whitelist: whitelist,
 	}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
diff --git a/rules/fileperms.go b/rules/fileperms.go
index f45dd6018c..bf2a959533 100644
--- a/rules/fileperms.go
+++ b/rules/fileperms.go
@@ -20,21 +20,23 @@ import (
 	"strconv"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type filePermissions struct {
-	gosec.MetaData
+	issue.MetaData
 	mode  int64
 	pkgs  []string
 	calls []string
 }
 
+// ID returns the ID of the rule.
 func (r *filePermissions) ID() string {
 	return r.MetaData.ID
 }
 
 func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMode int64) int64 {
-	var mode = defaultMode
+	mode := defaultMode
 	if value, ok := conf[configKey]; ok {
 		switch value := value.(type) {
 		case int64:
@@ -50,29 +52,46 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod
 	return mode
 }
 
-func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func modeIsSubset(subset int64, superset int64) bool {
+	return (subset | superset) == superset
+}
+
+// Match checks if the rule is matched.
+func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	for _, pkg := range r.pkgs {
 		if callexpr, matched := gosec.MatchCallByPackage(n, c, pkg, r.calls...); matched {
 			modeArg := callexpr.Args[len(callexpr.Args)-1]
-			if mode, err := gosec.GetInt(modeArg); err == nil && mode > r.mode {
-				return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+			if mode, err := gosec.GetInt(modeArg); err == nil && !modeIsSubset(mode, r.mode) || isOsPerm(modeArg) {
+				return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 			}
 		}
 	}
 	return nil, nil
 }
 
+// isOsPerm check if the provide ast node contains a os.PermMode symbol
+func isOsPerm(n ast.Node) bool {
+	if node, ok := n.(*ast.SelectorExpr); ok {
+		if identX, ok := node.X.(*ast.Ident); ok {
+			if identX.Name == "os" && node.Sel != nil && node.Sel.Name == "ModePerm" {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 // NewWritePerms creates a rule to detect file Writes with bad permissions.
 func NewWritePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	mode := getConfiguredMode(conf, "G306", 0600)
+	mode := getConfiguredMode(conf, id, 0o600)
 	return &filePermissions{
 		mode:  mode,
 		pkgs:  []string{"io/ioutil", "os"},
 		calls: []string{"WriteFile"},
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       fmt.Sprintf("Expect WriteFile permissions to be %#o or less", mode),
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
@@ -81,15 +100,15 @@ func NewWritePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 // NewFilePerms creates a rule to detect file creation with a more permissive than configured
 // permission mask.
 func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	mode := getConfiguredMode(conf, "G302", 0600)
+	mode := getConfiguredMode(conf, id, 0o600)
 	return &filePermissions{
 		mode:  mode,
 		pkgs:  []string{"os"},
 		calls: []string{"OpenFile", "Chmod"},
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       fmt.Sprintf("Expect file permissions to be %#o or less", mode),
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
@@ -98,16 +117,60 @@ func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 // NewMkdirPerms creates a rule to detect directory creation with more permissive than
 // configured permission mask.
 func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	mode := getConfiguredMode(conf, "G301", 0750)
+	mode := getConfiguredMode(conf, id, 0o750)
 	return &filePermissions{
 		mode:  mode,
 		pkgs:  []string{"os"},
 		calls: []string{"Mkdir", "MkdirAll"},
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       fmt.Sprintf("Expect directory permissions to be %#o or less", mode),
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
 }
+
+type osCreatePermissions struct {
+	issue.MetaData
+	mode  int64
+	pkgs  []string
+	calls []string
+}
+
+const defaultOsCreateMode = 0o666
+
+// ID returns the ID of the rule.
+func (r *osCreatePermissions) ID() string {
+	return r.MetaData.ID
+}
+
+// Match checks if the rule is matched.
+func (r *osCreatePermissions) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	for _, pkg := range r.pkgs {
+		if _, matched := gosec.MatchCallByPackage(n, c, pkg, r.calls...); matched {
+			if !modeIsSubset(defaultOsCreateMode, r.mode) {
+				return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
+			}
+		}
+	}
+	return nil, nil
+}
+
+// NewOsCreatePerms creates a rule to detect file creation with a more permissive than configured
+// permission mask.
+func NewOsCreatePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+	mode := getConfiguredMode(conf, id, 0o666)
+	return &osCreatePermissions{
+		mode:  mode,
+		pkgs:  []string{"os"},
+		calls: []string{"Create"},
+		MetaData: issue.MetaData{
+			ID:         id,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
+			What: fmt.Sprintf("Expect file permissions to be %#o or less but os.Create used with default permissions %#o",
+				mode, defaultOsCreateMode),
+		},
+	}, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/rules/fileperms_test.go b/rules/fileperms_test.go
new file mode 100644
index 0000000000..cd49e75c3c
--- /dev/null
+++ b/rules/fileperms_test.go
@@ -0,0 +1,15 @@
+package rules
+
+import (
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
+)
+
+var _ = Describe("modeIsSubset", func() {
+	It("it compares modes correctly", func() {
+		Expect(modeIsSubset(0o600, 0o600)).To(BeTrue())
+		Expect(modeIsSubset(0o400, 0o600)).To(BeTrue())
+		Expect(modeIsSubset(0o644, 0o600)).To(BeFalse())
+		Expect(modeIsSubset(0o466, 0o600)).To(BeFalse())
+	})
+})
diff --git a/rules/hardcoded_credentials.go b/rules/hardcoded_credentials.go
index 6b360c5b9b..c10d18b307 100644
--- a/rules/hardcoded_credentials.go
+++ b/rules/hardcoded_credentials.go
@@ -15,17 +15,176 @@
 package rules
 
 import (
+	"fmt"
 	"go/ast"
 	"go/token"
 	"regexp"
 	"strconv"
 
-	zxcvbn "github.com/nbutton23/zxcvbn-go"
+	zxcvbn "github.com/ccojocar/zxcvbn-go"
+
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
+type secretPattern struct {
+	name   string
+	regexp *regexp.Regexp
+}
+
+var secretsPatterns = [...]secretPattern{
+	{
+		name:   "RSA private key",
+		regexp: regexp.MustCompile(`-----BEGIN RSA PRIVATE KEY-----`),
+	},
+	{
+		name:   "SSH (DSA) private key",
+		regexp: regexp.MustCompile(`-----BEGIN DSA PRIVATE KEY-----`),
+	},
+	{
+		name:   "SSH (EC) private key",
+		regexp: regexp.MustCompile(`-----BEGIN EC PRIVATE KEY-----`),
+	},
+	{
+		name:   "PGP private key block",
+		regexp: regexp.MustCompile(`-----BEGIN PGP PRIVATE KEY BLOCK-----`),
+	},
+	{
+		name:   "Slack Token",
+		regexp: regexp.MustCompile(`xox[pborsa]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}`),
+	},
+	{
+		name:   "AWS API Key",
+		regexp: regexp.MustCompile(`AKIA[0-9A-Z]{16}`),
+	},
+	{
+		name:   "Amazon MWS Auth Token",
+		regexp: regexp.MustCompile(`amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`),
+	},
+	{
+		name:   "AWS AppSync GraphQL Key",
+		regexp: regexp.MustCompile(`da2-[a-z0-9]{26}`),
+	},
+	{
+		name:   "GitHub personal access token",
+		regexp: regexp.MustCompile(`ghp_[a-zA-Z0-9]{36}`),
+	},
+	{
+		name:   "GitHub fine-grained access token",
+		regexp: regexp.MustCompile(`github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}`),
+	},
+	{
+		name:   "GitHub action temporary token",
+		regexp: regexp.MustCompile(`ghs_[a-zA-Z0-9]{36}`),
+	},
+	{
+		name:   "Google API Key",
+		regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`),
+	},
+	{
+		name:   "Google Cloud Platform API Key",
+		regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`),
+	},
+	{
+		name:   "Google Cloud Platform OAuth",
+		regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`),
+	},
+	{
+		name:   "Google Drive API Key",
+		regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`),
+	},
+	{
+		name:   "Google Drive OAuth",
+		regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`),
+	},
+	{
+		name:   "Google (GCP) Service-account",
+		regexp: regexp.MustCompile(`"type": "service_account"`),
+	},
+	{
+		name:   "Google Gmail API Key",
+		regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`),
+	},
+	{
+		name:   "Google Gmail OAuth",
+		regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`),
+	},
+	{
+		name:   "Google OAuth Access Token",
+		regexp: regexp.MustCompile(`ya29\.[0-9A-Za-z\-_]+`),
+	},
+	{
+		name:   "Google YouTube API Key",
+		regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`),
+	},
+	{
+		name:   "Google YouTube OAuth",
+		regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`),
+	},
+	{
+		name:   "Generic API Key",
+		regexp: regexp.MustCompile(`[aA][pP][iI]_?[kK][eE][yY].*[''|"][0-9a-zA-Z]{32,45}[''|"]`),
+	},
+	{
+		name:   "Generic Secret",
+		regexp: regexp.MustCompile(`[sS][eE][cC][rR][eE][tT].*[''|"][0-9a-zA-Z]{32,45}[''|"]`),
+	},
+	{
+		name:   "Heroku API Key",
+		regexp: regexp.MustCompile(`[hH][eE][rR][oO][kK][uU].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}`),
+	},
+	{
+		name:   "MailChimp API Key",
+		regexp: regexp.MustCompile(`[0-9a-f]{32}-us[0-9]{1,2}`),
+	},
+	{
+		name:   "Mailgun API Key",
+		regexp: regexp.MustCompile(`key-[0-9a-zA-Z]{32}`),
+	},
+	{
+		name:   "Password in URL",
+		regexp: regexp.MustCompile(`[a-zA-Z]{3,10}://[^/\\s:@]{3,20}:[^/\\s:@]{3,20}@.{1,100}["'\\s]`),
+	},
+	{
+		name:   "Slack Webhook",
+		regexp: regexp.MustCompile(`https://hooks\.slack\.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}`),
+	},
+	{
+		name:   "Stripe API Key",
+		regexp: regexp.MustCompile(`sk_live_[0-9a-zA-Z]{24}`),
+	},
+	{
+		name:   "Stripe Restricted API Key",
+		regexp: regexp.MustCompile(`rk_live_[0-9a-zA-Z]{24}`),
+	},
+	{
+		name:   "Square Access Token",
+		regexp: regexp.MustCompile(`sq0atp-[0-9A-Za-z\-_]{22}`),
+	},
+	{
+		name:   "Square OAuth Secret",
+		regexp: regexp.MustCompile(`sq0csp-[0-9A-Za-z\-_]{43}`),
+	},
+	{
+		name:   "Telegram Bot API Key",
+		regexp: regexp.MustCompile(`[0-9]+:AA[0-9A-Za-z\-_]{33}`),
+	},
+	{
+		name:   "Twilio API Key",
+		regexp: regexp.MustCompile(`SK[0-9a-fA-F]{32}`),
+	},
+	{
+		name:   "Twitter Access Token",
+		regexp: regexp.MustCompile(`[tT][wW][iI][tT][tT][eE][rR].*[1-9][0-9]+-[0-9a-zA-Z]{40}`),
+	},
+	{
+		name:   "Twitter OAuth",
+		regexp: regexp.MustCompile(`[tT][wW][iI][tT][tT][eE][rR].*[''|"][0-9a-zA-Z]{35,44}[''|"]`),
+	},
+}
+
 type credentials struct {
-	gosec.MetaData
+	issue.MetaData
 	pattern          *regexp.Regexp
 	entropyThreshold float64
 	perCharThreshold float64
@@ -53,7 +212,16 @@ func (r *credentials) isHighEntropyString(str string) bool {
 			entropyPerChar >= r.perCharThreshold))
 }
 
-func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (r *credentials) isSecretPattern(str string) (bool, string) {
+	for _, pattern := range secretsPatterns {
+		if pattern.regexp.MatchString(str) {
+			return true, pattern.name
+		}
+	}
+	return false, ""
+}
+
+func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	switch node := n.(type) {
 	case *ast.AssignStmt:
 		return r.matchAssign(node, ctx)
@@ -65,24 +233,41 @@ func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error
 	return nil, nil
 }
 
-func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*gosec.Issue, error) {
+func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*issue.Issue, error) {
 	for _, i := range assign.Lhs {
 		if ident, ok := i.(*ast.Ident); ok {
+			// First check LHS to find anything being assigned to variables whose name appears to be a cred
 			if r.pattern.MatchString(ident.Name) {
 				for _, e := range assign.Rhs {
 					if val, err := gosec.GetString(e); err == nil {
 						if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
-							return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil
+							return ctx.NewIssue(assign, r.ID(), r.What, r.Severity, r.Confidence), nil
 						}
 					}
 				}
 			}
+
+			// Now that no names were matched, match the RHS to see if the actual values being assigned are creds
+			for _, e := range assign.Rhs {
+				val, err := gosec.GetString(e)
+				if err != nil {
+					continue
+				}
+
+				if r.ignoreEntropy || r.isHighEntropyString(val) {
+					if ok, patternName := r.isSecretPattern(val); ok {
+						return ctx.NewIssue(assign, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil
+					}
+				}
+			}
 		}
 	}
 	return nil, nil
 }
 
-func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*gosec.Issue, error) {
+func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*issue.Issue, error) {
+	// Running match against the variable name(s) first. Will catch any creds whose var name matches the pattern,
+	// then will go back over to check the values themselves.
 	for index, ident := range valueSpec.Names {
 		if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil {
 			// const foo, bar = "same value"
@@ -91,22 +276,57 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Contex
 			}
 			if val, err := gosec.GetString(valueSpec.Values[index]); err == nil {
 				if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
-					return gosec.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil
+					return ctx.NewIssue(valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil
+				}
+			}
+		}
+	}
+
+	// Now that no variable names have been matched, match the actual values to find any creds
+	for _, ident := range valueSpec.Values {
+		if val, err := gosec.GetString(ident); err == nil {
+			if r.ignoreEntropy || r.isHighEntropyString(val) {
+				if ok, patternName := r.isSecretPattern(val); ok {
+					return ctx.NewIssue(valueSpec, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil
 				}
 			}
 		}
 	}
+
 	return nil, nil
 }
 
-func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.Context) (*gosec.Issue, error) {
+func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.Context) (*issue.Issue, error) {
 	if binaryExpr.Op == token.EQL || binaryExpr.Op == token.NEQ {
-		if ident, ok := binaryExpr.X.(*ast.Ident); ok {
-			if r.pattern.MatchString(ident.Name) {
-				if val, err := gosec.GetString(binaryExpr.Y); err == nil {
-					if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
-						return gosec.NewIssue(ctx, binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil
-					}
+		ident, ok := binaryExpr.X.(*ast.Ident)
+		if !ok {
+			ident, _ = binaryExpr.Y.(*ast.Ident)
+		}
+
+		if ident != nil && r.pattern.MatchString(ident.Name) {
+			valueNode := binaryExpr.Y
+			if !ok {
+				valueNode = binaryExpr.X
+			}
+			if val, err := gosec.GetString(valueNode); err == nil {
+				if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
+					return ctx.NewIssue(binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil
+				}
+			}
+		}
+
+		// Now that the variable names have been checked, and no matches were found, make sure that
+		// either the left or right operands is a string literal so we can match the value.
+		identStrConst, ok := binaryExpr.X.(*ast.BasicLit)
+		if !ok {
+			identStrConst, ok = binaryExpr.Y.(*ast.BasicLit)
+		}
+
+		if ok && identStrConst.Kind == token.STRING {
+			s, _ := gosec.GetString(identStrConst)
+			if r.ignoreEntropy || r.isHighEntropyString(s) {
+				if ok, patternName := r.isSecretPattern(s); ok {
+					return ctx.NewIssue(binaryExpr, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil
 				}
 			}
 		}
@@ -117,18 +337,19 @@ func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.
 // NewHardcodedCredentials attempts to find high entropy string constants being
 // assigned to variables that appear to be related to credentials.
 func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	pattern := `(?i)passwd|pass|password|pwd|secret|token`
+	pattern := `(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred`
 	entropyThreshold := 80.0
 	perCharThreshold := 3.0
 	ignoreEntropy := false
-	var truncateString = 16
-	if val, ok := conf["G101"]; ok {
+	truncateString := 16
+	if val, ok := conf[id]; ok {
 		conf := val.(map[string]interface{})
 		if configPattern, ok := conf["pattern"]; ok {
 			if cfgPattern, ok := configPattern.(string); ok {
 				pattern = cfgPattern
 			}
 		}
+
 		if configIgnoreEntropy, ok := conf["ignore_entropy"]; ok {
 			if cfgIgnoreEntropy, ok := configIgnoreEntropy.(bool); ok {
 				ignoreEntropy = cfgIgnoreEntropy
@@ -163,11 +384,11 @@ func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.No
 		perCharThreshold: perCharThreshold,
 		ignoreEntropy:    ignoreEntropy,
 		truncate:         truncateString,
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
 			What:       "Potential hardcoded credentials",
-			Confidence: gosec.Low,
-			Severity:   gosec.High,
+			Confidence: issue.Low,
+			Severity:   issue.High,
 		},
 	}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil), (*ast.BinaryExpr)(nil)}
 }
diff --git a/rules/http_serve.go b/rules/http_serve.go
new file mode 100644
index 0000000000..525ed4ebc7
--- /dev/null
+++ b/rules/http_serve.go
@@ -0,0 +1,39 @@
+package rules
+
+import (
+	"go/ast"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+type httpServeWithoutTimeouts struct {
+	issue.MetaData
+	pkg   string
+	calls []string
+}
+
+func (r *httpServeWithoutTimeouts) ID() string {
+	return r.MetaData.ID
+}
+
+func (r *httpServeWithoutTimeouts) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) {
+	if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
+		return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
+	}
+	return nil, nil
+}
+
+// NewHTTPServeWithoutTimeouts detects use of net/http serve functions that have no support for setting timeouts.
+func NewHTTPServeWithoutTimeouts(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	return &httpServeWithoutTimeouts{
+		pkg:   "net/http",
+		calls: []string{"ListenAndServe", "ListenAndServeTLS", "Serve", "ServeTLS"},
+		MetaData: issue.MetaData{
+			ID:         id,
+			What:       "Use of net/http serve function that has no support for setting timeouts",
+			Severity:   issue.Medium,
+			Confidence: issue.High,
+		},
+	}, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/rules/implicit_aliasing.go b/rules/implicit_aliasing.go
index b2668dec88..ee2358c766 100644
--- a/rules/implicit_aliasing.go
+++ b/rules/implicit_aliasing.go
@@ -3,12 +3,14 @@ package rules
 import (
 	"go/ast"
 	"go/token"
+	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type implicitAliasing struct {
-	gosec.MetaData
+	issue.MetaData
 	aliases         map[*ast.Object]struct{}
 	rightBrace      token.Pos
 	acceptableAlias []*ast.UnaryExpr
@@ -27,7 +29,30 @@ func containsUnary(exprs []*ast.UnaryExpr, expr *ast.UnaryExpr) bool {
 	return false
 }
 
-func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func getIdentExpr(expr ast.Expr) (*ast.Ident, bool) {
+	return doGetIdentExpr(expr, false)
+}
+
+func doGetIdentExpr(expr ast.Expr, hasSelector bool) (*ast.Ident, bool) {
+	switch node := expr.(type) {
+	case *ast.Ident:
+		return node, hasSelector
+	case *ast.SelectorExpr:
+		return doGetIdentExpr(node.X, true)
+	case *ast.UnaryExpr:
+		return doGetIdentExpr(node.X, hasSelector)
+	default:
+		return nil, false
+	}
+}
+
+func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	// This rule does not apply for Go 1.22, see https://go.dev/doc/go1.22#language.
+	major, minor, _ := gosec.GoVersion()
+	if major >= 1 && minor >= 22 {
+		return nil, nil
+	}
+
 	switch node := n.(type) {
 	case *ast.RangeStmt:
 		// When presented with a range statement, get the underlying Object bound to
@@ -71,9 +96,13 @@ func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, er
 		}
 
 		// If we find a unary op of & (reference) of an object within r.aliases, complain.
-		if ident, ok := node.X.(*ast.Ident); ok && node.Op.String() == "&" {
-			if _, contains := r.aliases[ident.Obj]; contains {
-				return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+		if identExpr, hasSelector := getIdentExpr(node); identExpr != nil && node.Op.String() == "&" {
+			if _, contains := r.aliases[identExpr.Obj]; contains {
+				_, isPointer := c.Info.TypeOf(identExpr).(*types.Pointer)
+
+				if !hasSelector || !isPointer {
+					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
+				}
 			}
 		}
 	case *ast.ReturnStmt:
@@ -89,15 +118,15 @@ func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, er
 }
 
 // NewImplicitAliasing detects implicit memory aliasing of type: for blah := SomeCall() {... SomeOtherCall(&blah) ...}
-func NewImplicitAliasing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewImplicitAliasing(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &implicitAliasing{
 		aliases:         make(map[*ast.Object]struct{}),
 		rightBrace:      token.NoPos,
 		acceptableAlias: make([]*ast.UnaryExpr, 0),
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.Medium,
+			Severity:   issue.Medium,
+			Confidence: issue.Medium,
 			What:       "Implicit memory aliasing in for loop.",
 		},
 	}, []ast.Node{(*ast.RangeStmt)(nil), (*ast.UnaryExpr)(nil), (*ast.ReturnStmt)(nil)}
diff --git a/rules/integer_overflow.go b/rules/integer_overflow.go
index dfcda94a8f..1d57906642 100644
--- a/rules/integer_overflow.go
+++ b/rules/integer_overflow.go
@@ -19,10 +19,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type integerOverflowCheck struct {
-	gosec.MetaData
+	issue.MetaData
 	calls gosec.CallList
 }
 
@@ -30,7 +31,7 @@ func (i *integerOverflowCheck) ID() string {
 	return i.MetaData.ID
 }
 
-func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	var atoiVarObj map[*ast.Object]ast.Node
 
 	// To check multiple lines, ctx.PassedValues is used to store temporary data.
@@ -61,9 +62,9 @@ func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.
 		if fun, ok := n.Fun.(*ast.Ident); ok {
 			if fun.Name == "int32" || fun.Name == "int16" {
 				if idt, ok := n.Args[0].(*ast.Ident); ok {
-					if n, ok := atoiVarObj[idt.Obj]; ok {
+					if _, ok := atoiVarObj[idt.Obj]; ok {
 						// Detect int32(v) and int16(v)
-						return gosec.NewIssue(ctx, n, i.ID(), i.What, i.Severity, i.Confidence), nil
+						return ctx.NewIssue(n, i.ID(), i.What, i.Severity, i.Confidence), nil
 					}
 				}
 			}
@@ -74,14 +75,14 @@ func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.
 }
 
 // NewIntegerOverflowCheck detects if there is potential Integer OverFlow
-func NewIntegerOverflowCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewIntegerOverflowCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
 	calls.Add("strconv", "Atoi")
 	return &integerOverflowCheck{
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.High,
-			Confidence: gosec.Medium,
+			Severity:   issue.High,
+			Confidence: issue.Medium,
 			What:       "Potential Integer overflow made by strconv.Atoi result conversion to int16/32",
 		},
 		calls: calls,
diff --git a/rules/pprof.go b/rules/pprof.go
index 4c99af7523..68498dd5e0 100644
--- a/rules/pprof.go
+++ b/rules/pprof.go
@@ -4,10 +4,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type pprofCheck struct {
-	gosec.MetaData
+	issue.MetaData
 	importPath string
 	importName string
 }
@@ -18,22 +19,22 @@ func (p *pprofCheck) ID() string {
 }
 
 // Match checks for pprof imports
-func (p *pprofCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (p *pprofCheck) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if node, ok := n.(*ast.ImportSpec); ok {
 		if p.importPath == unquote(node.Path.Value) && node.Name != nil && p.importName == node.Name.Name {
-			return gosec.NewIssue(c, node, p.ID(), p.What, p.Severity, p.Confidence), nil
+			return c.NewIssue(node, p.ID(), p.What, p.Severity, p.Confidence), nil
 		}
 	}
 	return nil, nil
 }
 
 // NewPprofCheck detects when the profiling endpoint is automatically exposed
-func NewPprofCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewPprofCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &pprofCheck{
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.High,
-			Confidence: gosec.High,
+			Severity:   issue.High,
+			Confidence: issue.High,
 			What:       "Profiling endpoint is automatically exposed on /debug/pprof",
 		},
 		importPath: "net/http/pprof",
diff --git a/rules/rand.go b/rules/rand.go
index bf86b762df..fe34ca9c3a 100644
--- a/rules/rand.go
+++ b/rules/rand.go
@@ -18,22 +18,22 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type weakRand struct {
-	gosec.MetaData
-	funcNames   []string
-	packagePath string
+	issue.MetaData
+	blocklist map[string][]string
 }
 
 func (w *weakRand) ID() string {
 	return w.MetaData.ID
 }
 
-func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
-	for _, funcName := range w.funcNames {
-		if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
-			return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
+func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	for pkg, funcs := range w.blocklist {
+		if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
+			return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil
 		}
 	}
 
@@ -41,16 +41,23 @@ func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 }
 
 // NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
-func NewWeakRandCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	calls := make(map[string][]string)
+	calls["math/rand"] = []string{
+		"New", "Read", "Float32", "Float64", "Int", "Int31", "Int31n",
+		"Int63", "Int63n", "Intn", "NormFloat64", "Uint32", "Uint64",
+	}
+	calls["math/rand/v2"] = []string{
+		"New", "Float32", "Float64", "Int", "Int32", "Int32N",
+		"Int64", "Int64N", "IntN", "N", "NormFloat64", "Uint32", "Uint32N", "Uint64", "Uint64N", "UintN",
+	}
 	return &weakRand{
-		funcNames: []string{"New", "Read", "Float32", "Float64", "Int", "Int31",
-			"Int31n", "Int63", "Int63n", "Intn", "NormalFloat64", "Uint32", "Uint64"},
-		packagePath: "math/rand",
-		MetaData: gosec.MetaData{
+		blocklist: calls,
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.High,
-			Confidence: gosec.Medium,
-			What:       "Use of weak random number generator (math/rand instead of crypto/rand)",
+			Severity:   issue.High,
+			Confidence: issue.Medium,
+			What:       "Use of weak random number generator (math/rand or math/rand/v2 instead of crypto/rand)",
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/readfile.go b/rules/readfile.go
index 072b016e2c..da6b9c965e 100644
--- a/rules/readfile.go
+++ b/rules/readfile.go
@@ -19,13 +19,15 @@ import (
 	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type readfile struct {
-	gosec.MetaData
+	issue.MetaData
 	gosec.CallList
-	pathJoin gosec.CallList
-	clean    gosec.CallList
+	pathJoin   gosec.CallList
+	clean      gosec.CallList
+	cleanedVar map[any]ast.Node
 }
 
 // ID returns the identifier for this rule
@@ -57,8 +59,11 @@ func (r *readfile) isJoinFunc(n ast.Node, c *gosec.Context) bool {
 	return false
 }
 
-// isFilepathClean checks if there is a filepath.Clean before assigning to a variable
+// isFilepathClean checks if there is a filepath.Clean for given variable
 func (r *readfile) isFilepathClean(n *ast.Ident, c *gosec.Context) bool {
+	if _, ok := r.cleanedVar[n.Obj.Decl]; ok {
+		return true
+	}
 	if n.Obj.Kind != ast.Var {
 		return false
 	}
@@ -72,22 +77,38 @@ func (r *readfile) isFilepathClean(n *ast.Ident, c *gosec.Context) bool {
 	return false
 }
 
+// trackFilepathClean tracks back the declaration of variable from filepath.Clean argument
+func (r *readfile) trackFilepathClean(n ast.Node) {
+	if clean, ok := n.(*ast.CallExpr); ok && len(clean.Args) > 0 {
+		if ident, ok := clean.Args[0].(*ast.Ident); ok {
+			// ident.Obj may be nil if the referenced declaration is in another file. It also may be incorrect.
+			// if it is nil, do not follow it.
+			if ident.Obj != nil {
+				r.cleanedVar[ident.Obj.Decl] = n
+			}
+		}
+	}
+}
+
 // Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile`
-func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
-	if node := r.ContainsPkgCallExpr(n, c, false); node != nil {
+func (r *readfile) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	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 gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+					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 gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 				}
 			}
 
@@ -96,7 +117,7 @@ func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 				if _, ok := obj.(*types.Var); ok &&
 					!gosec.TryResolve(ident, c) &&
 					!r.isFilepathClean(ident, c) {
-					return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 				}
 			}
 		}
@@ -105,24 +126,28 @@ func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 }
 
 // NewReadFile detects cases where we read files
-func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewReadFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	rule := &readfile{
 		pathJoin: gosec.NewCallList(),
 		clean:    gosec.NewCallList(),
 		CallList: gosec.NewCallList(),
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
 			What:       "Potential file inclusion via variable",
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 		},
+		cleanedVar: map[any]ast.Node{},
 	}
 	rule.pathJoin.Add("path/filepath", "Join")
 	rule.pathJoin.Add("path", "Join")
 	rule.clean.Add("path/filepath", "Clean")
 	rule.clean.Add("path/filepath", "Rel")
+	rule.clean.Add("path/filepath", "EvalSymlinks")
 	rule.Add("io/ioutil", "ReadFile")
+	rule.Add("os", "ReadFile")
 	rule.Add("os", "Open")
 	rule.Add("os", "OpenFile")
+	rule.Add("os", "Create")
 	return rule, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/rsa.go b/rules/rsa.go
index f2ed5db53d..331e7fc80a 100644
--- a/rules/rsa.go
+++ b/rules/rsa.go
@@ -19,10 +19,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type weakKeyStrength struct {
-	gosec.MetaData
+	issue.MetaData
 	calls gosec.CallList
 	bits  int
 }
@@ -31,27 +32,27 @@ func (w *weakKeyStrength) ID() string {
 	return w.MetaData.ID
 }
 
-func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if callExpr := w.calls.ContainsPkgCallExpr(n, c, false); callExpr != nil {
 		if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
-			return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
+			return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil
 		}
 	}
 	return nil, nil
 }
 
 // NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits
-func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewWeakKeyStrength(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
 	calls.Add("crypto/rsa", "GenerateKey")
 	bits := 2048
 	return &weakKeyStrength{
 		calls: calls,
 		bits:  bits,
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       fmt.Sprintf("RSA keys should be at least %d bits", bits),
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
diff --git a/rules/rulelist.go b/rules/rulelist.go
index a3d9ca2f60..3f8598009f 100644
--- a/rules/rulelist.go
+++ b/rules/rulelist.go
@@ -24,16 +24,21 @@ type RuleDefinition struct {
 	Create      gosec.RuleBuilder
 }
 
-// RuleList is a mapping of rule ID's to rule definitions
-type RuleList map[string]RuleDefinition
+// RuleList contains a mapping of rule ID's to rule definitions and a mapping
+// of rule ID's to whether rules are suppressed.
+type RuleList struct {
+	Rules          map[string]RuleDefinition
+	RuleSuppressed map[string]bool
+}
 
-// Builders returns all the create methods for a given rule list
-func (rl RuleList) Builders() map[string]gosec.RuleBuilder {
+// RulesInfo returns all the create methods and the rule suppressed map for a
+// given list
+func (rl RuleList) RulesInfo() (map[string]gosec.RuleBuilder, map[string]bool) {
 	builders := make(map[string]gosec.RuleBuilder)
-	for _, def := range rl {
+	for _, def := range rl.Rules {
 		builders[def.ID] = def.Create
 	}
-	return builders
+	return builders, rl.RuleSuppressed
 }
 
 // RuleFilter can be used to include or exclude a rule depending on the return
@@ -56,7 +61,7 @@ func NewRuleFilter(action bool, ruleIDs ...string) RuleFilter {
 }
 
 // Generate the list of rules to use
-func Generate(filters ...RuleFilter) RuleList {
+func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList {
 	rules := []RuleDefinition{
 		// misc
 		{"G101", "Look for hardcoded credentials", NewHardcodedCredentials},
@@ -68,6 +73,9 @@ func Generate(filters ...RuleFilter) RuleList {
 		{"G108", "Profiling endpoint is automatically exposed", NewPprofCheck},
 		{"G109", "Converting strconv.Atoi result to int32/int16", NewIntegerOverflowCheck},
 		{"G110", "Detect io.Copy instead of io.CopyN when decompression", NewDecompressionBombCheck},
+		{"G111", "Detect http.Dir('/') as a potential risk", NewDirectoryTraversal},
+		{"G112", "Detect ReadHeaderTimeout not configured as a potential risk", NewSlowloris},
+		{"G114", "Use of net/http serve function that has no support for setting timeouts", NewHTTPServeWithoutTimeouts},
 
 		// injection
 		{"G201", "SQL query construction using format string", NewSQLStrFormat},
@@ -82,13 +90,15 @@ func Generate(filters ...RuleFilter) RuleList {
 		{"G304", "File path provided as taint input", NewReadFile},
 		{"G305", "File path traversal when extracting zip archive", NewArchive},
 		{"G306", "Poor file permissions used when writing to a file", NewWritePerms},
-		{"G307", "Unsafe defer call of a method returning an error", NewDeferredClosing},
+		{"G307", "Poor file permissions used when creating a file with os.Create", NewOsCreatePerms},
 
 		// crypto
-		{"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography},
+		{"G401", "Detect the usage of MD5 or SHA1", NewUsesWeakCryptographyHash},
 		{"G402", "Look for bad TLS connection settings", NewIntermediateTLSCheck},
 		{"G403", "Ensure minimum RSA key length of 2048 bits", NewWeakKeyStrength},
 		{"G404", "Insecure random number source (rand)", NewWeakRandCheck},
+		{"G405", "Detect the usage of DES or RC4", NewUsesWeakCryptographyEncryption},
+		{"G406", "Detect the usage of deprecated MD4 or RIPEMD160", NewUsesWeakDeprecatedCryptographyHash},
 
 		// blocklist
 		{"G501", "Import blocklist: crypto/md5", NewBlocklistedImportMD5},
@@ -96,21 +106,28 @@ func Generate(filters ...RuleFilter) RuleList {
 		{"G503", "Import blocklist: crypto/rc4", NewBlocklistedImportRC4},
 		{"G504", "Import blocklist: net/http/cgi", NewBlocklistedImportCGI},
 		{"G505", "Import blocklist: crypto/sha1", NewBlocklistedImportSHA1},
+		{"G506", "Import blocklist: golang.org/x/crypto/md4", NewBlocklistedImportMD4},
+		{"G507", "Import blocklist: golang.org/x/crypto/ripemd160", NewBlocklistedImportRIPEMD160},
 
 		// memory safety
 		{"G601", "Implicit memory aliasing in RangeStmt", NewImplicitAliasing},
 	}
 
 	ruleMap := make(map[string]RuleDefinition)
+	ruleSuppressedMap := make(map[string]bool)
 
 RULES:
 	for _, rule := range rules {
+		ruleSuppressedMap[rule.ID] = false
 		for _, filter := range filters {
 			if filter(rule.ID) {
-				continue RULES
+				ruleSuppressedMap[rule.ID] = true
+				if !trackSuppressions {
+					continue RULES
+				}
 			}
 		}
 		ruleMap[rule.ID] = rule
 	}
-	return ruleMap
+	return RuleList{ruleMap, ruleSuppressedMap}
 }
diff --git a/rules/rules_suite_test.go b/rules/rules_suite_test.go
index 51a204eb76..8bc8baa841 100644
--- a/rules/rules_suite_test.go
+++ b/rules/rules_suite_test.go
@@ -1,10 +1,10 @@
 package rules_test
 
 import (
-	. "github.com/onsi/ginkgo"
-	. "github.com/onsi/gomega"
-
 	"testing"
+
+	. "github.com/onsi/ginkgo/v2"
+	. "github.com/onsi/gomega"
 )
 
 func TestRules(t *testing.T) {
diff --git a/rules/rules_test.go b/rules/rules_test.go
index f1c80b7e10..0f5314bc37 100644
--- a/rules/rules_test.go
+++ b/rules/rules_test.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"log"
 
-	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
 	"github.com/securego/gosec/v2"
@@ -13,7 +13,6 @@ import (
 )
 
 var _ = Describe("gosec rules", func() {
-
 	var (
 		logger    *log.Logger
 		config    gosec.Config
@@ -26,12 +25,12 @@ var _ = Describe("gosec rules", func() {
 	BeforeEach(func() {
 		logger, _ = testutils.NewLogger()
 		config = gosec.NewConfig()
-		analyzer = gosec.NewAnalyzer(config, tests, logger)
+		analyzer = gosec.NewAnalyzer(config, tests, false, false, 1, logger)
 		runner = func(rule string, samples []testutils.CodeSample) {
 			for n, sample := range samples {
 				analyzer.Reset()
 				analyzer.SetConfig(sample.Config)
-				analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
+				analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, rule)).RulesInfo())
 				pkg := testutils.NewTestPackage()
 				defer pkg.Close()
 				for i, code := range sample.Code {
@@ -56,6 +55,10 @@ var _ = Describe("gosec rules", func() {
 			runner("G101", testutils.SampleCodeG101)
 		})
 
+		It("should detect hardcoded credential values", func() {
+			runner("G101", testutils.SampleCodeG101Values)
+		})
+
 		It("should detect binding to all network interfaces", func() {
 			runner("G102", testutils.SampleCodeG102)
 		})
@@ -92,6 +95,18 @@ var _ = Describe("gosec rules", func() {
 			runner("G110", testutils.SampleCodeG110)
 		})
 
+		It("should detect potential directory traversal", func() {
+			runner("G111", testutils.SampleCodeG111)
+		})
+
+		It("should detect potential slowloris attack", func() {
+			runner("G112", testutils.SampleCodeG112)
+		})
+
+		It("should detect uses of net/http serve functions that have no support for setting timeouts", func() {
+			runner("G114", testutils.SampleCodeG114)
+		})
+
 		It("should detect sql injection via format strings", func() {
 			runner("G201", testutils.SampleCodeG201)
 		})
@@ -132,10 +147,6 @@ var _ = Describe("gosec rules", func() {
 			runner("G306", testutils.SampleCodeG306)
 		})
 
-		It("should detect unsafe defer of os.Close", func() {
-			runner("G307", testutils.SampleCodeG307)
-		})
-
 		It("should detect weak crypto algorithms", func() {
 			runner("G401", testutils.SampleCodeG401)
 		})
@@ -156,6 +167,22 @@ var _ = Describe("gosec rules", func() {
 			runner("G404", testutils.SampleCodeG404)
 		})
 
+		It("should detect weak crypto algorithms", func() {
+			runner("G405", testutils.SampleCodeG405)
+		})
+
+		It("should detect weak crypto algorithms", func() {
+			runner("G405", testutils.SampleCodeG405b)
+		})
+
+		It("should detect weak crypto algorithms", func() {
+			runner("G406", testutils.SampleCodeG406)
+		})
+
+		It("should detect weak crypto algorithms", func() {
+			runner("G406", testutils.SampleCodeG406b)
+		})
+
 		It("should detect blocklisted imports - MD5", func() {
 			runner("G501", testutils.SampleCodeG501)
 		})
@@ -176,10 +203,19 @@ var _ = Describe("gosec rules", func() {
 			runner("G505", testutils.SampleCodeG505)
 		})
 
-		It("should detect implicit aliasing in ForRange", func() {
-			runner("G601", testutils.SampleCodeG601)
+		It("should detect blocklisted imports - MD4", func() {
+			runner("G506", testutils.SampleCodeG506)
 		})
 
-	})
+		It("should detect blocklisted imports - RIPEMD160", func() {
+			runner("G507", testutils.SampleCodeG507)
+		})
 
+		It("should detect implicit aliasing in ForRange", func() {
+			major, minor, _ := gosec.GoVersion()
+			if major <= 1 && minor < 22 {
+				runner("G601", testutils.SampleCodeG601)
+			}
+		})
+	})
 })
diff --git a/rules/slowloris.go b/rules/slowloris.go
new file mode 100644
index 0000000000..70db73f5f3
--- /dev/null
+++ b/rules/slowloris.go
@@ -0,0 +1,71 @@
+// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rules
+
+import (
+	"go/ast"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+type slowloris struct {
+	issue.MetaData
+}
+
+func (r *slowloris) ID() string {
+	return r.MetaData.ID
+}
+
+func containsReadHeaderTimeout(node *ast.CompositeLit) bool {
+	if node == nil {
+		return false
+	}
+	for _, elt := range node.Elts {
+		if kv, ok := elt.(*ast.KeyValueExpr); ok {
+			if ident, ok := kv.Key.(*ast.Ident); ok {
+				if ident.Name == "ReadHeaderTimeout" || ident.Name == "ReadTimeout" {
+					return true
+				}
+			}
+		}
+	}
+	return false
+}
+
+func (r *slowloris) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
+	switch node := n.(type) {
+	case *ast.CompositeLit:
+		actualType := ctx.Info.TypeOf(node.Type)
+		if actualType != nil && actualType.String() == "net/http.Server" {
+			if !containsReadHeaderTimeout(node) {
+				return ctx.NewIssue(node, r.ID(), r.What, r.Severity, r.Confidence), nil
+			}
+		}
+	}
+	return nil, nil
+}
+
+// NewSlowloris attempts to find the http.Server struct and check if the ReadHeaderTimeout is configured.
+func NewSlowloris(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	return &slowloris{
+		MetaData: issue.MetaData{
+			ID:         id,
+			What:       "Potential Slowloris Attack because ReadHeaderTimeout is not configured in the http.Server",
+			Confidence: issue.Low,
+			Severity:   issue.Medium,
+		},
+	}, []ast.Node{(*ast.CompositeLit)(nil)}
+}
diff --git a/rules/sql.go b/rules/sql.go
index 127dec5042..42ace6c1d0 100644
--- a/rules/sql.go
+++ b/rules/sql.go
@@ -15,21 +15,73 @@
 package rules
 
 import (
+	"fmt"
 	"go/ast"
 	"regexp"
-	"strings"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type sqlStatement struct {
-	gosec.MetaData
+	issue.MetaData
 	gosec.CallList
 
 	// Contains a list of patterns which must all match for the rule to match.
 	patterns []*regexp.Regexp
 }
 
+var sqlCallIdents = map[string]map[string]int{
+	"*database/sql.Conn": {
+		"ExecContext":     1,
+		"QueryContext":    1,
+		"QueryRowContext": 1,
+		"PrepareContext":  1,
+	},
+	"*database/sql.DB": {
+		"Exec":            0,
+		"ExecContext":     1,
+		"Query":           0,
+		"QueryContext":    1,
+		"QueryRow":        0,
+		"QueryRowContext": 1,
+		"Prepare":         0,
+		"PrepareContext":  1,
+	},
+	"*database/sql.Tx": {
+		"Exec":            0,
+		"ExecContext":     1,
+		"Query":           0,
+		"QueryContext":    1,
+		"QueryRow":        0,
+		"QueryRowContext": 1,
+		"Prepare":         0,
+		"PrepareContext":  1,
+	},
+}
+
+// findQueryArg locates the argument taking raw SQL
+func findQueryArg(call *ast.CallExpr, ctx *gosec.Context) (ast.Expr, error) {
+	typeName, fnName, err := gosec.GetCallInfo(call, ctx)
+	if err != nil {
+		return nil, err
+	}
+	i := -1
+	if ni, ok := sqlCallIdents[typeName]; ok {
+		if i, ok = ni[fnName]; !ok {
+			i = -1
+		}
+	}
+	if i == -1 {
+		return nil, fmt.Errorf("SQL argument index not found for %s.%s", typeName, fnName)
+	}
+	if i >= len(call.Args) {
+		return nil, nil
+	}
+	query := call.Args[i]
+	return query, nil
+}
+
 func (s *sqlStatement) ID() string {
 	return s.MetaData.ID
 }
@@ -52,6 +104,32 @@ func (s *sqlStrConcat) ID() string {
 	return s.MetaData.ID
 }
 
+// findInjectionInBranch walks diwb a set if expressions, and will create new issues if it finds SQL injections
+// This method assumes you've already verified that the branch contains SQL syntax
+func (s *sqlStrConcat) findInjectionInBranch(ctx *gosec.Context, branch []ast.Expr) *ast.BinaryExpr {
+	for _, node := range branch {
+		be, ok := node.(*ast.BinaryExpr)
+		if !ok {
+			continue
+		}
+
+		operands := gosec.GetBinaryExprOperands(be)
+
+		for _, op := range operands {
+			if _, ok := op.(*ast.BasicLit); ok {
+				continue
+			}
+
+			if ident, ok := op.(*ast.Ident); ok && s.checkObject(ident, ctx) {
+				continue
+			}
+
+			return be
+		}
+	}
+	return nil
+}
+
 // see if we can figure out what it is
 func (s *sqlStrConcat) checkObject(n *ast.Ident, c *gosec.Context) bool {
 	if n.Obj != nil {
@@ -68,17 +146,11 @@ func (s *sqlStrConcat) checkObject(n *ast.Ident, c *gosec.Context) bool {
 }
 
 // checkQuery verifies if the query parameters is a string concatenation
-func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gosec.Issue, error) {
-	_, fnName, err := gosec.GetCallInfo(call, ctx)
+func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) {
+	query, err := findQueryArg(call, ctx)
 	if err != nil {
 		return nil, err
 	}
-	var query ast.Node
-	if strings.HasSuffix(fnName, "Context") {
-		query = call.Args[1]
-	} else {
-		query = call.Args[0]
-	}
 
 	if be, ok := query.(*ast.BinaryExpr); ok {
 		operands := gosec.GetBinaryExprOperands(be)
@@ -95,7 +167,29 @@ func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose
 				if op, ok := op.(*ast.Ident); ok && s.checkObject(op, ctx) {
 					continue
 				}
-				return gosec.NewIssue(ctx, be, s.ID(), s.What, s.Severity, s.Confidence), nil
+				return ctx.NewIssue(be, s.ID(), s.What, s.Severity, s.Confidence), nil
+			}
+		}
+	}
+
+	// Handle the case where an injection occurs as an infixed string concatenation, ie "SELECT * FROM foo WHERE name = '" + os.Args[0] + "' AND 1=1"
+	if id, ok := query.(*ast.Ident); ok {
+		var match bool
+		for _, str := range gosec.GetIdentStringValuesRecursive(id) {
+			if s.MatchPatterns(str) {
+				match = true
+				break
+			}
+		}
+
+		if !match {
+			return nil, nil
+		}
+
+		switch decl := id.Obj.Decl.(type) {
+		case *ast.AssignStmt:
+			if injection := s.findInjectionInBranch(ctx, decl.Rhs); injection != nil {
+				return ctx.NewIssue(injection, s.ID(), s.What, s.Severity, s.Confidence), nil
 			}
 		}
 	}
@@ -104,7 +198,7 @@ func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose
 }
 
 // Checks SQL query concatenation issues such as "SELECT * FROM table WHERE " + " ' OR 1=1"
-func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	switch stmt := n.(type) {
 	case *ast.AssignStmt:
 		for _, expr := range stmt.Rhs {
@@ -117,28 +211,32 @@ func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro
 			return s.checkQuery(sqlQueryCall, ctx)
 		}
 	}
+
 	return nil, nil
 }
 
 // NewSQLStrConcat looks for cases where we are building SQL strings via concatenation
-func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewSQLStrConcat(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	rule := &sqlStrConcat{
 		sqlStatement: sqlStatement{
 			patterns: []*regexp.Regexp{
-				regexp.MustCompile(`(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
+				regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE)( |\n|\r|\t)"),
 			},
-			MetaData: gosec.MetaData{
+			MetaData: issue.MetaData{
 				ID:         id,
-				Severity:   gosec.Medium,
-				Confidence: gosec.High,
+				Severity:   issue.Medium,
+				Confidence: issue.High,
 				What:       "SQL string concatenation",
 			},
 			CallList: gosec.NewCallList(),
 		},
 	}
 
-	rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext")
-	rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext")
+	for s, si := range sqlCallIdents {
+		for i := range si {
+			rule.Add(s, i)
+		}
+	}
 	return rule, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
 }
 
@@ -170,23 +268,17 @@ func (s *sqlStrFormat) constObject(e ast.Expr, c *gosec.Context) bool {
 	return false
 }
 
-func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gosec.Issue, error) {
-	_, fnName, err := gosec.GetCallInfo(call, ctx)
+func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) {
+	query, err := findQueryArg(call, ctx)
 	if err != nil {
 		return nil, err
 	}
-	var query ast.Node
-	if strings.HasSuffix(fnName, "Context") {
-		query = call.Args[1]
-	} else {
-		query = call.Args[0]
-	}
 
 	if ident, ok := query.(*ast.Ident); ok && ident.Obj != nil {
 		decl := ident.Obj.Decl
 		if assign, ok := decl.(*ast.AssignStmt); ok {
 			for _, expr := range assign.Rhs {
-				issue, err := s.checkFormatting(expr, ctx)
+				issue := s.checkFormatting(expr, ctx)
 				if issue != nil {
 					return issue, err
 				}
@@ -197,7 +289,7 @@ func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose
 	return nil, nil
 }
 
-func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) *issue.Issue {
 	// argIndex changes the function argument which gets matched to the regex
 	argIndex := 0
 	if node := s.fmtCalls.ContainsPkgCallExpr(n, ctx, false); node != nil {
@@ -208,7 +300,7 @@ func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) (*gosec.I
 				if arg, ok := node.Args[0].(*ast.SelectorExpr); ok {
 					if ident, ok := arg.X.(*ast.Ident); ok {
 						if s.noIssue.Contains(ident.Name, arg.Sel.Name) {
-							return nil, nil
+							return nil
 						}
 					}
 				}
@@ -219,7 +311,7 @@ func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) (*gosec.I
 
 		// no formatter
 		if len(node.Args) == 0 {
-			return nil, nil
+			return nil
 		}
 
 		var formatter string
@@ -233,7 +325,7 @@ func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) (*gosec.I
 			formatter = arg
 		}
 		if len(formatter) <= 0 {
-			return nil, nil
+			return nil
 		}
 
 		// If all formatter args are quoted or constant, then the SQL construction is safe
@@ -246,21 +338,34 @@ func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) (*gosec.I
 				}
 			}
 			if allSafe {
-				return nil, nil
+				return nil
 			}
 		}
 		if s.MatchPatterns(formatter) {
-			return gosec.NewIssue(ctx, n, s.ID(), s.What, s.Severity, s.Confidence), nil
+			return ctx.NewIssue(n, s.ID(), s.What, s.Severity, s.Confidence)
 		}
 	}
-	return nil, nil
+	return nil
 }
 
 // Check SQL query formatting issues such as "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)"
-func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
+func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
 	switch stmt := n.(type) {
 	case *ast.AssignStmt:
 		for _, expr := range stmt.Rhs {
+			if call, ok := expr.(*ast.CallExpr); ok {
+				selector, ok := call.Fun.(*ast.SelectorExpr)
+				if !ok {
+					continue
+				}
+				sqlQueryCall, ok := selector.X.(*ast.CallExpr)
+				if ok && s.ContainsCallExpr(sqlQueryCall, ctx) != nil {
+					issue, err := s.checkQuery(sqlQueryCall, ctx)
+					if err == nil && issue != nil {
+						return issue, err
+					}
+				}
+			}
 			if sqlQueryCall, ok := expr.(*ast.CallExpr); ok && s.ContainsCallExpr(expr, ctx) != nil {
 				return s.checkQuery(sqlQueryCall, ctx)
 			}
@@ -274,7 +379,7 @@ func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro
 }
 
 // NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
-func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewSQLStrFormat(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	rule := &sqlStrFormat{
 		CallList:      gosec.NewCallList(),
 		fmtCalls:      gosec.NewCallList(),
@@ -282,19 +387,22 @@ func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 		noIssueQuoted: gosec.NewCallList(),
 		sqlStatement: sqlStatement{
 			patterns: []*regexp.Regexp{
-				regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
+				regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE)( |\n|\r|\t)"),
 				regexp.MustCompile("%[^bdoxXfFp]"),
 			},
-			MetaData: gosec.MetaData{
+			MetaData: issue.MetaData{
 				ID:         id,
-				Severity:   gosec.Medium,
-				Confidence: gosec.High,
+				Severity:   issue.Medium,
+				Confidence: issue.High,
 				What:       "SQL string formatting",
 			},
 		},
 	}
-	rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext")
-	rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext")
+	for s, si := range sqlCallIdents {
+		for i := range si {
+			rule.Add(s, i)
+		}
+	}
 	rule.fmtCalls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln", "Fprintf")
 	rule.noIssue.AddAll("os", "Stdout", "Stderr")
 	rule.noIssueQuoted.Add("github.com/lib/pq", "QuoteIdentifier")
diff --git a/rules/ssh.go b/rules/ssh.go
index 01f37da510..e2ba5a3f4e 100644
--- a/rules/ssh.go
+++ b/rules/ssh.go
@@ -4,10 +4,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type sshHostKey struct {
-	gosec.MetaData
+	issue.MetaData
 	pkg   string
 	calls []string
 }
@@ -16,23 +17,23 @@ func (r *sshHostKey) ID() string {
 	return r.MetaData.ID
 }
 
-func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
+func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) {
 	if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
-		return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+		return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 	}
 	return nil, nil
 }
 
 // NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback.
-func NewSSHHostKey(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewSSHHostKey(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &sshHostKey{
 		pkg:   "golang.org/x/crypto/ssh",
 		calls: []string{"InsecureIgnoreHostKey"},
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
 			What:       "Use of ssh InsecureIgnoreHostKey should be audited",
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/ssrf.go b/rules/ssrf.go
index 86bb8278d3..dbf01081b2 100644
--- a/rules/ssrf.go
+++ b/rules/ssrf.go
@@ -5,10 +5,11 @@ import (
 	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type ssrf struct {
-	gosec.MetaData
+	issue.MetaData
 	gosec.CallList
 }
 
@@ -40,25 +41,25 @@ func (r *ssrf) ResolveVar(n *ast.CallExpr, c *gosec.Context) bool {
 }
 
 // Match inspects AST nodes to determine if certain net/http methods are called with variable input
-func (r *ssrf) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (r *ssrf) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	// Call expression is using http package directly
 	if node := r.ContainsPkgCallExpr(n, c, false); node != nil {
 		if r.ResolveVar(node, c) {
-			return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+			return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 		}
 	}
 	return nil, nil
 }
 
 // NewSSRFCheck detects cases where HTTP requests are sent
-func NewSSRFCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewSSRFCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	rule := &ssrf{
 		CallList: gosec.NewCallList(),
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
 			What:       "Potential HTTP request made with variable url",
-			Severity:   gosec.Medium,
-			Confidence: gosec.Medium,
+			Severity:   issue.Medium,
+			Confidence: issue.Medium,
 		},
 	}
 	rule.AddAll("net/http", "Do", "Get", "Head", "Post", "PostForm", "RoundTrip")
diff --git a/rules/subproc.go b/rules/subproc.go
index 30c32cc03a..1e2cedaa58 100644
--- a/rules/subproc.go
+++ b/rules/subproc.go
@@ -19,10 +19,11 @@ import (
 	"go/types"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type subprocess struct {
-	gosec.MetaData
+	issue.MetaData
 	gosec.CallList
 }
 
@@ -39,7 +40,7 @@ func (r *subprocess) ID() string {
 // is unsafe. For example:
 //
 // syscall.Exec("echo", "foobar" + tainted)
-func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if node := r.ContainsPkgCallExpr(n, c, false); node != nil {
 		args := node.Args
 		if r.isContext(n, c) {
@@ -48,12 +49,47 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 		for _, arg := range args {
 			if ident, ok := arg.(*ast.Ident); ok {
 				obj := c.Info.ObjectOf(ident)
-				if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
-					return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil
+
+				// need to cast and check whether it is for a variable ?
+				_, variable := obj.(*types.Var)
+
+				// .. indeed it is a variable then processing is different than a normal
+				// field assignment
+				if variable {
+					// skip the check when the declaration is not available
+					if ident.Obj == nil {
+						continue
+					}
+					switch ident.Obj.Decl.(type) {
+					case *ast.AssignStmt:
+						_, assignment := ident.Obj.Decl.(*ast.AssignStmt)
+						if variable && assignment {
+							if !gosec.TryResolve(ident, c) {
+								return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil
+							}
+						}
+					case *ast.Field:
+						_, field := ident.Obj.Decl.(*ast.Field)
+						if variable && field {
+							// check if the variable exist in the scope
+							vv, vvok := obj.(*types.Var)
+
+							if vvok && vv.Parent().Lookup(ident.Name) == nil {
+								return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil
+							}
+						}
+					case *ast.ValueSpec:
+						_, valueSpec := ident.Obj.Decl.(*ast.ValueSpec)
+						if variable && valueSpec {
+							if !gosec.TryResolve(ident, c) {
+								return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil
+							}
+						}
+					}
 				}
 			} else if !gosec.TryResolve(arg, c) {
 				// the arg is not a constant or a variable but instead a function call or os.Args[i]
-				return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with function call as argument or cmd arguments", gosec.Medium, gosec.High), nil
+				return c.NewIssue(n, r.ID(), "Subprocess launched with a potential tainted input or cmd arguments", issue.Medium, issue.High), nil
 			}
 		}
 	}
@@ -61,7 +97,7 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
 }
 
 // isContext checks whether or not the node is a CommandContext call or not
-// Thi is requried in order to skip the first argument from the check.
+// This is required in order to skip the first argument from the check.
 func (r *subprocess) isContext(n ast.Node, ctx *gosec.Context) bool {
 	selector, indent, err := gosec.GetCallInfo(n, ctx)
 	if err != nil {
@@ -74,12 +110,14 @@ func (r *subprocess) isContext(n ast.Node, ctx *gosec.Context) bool {
 }
 
 // NewSubproc detects cases where we are forking out to an external process
-func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-	rule := &subprocess{gosec.MetaData{ID: id}, gosec.NewCallList()}
+func NewSubproc(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	rule := &subprocess{issue.MetaData{ID: id}, gosec.NewCallList()}
 	rule.Add("os/exec", "Command")
 	rule.Add("os/exec", "CommandContext")
 	rule.Add("syscall", "Exec")
 	rule.Add("syscall", "ForkExec")
 	rule.Add("syscall", "StartProcess")
+	rule.Add("golang.org/x/sys/execabs", "Command")
+	rule.Add("golang.org/x/sys/execabs", "CommandContext")
 	return rule, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/tempfiles.go b/rules/tempfiles.go
index 36f0f979bc..6fef52a2cb 100644
--- a/rules/tempfiles.go
+++ b/rules/tempfiles.go
@@ -19,39 +19,69 @@ import (
 	"regexp"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type badTempFile struct {
-	gosec.MetaData
-	calls gosec.CallList
-	args  *regexp.Regexp
+	issue.MetaData
+	calls       gosec.CallList
+	args        *regexp.Regexp
+	argCalls    gosec.CallList
+	nestedCalls gosec.CallList
 }
 
 func (t *badTempFile) ID() string {
 	return t.MetaData.ID
 }
 
-func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
-	if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil {
-		if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
-			return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
+func (t *badTempFile) findTempDirArgs(n ast.Node, c *gosec.Context, suspect ast.Node) *issue.Issue {
+	if s, e := gosec.GetString(suspect); e == nil {
+		if t.args.MatchString(s) {
+			return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence)
 		}
+		return nil
+	}
+	if ce := t.argCalls.ContainsPkgCallExpr(suspect, c, false); ce != nil {
+		return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence)
+	}
+	if be, ok := suspect.(*ast.BinaryExpr); ok {
+		if ops := gosec.GetBinaryExprOperands(be); len(ops) != 0 {
+			return t.findTempDirArgs(n, c, ops[0])
+		}
+		return nil
+	}
+	if ce := t.nestedCalls.ContainsPkgCallExpr(suspect, c, false); ce != nil {
+		return t.findTempDirArgs(n, c, ce.Args[0])
+	}
+	return nil
+}
+
+func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) {
+	if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil {
+		return t.findTempDirArgs(n, c, node.Args[0]), nil
 	}
 	return nil, nil
 }
 
 // NewBadTempFile detects direct writes to predictable path in temporary directory
-func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewBadTempFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
 	calls.Add("io/ioutil", "WriteFile")
-	calls.Add("os", "Create")
+	calls.AddAll("os", "Create", "WriteFile")
+	argCalls := gosec.NewCallList()
+	argCalls.Add("os", "TempDir")
+	nestedCalls := gosec.NewCallList()
+	nestedCalls.Add("path", "Join")
+	nestedCalls.Add("path/filepath", "Join")
 	return &badTempFile{
-		calls: calls,
-		args:  regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
-		MetaData: gosec.MetaData{
+		calls:       calls,
+		args:        regexp.MustCompile(`^(/(usr|var))?/tmp(/.*)?$`),
+		argCalls:    argCalls,
+		nestedCalls: nestedCalls,
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       "File creation in shared tmp directory without using ioutil.Tempfile",
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
diff --git a/rules/templates.go b/rules/templates.go
index 8192409053..3d5f9a977a 100644
--- a/rules/templates.go
+++ b/rules/templates.go
@@ -18,10 +18,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type templateCheck struct {
-	gosec.MetaData
+	issue.MetaData
 	calls gosec.CallList
 }
 
@@ -29,11 +30,11 @@ func (t *templateCheck) ID() string {
 	return t.MetaData.ID
 }
 
-func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil {
 		for _, arg := range node.Args {
 			if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe
-				return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
+				return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence), nil
 			}
 		}
 	}
@@ -42,20 +43,22 @@ func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error
 
 // NewTemplateCheck constructs the template check rule. This rule is used to
 // find use of templates where HTML/JS escaping is not being used
-func NewTemplateCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
-
+func NewTemplateCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := gosec.NewCallList()
+	calls.Add("html/template", "CSS")
 	calls.Add("html/template", "HTML")
 	calls.Add("html/template", "HTMLAttr")
 	calls.Add("html/template", "JS")
+	calls.Add("html/template", "JSStr")
+	calls.Add("html/template", "Srcset")
 	calls.Add("html/template", "URL")
 	return &templateCheck{
 		calls: calls,
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.Low,
-			What:       "this method will not auto-escape HTML. Verify data is well formed.",
+			Severity:   issue.Medium,
+			Confidence: issue.Low,
+			What:       "The used method does not auto-escape HTML. This can potentially lead to 'Cross-site Scripting' vulnerabilities, in case the attacker controls the input.",
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/tls.go b/rules/tls.go
index dc0ab6aabc..65a0b5a33a 100644
--- a/rules/tls.go
+++ b/rules/tls.go
@@ -20,12 +20,15 @@ import (
 	"crypto/tls"
 	"fmt"
 	"go/ast"
+	"go/types"
+	"strconv"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type insecureConfigTLS struct {
-	gosec.MetaData
+	issue.MetaData
 	MinVersion       int64
 	MaxVersion       int64
 	requiredType     string
@@ -47,13 +50,13 @@ func stringInSlice(a string, list []string) bool {
 	return false
 }
 
-func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *gosec.Issue {
+func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *issue.Issue {
 	if ciphers, ok := n.(*ast.CompositeLit); ok {
 		for _, cipher := range ciphers.Elts {
 			if ident, ok := cipher.(*ast.SelectorExpr); ok {
 				if !stringInSlice(ident.Sel.Name, t.goodCiphers) {
 					err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name)
-					return gosec.NewIssue(c, ident, t.ID(), err, gosec.High, gosec.High)
+					return c.NewIssue(ident, t.ID(), err, issue.High, issue.High)
 				}
 			}
 		}
@@ -61,58 +64,112 @@ func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context)
 	return nil
 }
 
-func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Context) *gosec.Issue {
-	if ident, ok := n.Key.(*ast.Ident); ok {
+func (t *insecureConfigTLS) processTLSConf(n ast.Node, c *gosec.Context) *issue.Issue {
+	if kve, ok := n.(*ast.KeyValueExpr); ok {
+		issue := t.processTLSConfVal(kve.Key, kve.Value, c)
+		if issue != nil {
+			return issue
+		}
+	} else if assign, ok := n.(*ast.AssignStmt); ok {
+		if len(assign.Lhs) < 1 || len(assign.Rhs) < 1 {
+			return nil
+		}
+		if selector, ok := assign.Lhs[0].(*ast.SelectorExpr); ok {
+			issue := t.processTLSConfVal(selector.Sel, assign.Rhs[0], c)
+			if issue != nil {
+				return issue
+			}
+		}
+	}
+	return nil
+}
+
+func (t *insecureConfigTLS) processTLSConfVal(key ast.Expr, value ast.Expr, c *gosec.Context) *issue.Issue {
+	if ident, ok := key.(*ast.Ident); ok {
 		switch ident.Name {
 		case "InsecureSkipVerify":
-			if node, ok := n.Value.(*ast.Ident); ok {
+			if node, ok := value.(*ast.Ident); ok {
 				if node.Name != "false" {
-					return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gosec.High, gosec.High)
+					return c.NewIssue(value, t.ID(), "TLS InsecureSkipVerify set true.", issue.High, issue.High)
 				}
 			} else {
 				// TODO(tk): symbol tab look up to get the actual value
-				return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gosec.High, gosec.Low)
+				return c.NewIssue(value, t.ID(), "TLS InsecureSkipVerify may be true.", issue.High, issue.Low)
 			}
 
 		case "PreferServerCipherSuites":
-			if node, ok := n.Value.(*ast.Ident); ok {
+			if node, ok := value.(*ast.Ident); ok {
 				if node.Name == "false" {
-					return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gosec.Medium, gosec.High)
+					return c.NewIssue(value, t.ID(), "TLS PreferServerCipherSuites set false.", issue.Medium, issue.High)
 				}
 			} else {
 				// TODO(tk): symbol tab look up to get the actual value
-				return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gosec.Medium, gosec.Low)
+				return c.NewIssue(value, t.ID(), "TLS PreferServerCipherSuites may be false.", issue.Medium, issue.Low)
 			}
 
 		case "MinVersion":
-			if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
+			if d, ok := value.(*ast.Ident); ok {
+				obj := d.Obj
+				if obj == nil {
+					for _, f := range c.PkgFiles {
+						obj = f.Scope.Lookup(d.Name)
+						if obj != nil {
+							break
+						}
+					}
+				}
+				if vs, ok := obj.Decl.(*ast.ValueSpec); ok && len(vs.Values) > 0 {
+					if s, ok := vs.Values[0].(*ast.SelectorExpr); ok {
+						x := s.X.(*ast.Ident).Name
+						sel := s.Sel.Name
+
+						for _, imp := range c.Pkg.Imports() {
+							if imp.Name() == x {
+								tObj := imp.Scope().Lookup(sel)
+								if cst, ok := tObj.(*types.Const); ok {
+									// ..got the value check if this can be translated
+									if minVersion, err := strconv.ParseInt(cst.Val().String(), 0, 64); err == nil {
+										t.actualMinVersion = minVersion
+									}
+								}
+							}
+						}
+					}
+					if ival, ierr := gosec.GetInt(vs.Values[0]); ierr == nil {
+						t.actualMinVersion = ival
+					}
+				}
+			} else if ival, ierr := gosec.GetInt(value); ierr == nil {
 				t.actualMinVersion = ival
 			} else {
-				if se, ok := n.Value.(*ast.SelectorExpr); ok {
-					if pkg, ok := se.X.(*ast.Ident); ok && pkg.Name == "tls" {
-						t.actualMinVersion = t.mapVersion(se.Sel.Name)
+				if se, ok := value.(*ast.SelectorExpr); ok {
+					if pkg, ok := se.X.(*ast.Ident); ok {
+						if ip, ok := gosec.GetImportPath(pkg.Name, c); ok && ip == "crypto/tls" {
+							t.actualMinVersion = t.mapVersion(se.Sel.Name)
+						}
 					}
 				}
 			}
 
 		case "MaxVersion":
-			if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
+			if ival, ierr := gosec.GetInt(value); ierr == nil {
 				t.actualMaxVersion = ival
 			} else {
-				if se, ok := n.Value.(*ast.SelectorExpr); ok {
-					if pkg, ok := se.X.(*ast.Ident); ok && pkg.Name == "tls" {
-						t.actualMaxVersion = t.mapVersion(se.Sel.Name)
+				if se, ok := value.(*ast.SelectorExpr); ok {
+					if pkg, ok := se.X.(*ast.Ident); ok {
+						if ip, ok := gosec.GetImportPath(pkg.Name, c); ok && ip == "crypto/tls" {
+							t.actualMaxVersion = t.mapVersion(se.Sel.Name)
+						}
 					}
 				}
 			}
 
 		case "CipherSuites":
-			if ret := t.processTLSCipherSuites(n.Value, c); ret != nil {
+			if ret := t.processTLSCipherSuites(value, c); ret != nil {
 				return ret
 			}
 
 		}
-
 	}
 	return nil
 }
@@ -132,16 +189,16 @@ func (t *insecureConfigTLS) mapVersion(version string) int64 {
 	return v
 }
 
-func (t *insecureConfigTLS) checkVersion(n ast.Node, c *gosec.Context) *gosec.Issue {
+func (t *insecureConfigTLS) checkVersion(n ast.Node, c *gosec.Context) *issue.Issue {
 	if t.actualMaxVersion == 0 && t.actualMinVersion >= t.MinVersion {
 		// no warning is generated since the min version is greater than the secure min version
 		return nil
 	}
 	if t.actualMinVersion < t.MinVersion {
-		return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gosec.High, gosec.High)
+		return c.NewIssue(n, t.ID(), "TLS MinVersion too low.", issue.High, issue.High)
 	}
 	if t.actualMaxVersion < t.MaxVersion {
-		return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gosec.High, gosec.High)
+		return c.NewIssue(n, t.ID(), "TLS MaxVersion too low.", issue.High, issue.High)
 	}
 	return nil
 }
@@ -151,22 +208,32 @@ func (t *insecureConfigTLS) resetVersion() {
 	t.actualMinVersion = 0
 }
 
-func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil {
 		actualType := c.Info.TypeOf(complit.Type)
 		if actualType != nil && actualType.String() == t.requiredType {
 			for _, elt := range complit.Elts {
-				if kve, ok := elt.(*ast.KeyValueExpr); ok {
-					issue := t.processTLSConfVal(kve, c)
-					if issue != nil {
-						return issue, nil
-					}
+				issue := t.processTLSConf(elt, c)
+				if issue != nil {
+					return issue, nil
 				}
 			}
 			issue := t.checkVersion(complit, c)
 			t.resetVersion()
 			return issue, nil
 		}
+	} else {
+		if assign, ok := n.(*ast.AssignStmt); ok && len(assign.Lhs) > 0 {
+			if selector, ok := assign.Lhs[0].(*ast.SelectorExpr); ok {
+				actualType := c.Info.TypeOf(selector.X)
+				if actualType != nil && actualType.String() == t.requiredType {
+					issue := t.processTLSConf(assign, c)
+					if issue != nil {
+						return issue, nil
+					}
+				}
+			}
+		}
 	}
 	return nil, nil
 }
diff --git a/rules/tls_config.go b/rules/tls_config.go
index 5d68593d82..cbbdf7983a 100644
--- a/rules/tls_config.go
+++ b/rules/tls_config.go
@@ -4,13 +4,14 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 // NewModernTLSCheck creates a check for Modern TLS ciphers
 // DO NOT EDIT - generated by tlsconfig tool
-func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewModernTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &insecureConfigTLS{
-		MetaData:     gosec.MetaData{ID: id},
+		MetaData:     issue.MetaData{ID: id},
 		requiredType: "crypto/tls.Config",
 		MinVersion:   0x0304,
 		MaxVersion:   0x0304,
@@ -19,14 +20,14 @@ func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 			"TLS_AES_256_GCM_SHA384",
 			"TLS_CHACHA20_POLY1305_SHA256",
 		},
-	}, []ast.Node{(*ast.CompositeLit)(nil)}
+	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
 }
 
 // NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers
 // DO NOT EDIT - generated by tlsconfig tool
-func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewIntermediateTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &insecureConfigTLS{
-		MetaData:     gosec.MetaData{ID: id},
+		MetaData:     issue.MetaData{ID: id},
 		requiredType: "crypto/tls.Config",
 		MinVersion:   0x0303,
 		MaxVersion:   0x0304,
@@ -45,14 +46,14 @@ func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.No
 			"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
 			"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
 		},
-	}, []ast.Node{(*ast.CompositeLit)(nil)}
+	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
 }
 
 // NewOldTLSCheck creates a check for Old TLS ciphers
 // DO NOT EDIT - generated by tlsconfig tool
-func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewOldTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &insecureConfigTLS{
-		MetaData:     gosec.MetaData{ID: id},
+		MetaData:     issue.MetaData{ID: id},
 		requiredType: "crypto/tls.Config",
 		MinVersion:   0x0301,
 		MaxVersion:   0x0304,
@@ -88,5 +89,5 @@ func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
 			"TLS_RSA_WITH_AES_256_CBC_SHA",
 			"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
 		},
-	}, []ast.Node{(*ast.CompositeLit)(nil)}
+	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
 }
diff --git a/rules/unsafe.go b/rules/unsafe.go
index 88a298fb52..2e2adca7c7 100644
--- a/rules/unsafe.go
+++ b/rules/unsafe.go
@@ -18,10 +18,11 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
 type usingUnsafe struct {
-	gosec.MetaData
+	issue.MetaData
 	pkg   string
 	calls []string
 }
@@ -30,24 +31,24 @@ func (r *usingUnsafe) ID() string {
 	return r.MetaData.ID
 }
 
-func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
+func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) {
 	if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
-		return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+		return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 	}
 	return nil, nil
 }
 
 // NewUsingUnsafe rule detects the use of the unsafe package. This is only
 // really useful for auditing purposes.
-func NewUsingUnsafe(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+func NewUsingUnsafe(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	return &usingUnsafe{
 		pkg:   "unsafe",
-		calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
-		MetaData: gosec.MetaData{
+		calls: []string{"Pointer", "String", "StringData", "Slice", "SliceData"},
+		MetaData: issue.MetaData{
 			ID:         id,
 			What:       "Use of unsafe calls should be audited",
-			Severity:   gosec.Low,
-			Confidence: gosec.High,
+			Severity:   issue.Low,
+			Confidence: issue.High,
 		},
 	}, []ast.Node{(*ast.CallExpr)(nil)}
 }
diff --git a/rules/weakcrypto.go b/rules/weakcrypto.go
index eecb88f046..143f67d4e8 100644
--- a/rules/weakcrypto.go
+++ b/rules/weakcrypto.go
@@ -18,39 +18,38 @@ import (
 	"go/ast"
 
 	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
 )
 
-type usesWeakCryptography struct {
-	gosec.MetaData
+type usesWeakCryptographyEncryption struct {
+	issue.MetaData
 	blocklist map[string][]string
 }
 
-func (r *usesWeakCryptography) ID() string {
+func (r *usesWeakCryptographyEncryption) ID() string {
 	return r.MetaData.ID
 }
 
-func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+func (r *usesWeakCryptographyEncryption) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
 	for pkg, funcs := range r.blocklist {
 		if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
-			return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
+			return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
 		}
 	}
 	return nil, nil
 }
 
-// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.*
-func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+// NewUsesWeakCryptographyEncryption detects uses of des.*, rc4.*
+func NewUsesWeakCryptographyEncryption(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
 	calls := make(map[string][]string)
 	calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
-	calls["crypto/md5"] = []string{"New", "Sum"}
-	calls["crypto/sha1"] = []string{"New", "Sum"}
 	calls["crypto/rc4"] = []string{"NewCipher"}
-	rule := &usesWeakCryptography{
+	rule := &usesWeakCryptographyEncryption{
 		blocklist: calls,
-		MetaData: gosec.MetaData{
+		MetaData: issue.MetaData{
 			ID:         id,
-			Severity:   gosec.Medium,
-			Confidence: gosec.High,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
 			What:       "Use of weak cryptographic primitive",
 		},
 	}
diff --git a/rules/weakcryptohash.go b/rules/weakcryptohash.go
new file mode 100644
index 0000000000..298555de12
--- /dev/null
+++ b/rules/weakcryptohash.go
@@ -0,0 +1,55 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rules
+
+import (
+	"go/ast"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+type usesWeakCryptographyHash struct {
+	issue.MetaData
+	blocklist map[string][]string
+}
+
+func (r *usesWeakCryptographyHash) ID() string {
+	return r.MetaData.ID
+}
+
+func (r *usesWeakCryptographyHash) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	for pkg, funcs := range r.blocklist {
+		if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
+			return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
+		}
+	}
+	return nil, nil
+}
+
+// NewUsesWeakCryptographyHash detects uses of md5.*, sha1.*
+func NewUsesWeakCryptographyHash(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	calls := make(map[string][]string)
+	calls["crypto/md5"] = []string{"New", "Sum"}
+	calls["crypto/sha1"] = []string{"New", "Sum"}
+	rule := &usesWeakCryptographyHash{
+		blocklist: calls,
+		MetaData: issue.MetaData{
+			ID:         id,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
+			What:       "Use of weak cryptographic primitive",
+		},
+	}
+	return rule, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/rules/weakdepricatedcryptohash.go b/rules/weakdepricatedcryptohash.go
new file mode 100644
index 0000000000..68297355c1
--- /dev/null
+++ b/rules/weakdepricatedcryptohash.go
@@ -0,0 +1,57 @@
+// (c) Copyright 2024 Mercedes-Benz Tech Innovation GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rules
+
+import (
+	"go/ast"
+
+	"github.com/securego/gosec/v2"
+	"github.com/securego/gosec/v2/issue"
+)
+
+type usesWeakDeprecatedCryptographyHash struct {
+	issue.MetaData
+	blocklist map[string][]string
+}
+
+func (r *usesWeakDeprecatedCryptographyHash) ID() string {
+	return r.MetaData.ID
+}
+
+func (r *usesWeakDeprecatedCryptographyHash) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
+	for pkg, funcs := range r.blocklist {
+		if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
+			return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
+		}
+	}
+	return nil, nil
+}
+
+// NewUsesWeakCryptographyHash detects uses of md4.New, ripemd160.New
+func NewUsesWeakDeprecatedCryptographyHash(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
+	calls := make(map[string][]string)
+	calls["golang.org/x/crypto/md4"] = []string{"New"}
+	calls["golang.org/x/crypto/ripemd160"] = []string{"New"}
+	rule := &usesWeakDeprecatedCryptographyHash{
+		blocklist: calls,
+		MetaData: issue.MetaData{
+			ID:         id,
+			Severity:   issue.Medium,
+			Confidence: issue.High,
+			What:       "Use of deprecated weak cryptographic primitive",
+		},
+	}
+	return rule, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/testutils/cgo_samples.go b/testutils/cgo_samples.go
new file mode 100644
index 0000000000..80af013001
--- /dev/null
+++ b/testutils/cgo_samples.go
@@ -0,0 +1,50 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeCgo - Cgo file sample
+var SampleCodeCgo = []CodeSample{
+	{[]string{`
+package main
+
+import (
+        "fmt"
+        "unsafe"
+)
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+int printData(unsigned char *data) {
+    return printf("cData: %lu \"%s\"\n", (long unsigned int)strlen(data), data);
+}
+*/
+import "C"
+
+func main() {
+        // Allocate C data buffer.
+        width, height := 8, 2
+        lenData := width * height
+        // add string terminating null byte
+        cData := (*C.uchar)(C.calloc(C.size_t(lenData+1), C.sizeof_uchar))
+
+        // When no longer in use, free C allocations.
+        defer C.free(unsafe.Pointer(cData))
+
+        // Go slice reference to C data buffer,
+        // minus string terminating null byte
+        gData := (*[1 << 30]byte)(unsafe.Pointer(cData))[:lenData:lenData]
+
+        // Write and read cData via gData.
+        for i := range gData {
+                gData[i] = '.'
+        }
+        copy(gData[0:], "Data")
+        gData[len(gData)-1] = 'X'
+        fmt.Printf("gData: %d %q\n", len(gData), gData)
+        C.printData(cData)
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g101_samples.go b/testutils/g101_samples.go
new file mode 100644
index 0000000000..1dbbea802d
--- /dev/null
+++ b/testutils/g101_samples.go
@@ -0,0 +1,436 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG101 code snippets for hardcoded credentials
+	SampleCodeG101 = []CodeSample{
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	username := "admin"
+	password := "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+	fmt.Println("Doing something with: ", username, password)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+// Entropy check should not report this error by default
+package main
+
+import "fmt"
+
+func main() {
+	username := "admin"
+	password := "secret"
+	fmt.Println("Doing something with: ", username, password)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+var password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+
+func main() {
+	username := "admin"
+	fmt.Println("Doing something with: ", username, password)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+
+func main() {
+	username := "admin"
+	fmt.Println("Doing something with: ", username, password)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	username = "user"
+	password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+)
+
+func main() {
+	fmt.Println("Doing something with: ", username, password)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+var password string
+
+func init() {
+	password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+const (
+	ATNStateSomethingElse = 1
+	ATNStateTokenStart = 42
+)
+
+func main() {
+	println(ATNStateTokenStart)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+const (
+	ATNStateTokenStart = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
+)
+
+func main() {
+	println(ATNStateTokenStart)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var password string
+	if password == "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
+		fmt.Println("password equality")
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var password string
+	if "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" == password {
+		fmt.Println("password equality")
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var password string
+	if password != "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
+		fmt.Println("password equality")
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var password string
+	if "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" != password {
+		fmt.Println("password equality")
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var p string
+	if p != "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
+		fmt.Println("password equality")
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	var p string
+	if "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" != p {
+		fmt.Println("password equality")
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	pw = "KjasdlkjapoIKLlka98098sdf012U/rL2sLdBqOHQUlt5Z6kCgKGDyCFA=="
+)
+
+func main() {
+	fmt.Println(pw)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+var (
+	pw string
+)
+
+func main() {
+	pw = "KjasdlkjapoIKLlka98098sdf012U/rL2sLdBqOHQUlt5Z6kCgKGDyCFA=="
+	fmt.Println(pw)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	cred = "KjasdlkjapoIKLlka98098sdf012U/rL2sLdBqOHQUlt5Z6kCgKGDyCFA=="
+)
+
+func main() {
+	fmt.Println(cred)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+var (
+	cred string
+)
+
+func main() {
+	cred = "KjasdlkjapoIKLlka98098sdf012U/rL2sLdBqOHQUlt5Z6kCgKGDyCFA=="
+	fmt.Println(cred)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	apiKey = "KjasdlkjapoIKLlka98098sdf012U"
+)
+
+func main() {
+	fmt.Println(apiKey)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+var (
+	apiKey string
+)
+
+func main() {
+	apiKey = "KjasdlkjapoIKLlka98098sdf012U"
+	fmt.Println(apiKey)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	bearer = "Bearer: 2lkjdfoiuwer092834kjdwf09"
+)
+
+func main() {
+	fmt.Println(bearer)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+var (
+	bearer string
+)
+
+func main() {
+	bearer = "Bearer: 2lkjdfoiuwer092834kjdwf09"
+	fmt.Println(bearer)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+// #nosec G101
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config" // #nosec G101
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+// #nosec G101
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config"
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+	
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config" // #nosec G101
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+	
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+//gosec:disable G101
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config" //gosec:disable G101
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+//gosec:disable G101
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config"
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+	
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+const (
+	ConfigLearnerTokenAuth string = "learner_auth_token_config" //gosec:disable G101
+)
+
+func main() {
+	fmt.Printf("%s\n", ConfigLearnerTokenAuth)
+}
+	
+`}, 0, gosec.NewConfig()},
+	}
+
+	// SampleCodeG101Values code snippets for hardcoded credentials
+	SampleCodeG101Values = []CodeSample{
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	customerNameEnvKey := "FOO_CUSTOMER_NAME"
+	fmt.Println(customerNameEnvKey)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	txnID := "3637cfcc1eec55a50f78a7c435914583ccbc75a21dec9a0e94dfa077647146d7"
+	fmt.Println(txnID)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	urlSecret := "https://username:abcdef0123456789abcdef0123456789abcdef01@contoso.com/"
+	fmt.Println(urlSecret)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	githubToken := "ghp_iR54dhCYg9Tfmoywi9xLmmKZrrnAw438BYh3"
+	fmt.Println(githubToken)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	awsAccessKeyID := "AKIAI44QH8DHBEXAMPLE"
+	fmt.Println(awsAccessKeyID)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	compareGoogleAPI := "test"
+	if compareGoogleAPI == "AIzajtGS_aJGkoiAmSbXzu9I-1eytAi9Lrlh-vT" {
+		fmt.Println(compareGoogleAPI)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	}
+)
diff --git a/testutils/g102_samples.go b/testutils/g102_samples.go
new file mode 100644
index 0000000000..8e83ec3b44
--- /dev/null
+++ b/testutils/g102_samples.go
@@ -0,0 +1,104 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG102 code snippets for network binding
+var SampleCodeG102 = []CodeSample{
+	// Bind to all networks explicitly
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+)
+
+func main() {
+	l, err := net.Listen("tcp", "0.0.0.0:2000")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+}
+`}, 1, gosec.NewConfig()},
+	// Bind to all networks implicitly (default if host omitted)
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+)
+
+func main() {
+		l, err := net.Listen("tcp", ":2000")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+}
+`}, 1, gosec.NewConfig()},
+	// Bind to all networks indirectly through a parsing function
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+)
+
+func parseListenAddr(listenAddr string) (network string, addr string) {
+	return "", ""
+}
+
+func main() {
+	addr := ":2000"
+	l, err := net.Listen(parseListenAddr(addr))
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+}
+`}, 1, gosec.NewConfig()},
+	// Bind to all networks indirectly through a parsing function
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+)
+
+const addr = ":2000"
+
+func parseListenAddr(listenAddr string) (network string, addr string) {
+	return "", ""
+}
+
+func main() {
+	l, err := net.Listen(parseListenAddr(addr))
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+)
+
+const addr = "0.0.0.0:2000"
+
+func main() {
+	l, err := net.Listen("tcp", addr)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g103_samples.go b/testutils/g103_samples.go
new file mode 100644
index 0000000000..feeb6b682c
--- /dev/null
+++ b/testutils/g103_samples.go
@@ -0,0 +1,65 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG103 find instances of unsafe blocks for auditing purposes
+var SampleCodeG103 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+type Fake struct{}
+
+func (Fake) Good() {}
+
+func main() {
+	unsafeM := Fake{}
+	unsafeM.Good()
+	intArray := [...]int{1, 2}
+	fmt.Printf("\nintArray: %v\n", intArray)
+	intPtr := &intArray[0]
+	fmt.Printf("\nintPtr=%p, *intPtr=%d.\n", intPtr, *intPtr)
+	addressHolder := uintptr(unsafe.Pointer(intPtr)) 
+	intPtr = (*int)(unsafe.Pointer(addressHolder))
+	fmt.Printf("\nintPtr=%p, *intPtr=%d.\n\n", intPtr, *intPtr)
+}
+`}, 2, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+func main() {
+	chars := [...]byte{1, 2}
+	charsPtr := &chars[0]
+	str := unsafe.String(charsPtr, len(chars))
+	fmt.Printf("%s\n", str)
+	ptr := unsafe.StringData(str)
+	fmt.Printf("ptr: %p\n", ptr)
+}
+`}, 2, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+func main() {
+	chars := [...]byte{1, 2}
+	charsPtr := &chars[0]
+	slice := unsafe.Slice(charsPtr, len(chars))
+	fmt.Printf("%v\n", slice)
+	ptr := unsafe.SliceData(slice)
+	fmt.Printf("ptr: %p\n", ptr)
+}
+`}, 2, gosec.NewConfig()},
+}
diff --git a/testutils/g104_samples.go b/testutils/g104_samples.go
new file mode 100644
index 0000000000..fcd8ec85c4
--- /dev/null
+++ b/testutils/g104_samples.go
@@ -0,0 +1,227 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG104 finds errors that aren't being handled
+	SampleCodeG104 = []CodeSample{
+		{[]string{`
+package main
+
+import "fmt"
+
+func test() (int,error) {
+	return 0, nil
+}
+
+func main() {
+	v, _ := test()
+	fmt.Println(v)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"io/ioutil"
+	"os"
+	"fmt"
+)
+
+func a() error {
+	return fmt.Errorf("This is an error")
+}
+
+func b() {
+	fmt.Println("b")
+	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
+}
+
+func c() string {
+	return fmt.Sprintf("This isn't anything")
+}
+
+func main() {
+	_ = a()
+	a()
+	b()
+	c()
+}
+`}, 2, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import "fmt"
+
+func test() error {
+	return nil
+}
+
+func main() {
+	e := test()
+	fmt.Println(e)
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+// +build go1.10
+
+package main
+
+import "strings"
+
+func main() {
+	var buf strings.Builder
+	_, err := buf.WriteString("test string")
+	if err != nil {
+		panic(err)
+	}
+}`, `
+package main
+
+func dummy(){}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"bytes"
+)
+
+type a struct {
+	buf *bytes.Buffer
+}
+
+func main() {
+	a := &a{
+		buf: new(bytes.Buffer),
+	}
+	a.buf.Write([]byte{0})
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"io/ioutil"
+	"os"
+	"fmt"
+)
+
+func a() {
+	fmt.Println("a")
+	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
+}
+
+func main() {
+	a()
+}
+`}, 0, gosec.Config{"G104": map[string]interface{}{"ioutil": []interface{}{"WriteFile"}}}},
+		{[]string{`
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+)
+
+func createBuffer() *bytes.Buffer {
+	return new(bytes.Buffer)
+}
+
+func main() {
+	new(bytes.Buffer).WriteString("*bytes.Buffer")
+	fmt.Fprintln(os.Stderr, "fmt")
+	new(strings.Builder).WriteString("*strings.Builder")
+	_, pw := io.Pipe()
+	pw.CloseWithError(io.EOF)
+
+	createBuffer().WriteString("*bytes.Buffer")
+	b := createBuffer()
+	b.WriteString("*bytes.Buffer")
+}
+`}, 0, gosec.NewConfig()},
+	} // it shouldn't return any errors because all method calls are whitelisted by default
+
+	// SampleCodeG104Audit finds errors that aren't being handled in audit mode
+	SampleCodeG104Audit = []CodeSample{
+		{[]string{`
+package main
+
+import "fmt"
+
+func test() (int,error) {
+	return 0, nil
+}
+
+func main() {
+	v, _ := test()
+	fmt.Println(v)
+}
+`}, 1, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}},
+		{[]string{`
+package main
+
+import (
+	"io/ioutil"
+	"os"
+	"fmt"
+)
+
+func a() error {
+	return fmt.Errorf("This is an error")
+}
+
+func b() {
+	fmt.Println("b")
+	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
+}
+
+func c() string {
+	return fmt.Sprintf("This isn't anything")
+}
+
+func main() {
+	_ = a()
+	a()
+	b()
+	c()
+}
+`}, 3, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}},
+		{[]string{`
+package main
+
+import "fmt"
+
+func test() error {
+	return nil
+}
+
+func main() {
+	e := test()
+	fmt.Println(e)
+}
+`}, 0, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}},
+		{[]string{`
+// +build go1.10
+
+package main
+
+import "strings"
+
+func main() {
+	var buf strings.Builder
+	_, err := buf.WriteString("test string")
+	if err != nil {
+		panic(err)
+	}
+}
+`, `
+package main
+
+func dummy(){}
+`}, 0, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}},
+	}
+)
diff --git a/testutils/g106_samples.go b/testutils/g106_samples.go
new file mode 100644
index 0000000000..1f8f472579
--- /dev/null
+++ b/testutils/g106_samples.go
@@ -0,0 +1,18 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG106 - ssh InsecureIgnoreHostKey
+var SampleCodeG106 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+		_ =  ssh.InsecureIgnoreHostKey()
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g107_samples.go b/testutils/g107_samples.go
new file mode 100644
index 0000000000..ec3efadfe1
--- /dev/null
+++ b/testutils/g107_samples.go
@@ -0,0 +1,196 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG107 - SSRF via http requests with variable url
+var SampleCodeG107 = []CodeSample{
+	{[]string{`
+// Input from the std in is considered insecure
+package main
+import (
+	"net/http"
+	"io/ioutil"
+	"fmt"
+	"os"
+	"bufio"
+)
+func main() {
+	in := bufio.NewReader(os.Stdin)
+	url, err := in.ReadString('\n')
+	if err != nil {
+		panic(err)
+	}
+	resp, err := http.Get(url)
+	if err != nil {
+		panic(err)
+	}
+		defer resp.Body.Close()
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			panic(err)
+		}
+		fmt.Printf("%s", body)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Variable defined a package level can be changed at any time
+// regardless of the initial value
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/http"
+)
+
+var url string = "https://www.google.com"
+
+func main() {
+	resp, err := http.Get(url)
+	if err != nil {
+		panic(err)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		panic(err)
+	}
+	fmt.Printf("%s", body)
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Environmental variables are not considered as secure source
+package main
+import (
+	"net/http"
+	"io/ioutil"
+	"fmt"
+	"os"
+)
+func main() {
+	url := os.Getenv("tainted_url")
+	resp, err := http.Get(url)
+	if err != nil {
+		panic(err)
+	}
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+			panic(err)
+	}
+	fmt.Printf("%s", body)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Constant variables or hard-coded strings are secure
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+const url = "http://127.0.0.1"
+func main() {
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// A variable at function scope which is initialized to
+// a constant string is secure (e.g. cannot be changed concurrently)
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+func main() {
+	var url string = "http://127.0.0.1"
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// A variable at function scope which is initialized to
+// a constant string is secure (e.g. cannot be changed concurrently)
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+func main() {
+	url := "http://127.0.0.1"
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// A variable at function scope which is initialized to
+// a constant string is secure (e.g. cannot be changed concurrently)
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+func main() {
+	url1 := "test"
+	var url2 string = "http://127.0.0.1"
+	url2 = url1
+	resp, err := http.Get(url2)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// An exported variable declared a packaged scope is not secure
+// because it can changed at any time
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+
+var Url string
+
+func main() {
+	resp, err := http.Get(Url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// An url provided as a function argument is not secure
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+func get(url string) {
+	resp, err := http.Get(url)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(resp.Status)
+}
+func main() {
+	url := "http://127.0.0.1"
+	get(url)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g108_samples.go b/testutils/g108_samples.go
new file mode 100644
index 0000000000..3702519f26
--- /dev/null
+++ b/testutils/g108_samples.go
@@ -0,0 +1,40 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG108 - pprof endpoint automatically exposed
+var SampleCodeG108 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	_ "net/http/pprof"
+)
+
+func main() {
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, "Hello World!")
+	})
+	log.Fatal(http.ListenAndServe(":8080", nil))
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+)
+
+func main() {
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, "Hello World!")
+	})
+	log.Fatal(http.ListenAndServe(":8080", nil))
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g109_samples.go b/testutils/g109_samples.go
new file mode 100644
index 0000000000..9dc784181c
--- /dev/null
+++ b/testutils/g109_samples.go
@@ -0,0 +1,112 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG109 - Potential Integer OverFlow
+var SampleCodeG109 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func main() {
+	bigValue, err := strconv.Atoi("2147483648")
+	if err != nil {
+		panic(err)
+	}
+	value := int32(bigValue)
+	fmt.Println(value)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func main() {
+	bigValue, err := strconv.Atoi("32768")
+	if err != nil {
+		panic(err)
+	}
+	if int16(bigValue) < 0 {
+		fmt.Println(bigValue)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func main() {
+	bigValue, err := strconv.Atoi("2147483648")
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(bigValue)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func main() {
+	bigValue, err := strconv.Atoi("2147483648")
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(bigValue)
+	test()
+}
+
+func test() {
+	bigValue := 30
+	value := int64(bigValue)
+	fmt.Println(value)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+
+func main() {
+	value := 10
+	if value == 10 {
+		value, _ := strconv.Atoi("2147483648")
+		fmt.Println(value)
+	}
+	v := int64(value)
+	fmt.Println(v)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"strconv"
+)
+func main() {
+	a, err := strconv.Atoi("a")
+	b := int64(a) //#nosec G109
+	fmt.Println(b, err)
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g110_samples.go b/testutils/g110_samples.go
new file mode 100644
index 0000000000..e0c61d9098
--- /dev/null
+++ b/testutils/g110_samples.go
@@ -0,0 +1,126 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG110 - potential DoS vulnerability via decompression bomb
+var SampleCodeG110 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"bytes"
+	"compress/zlib"
+	"io"
+	"os"
+)
+
+func main() {
+	buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207,
+		47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
+	b := bytes.NewReader(buff)
+
+	r, err := zlib.NewReader(b)
+	if err != nil {
+		panic(err)
+	}
+	_, err = io.Copy(os.Stdout, r)
+	if err != nil {
+		panic(err)
+	}
+
+	r.Close()
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"bytes"
+	"compress/zlib"
+	"io"
+	"os"
+)
+
+func main() {
+	buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207,
+		47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
+	b := bytes.NewReader(buff)
+
+	r, err := zlib.NewReader(b)
+	if err != nil {
+		panic(err)
+	}
+	buf := make([]byte, 8)
+	_, err = io.CopyBuffer(os.Stdout, r, buf)
+	if err != nil {
+		panic(err)
+	}
+	r.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"archive/zip"
+	"io"
+	"os"
+	"strconv"
+)
+
+func main() {
+	r, err := zip.OpenReader("tmp.zip")
+	if err != nil {
+		panic(err)
+	}
+	defer r.Close()
+
+	for i, f := range r.File {
+		out, err := os.OpenFile("output" + strconv.Itoa(i), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
+		if err != nil {
+			panic(err)
+		}
+
+		rc, err := f.Open()
+		if err != nil {
+			panic(err)
+		}
+
+		_, err = io.Copy(out, rc)
+
+		out.Close()
+		rc.Close()
+
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"io"
+	"os"
+)
+
+func main() {
+	s, err := os.Open("src")
+	if err != nil {
+		panic(err)
+	}
+	defer s.Close()
+
+	d, err := os.Create("dst")
+	if err != nil {
+		panic(err)
+	}
+	defer d.Close()
+
+	_, err = io.Copy(d, s)
+	if  err != nil {
+		panic(err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g111_samples.go b/testutils/g111_samples.go
new file mode 100644
index 0000000000..75007caf56
--- /dev/null
+++ b/testutils/g111_samples.go
@@ -0,0 +1,27 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG111 - potential directory traversal
+var SampleCodeG111 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+)
+
+func main() {
+	http.Handle("/bad/", http.StripPrefix("/bad/", http.FileServer(http.Dir("/"))))
+	http.HandleFunc("/", HelloServer)
+	log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), nil))
+}
+
+func HelloServer(w http.ResponseWriter, r *http.Request) {
+	fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g112_samples.go b/testutils/g112_samples.go
new file mode 100644
index 0000000000..02e6bc6363
--- /dev/null
+++ b/testutils/g112_samples.go
@@ -0,0 +1,137 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG112 - potential slowloris attack
+var SampleCodeG112 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
+	})
+	err := (&http.Server{
+		Addr: ":1234",
+	}).ListenAndServe()
+	if err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"time"
+	"net/http"
+)
+
+func main() {
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
+	})
+	server := &http.Server{
+		Addr:              ":1234",
+		ReadHeaderTimeout: 3 * time.Second,
+	}
+	err := server.ListenAndServe()
+	if err != nil {
+		panic(err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"time"
+	"net/http"
+)
+
+func main() {
+	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
+	})
+	server := &http.Server{
+		Addr:              ":1234",
+		ReadTimeout:  	   1 * time.Second,
+	}
+	err := server.ListenAndServe()
+	if err != nil {
+		panic(err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"net/http"
+	"sync"
+)
+
+type Server struct {
+	hs  *http.Server
+	mux *http.ServeMux
+	mu  sync.Mutex
+}
+
+func New(listenAddr string) *Server {
+	mux := http.NewServeMux()
+
+	return &Server{
+	hs: &http.Server{ // #nosec G112 - Not publicly exposed
+		Addr:    listenAddr,
+		Handler: mux,
+	},
+	mux: mux,
+	mu:  sync.Mutex{},
+	}
+}
+
+func main() {
+	fmt.Print("test")
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"net/http"
+	"sync"
+)
+
+type Server struct {
+	hs  *http.Server
+	mux *http.ServeMux
+	mu  sync.Mutex
+}
+
+func New(listenAddr string) *Server {
+	mux := http.NewServeMux()
+
+	return &Server{
+	hs: &http.Server{ //gosec:disable G112 - Not publicly exposed
+		Addr:    listenAddr,
+		Handler: mux,
+	},
+	mux: mux,
+	mu:  sync.Mutex{},
+	}
+}
+
+func main() {
+	fmt.Print("test")
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g114_samples.go b/testutils/g114_samples.go
new file mode 100644
index 0000000000..19edecea40
--- /dev/null
+++ b/testutils/g114_samples.go
@@ -0,0 +1,71 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG114 - Use of net/http serve functions that have no support for setting timeouts
+var SampleCodeG114 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func main() {
+	err := http.ListenAndServe(":8080", nil)
+	log.Fatal(err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net/http"
+)
+
+func main() {
+	err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
+	log.Fatal(err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("tcp", ":8080")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+	err = http.Serve(l, nil)
+	log.Fatal(err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("tcp", ":8443")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer l.Close()
+	err = http.ServeTLS(l, nil, "cert.pem", "key.pem")
+	log.Fatal(err)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g115_samples.go b/testutils/g115_samples.go
new file mode 100644
index 0000000000..c6c79ac5f4
--- /dev/null
+++ b/testutils/g115_samples.go
@@ -0,0 +1,865 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var SampleCodeG115 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint32 = math.MaxUint32
+    b := int32(a)
+    fmt.Println(b)
+}
+	`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint16 = math.MaxUint16
+    b := int32(a)
+    fmt.Println(b)
+}
+	`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint32 = math.MaxUint32
+    b := uint16(a)
+    fmt.Println(b)
+}
+	`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a int32 = math.MaxInt32
+    b := int16(a)
+    fmt.Println(b)
+}
+	`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a int16 = math.MaxInt16
+    b := int32(a)
+    fmt.Println(b)
+}
+	`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a int32 = math.MaxInt32
+    b := uint32(a)
+    fmt.Println(b)
+}
+	`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint = math.MaxUint
+    b := int16(a)
+    fmt.Println(b)
+}
+	`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint = math.MaxUint
+    b := int64(a)
+    fmt.Println(b)
+}
+	`}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+	var a uint = math.MaxUint
+	// #nosec G115
+	b := int64(a)
+	fmt.Println(b)
+}
+		`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+func main() {
+    var a uint = math.MaxUint
+	// #nosec G115
+    b := int64(a)
+    fmt.Println(b)
+}
+	`, `
+package main
+
+func ExampleFunction() {
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type Uint uint
+
+func main() {
+    var a uint8 = math.MaxUint8
+    b := Uint(a)
+    fmt.Println(b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+    var a byte = '\xff'
+    b := int64(a)
+    fmt.Println(b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+    var a int8 = -1
+    b := int64(a)
+    fmt.Println(b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+	"math"
+)
+
+type CustomType int
+
+func main() {
+    var a uint = math.MaxUint
+    b := CustomType(a)
+    fmt.Println(b)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+    a := []int{1,2,3}
+    b := uint32(len(a))
+    fmt.Println(b)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+)
+
+func main() {
+        a := "A\xFF"
+        b := int64(a[0])
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+)
+
+func main() {
+        var a uint8 = 13
+        b := int(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+)
+
+func main() {
+        const a int64 = 13
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < math.MinInt32 {
+            panic("out of range")
+        }
+        if a > math.MaxInt32 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < math.MinInt32 && a > math.MaxInt32 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < math.MinInt32 || a > math.MaxInt32 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < math.MinInt64 || a > math.MaxInt32 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+)
+
+func main() {
+        var a int32 = math.MaxInt32
+        if a < math.MinInt32 && a > math.MaxInt32 {
+            panic("out of range")
+        }
+        var b int64 = int64(a) * 2
+        c := int32(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "strconv"
+)
+
+func main() {
+        var a string = "13"
+        b, _ := strconv.ParseInt(a, 10, 32)
+        c := int32(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "strconv"
+)
+
+func main() {
+        var a string = "13"
+        b, _ := strconv.ParseUint(a, 10, 8)
+        c := uint8(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "strconv"
+)
+
+func main() {
+        var a string = "13"
+        b, _ := strconv.ParseUint(a, 10, 16)
+        c := int(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "strconv"
+)
+
+func main() {
+        var a string = "13"
+        b, _ := strconv.ParseUint(a, 10, 31)
+        c := int32(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "strconv"
+)
+
+func main() {
+        var a string = "13"
+        b, _ := strconv.ParseInt(a, 10, 8)
+        c := uint8(b)
+        fmt.Printf("%d\n", c)
+}
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < 0 {
+            panic("out of range")
+        }
+        if a > math.MaxUint32 {
+            panic("out of range")
+        }
+        b := uint32(a)
+        fmt.Printf("%d\n", b)
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a < 0 {
+            panic("out of range")
+        }
+        b := uint32(a)
+        fmt.Printf("%d\n", b)
+}
+`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "math"
+)
+
+func foo(x int) uint32 {
+        if x < 0 {
+            return 0
+        }
+        if x > math.MaxUint32 {
+            return math.MaxUint32
+        }
+        return uint32(x)
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "math"
+)
+
+func foo(items []string) uint32 {
+        x := len(items)
+        if x > math.MaxUint32 {
+            return math.MaxUint32
+        }
+        return uint32(x)
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "math"
+)
+
+func foo(items []string) uint32 {
+        x := cap(items)
+        if x > math.MaxUint32 {
+            return math.MaxUint32
+        }
+        return uint32(x)
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "math"
+)
+
+func foo(items []string) uint32 {
+        x := len(items)
+        if x < math.MaxUint32 {
+            return uint32(x)
+        }
+        return math.MaxUint32
+}
+`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a >= math.MinInt32 && a <= math.MaxInt32 {
+            b := int32(a)
+            fmt.Printf("%d\n", b)
+        }
+        panic("out of range")
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a >= math.MinInt32 && a <= math.MaxInt32 {
+            b := int32(a)
+            fmt.Printf("%d\n", b)
+        }
+        panic("out of range")
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if !(a >= math.MinInt32) && a > math.MaxInt32 {
+            b := int32(a)
+            fmt.Printf("%d\n", b)
+        }
+        panic("out of range")
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if !(a >= math.MinInt32) || a > math.MaxInt32 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if math.MinInt32 <= a && math.MaxInt32 >= a {
+            b := int32(a)
+            fmt.Printf("%d\n", b)
+        }
+        panic("out of range")
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a == 3 || a == 4 {
+            b := int32(a)
+            fmt.Printf("%d\n", b)
+        }
+        panic("out of range")
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import (
+        "fmt"
+        "math/rand"
+)
+
+func main() {
+        a := rand.Int63()
+        if a != 3 || a != 4 {
+            panic("out of range")
+        }
+        b := int32(a)
+        fmt.Printf("%d\n", b)
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+package main
+
+import "unsafe"
+
+func main() {
+	i := uintptr(123)
+	p := unsafe.Pointer(i)
+	_ = p
+}
+	`,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+            "fmt"
+            "math/rand"
+        )
+
+        func main() {
+            a := rand.Int63()
+            if a >= 0 {
+                panic("no positivity allowed")
+            }
+            b := uint64(-a)
+            fmt.Printf("%d\n", b)
+        }
+            `,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+            "fmt"
+            "math"
+        )
+
+        type CustomStruct struct {
+            Value int
+        }
+
+        func main() {
+            results := CustomStruct{Value: 0}
+            if results.Value < math.MinInt32 || results.Value > math.MaxInt32 {
+                panic("value out of range for int32")
+            }
+            convertedValue := int32(results.Value)
+
+            fmt.Println(convertedValue)
+        }
+        `,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+            "fmt"
+            "math"
+        )
+
+        type CustomStruct struct {
+            Value int
+        }
+
+        func main() {
+            results := CustomStruct{Value: 0}
+            if results.Value >= math.MinInt32 && results.Value <= math.MaxInt32 {
+                convertedValue := int32(results.Value)
+                fmt.Println(convertedValue)
+            }
+            panic("value out of range for int32")
+        }
+        `,
+	}, 0, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+            "fmt"
+            "math"
+        )
+
+        type CustomStruct struct {
+            Value int
+        }
+
+        func main() {
+            results := CustomStruct{Value: 0}
+            if results.Value < math.MinInt32 || results.Value > math.MaxInt32 {
+                panic("value out of range for int32")
+            }
+            // checked value is decremented by 1 before conversion which is unsafe
+            convertedValue := int32(results.Value-1)
+
+            fmt.Println(convertedValue)
+        }
+        `,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+                "fmt"
+                "math"
+                "math/rand"
+        )
+
+        func main() {
+            a := rand.Int63()
+            if a < math.MinInt32 || a > math.MaxInt32 {
+                panic("out of range")
+            }
+            // checked value is incremented by 1 before conversion which is unsafe
+            b := int32(a+1)
+            fmt.Printf("%d\n", b)
+        }
+	`,
+	}, 1, gosec.NewConfig()},
+	{[]string{
+		`
+        package main
+
+        import (
+                "fmt"
+                "strconv"
+        )
+
+        func main() {
+            a, err := strconv.ParseUint("100", 10, 16)
+            if err != nil {
+              panic("parse error")
+            }
+            b := uint16(a)
+            fmt.Printf("%d\n", b)
+        }
+	`,
+	}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g201_samples.go b/testutils/g201_samples.go
new file mode 100644
index 0000000000..b2811e7cbf
--- /dev/null
+++ b/testutils/g201_samples.go
@@ -0,0 +1,431 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG201 - SQL injection via format string
+var SampleCodeG201 = []CodeSample{
+	{[]string{`
+// Format string without proper quoting
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM foo where name = '%s'", os.Args[1])
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string without proper quoting case insensitive
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string without proper quoting with context
+package main
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
+	rows, err := db.QueryContext(context.Background(), q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string without proper quoting with transaction
+package main
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	tx, err := db.Begin()
+	if err != nil {
+		panic(err)
+	}
+	defer tx.Rollback()
+	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
+	rows, err := tx.QueryContext(context.Background(), q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+	if err := tx.Commit(); err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string without proper quoting with connection
+package main
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	conn, err := db.Conn(context.Background())
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
+	rows, err := conn.QueryContext(context.Background(), q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+	if err := conn.Close(); err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string false positive, safe string spec.
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM foo where id = %d", os.Args[1])
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Format string false positive
+package main
+
+import (
+		"database/sql"
+)
+
+const staticQuery = "SELECT * FROM foo WHERE age < 32"
+
+func main(){
+		db, err := sql.Open("sqlite3", ":memory:")
+		if err != nil {
+			panic(err)
+		}
+		rows, err := db.Query(staticQuery)
+		if err != nil {
+			panic(err)
+		}
+		defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Format string false positive, quoted formatter argument.
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+	"github.com/lib/pq"
+)
+
+func main(){
+	db, err := sql.Open("postgres", "localhost")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM %s where id = 1", pq.QuoteIdentifier(os.Args[1]))
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// false positive
+package main
+
+import (
+	"database/sql"
+	"fmt"
+)
+
+const Table = "foo"
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM %s where id = 1", Table)
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+import (
+	"fmt"
+)
+
+func main(){
+	fmt.Sprintln()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Format string with \n\r
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM foo where\n name = '%s'", os.Args[1])
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Format string with \n\r
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT * FROM foo where\nname = '%s'", os.Args[1])
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// SQLI by db.Query(some).Scan(&other)
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main() {
+	var name string
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT name FROM users where id = '%s'", os.Args[1])
+	row := db.QueryRow(q)
+	err = row.Scan(&name)
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+// SQLI by db.Query(some).Scan(&other)
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main() {
+	var name string
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT name FROM users where id = '%s'", os.Args[1])
+	err = db.QueryRow(q).Scan(&name)
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+// SQLI by db.Prepare(some)
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+	"os"
+)
+
+const Table = "foo"
+
+func main() {
+	var album string
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT name FROM users where '%s' = ?", os.Args[1])
+	stmt, err := db.Prepare(q)
+	if err != nil {
+		log.Fatal(err)
+	}
+	stmt.QueryRow(fmt.Sprintf("%s", os.Args[2])).Scan(&album)
+	if err != nil {
+		if err == sql.ErrNoRows {
+			log.Fatal(err)
+		}
+	}
+	defer stmt.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// SQLI by db.PrepareContext(some)
+package main
+
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"log"
+	"os"
+)
+
+const Table = "foo"
+
+func main() {
+	var album string
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	q := fmt.Sprintf("SELECT name FROM users where '%s' = ?", os.Args[1])
+	stmt, err := db.PrepareContext(context.Background(), q)
+	if err != nil {
+		log.Fatal(err)
+	}
+	stmt.QueryRow(fmt.Sprintf("%s", os.Args[2])).Scan(&album)
+	if err != nil {
+		if err == sql.ErrNoRows {
+			log.Fatal(err)
+		}
+	}
+	defer stmt.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// false positive
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+	"os"
+)
+
+const Table = "foo"
+
+func main() {
+	var album string
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	stmt, err := db.Prepare("SELECT * FROM album WHERE id = ?")
+	if err != nil {
+		log.Fatal(err)
+	}
+	stmt.QueryRow(fmt.Sprintf("%s", os.Args[1])).Scan(&album)
+	if err != nil {
+		if err == sql.ErrNoRows {
+			log.Fatal(err)
+		}
+	}
+	defer stmt.Close()
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g202_samples.go b/testutils/g202_samples.go
new file mode 100644
index 0000000000..711d59d044
--- /dev/null
+++ b/testutils/g202_samples.go
@@ -0,0 +1,311 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG202 - SQL query string building via string concatenation
+var SampleCodeG202 = []CodeSample{
+	{[]string{`
+// infixed concatenation
+package main
+
+import (
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+
+  q := "INSERT INTO foo (name) VALUES ('" + os.Args[0] + "')"
+	rows, err := db.Query(q)
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	rows, err := db.Query("SELECT * FROM foo WHERE name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// case insensitive match
+package main
+
+import (
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	rows, err := db.Query("select * from foo where name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// context match
+package main
+
+import (
+    "context"
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	rows, err := db.QueryContext(context.Background(), "select * from foo where name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// DB transaction check
+package main
+
+import (
+    "context"
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	tx, err := db.Begin()
+	if err != nil {
+		panic(err)
+	}
+	defer tx.Rollback()
+	rows, err := tx.QueryContext(context.Background(), "select * from foo where name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+	if err := tx.Commit(); err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// DB connection check
+package main
+
+import (
+    "context"
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	conn, err := db.Conn(context.Background())
+	if err != nil {
+		panic(err)
+	}
+	rows, err := conn.QueryContext(context.Background(), "select * from foo where name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+	if err := conn.Close(); err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// multiple string concatenation
+package main
+
+import (
+	"database/sql"
+	"os"
+)
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	rows, err := db.Query("SELECT * FROM foo" + "WHERE name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// false positive
+package main
+
+import (
+	"database/sql"
+)
+
+var staticQuery = "SELECT * FROM foo WHERE age < "
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	rows, err := db.Query(staticQuery + "32")
+	if err != nil {
+		panic(err)
+	}
+	defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"database/sql"
+)
+
+const age = "32"
+
+var staticQuery = "SELECT * FROM foo WHERE age < "
+
+func main(){
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+			panic(err)
+	}
+	rows, err := db.Query(staticQuery + age)
+	if err != nil {
+			panic(err)
+	}
+	defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+const gender = "M"
+`, `
+package main
+
+import (
+		"database/sql"
+)
+
+const age = "32"
+
+var staticQuery = "SELECT * FROM foo WHERE age < "
+
+func main(){
+		db, err := sql.Open("sqlite3", ":memory:")
+		if err != nil {
+				panic(err)
+		}
+		rows, err := db.Query("SELECT * FROM foo WHERE gender = " + gender)
+		if err != nil {
+				panic(err)
+		}
+		defer rows.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// ExecContext match
+package main
+
+import (
+	"context"
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main() {
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	result, err := db.ExecContext(context.Background(), "select * from foo where name = "+os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(result)
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Exec match
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"os"
+)
+
+func main() {
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	result, err := db.Exec("select * from foo where name = " + os.Args[1])
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(result)
+}`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"database/sql"
+	"fmt"
+)
+const gender = "M"
+const age = "32"
+
+var staticQuery = "SELECT * FROM foo WHERE age < "
+
+func main() {
+	db, err := sql.Open("sqlite3", ":memory:")
+	if err != nil {
+		panic(err)
+	}
+	result, err := db.Exec("SELECT * FROM foo WHERE gender = " + gender)
+	if err != nil {
+		panic(err)
+	}
+	fmt.Println(result)
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g203_samples.go b/testutils/g203_samples.go
new file mode 100644
index 0000000000..61c12be5cc
--- /dev/null
+++ b/testutils/g203_samples.go
@@ -0,0 +1,90 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG203 - Template checks
+var SampleCodeG203 = []CodeSample{
+	{[]string{`
+// We assume that hardcoded template strings are safe as the programmer would
+// need to be explicitly shooting themselves in the foot (as below)
+package main
+
+import (
+	"html/template"
+	"os"
+)
+
+const tmpl = ""
+
+func main() {
+	t := template.Must(template.New("ex").Parse(tmpl))
+	v := map[string]interface{}{
+		"Title":    "Test <b>World</b>",
+		"Body":     template.HTML("<script>alert(1)</script>"),
+	}
+	t.Execute(os.Stdout, v)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Using a variable to initialize could potentially be dangerous. Under the
+// current model this will likely produce some false positives.
+package main
+
+import (
+	"html/template"
+	"os"
+)
+
+const tmpl = ""
+
+func main() {
+	a := "something from another place"
+	t := template.Must(template.New("ex").Parse(tmpl))
+	v := map[string]interface{}{
+		"Title":    "Test <b>World</b>",
+		"Body":     template.HTML(a),
+	}
+	t.Execute(os.Stdout, v)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"html/template"
+	"os"
+)
+
+const tmpl = ""
+
+func main() {
+	a := "something from another place"
+	t := template.Must(template.New("ex").Parse(tmpl))
+	v := map[string]interface{}{
+		"Title":    "Test <b>World</b>",
+		"Body":     template.JS(a),
+	}
+	t.Execute(os.Stdout, v)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"html/template"
+	"os"
+)
+
+const tmpl = ""
+
+func main() {
+	a := "something from another place"
+	t := template.Must(template.New("ex").Parse(tmpl))
+	v := map[string]interface{}{
+		"Title":    "Test <b>World</b>",
+		"Body":     template.URL(a),
+	}
+	t.Execute(os.Stdout, v)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g204_samples.go b/testutils/g204_samples.go
new file mode 100644
index 0000000000..746c95c8d0
--- /dev/null
+++ b/testutils/g204_samples.go
@@ -0,0 +1,245 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG204 - Subprocess auditing
+var SampleCodeG204 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"log"
+	"os/exec"
+	"context"
+)
+
+func main() {
+	err := exec.CommandContext(context.Background(), "git", "rev-parse", "--show-toplevel").Run()
+ 	if err != nil {
+		log.Fatal(err)
+	}
+  	log.Printf("Command finished with error: %v", err)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Calling any function which starts a new process with using
+// command line arguments as it's arguments is considered dangerous
+package main
+
+import (
+	"context"
+	"log"
+	"os"
+	"os/exec"
+)
+
+func main() {
+	err := exec.CommandContext(context.Background(), os.Args[0], "5").Run()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Command finished with error: %v", err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Initializing a local variable using a environmental
+// variable is consider as a dangerous user input
+package main
+
+import (
+	"log"
+	"os"
+	"os/exec"
+)
+
+func main() {
+	run := "sleep" + os.Getenv("SOMETHING")
+	cmd := exec.Command(run, "5")
+	err := cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+	log.Printf("Command finished with error: %v", err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// gosec doesn't have enough context to decide that the
+// command argument of the RunCmd function is hardcoded string
+// and that's why it's better to warn the user so he can audit it
+package main
+
+import (
+	"log"
+	"os/exec"
+)
+
+func RunCmd(command string) {
+	cmd := exec.Command(command, "5")
+	err := cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+}
+
+func main() {
+	RunCmd("sleep")
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"os/exec"
+)
+
+func RunCmd(a string, c string) {
+	cmd := exec.Command(c)
+	err := cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+
+	cmd = exec.Command(a)
+	err = cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+}
+
+func main() {
+	RunCmd("ll", "ls")
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// syscall.Exec function called with hardcoded arguments
+// shouldn't be consider as a command injection
+package main
+
+import (
+	"fmt"
+	"syscall"
+)
+
+func main() {
+	err := syscall.Exec("/bin/cat", []string{"/etc/passwd"}, nil)
+	if err != nil {
+		fmt.Printf("Error: %v\n", err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{
+		[]string{`
+package main
+
+import (
+	"fmt"
+	"syscall"
+)
+
+func RunCmd(command string) {
+	_, err := syscall.ForkExec(command, []string{}, nil)
+	if err != nil {
+	    fmt.Printf("Error: %v\n", err)
+	}
+}
+
+func main() {
+	RunCmd("sleep")
+}
+`}, 1, gosec.NewConfig(),
+	},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"syscall"
+)
+
+func RunCmd(command string) {
+	_, _, err := syscall.StartProcess(command, []string{}, nil)
+	if err != nil {
+	    fmt.Printf("Error: %v\n", err)
+	}
+}
+
+func main() {
+	RunCmd("sleep")
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// starting a process with a variable as an argument
+// even if not constant is not considered as dangerous
+// because it has hardcoded value
+package main
+
+import (
+	"log"
+	"os/exec"
+)
+
+func main() {
+	run := "sleep"
+	cmd := exec.Command(run, "5")
+	err := cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+	log.Printf("Command finished with error: %v", err)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// exec.Command from supplemental package sys/execabs
+// using variable arguments
+package main
+
+import (
+	"context"
+	"log"
+	"os"
+	exec "golang.org/x/sys/execabs"
+)
+
+func main() {
+	err := exec.CommandContext(context.Background(), os.Args[0], "5").Run()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Command finished with error: %v", err)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Initializing a local variable using a environmental
+// variable is consider as a dangerous user input
+package main
+
+import (
+	"log"
+	"os"
+	"os/exec"
+)
+
+func main() {
+	var run = "sleep" + os.Getenv("SOMETHING")
+	cmd := exec.Command(run, "5")
+	err := cmd.Start()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Printf("Waiting for command to finish...")
+	err = cmd.Wait()
+	log.Printf("Command finished with error: %v", err)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g301_samples.go b/testutils/g301_samples.go
new file mode 100644
index 0000000000..8a7aeaacc3
--- /dev/null
+++ b/testutils/g301_samples.go
@@ -0,0 +1,55 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG301 - mkdir permission check
+var SampleCodeG301 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	err := os.Mkdir("/tmp/mydir", 0777)
+	if err != nil {
+		fmt.Println("Error when creating a directory!")
+		return
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	err := os.MkdirAll("/tmp/mydir", 0777)
+	if err != nil {
+		fmt.Println("Error when creating a directory!")
+		return
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	err := os.Mkdir("/tmp/mydir", 0600)
+	if err != nil {
+		fmt.Println("Error when creating a directory!")
+		return
+	}
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g302_samples.go b/testutils/g302_samples.go
new file mode 100644
index 0000000000..3cc9fde577
--- /dev/null
+++ b/testutils/g302_samples.go
@@ -0,0 +1,71 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG302 - file create / chmod permissions check
+var SampleCodeG302 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	err := os.Chmod("/tmp/somefile", 0777)
+	if err != nil {
+		fmt.Println("Error when changing file permissions!")
+		return
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	_, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0666)
+	if err != nil {
+		fmt.Println("Error opening a file!")
+		return
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	err := os.Chmod("/tmp/mydir", 0400)
+	if err != nil {
+		fmt.Println("Error")
+		return
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	_, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0600)
+	if err != nil {
+		fmt.Println("Error opening a file!")
+		return
+	}
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g303_samples.go b/testutils/g303_samples.go
new file mode 100644
index 0000000000..bdc96091ea
--- /dev/null
+++ b/testutils/g303_samples.go
@@ -0,0 +1,59 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG303 - bad tempfile permissions & hardcoded shared path
+var SampleCodeG303 = []CodeSample{
+	{[]string{`
+package samples
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+)
+
+func main() {
+	err := ioutil.WriteFile("/tmp/demo2", []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	f, err := os.Create("/tmp/demo2")
+	if err != nil {
+		fmt.Println("Error while writing!")
+	} else if err = f.Close(); err != nil {
+		fmt.Println("Error while closing!")
+	}
+	err = os.WriteFile("/tmp/demo2", []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile("/usr/tmp/demo2", []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile("/tmp/" + "demo2", []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile(os.TempDir() + "/demo2", []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile(path.Join("/var/tmp", "demo2"), []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile(path.Join(os.TempDir(), "demo2"), []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+	err = os.WriteFile(filepath.Join(os.TempDir(), "demo2"), []byte("This is some data"), 0644)
+	if err != nil {
+		fmt.Println("Error while writing!")
+	}
+}
+`}, 9, gosec.NewConfig()},
+}
diff --git a/testutils/g304_samples.go b/testutils/g304_samples.go
new file mode 100644
index 0000000000..7ef7139407
--- /dev/null
+++ b/testutils/g304_samples.go
@@ -0,0 +1,305 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG304 - potential file inclusion vulnerability
+var SampleCodeG304 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+"os"
+"io/ioutil"
+"log"
+)
+
+func main() {
+	f := os.Getenv("tainted_file")
+	body, err := ioutil.ReadFile(f)
+	if err != nil {
+	log.Printf("Error: %v\n", err)
+	}
+	log.Print(body)
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+"os"
+"log"
+)
+
+func main() {
+	f := os.Getenv("tainted_file")
+	body, err := os.ReadFile(f)
+	if err != nil {
+	log.Printf("Error: %v\n", err)
+	}
+	log.Print(body)
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+)
+
+func main() {
+	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
+  		title := r.URL.Query().Get("title")
+		f, err := os.Open(title)
+		if err != nil {
+			fmt.Printf("Error: %v\n", err)
+		}
+		body := make([]byte, 5)
+		if _, err = f.Read(body); err != nil {
+			fmt.Printf("Error: %v\n", err)
+		}
+		fmt.Fprintf(w, "%s", body)
+	})
+	log.Fatal(http.ListenAndServe(":3000", nil))
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+)
+
+func main() {
+	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
+  		title := r.URL.Query().Get("title")
+		f, err := os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755)
+		if err != nil {
+			fmt.Printf("Error: %v\n", err)
+		}
+		body := make([]byte, 5)
+		if _, err = f.Read(body); err != nil {
+			fmt.Printf("Error: %v\n", err)
+		}
+		fmt.Fprintf(w, "%s", body)
+	})
+	log.Fatal(http.ListenAndServe(":3000", nil))
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"os"
+	"io/ioutil"
+)
+
+	func main() {
+		f2 := os.Getenv("tainted_file2")
+		body, err := ioutil.ReadFile("/tmp/" + f2)
+		if err != nil {
+		log.Printf("Error: %v\n", err)
+	  }
+		log.Print(body)
+ }
+ `}, 1, gosec.NewConfig()},
+	{[]string{`
+ package main
+
+ import (
+	 "bufio"
+	 "fmt"
+	 "os"
+	 "path/filepath"
+ )
+
+func main() {
+	reader := bufio.NewReader(os.Stdin)
+  fmt.Print("Please enter file to read: ")
+	file, _ := reader.ReadString('\n')
+	file = file[:len(file)-1]
+	f, err := os.Open(filepath.Join("/tmp/service/", file))
+	if err != nil {
+		fmt.Printf("Error: %v\n", err)
+	}
+	contents := make([]byte, 15)
+  if _, err = f.Read(contents); err != nil {
+		fmt.Printf("Error: %v\n", err)
+	}
+  fmt.Println(string(contents))
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"log"
+	"os"
+	"io/ioutil"
+	"path/filepath"
+)
+
+func main() {
+	dir := os.Getenv("server_root")
+	f3 := os.Getenv("tainted_file3")
+	// edge case where both a binary expression and file Join are used.
+	body, err := ioutil.ReadFile(filepath.Join("/var/"+dir, f3))
+	if err != nil {
+		log.Printf("Error: %v\n", err)
+	}
+	log.Print(body)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+    "os"
+    "path/filepath"
+)
+
+func main() {
+    repoFile := "path_of_file"
+    cleanRepoFile := filepath.Clean(repoFile)
+    _, err := os.OpenFile(cleanRepoFile, os.O_RDONLY, 0600)
+    if err != nil {
+        panic(err)
+    }
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+    "os"
+    "path/filepath"
+)
+
+func openFile(filePath string) {
+	_, err := os.OpenFile(filepath.Clean(filePath), os.O_RDONLY, 0600)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func main() {
+    repoFile := "path_of_file"
+	openFile(repoFile)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+    "os"
+    "path/filepath"
+)
+
+func openFile(dir string, filePath string) {
+	fp := filepath.Join(dir, filePath)
+	fp = filepath.Clean(fp)
+	_, err := os.OpenFile(fp, os.O_RDONLY, 0600)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func main() {
+    repoFile := "path_of_file"
+	dir := "path_of_dir"
+	openFile(dir, repoFile)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+    "os"
+    "path/filepath"
+)
+
+func main() {
+    repoFile := "path_of_file"
+	relFile, err := filepath.Rel("./", repoFile)
+	if err != nil {
+		panic(err)
+	}
+    _, err = os.OpenFile(relFile, os.O_RDONLY, 0600)
+    if err != nil {
+        panic(err)
+    }
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"io"
+	"os"
+)
+
+func createFile(file string) *os.File {
+	f, err := os.Create(file)
+	if err != nil {
+		panic(err)
+	}
+	return f
+}
+
+func main() {
+	s, err := os.Open("src")
+	if err != nil {
+		panic(err)
+	}
+	defer s.Close()
+
+	d := createFile("dst")
+	defer d.Close()
+
+	_, err = io.Copy(d, s)
+	if  err != nil {
+		panic(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"path/filepath"
+)
+
+type foo struct {
+}
+
+func (f *foo) doSomething(silly string) error {
+	whoCares, err := filepath.Rel(THEWD, silly)
+	if err != nil {
+		return err
+	}
+	fmt.Printf("%s", whoCares)
+	return nil
+}
+
+func main() {
+	f := &foo{}
+
+	if err := f.doSomething("irrelevant"); err != nil {
+		panic(err)
+	}
+}
+`, `
+package main
+
+var THEWD string
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g305_samples.go b/testutils/g305_samples.go
new file mode 100644
index 0000000000..784100a83e
--- /dev/null
+++ b/testutils/g305_samples.go
@@ -0,0 +1,176 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG305 - File path traversal when extracting zip/tar archives
+var SampleCodeG305 = []CodeSample{
+	{[]string{`
+package unzip
+
+import (
+	"archive/zip"
+	"io"
+	"os"
+	"path/filepath"
+)
+
+func unzip(archive, target string) error {
+	reader, err := zip.OpenReader(archive)
+	if err != nil {
+		return err
+	}
+
+	if err := os.MkdirAll(target, 0750); err != nil {
+		return err
+	}
+
+	for _, file := range reader.File {
+		path := filepath.Join(target, file.Name)
+		if file.FileInfo().IsDir() {
+			os.MkdirAll(path, file.Mode()) //#nosec
+			continue
+		}
+
+		fileReader, err := file.Open()
+		if err != nil {
+			return err
+		}
+		defer fileReader.Close()
+
+		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
+		if err != nil {
+			return err
+		}
+		defer targetFile.Close()
+
+		if _, err := io.Copy(targetFile, fileReader); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package unzip
+
+import (
+	"archive/zip"
+	"io"
+	"os"
+	"path/filepath"
+)
+
+func unzip(archive, target string) error {
+	reader, err := zip.OpenReader(archive)
+	if err != nil {
+		return err
+	}
+
+	if err := os.MkdirAll(target, 0750); err != nil {
+		return err
+	}
+
+	for _, file := range reader.File {
+                archiveFile := file.Name
+		path := filepath.Join(target, archiveFile)
+		if file.FileInfo().IsDir() {
+			os.MkdirAll(path, file.Mode()) //#nosec
+			continue
+		}
+
+		fileReader, err := file.Open()
+		if err != nil {
+			return err
+		}
+		defer fileReader.Close()
+
+		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
+		if err != nil {
+			return err
+		}
+		defer targetFile.Close()
+
+		if _, err := io.Copy(targetFile, fileReader); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package zip
+
+import (
+    "archive/zip"
+    "io"
+    "os"
+    "path"
+)
+
+func extractFile(f *zip.File, destPath string) error {
+    filePath := path.Join(destPath, f.Name)
+    os.MkdirAll(path.Dir(filePath), os.ModePerm)
+
+    rc, err := f.Open()
+    if err != nil {
+        return err
+    }
+    defer rc.Close()
+
+    fw, err := os.Create(filePath)
+    if err != nil {
+        return err
+    }
+    defer fw.Close()
+
+    if _, err = io.Copy(fw, rc); err != nil {
+        return err
+    }
+
+    if f.FileInfo().Mode()&os.ModeSymlink != 0 {
+        return nil
+    }
+
+    if err = os.Chtimes(filePath, f.ModTime(), f.ModTime()); err != nil {
+        return err
+    }
+    return os.Chmod(filePath, f.FileInfo().Mode())
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package tz
+
+import (
+    "archive/tar"
+    "io"
+    "os"
+    "path"
+)
+
+func extractFile(f *tar.Header, tr *tar.Reader, destPath string) error {
+    filePath := path.Join(destPath, f.Name)
+    os.MkdirAll(path.Dir(filePath), os.ModePerm)
+
+    fw, err := os.Create(filePath)
+    if err != nil {
+        return err
+    }
+    defer fw.Close()
+
+    if _, err = io.Copy(fw, tr); err != nil {
+        return err
+    }
+
+    if f.FileInfo().Mode()&os.ModeSymlink != 0 {
+        return nil
+    }
+
+    if err = os.Chtimes(filePath, f.FileInfo().ModTime(), f.FileInfo().ModTime()); err != nil {
+        return err
+    }
+    return os.Chmod(filePath, f.FileInfo().Mode())
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g306_samples.go b/testutils/g306_samples.go
new file mode 100644
index 0000000000..0f1e8c9ced
--- /dev/null
+++ b/testutils/g306_samples.go
@@ -0,0 +1,75 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG306 - Poor permissions for WriteFile
+var SampleCodeG306 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"io/ioutil"
+	"os"
+)
+
+func check(e error) {
+	if e != nil {
+		panic(e)
+	}
+}
+
+func main() {
+	d1 := []byte("hello\ngo\n")
+	err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
+	check(err)
+
+	allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
+	check(allowed)
+
+	f, err := os.Create("/tmp/dat2")
+	check(err)
+
+	defer f.Close()
+
+	d2 := []byte{115, 111, 109, 101, 10}
+	n2, err := f.Write(d2)
+
+	defer check(err)
+	fmt.Printf("wrote %d bytes\n", n2)
+
+	n3, err := f.WriteString("writes\n")
+	fmt.Printf("wrote %d bytes\n", n3)
+
+	f.Sync()
+
+	w := bufio.NewWriter(f)
+	n4, err := w.WriteString("buffered\n")
+	fmt.Printf("wrote %d bytes\n", n4)
+
+	w.Flush()
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"io/ioutil"
+	"os"
+)
+
+func check(e error) {
+	if e != nil {
+		panic(e)
+	}
+}
+
+func main() {
+	content := []byte("hello\ngo\n")
+	err := ioutil.WriteFile("/tmp/dat1", content, os.ModePerm)
+	check(err)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g307_samples.go b/testutils/g307_samples.go
new file mode 100644
index 0000000000..aa4b8f4226
--- /dev/null
+++ b/testutils/g307_samples.go
@@ -0,0 +1,47 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG307 - Poor permissions for os.Create
+var SampleCodeG307 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func check(e error) {
+	if e != nil {
+		panic(e)
+	}
+}
+
+func main() {
+		f, err := os.Create("/tmp/dat2")
+	check(err)
+	defer f.Close()
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func check(e error) {
+	if e != nil {
+		panic(e)
+	}
+}
+
+func main() {
+		f, err := os.Create("/tmp/dat2")
+	check(err)
+	defer f.Close()
+}
+`}, 1, gosec.Config{"G307": "0o600"}},
+}
diff --git a/testutils/g401_samples.go b/testutils/g401_samples.go
new file mode 100644
index 0000000000..90a22c4018
--- /dev/null
+++ b/testutils/g401_samples.go
@@ -0,0 +1,69 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG401 - Use of weak crypto hash MD5
+	SampleCodeG401 = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"crypto/md5"
+	"fmt"
+	"io"
+	"log"
+	"os"
+)
+
+func main() {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+
+	defer func() {
+	  err := f.Close()
+	  if err != nil {
+		 log.Printf("error closing the file: %s", err)
+	  }
+	}()
+
+	h := md5.New()
+	if _, err := io.Copy(h, f); err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("%x", h.Sum(nil))
+}
+`}, 1, gosec.NewConfig()},
+	}
+
+	// SampleCodeG401b - Use of weak crypto hash SHA1
+	SampleCodeG401b = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"crypto/sha1"
+	"fmt"
+	"io"
+	"log"
+	"os"
+)
+func main() {
+	f, err := os.Open("file.txt")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+
+	h := sha1.New()
+	if _, err := io.Copy(h, f); err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf("%x", h.Sum(nil))
+}
+`}, 1, gosec.NewConfig()},
+	}
+)
diff --git a/testutils/g402_samples.go b/testutils/g402_samples.go
new file mode 100644
index 0000000000..8fd01dd5a8
--- /dev/null
+++ b/testutils/g402_samples.go
@@ -0,0 +1,296 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG402 - TLS settings
+var SampleCodeG402 = []CodeSample{
+	{[]string{`
+// InsecureSkipVerify
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+	}
+
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// InsecureSkipVerify from variable
+package main
+
+import (
+	"crypto/tls"
+)
+
+func main() {
+	var conf tls.Config
+	conf.InsecureSkipVerify = true
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{MinVersion: 0},
+	}
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+)
+
+func CaseNotError() *tls.Config {
+	var v uint16 = tls.VersionTLS13
+
+	return &tls.Config{
+		MinVersion: v,
+	}
+}
+
+func main() {
+    a := CaseNotError()
+	fmt.Printf("Debug: %v\n", a.MinVersion)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+)
+
+func CaseNotError() *tls.Config {
+	return &tls.Config{
+		MinVersion: tls.VersionTLS13,
+	}
+}
+
+func main() {
+    a := CaseNotError()
+	fmt.Printf("Debug: %v\n", a.MinVersion)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+import (
+	"crypto/tls"
+	"fmt"
+)
+
+func CaseError() *tls.Config {
+	var v = &tls.Config{
+		MinVersion: 0,
+	}
+	return v
+}
+
+func main() {
+    a := CaseError()
+	fmt.Printf("Debug: %v\n", a.MinVersion)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+)
+
+func CaseError() *tls.Config {
+	var v = &tls.Config{
+		MinVersion: getVersion(),
+	}
+	return v
+}
+
+func getVersion() uint16 {
+	return tls.VersionTLS12
+}
+
+func main() {
+    a := CaseError()
+	fmt.Printf("Debug: %v\n", a.MinVersion)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Insecure minimum version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+var theValue uint16 = 0x0304
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{MinVersion: theValue},
+	}
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+// Insecure max version
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{MaxVersion: 0},
+	}
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// Insecure ciphersuite selection
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{
+			CipherSuites: []uint16{
+				tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
+				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			},
+		},
+	}
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+// secure max version when min version is specified
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{
+			MaxVersion: 0,
+			MinVersion: tls.VersionTLS13,
+		},
+	}
+	client := &http.Client{Transport: tr}
+	_, err := client.Get("https://go.dev/")
+	if err != nil {
+		fmt.Println(err)
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package p0
+
+import "crypto/tls"
+
+func TlsConfig0() *tls.Config {
+	var v uint16 = 0
+	return &tls.Config{MinVersion: v}
+}
+`, `
+package p0
+
+import "crypto/tls"
+
+func TlsConfig1() *tls.Config {
+   return &tls.Config{MinVersion: 0x0304}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/tls"
+	"fmt"
+)
+
+func main() {
+	cfg := tls.Config{
+		MinVersion: MinVer,
+	}
+	fmt.Println("tls min version", cfg.MinVersion)
+}
+`, `
+package main
+
+import "crypto/tls"
+
+const MinVer = tls.VersionTLS13
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/tls"
+	cryptotls "crypto/tls"
+)
+
+func main() {
+	_ = tls.Config{MinVersion: tls.VersionTLS12}
+	_ = cryptotls.Config{MinVersion: cryptotls.VersionTLS12}
+}
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/g403_samples.go b/testutils/g403_samples.go
new file mode 100644
index 0000000000..1b2d9d1cad
--- /dev/null
+++ b/testutils/g403_samples.go
@@ -0,0 +1,25 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG403 - weak key strength
+var SampleCodeG403 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"fmt"
+)
+
+func main() {
+	//Generate Private Key
+	pvk, err := rsa.GenerateKey(rand.Reader, 1024)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(pvk)
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g404_samples.go b/testutils/g404_samples.go
new file mode 100644
index 0000000000..72e103489a
--- /dev/null
+++ b/testutils/g404_samples.go
@@ -0,0 +1,187 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG404 - weak random number
+var SampleCodeG404 = []CodeSample{
+	{[]string{`
+package main
+
+import "crypto/rand"
+
+func main() {
+	good, _ := rand.Read(nil)
+	println(good)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "math/rand"
+
+func main() {
+	bad := rand.Int()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "math/rand/v2"
+
+func main() {
+	bad := rand.Int()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/rand"
+	mrand "math/rand"
+)
+
+func main() {
+	good, _ := rand.Read(nil)
+	println(good)
+	bad := mrand.Int31()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/rand"
+	mrand "math/rand/v2"
+)
+
+func main() {
+	good, _ := rand.Read(nil)
+	println(good)
+	bad := mrand.Int32()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"math/rand"
+)
+
+func main() {
+	gen := rand.New(rand.NewSource(10))
+	bad := gen.Int()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"math/rand/v2"
+)
+
+func main() {
+	gen := rand.New(rand.NewPCG(1, 2))
+	bad := gen.Int()
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"math/rand"
+)
+
+func main() {
+	bad := rand.Intn(10)
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"math/rand/v2"
+)
+
+func main() {
+	bad := rand.IntN(10)
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/rand"
+	"math/big"
+	rnd "math/rand"
+)
+
+func main() {
+	good, _ := rand.Int(rand.Reader, big.NewInt(int64(2)))
+	println(good)
+	bad := rnd.Intn(2)
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	"crypto/rand"
+	"math/big"
+	rnd "math/rand/v2"
+)
+
+func main() {
+	good, _ := rand.Int(rand.Reader, big.NewInt(int64(2)))
+	println(good)
+	bad := rnd.IntN(2)
+	println(bad)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	crand "crypto/rand"
+	"math/big"
+	"math/rand"
+	rand2 "math/rand"
+	rand3 "math/rand"
+)
+
+func main() {
+	_, _ = crand.Int(crand.Reader, big.NewInt(int64(2))) // good
+
+	_ = rand.Intn(2) // bad
+	_ = rand2.Intn(2)  // bad
+	_ = rand3.Intn(2)  // bad
+}
+`}, 3, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import (
+	crand "crypto/rand"
+	"math/big"
+	"math/rand/v2"
+	rand2 "math/rand/v2"
+	rand3 "math/rand/v2"
+)
+
+func main() {
+	_, _ = crand.Int(crand.Reader, big.NewInt(int64(2))) // good
+
+	_ = rand.IntN(2) // bad
+	_ = rand2.IntN(2)  // bad
+	_ = rand3.IntN(2)  // bad
+}
+`}, 3, gosec.NewConfig()},
+}
diff --git a/testutils/g405_samples.go b/testutils/g405_samples.go
new file mode 100644
index 0000000000..9bf0114b81
--- /dev/null
+++ b/testutils/g405_samples.go
@@ -0,0 +1,67 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG405 - Use of weak crypto encryption DES
+	SampleCodeG405 = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"crypto/des"
+	"fmt"
+)
+
+func main() {
+	// Weakness: Usage of weak encryption algorithm
+
+	c, e := des.NewCipher([]byte("mySecret"))
+
+	if e != nil {
+		panic("We have a problem: " + e.Error())
+	}
+
+	data := []byte("hello world")
+	fmt.Println("Plain", string(data))
+	c.Encrypt(data, data)
+
+	fmt.Println("Encrypted", string(data))
+	c.Decrypt(data, data)
+
+	fmt.Println("Plain Decrypted", string(data))
+}
+
+`}, 1, gosec.NewConfig()},
+	}
+
+	// SampleCodeG405b - Use of weak crypto encryption RC4
+	SampleCodeG405b = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"crypto/rc4"
+	"fmt"
+)
+
+func main() {
+	// Weakness: Usage of weak encryption algorithm
+	
+	c, _ := rc4.NewCipher([]byte("mySecret"))
+
+	data := []byte("hello world")
+	fmt.Println("Plain", string(data))
+	c.XORKeyStream(data, data)
+
+	cryptCipher2, _ := rc4.NewCipher([]byte("mySecret"))
+
+	fmt.Println("Encrypted", string(data))
+	cryptCipher2.XORKeyStream(data, data)
+
+	fmt.Println("Plain Decrypted", string(data))
+}
+
+`}, 2, gosec.NewConfig()},
+	}
+)
diff --git a/testutils/g406_samples.go b/testutils/g406_samples.go
new file mode 100644
index 0000000000..911a8777a7
--- /dev/null
+++ b/testutils/g406_samples.go
@@ -0,0 +1,45 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG406 - Use of deprecated weak crypto hash MD4
+	SampleCodeG406 = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"golang.org/x/crypto/md4"
+)
+
+func main() {
+	h := md4.New()
+	h.Write([]byte("test"))
+	fmt.Println(hex.EncodeToString(h.Sum(nil)))
+}
+`}, 1, gosec.NewConfig()},
+	}
+
+	// SampleCodeG406b - Use of deprecated weak crypto hash RIPEMD160
+	SampleCodeG406b = []CodeSample{
+		{[]string{`
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"golang.org/x/crypto/ripemd160"
+)
+
+func main() {
+	h := ripemd160.New()
+	h.Write([]byte("test"))
+	fmt.Println(hex.EncodeToString(h.Sum(nil)))
+}
+`}, 1, gosec.NewConfig()},
+	}
+)
diff --git a/testutils/g407_samples.go b/testutils/g407_samples.go
new file mode 100644
index 0000000000..ae0be56ac9
--- /dev/null
+++ b/testutils/g407_samples.go
@@ -0,0 +1,496 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG407 - Use of hardcoded nonce/IV
+var SampleCodeG407 = []CodeSample{
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesOFB := cipher.NewOFB(block, []byte("ILoveMyNonceAlot"))
+	var output = make([]byte, 16)
+	aesOFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func encrypt(nonce []byte) {
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesOFB := cipher.NewOFB(block, nonce)
+	var output = make([]byte, 16)
+	aesOFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+}
+
+func main() {
+
+	var nonce = []byte("ILoveMyNonceAlot")
+	encrypt(nonce)
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesOFB := cipher.NewOFB(block, []byte("ILoveMyNonceAlot")) // #nosec G407
+	var output = make([]byte, 16)
+	aesOFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+}
+
+`}, 0, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher( []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+
+	cipherText := aesGCM.Seal(nil, func() []byte {
+		if true {
+			return []byte("ILoveMyNonce")
+		} else {
+			return []byte("IDont'Love..")
+		}
+	}(), []byte("My secret message"), nil) // #nosec G407
+	fmt.Println(string(cipherText))
+
+	cipherText, _ = aesGCM.Open(nil, func() []byte {
+		if true {
+			return []byte("ILoveMyNonce")
+		} else {
+			return []byte("IDont'Love..")
+		}
+	}(), cipherText, nil) // #nosec G407
+
+	fmt.Println(string(cipherText))
+}
+`}, 0, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesOFB := cipher.NewOFB(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	var output = make([]byte, 16)
+	aesOFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+}`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCTR := cipher.NewCTR(block, []byte("ILoveMyNonceAlot"))
+	var output = make([]byte, 16)
+	aesCTR.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+}`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCTR := cipher.NewCTR(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	var output = make([]byte, 16)
+	aesCTR.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+
+	cipherText := aesGCM.Seal(nil, []byte("ILoveMyNonce"), []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+	cipherText, _ = aesGCM.Open(nil, []byte("ILoveMyNonce"), cipherText, nil)
+	fmt.Println(string(cipherText))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+	cipherText := aesGCM.Seal(nil, []byte{}, []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+
+	cipherText, _ = aesGCM.Open(nil, []byte{}, cipherText, nil)
+	fmt.Println(string(cipherText))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+
+	cipherText := aesGCM.Seal(nil, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+
+	cipherText, _ = aesGCM.Open(nil, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, cipherText, nil)
+	fmt.Println(string(cipherText))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher( []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+
+	cipherText := aesGCM.Seal(nil, func() []byte {
+		if true {
+			return []byte("ILoveMyNonce")
+		} else {
+			return []byte("IDont'Love..")
+		}
+	}(), []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+
+	cipherText, _ = aesGCM.Open(nil, func() []byte {
+		if true {
+			return []byte("ILoveMyNonce")
+		} else {
+			return []byte("IDont'Love..")
+		}
+	}(), cipherText, nil)
+
+	fmt.Println(string(cipherText))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+
+	cipherText := aesGCM.Seal(nil, func() []byte {
+		if true {
+			return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+		} else {
+			return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+		}
+	}(), []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+
+	cipherText, _ = aesGCM.Open(nil, func() []byte {
+		if true {
+			return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+		} else {
+			return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+		}
+	}(), cipherText, nil)
+	fmt.Println(string(cipherText))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+	cipheredText := aesGCM.Seal(nil, func() []byte { return []byte("ILoveMyNonce") }(), []byte("My secret message"), nil)
+	fmt.Println(string(cipheredText))
+	cipheredText, _ = aesGCM.Open(nil, func() []byte { return []byte("ILoveMyNonce") }(), cipheredText, nil)
+	fmt.Println(string(cipheredText))
+
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+	cipheredText := aesGCM.Seal(nil, func() []byte { return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} }(), []byte("My secret message"), nil)
+	fmt.Println(string(cipheredText))
+	cipheredText, _ = aesGCM.Open(nil, func() []byte { return []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} }(), cipheredText, nil)
+	fmt.Println(string(cipheredText))
+
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCFB := cipher.NewCFBEncrypter(block, []byte("ILoveMyNonceAlot"))
+	var output = make([]byte, 16)
+	aesCFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+	aesCFB = cipher.NewCFBDecrypter(block, []byte("ILoveMyNonceAlot"))
+	aesCFB.XORKeyStream(output, output)
+	fmt.Println(string(output))
+
+}`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCFB := cipher.NewCFBEncrypter(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	var output = make([]byte, 16)
+	aesCFB.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+	aesCFB = cipher.NewCFBDecrypter(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCFB.XORKeyStream(output, output)
+	fmt.Println(string(output))
+
+}`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCBC := cipher.NewCBCEncrypter(block, []byte("ILoveMyNonceAlot"))
+
+	var output = make([]byte, 16)
+	aesCBC.CryptBlocks(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+	aesCBC = cipher.NewCBCDecrypter(block, []byte("ILoveMyNonceAlot"))
+	aesCBC.CryptBlocks(output, output)
+	fmt.Println(string(output))
+
+}`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCBC := cipher.NewCBCEncrypter(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+
+	var output = make([]byte, 16)
+	aesCBC.CryptBlocks(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+
+	aesCBC = cipher.NewCBCDecrypter(block, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCBC.CryptBlocks(output, output)
+	fmt.Println(string(output))
+
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	var nonce = []byte("ILoveMyNonce")
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+	fmt.Println(string(aesGCM.Seal(nil, nonce, []byte("My secret message"), nil)))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+func main() {
+
+	var nonce = []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCTR := cipher.NewCTR(block, nonce)
+	var output = make([]byte, 16)
+	aesCTR.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+}
+`}, 1, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"fmt"
+)
+
+func coolFunc(size int) []byte{
+	buf := make([]byte, size)
+	rand.Read(buf)
+	return buf
+}
+
+func main() {
+
+	var nonce = coolFunc(16)
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesCTR := cipher.NewCTR(block, nonce)
+	var output = make([]byte, 16)
+	aesCTR.XORKeyStream(output, []byte("Very Cool thing!"))
+	fmt.Println(string(output))
+}
+`}, 0, gosec.NewConfig()},
+
+	{[]string{`package main
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"fmt"
+)
+
+var nonce = []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+
+func main() {
+
+	block, _ := aes.NewCipher([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
+	aesGCM, _ := cipher.NewGCM(block)
+	cipherText := aesGCM.Seal(nil, nonce, []byte("My secret message"), nil)
+	fmt.Println(string(cipherText))
+
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g501_samples.go b/testutils/g501_samples.go
new file mode 100644
index 0000000000..238dd5264d
--- /dev/null
+++ b/testutils/g501_samples.go
@@ -0,0 +1,22 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG501 - Blocklisted import MD5
+var SampleCodeG501 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"crypto/md5"
+	"fmt"
+	"os"
+)
+
+func main() {
+	for _, arg := range os.Args {
+		fmt.Printf("%x - %s\n", md5.Sum([]byte(arg)), arg)
+	}
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g502_samples.go b/testutils/g502_samples.go
new file mode 100644
index 0000000000..dfb5b95d65
--- /dev/null
+++ b/testutils/g502_samples.go
@@ -0,0 +1,35 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG502 - Blocklisted import DES
+var SampleCodeG502 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"crypto/cipher"
+	"crypto/des"
+	"crypto/rand"
+	"encoding/hex"
+	"fmt"
+	"io"
+)
+
+func main() {
+	block, err := des.NewCipher([]byte("sekritz"))
+	if err != nil {
+		panic(err)
+	}
+	plaintext := []byte("I CAN HAZ SEKRIT MSG PLZ")
+	ciphertext := make([]byte, des.BlockSize+len(plaintext))
+	iv := ciphertext[:des.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		panic(err)
+	}
+	stream := cipher.NewCFBEncrypter(block, iv)
+	stream.XORKeyStream(ciphertext[des.BlockSize:], plaintext)
+	fmt.Println("Secret message is: %s", hex.EncodeToString(ciphertext))
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g503_samples.go b/testutils/g503_samples.go
new file mode 100644
index 0000000000..d5c9c23324
--- /dev/null
+++ b/testutils/g503_samples.go
@@ -0,0 +1,27 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG503 - Blocklisted import RC4
+var SampleCodeG503 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"crypto/rc4"
+	"encoding/hex"
+	"fmt"
+)
+
+func main() {
+	cipher, err := rc4.NewCipher([]byte("sekritz"))
+	if err != nil {
+		panic(err)
+	}
+	plaintext := []byte("I CAN HAZ SEKRIT MSG PLZ")
+	ciphertext := make([]byte, len(plaintext))
+	cipher.XORKeyStream(ciphertext, plaintext)
+	fmt.Println("Secret message is: %s", hex.EncodeToString(ciphertext))
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g504_samples.go b/testutils/g504_samples.go
new file mode 100644
index 0000000000..520fb00690
--- /dev/null
+++ b/testutils/g504_samples.go
@@ -0,0 +1,19 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG504 - Blocklisted import CGI
+var SampleCodeG504 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"net/http/cgi"
+	"net/http"
+ )
+
+func main() {
+	cgi.Serve(http.FileServer(http.Dir("/usr/share/doc")))
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g505_samples.go b/testutils/g505_samples.go
new file mode 100644
index 0000000000..3600dcded4
--- /dev/null
+++ b/testutils/g505_samples.go
@@ -0,0 +1,22 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG505 - Blocklisted import SHA1
+var SampleCodeG505 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"crypto/sha1"
+	"fmt"
+	"os"
+)
+
+func main() {
+	for _, arg := range os.Args {
+		fmt.Printf("%x - %s\n", sha1.Sum([]byte(arg)), arg)
+	}
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g506_samples.go b/testutils/g506_samples.go
new file mode 100644
index 0000000000..676aa55bee
--- /dev/null
+++ b/testutils/g506_samples.go
@@ -0,0 +1,23 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG506 - Blocklisted import MD4
+var SampleCodeG506 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"golang.org/x/crypto/md4"
+)
+
+func main() {
+	h := md4.New()
+	h.Write([]byte("test"))
+	fmt.Println(hex.EncodeToString(h.Sum(nil)))
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g507_samples.go b/testutils/g507_samples.go
new file mode 100644
index 0000000000..6c06e882e5
--- /dev/null
+++ b/testutils/g507_samples.go
@@ -0,0 +1,23 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG507 - Blocklisted import RIPEMD160
+var SampleCodeG507 = []CodeSample{
+	{[]string{`
+package main
+
+import (
+	"encoding/hex"
+	"fmt"
+
+	"golang.org/x/crypto/ripemd160"
+)
+
+func main() {
+	h := ripemd160.New()
+	h.Write([]byte("test"))
+	fmt.Println(hex.EncodeToString(h.Sum(nil)))
+}
+`}, 1, gosec.NewConfig()},
+}
diff --git a/testutils/g601_samples.go b/testutils/g601_samples.go
new file mode 100644
index 0000000000..f6dfc0226f
--- /dev/null
+++ b/testutils/g601_samples.go
@@ -0,0 +1,221 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+var (
+	// SampleCodeG601 - Implicit aliasing over range statement
+	SampleCodeG601 = []CodeSample{
+		{[]string{`
+package main
+
+import "fmt"
+
+var vector []*string
+func appendVector(s *string) {
+	vector = append(vector, s)
+}
+
+func printVector() {
+	for _, item := range vector {
+		fmt.Printf("%s", *item)
+	}
+	fmt.Println()
+}
+
+func foo() (int, **string, *string) {
+	for _, item := range vector {
+		return 0, &item, item
+	}
+	return 0, nil, nil
+}
+
+func main() {
+	for _, item := range []string{"A", "B", "C"} {
+		appendVector(&item)
+	}
+
+	printVector()
+
+	zero, c_star, c := foo()
+	fmt.Printf("%d %v %s", zero, c_star, c)
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+// see: github.com/securego/gosec/issues/475
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	sampleMap := map[string]string{}
+	sampleString := "A string"
+	for sampleString, _ = range sampleMap {
+		fmt.Println(sampleString)
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type sampleStruct struct {
+	name string
+}
+
+func main() {
+	samples := []sampleStruct{
+		{name: "a"},
+		{name: "b"},
+	}
+	for _, sample := range samples {
+		fmt.Println(sample.name)
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type sampleStruct struct {
+	name string
+}
+
+func main() {
+	samples := []*sampleStruct{
+		{name: "a"},
+		{name: "b"},
+	}
+	for _, sample := range samples {
+		fmt.Println(&sample)
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type sampleStruct struct {
+	name string
+}
+
+func main() {
+	samples := []*sampleStruct{
+		{name: "a"},
+		{name: "b"},
+	}
+	for _, sample := range samples {
+		fmt.Println(&sample.name)
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type sampleStruct struct {
+	name string
+}
+
+func main() {
+	samples := []sampleStruct{
+		{name: "a"},
+		{name: "b"},
+	}
+	for _, sample := range samples {
+		fmt.Println(&sample.name)
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type subStruct struct {
+	name string
+}
+
+type sampleStruct struct {
+	sub subStruct
+}
+
+func main() {
+	samples := []sampleStruct{
+		{sub: subStruct{name: "a"}},
+		{sub: subStruct{name: "b"}},
+	}
+	for _, sample := range samples {
+		fmt.Println(&sample.sub.name)
+	}
+}
+`}, 1, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+type subStruct struct {
+	name string
+}
+
+type sampleStruct struct {
+	sub subStruct
+}
+
+func main() {
+	samples := []*sampleStruct{
+		{sub: subStruct{name: "a"}},
+		{sub: subStruct{name: "b"}},
+	}
+	for _, sample := range samples {
+		fmt.Println(&sample.sub.name)
+	}
+}
+`}, 0, gosec.NewConfig()},
+		{[]string{`
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	one, two := 1, 2
+	samples := []*int{&one, &two}
+	for _, sample := range samples {
+		fmt.Println(&sample)
+	}
+}
+`}, 1, gosec.NewConfig()},
+	}
+
+	// SampleCodeBuildTag - G601 build tags
+	SampleCodeBuildTag = []CodeSample{
+		{[]string{`
+// +build tag
+package main
+		
+func main() {
+  fmt.Println("no package imported error")
+}
+`}, 1, gosec.NewConfig()},
+	}
+)
diff --git a/testutils/g602_samples.go b/testutils/g602_samples.go
new file mode 100644
index 0000000000..c0fee62949
--- /dev/null
+++ b/testutils/g602_samples.go
@@ -0,0 +1,341 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// SampleCodeG602 - Slice access out of bounds
+var SampleCodeG602 = []CodeSample{
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0)
+
+	fmt.Println(s[:3])
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0)
+
+	fmt.Println(s[3:])
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 16)
+
+	fmt.Println(s[:17])
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 16)
+
+	fmt.Println(s[:16])
+
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 16)
+
+	fmt.Println(s[5:17])
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 4)
+
+	fmt.Println(s[3])
+
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 4)
+
+	fmt.Println(s[5])
+
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0)
+	s = make([]byte, 3)
+
+	fmt.Println(s[:3])
+
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0, 4)
+
+	fmt.Println(s[:3])
+	fmt.Println(s[3])
+
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0, 4)
+
+	fmt.Println(s[:5])
+	fmt.Println(s[7])
+
+}
+`}, 2, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]byte, 0, 4)
+	x := s[:2]
+	y := x[:10]
+	fmt.Println(y)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]int, 0, 4)
+	doStuff(s)
+}
+
+func doStuff(x []int) {
+	newSlice := x[:10]
+	fmt.Println(newSlice)
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+
+	s := make([]int, 0, 30)
+	doStuff(s)
+	x := make([]int, 20)
+	y := x[10:]
+	doStuff(y)
+	z := y[5:]
+	doStuff(z)
+}
+
+func doStuff(x []int) {
+	newSlice := x[:10]
+	fmt.Println(newSlice)
+	newSlice2 := x[:6]
+	fmt.Println(newSlice2)
+}
+`}, 2, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	testMap := make(map[string]any, 0)
+	testMap["test1"] = map[string]interface{}{
+	"test2": map[string]interface{}{
+			"value": 0,
+		},
+	}
+	fmt.Println(testMap)
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		fmt.Println(s[0])
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		switch s[0] {
+		case 0:
+			fmt.Println("zero")
+			return
+		default:
+			fmt.Println(s[0])
+			return
+		}
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		switch s[0] {
+		case 0:
+			b := true
+			if b == true {
+				// Should work for many-levels of nesting when the condition is not on the target slice
+				fmt.Println(s[0])
+			}
+			return
+		default:
+			fmt.Println(s[0])
+			return
+		}
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		if len(s) > 1 {
+			fmt.Println(s[1])
+		}
+		fmt.Println(s[0])
+	}
+}
+`}, 0, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+s := make([]byte, 2)
+fmt.Println(s[1])
+s = make([]byte, 0)
+fmt.Println(s[1])
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		if len(s) > 4 {
+			fmt.Println(s[3])
+		} else {
+			// Should error
+			fmt.Println(s[2])
+		}
+		fmt.Println(s[0])
+	}
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]byte, 0)
+	if len(s) > 0 {
+		fmt.Println("fake test")
+	}
+	fmt.Println(s[0])
+}
+`}, 1, gosec.NewConfig()},
+	{[]string{`
+package main
+
+import "fmt"
+
+func main() {
+	s := make([]int, 16)
+	for i := 0; i < 17; i++ {
+		s = append(s, i)
+	}
+	if len(s) < 16 {
+		fmt.Println(s[10:16])
+	} else {
+		fmt.Println(s[3:18])
+	}
+	fmt.Println(s[0])
+	for i := range s {
+		fmt.Println(s[i])
+	}
+}
+
+`}, 0, gosec.NewConfig()},
+}
diff --git a/testutils/pkg.go b/testutils/pkg.go
index eb9cd42ad5..6dcf79ec03 100644
--- a/testutils/pkg.go
+++ b/testutils/pkg.go
@@ -3,14 +3,14 @@ package testutils
 import (
 	"fmt"
 	"go/build"
-	"io/ioutil"
 	"log"
 	"os"
 	"path"
 	"strings"
 
-	"github.com/securego/gosec/v2"
 	"golang.org/x/tools/go/packages"
+
+	"github.com/securego/gosec/v2"
 )
 
 type buildObj struct {
@@ -30,7 +30,7 @@ type TestPackage struct {
 // NewTestPackage will create a new and empty package. Must call Close() to cleanup
 // auxiliary files
 func NewTestPackage() *TestPackage {
-	workingDir, err := ioutil.TempDir("", "gosecs_test")
+	workingDir, err := os.MkdirTemp("", "gosecs_test")
 	if err != nil {
 		return nil
 	}
@@ -53,9 +53,9 @@ func (p *TestPackage) write() error {
 		return nil
 	}
 	for filename, content := range p.Files {
-		if e := ioutil.WriteFile(filename, []byte(content), 0644); e != nil {
+		if e := os.WriteFile(filename, []byte(content), 0o644); e != nil /* #nosec G306 */ {
 			return e
-		} // #nosec G306
+		}
 	}
 	p.onDisk = true
 	return nil
diff --git a/testutils/sample_types.go b/testutils/sample_types.go
new file mode 100644
index 0000000000..f99159a8b7
--- /dev/null
+++ b/testutils/sample_types.go
@@ -0,0 +1,10 @@
+package testutils
+
+import "github.com/securego/gosec/v2"
+
+// CodeSample encapsulates a snippet of source code that compiles, and how many errors should be detected
+type CodeSample struct {
+	Code   []string
+	Errors int
+	Config gosec.Config
+}
diff --git a/testutils/source.go b/testutils/source.go
deleted file mode 100644
index 8bb162abe2..0000000000
--- a/testutils/source.go
+++ /dev/null
@@ -1,2336 +0,0 @@
-package testutils
-
-import "github.com/securego/gosec/v2"
-
-// CodeSample encapsulates a snippet of source code that compiles, and how many errors should be detected
-type CodeSample struct {
-	Code   []string
-	Errors int
-	Config gosec.Config
-}
-
-var (
-	// SampleCodeG101 code snippets for hardcoded credentials
-	SampleCodeG101 = []CodeSample{{[]string{`
-package main
-import "fmt"
-func main() {
-	username := "admin"
-	password := "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-	fmt.Println("Doing something with: ", username, password)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Entropy check should not report this error by default
-package main
-import "fmt"
-func main() {
-	username := "admin"
-	password := "secret"
-	fmt.Println("Doing something with: ", username, password)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-import "fmt"
-var password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-func main() {
-	username := "admin"
-	fmt.Println("Doing something with: ", username, password)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-import "fmt"
-const password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-func main() {
-	username := "admin"
-	fmt.Println("Doing something with: ", username, password)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-import "fmt"
-const (
-	username = "user"
-	password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-)
-func main() {
-	fmt.Println("Doing something with: ", username, password)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-var password string
-func init() {
-	password = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-const (
-	ATNStateSomethingElse = 1
-	ATNStateTokenStart = 42
-)
-func main() {
-	println(ATNStateTokenStart)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-const (
-	ATNStateTokenStart = "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
-)
-func main() {
-	println(ATNStateTokenStart)
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import "fmt"
-func main() {
-	var password string
-	if password == "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
-		fmt.Println("password equality")
-	}
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import "fmt"
-func main() {
-	var password string
-	if password != "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
-		fmt.Println("password equality")
-	}
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import "fmt"
-func main() {
-	var p string
-	if p != "f62e5bcda4fae4f82370da0c6f20697b8f8447ef" {
-		fmt.Println("password equality")
-	}
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG102 code snippets for network binding
-	SampleCodeG102 = []CodeSample{
-		// Bind to all networks explicitly
-		{[]string{`
-package main
-import (
-	"log"
-   	"net"
-)
-func main() {
-	l, err := net.Listen("tcp", "0.0.0.0:2000")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer l.Close()
-}`}, 1, gosec.NewConfig()},
-
-		// Bind to all networks implicitly (default if host omitted)
-		{[]string{`
-package main
-import (
-	"log"
-   	"net"
-)
-func main() {
-   	l, err := net.Listen("tcp", ":2000")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer l.Close()
-}`}, 1, gosec.NewConfig()},
-		// Bind to all networks indirectly through a parsing function
-		{[]string{`
-package main
-import (
-	"log"
-   	"net"
-)
-func parseListenAddr(listenAddr string) (network string, addr string) {
-	return "", ""
-}
-func main() {
-	addr := ":2000"
-	l, err := net.Listen(parseListenAddr(addr))
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer l.Close()
-}`}, 1, gosec.NewConfig()},
-		// Bind to all networks indirectly through a parsing function
-		{[]string{`
-package main
-import (
-	"log"
-   	"net"
-)
-const addr = ":2000"
-func parseListenAddr(listenAddr string) (network string, addr string) {
-	return "", ""
-}
-func main() {
-	l, err := net.Listen(parseListenAddr(addr))
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer l.Close()
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import (
-	"log"
-   	"net"
-)
-const addr = "0.0.0.0:2000"
-
-func main() {
-	l, err := net.Listen("tcp", addr)
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer l.Close()
-}`}, 1, gosec.NewConfig()},
-	}
-	// SampleCodeG103 find instances of unsafe blocks for auditing purposes
-	SampleCodeG103 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"fmt"
-	"unsafe"
-)
-type Fake struct{}
-func (Fake) Good() {}
-func main() {
-	unsafeM := Fake{}
-   	unsafeM.Good()
-   	intArray := [...]int{1, 2}
-   	fmt.Printf("\nintArray: %v\n", intArray)
-   	intPtr := &intArray[0]
-   	fmt.Printf("\nintPtr=%p, *intPtr=%d.\n", intPtr, *intPtr)
-   	addressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0])
-   	intPtr = (*int)(unsafe.Pointer(addressHolder))
-   	fmt.Printf("\nintPtr=%p, *intPtr=%d.\n\n", intPtr, *intPtr)
-}`}, 3, gosec.NewConfig()}}
-
-	// SampleCodeG104 finds errors that aren't being handled
-	SampleCodeG104 = []CodeSample{
-		{[]string{`
-package main
-import "fmt"
-func test() (int,error) {
-	return 0, nil
-}
-func main() {
-	v, _ := test()
-	fmt.Println(v)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-import (
-	"io/ioutil"
-	"os"
-	"fmt"
-)
-func a() error {
-	return fmt.Errorf("This is an error")
-}
-func b() {
-	fmt.Println("b")
-	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
-}
-func c() string {
-	return fmt.Sprintf("This isn't anything")
-}
-func main() {
-	_ = a()
-	a()
-	b()
-	c()
-}`}, 2, gosec.NewConfig()}, {[]string{`
-package main
-import "fmt"
-func test() error {
-	return nil
-}
-func main() {
-	e := test()
-	fmt.Println(e)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// +build go1.10
-
-package main
-import "strings"
-func main() {
-	var buf strings.Builder
-	_, err := buf.WriteString("test string")
-	if err != nil {
-		panic(err)
-	}
-}`, `
-package main
-func dummy(){}
-`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"bytes"
-)
-
-type a struct {
-	buf *bytes.Buffer
-}
-
-func main() {
-	a := &a{
-		buf: new(bytes.Buffer),
-	}
-	a.buf.Write([]byte{0})
-}
-`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-import (
-	"io/ioutil"
-	"os"
-	"fmt"
-)
-func a() {
-	fmt.Println("a")
-	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
-}
-func main() {
-	a()
-}`}, 0, gosec.Config{"G104": map[string]interface{}{"ioutil": []interface{}{"WriteFile"}}}}, {[]string{`
-package main
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"os"
-	"strings"
-)
-
-func createBuffer() *bytes.Buffer {
-	return new(bytes.Buffer)
-}
-
-func main() {
-	new(bytes.Buffer).WriteString("*bytes.Buffer")
-	fmt.Fprintln(os.Stderr, "fmt")
-	new(strings.Builder).WriteString("*strings.Builder")
-	_, pw := io.Pipe()
-	pw.CloseWithError(io.EOF)
-
-	createBuffer().WriteString("*bytes.Buffer")
-	b := createBuffer()
-	b.WriteString("*bytes.Buffer")
-}`}, 0, gosec.NewConfig()}}  // it shoudn't return any errors because all method calls are whitelisted by default
-
-	// SampleCodeG104Audit finds errors that aren't being handled in audit mode
-	SampleCodeG104Audit = []CodeSample{
-		{[]string{`
-package main
-import "fmt"
-func test() (int,error) {
-	return 0, nil
-}
-func main() {
-	v, _ := test()
-	fmt.Println(v)
-}`}, 1, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}}, {[]string{`
-package main
-import (
-	"io/ioutil"
-	"os"
-	"fmt"
-)
-func a() error {
-	return fmt.Errorf("This is an error")
-}
-func b() {
-	fmt.Println("b")
-	ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
-}
-func c() string {
-	return fmt.Sprintf("This isn't anything")
-}
-func main() {
-	_ = a()
-	a()
-	b()
-	c()
-}`}, 3, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}}, {[]string{`
-package main
-import "fmt"
-func test() error {
-	return nil
-}
-func main() {
-	e := test()
-	fmt.Println(e)
-}`}, 0, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}}, {[]string{`
-// +build go1.10
-
-package main
-import "strings"
-func main() {
-	var buf strings.Builder
-	_, err := buf.WriteString("test string")
-	if err != nil {
-		panic(err)
-	}
-}`, `
-package main
-func dummy(){}
-`}, 0, gosec.Config{gosec.Globals: map[gosec.GlobalOption]string{gosec.Audit: "enabled"}}}}
-
-	// SampleCodeG106 - ssh InsecureIgnoreHostKey
-	SampleCodeG106 = []CodeSample{{[]string{`
-package main
-import (
-        "golang.org/x/crypto/ssh"
-)
-func main() {
-        _ =  ssh.InsecureIgnoreHostKey()
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG107 - SSRF via http requests with variable url
-	SampleCodeG107 = []CodeSample{{[]string{`
-// Input from the std in is considered insecure
-package main
-import (
-	"net/http"
-	"io/ioutil"
-	"fmt"
-	"os"
-	"bufio"
-)
-func main() {
-	in := bufio.NewReader(os.Stdin)
-	url, err := in.ReadString('\n')
-	if err != nil {
-		panic(err)
-	}
-	resp, err := http.Get(url)
-	if err != nil {
-		panic(err)
-	}
-  	defer resp.Body.Close()
-  	body, err := ioutil.ReadAll(resp.Body)
-  	if err != nil {
-    		panic(err)
-  	}
-  	fmt.Printf("%s", body)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Variable defined a package level can be changed at any time
-// regardless of the initial value
-package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"net/http"
-)
-
-var url string = "https://www.google.com"
-
-func main() {
-	resp, err := http.Get(url)
-	if err != nil {
-		panic(err)
-	}
-	defer resp.Body.Close()
-	body, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-		panic(err)
-	}
-	fmt.Printf("%s", body)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Environmental variables are not considered as secure source
-package main
-import (
-	"net/http"
-	"io/ioutil"
-	"fmt"
-	"os"
-)
-func main() {
-	url := os.Getenv("tainted_url")
-	resp, err := http.Get(url)
-	if err != nil {
-		panic(err)
-	}
-	defer resp.Body.Close()
-	body, err := ioutil.ReadAll(resp.Body)
-	if err != nil {
-			panic(err)
-	}
-	fmt.Printf("%s", body)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Constant variables or hard-coded strings are secure
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-const url = "http://127.0.0.1"
-func main() {
-	resp, err := http.Get(url)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// A variable at function scope which is initialized to
-// a constant string is secure (e.g. cannot be changed concurrently)
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-func main() {
-    var url string = "http://127.0.0.1"
-	resp, err := http.Get(url)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// A variable at function scope which is initialized to
-// a constant string is secure (e.g. cannot be changed concurrently)
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-func main() {
-	url := "http://127.0.0.1"
-	resp, err := http.Get(url)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// A variable at function scope which is initialized to
-// a constant string is secure (e.g. cannot be changed concurrently)
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-func main() {
-	url1 := "test"
-    var url2 string = "http://127.0.0.1"
-	url2 = url1
-	resp, err := http.Get(url2)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// An exported variable declared a packaged scope is not secure
-// because it can changed at any time
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-
-var Url string
-
-func main() {
-	resp, err := http.Get(Url)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// An url provided as a function argument is not secure
-package main
-
-import (
-	"fmt"
-	"net/http"
-)
-func get(url string) {
-	resp, err := http.Get(url)
-	if err != nil {
-		fmt.Println(err)
-    }
-    fmt.Println(resp.Status)
-}
-func main() {
-	url := "http://127.0.0.1"
-	get(url)
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG108 - pprof endpoint automatically exposed
-	SampleCodeG108 = []CodeSample{{[]string{`
-package main
-
-import (
-	"fmt"
-	"log"
-	"net/http"
-	_ "net/http/pprof"
-)
-
-func main() {
-	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, "Hello World!")
-	})
-	log.Fatal(http.ListenAndServe(":8080", nil))
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"log"
-	"net/http"
-)
-
-func main() {
-	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(w, "Hello World!")
-	})
-	log.Fatal(http.ListenAndServe(":8080", nil))
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG109 - Potential Integer OverFlow
-	SampleCodeG109 = []CodeSample{
-		{[]string{`
-package main
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func main() {
-	bigValue, err := strconv.Atoi("2147483648")
-	if err != nil {
-		panic(err)
-	}
-	value := int32(bigValue)
-	fmt.Println(value)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func main() {
-	bigValue, err := strconv.Atoi("32768")
-	if err != nil {
-		panic(err)
-	}
-	if int16(bigValue) < 0 {
-		fmt.Println(bigValue)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func main() {
-	bigValue, err := strconv.Atoi("2147483648")
-	if err != nil {
-		panic(err)
-	}
-	fmt.Println(bigValue)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func main() {
-	bigValue, err := strconv.Atoi("2147483648")
-	if err != nil {
-		panic(err)
-	}
-	fmt.Println(bigValue)
-	test()
-}
-
-func test() {
-	bigValue := 30
-	value := int32(bigValue)
-	fmt.Println(value)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"strconv"
-)
-
-func main() {
-	value := 10
-	if value == 10 {
-		value, _ := strconv.Atoi("2147483648")
-		fmt.Println(value)
-	}
-	v := int32(value)
-	fmt.Println(v)
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG110 - potential DoS vulnerability via decompression bomb
-	SampleCodeG110 = []CodeSample{
-		{[]string{`
-package main
-
-import (
-	"bytes"
-	"compress/zlib"
-	"io"
-	"os"
-)
-
-func main() {
-	buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207,
-		47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
-	b := bytes.NewReader(buff)
-
-	r, err := zlib.NewReader(b)
-	if err != nil {
-		panic(err)
-	}
-	_, err = io.Copy(os.Stdout, r)
-	if err != nil {
-		panic(err)
-	}
-
-	r.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"bytes"
-	"compress/zlib"
-	"io"
-	"os"
-)
-
-func main() {
-	buff := []byte{120, 156, 202, 72, 205, 201, 201, 215, 81, 40, 207,
-		47, 202, 73, 225, 2, 4, 0, 0, 255, 255, 33, 231, 4, 147}
-	b := bytes.NewReader(buff)
-
-	r, err := zlib.NewReader(b)
-	if err != nil {
-		panic(err)
-	}
-	buf := make([]byte, 8)
-	_, err = io.CopyBuffer(os.Stdout, r, buf)
-	if err != nil {
-		panic(err)
-	}
-	r.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"archive/zip"
-	"io"
-	"os"
-	"strconv"
-)
-
-func main() {
-	r, err := zip.OpenReader("tmp.zip")
-	if err != nil {
-		panic(err)
-	}
-	defer r.Close()
-
-	for i, f := range r.File {
-		out, err := os.OpenFile("output" + strconv.Itoa(i), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
-		if err != nil {
-			panic(err)
-		}
-
-		rc, err := f.Open()
-		if err != nil {
-			panic(err)
-		}
-
-		_, err = io.Copy(out, rc)
-
-		out.Close()
-		rc.Close()
-
-		if err != nil {
-			panic(err)
-		}
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"io"
-	"os"
-)
-
-func main() {
-	s, err := os.Open("src")
-	if err != nil {
-		panic(err)
-	}
-	defer s.Close()
-
-	d, err := os.Create("dst")
-	if err != nil {
-		panic(err)
-	}
-	defer d.Close()
-
-	_, err = io.Copy(d, s)
-	if  err != nil {
-		panic(err)
-	}
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG201 - SQL injection via format string
-	SampleCodeG201 = []CodeSample{
-		{[]string{`
-// Format string without proper quoting
-package main
-import (
-	"database/sql"
-	"fmt"
-	"os"
-)
-
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("SELECT * FROM foo where name = '%s'", os.Args[1])
-	rows, err := db.Query(q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Format string without proper quoting case insensitive
-package main
-import (
-	"database/sql"
-	"fmt"
-	"os"
-)
-
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
-	rows, err := db.Query(q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Format string without proper quoting with context
-package main
-import (
-	"context"
-	"database/sql"
-	"fmt"
-	"os"
-)
-
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
-	rows, err := db.QueryContext(context.Background(), q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Format string without proper quoting with transation
-package main
-import (
-	"context"
-	"database/sql"
-	"fmt"
-	"os"
-)
-
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	tx, err := db.Begin()
-	if err != nil {
-		panic(err)
-	}
-	defer tx.Rollback()
-	q := fmt.Sprintf("select * from foo where name = '%s'", os.Args[1])
-	rows, err := tx.QueryContext(context.Background(), q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-	if err := tx.Commit(); err != nil {
-		panic(err)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Format string false positive, safe string spec.
-package main
-import (
-	"database/sql"
-	"fmt"
-	"os"
-)
-
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("SELECT * FROM foo where id = %d", os.Args[1])
-	rows, err := db.Query(q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// Format string false positive
-package main
-import (
-		"database/sql"
-)
-const staticQuery = "SELECT * FROM foo WHERE age < 32"
-func main(){
-		db, err := sql.Open("sqlite3", ":memory:")
-		if err != nil {
-			panic(err)
-		}
-		rows, err := db.Query(staticQuery)
-		if err != nil {
-			panic(err)
-		}
-		defer rows.Close()
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// Format string false positive, quoted formatter argument.
-package main
-import (
-	"database/sql"
-	"fmt"
-	"os"
-	"github.com/lib/pq"
-)
-
-func main(){
-	db, err := sql.Open("postgres", "localhost")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("SELECT * FROM %s where id = 1", pq.QuoteIdentifier(os.Args[1]))
-	rows, err := db.Query(q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// false positive
-package main
-import (
-	"database/sql"
-	"fmt"
-)
-
-const Table = "foo"
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	q := fmt.Sprintf("SELECT * FROM %s where id = 1", Table)
-	rows, err := db.Query(q)
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-import (
-	"fmt"
-)
-
-func main(){
-	fmt.Sprintln()
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG202 - SQL query string building via string concatenation
-	SampleCodeG202 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"database/sql"
-	"os"
-)
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	rows, err := db.Query("SELECT * FROM foo WHERE name = " + os.Args[1])
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// case insensitive match
-package main
-import (
-	"database/sql"
-	"os"
-)
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	rows, err := db.Query("select * from foo where name = " + os.Args[1])
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// context match
-package main
-import (
-    "context"
-	"database/sql"
-	"os"
-)
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	rows, err := db.QueryContext(context.Background(), "select * from foo where name = " + os.Args[1])
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// DB transation check
-package main
-import (
-    "context"
-	"database/sql"
-	"os"
-)
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	tx, err := db.Begin()
-	if err != nil {
-		panic(err)
-	}
-	defer tx.Rollback()
-	rows, err := tx.QueryContext(context.Background(), "select * from foo where name = " + os.Args[1])
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-	if err := tx.Commit(); err != nil {
-		panic(err)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// multiple string concatenation
-package main
-import (
-	"database/sql"
-	"os"
-)
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	rows, err := db.Query("SELECT * FROM foo" + "WHERE name = " + os.Args[1])
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// false positive
-package main
-import (
-	"database/sql"
-)
-var staticQuery = "SELECT * FROM foo WHERE age < "
-func main(){
-	db, err := sql.Open("sqlite3", ":memory:")
-	if err != nil {
-		panic(err)
-	}
-	rows, err := db.Query(staticQuery + "32")
-	if err != nil {
-		panic(err)
-	}
-	defer rows.Close()
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-import (
-		"database/sql"
-)
-const age = "32"
-var staticQuery = "SELECT * FROM foo WHERE age < "
-func main(){
-		db, err := sql.Open("sqlite3", ":memory:")
-		if err != nil {
-				panic(err)
-		}
-		rows, err := db.Query(staticQuery + age)
-		if err != nil {
-				panic(err)
-		}
-		defer rows.Close()
-}
-`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-const gender = "M"
-`, `
-package main
-import (
-		"database/sql"
-)
-const age = "32"
-var staticQuery = "SELECT * FROM foo WHERE age < "
-func main(){
-		db, err := sql.Open("sqlite3", ":memory:")
-		if err != nil {
-				panic(err)
-		}
-		rows, err := db.Query("SELECT * FROM foo WHERE gender = " + gender)
-		if err != nil {
-				panic(err)
-		}
-		defer rows.Close()
-}
-`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG203 - Template checks
-	SampleCodeG203 = []CodeSample{
-		{[]string{`
-// We assume that hardcoded template strings are safe as the programmer would
-// need to be explicitly shooting themselves in the foot (as below)
-package main
-import (
-	"html/template"
-	"os"
-)
-const tmpl = ""
-func main() {
-	t := template.Must(template.New("ex").Parse(tmpl))
-	v := map[string]interface{}{
-		"Title":    "Test <b>World</b>",
-		"Body":     template.HTML("<script>alert(1)</script>"),
-	}
-	t.Execute(os.Stdout, v)
-}`}, 0, gosec.NewConfig()}, {[]string{
-			`
-// Using a variable to initialize could potentially be dangerous. Under the
-// current model this will likely produce some false positives.
-package main
-import (
-	"html/template"
-	"os"
-)
-const tmpl = ""
-func main() {
-	a := "something from another place"
-	t := template.Must(template.New("ex").Parse(tmpl))
-	v := map[string]interface{}{
-		"Title":    "Test <b>World</b>",
-		"Body":     template.HTML(a),
-	}
-	t.Execute(os.Stdout, v)
-}`}, 1, gosec.NewConfig()}, {[]string{
-			`
-package main
-import (
-	"html/template"
-	"os"
-)
-const tmpl = ""
-func main() {
-	a := "something from another place"
-	t := template.Must(template.New("ex").Parse(tmpl))
-	v := map[string]interface{}{
-		"Title":    "Test <b>World</b>",
-		"Body":     template.JS(a),
-	}
-	t.Execute(os.Stdout, v)
-}`}, 1, gosec.NewConfig()}, {[]string{
-			`
-package main
-import (
-	"html/template"
-	"os"
-)
-const tmpl = ""
-func main() {
-	a := "something from another place"
-	t := template.Must(template.New("ex").Parse(tmpl))
-	v := map[string]interface{}{
-		"Title":    "Test <b>World</b>",
-		"Body":     template.URL(a),
-	}
-	t.Execute(os.Stdout, v)
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG204 - Subprocess auditing
-	SampleCodeG204 = []CodeSample{{[]string{`
-package main
-import (
-	"log"
-	"os/exec"
-	"context"
-)
-func main() {
-	err := exec.CommandContext(context.Background(), "git", "rev-parse", "--show-toplavel").Run()
- 	if err != nil {
-		log.Fatal(err)
-	}
-  	log.Printf("Command finished with error: %v", err)
-}`}, 0, gosec.NewConfig()}, {[]string{`
-// Calling any function which starts a new process with using
-// command line arguments as it's arguments is considered dangerous
-package main
-import (
-	"context"
-	"log"
-	"os"
-	"os/exec"
-)
-func main() {
-	err := exec.CommandContext(context.Background(), os.Args[0], "5").Run()
-	if err != nil {
-		log.Fatal(err)
-	}
-	log.Printf("Command finished with error: %v", err)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Initializing a local variable using a environmental
-// variable is consider as a dangerous user input
-package main
-import (
-	"log"
-	"os"
-	"os/exec"
-)
-func main() {
-	run := "sleep" + os.Getenv("SOMETHING")
-	cmd := exec.Command(run, "5")
-	err := cmd.Start()
-	if err != nil {
-		log.Fatal(err)
-	}
-	log.Printf("Waiting for command to finish...")
-	err = cmd.Wait()
-	log.Printf("Command finished with error: %v", err)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// gosec doesn't have enough context to decide that the
-// command argument of the RunCmd function is harcoded string
-// and that's why it's better to warn the user so he can audit it
-package main
-
-import (
-	"log"
-	"os/exec"
-)
-
-func RunCmd(command string) {
-	cmd := exec.Command(command, "5")
-	err := cmd.Start()
-	if err != nil {
-		log.Fatal(err)
-	}
-	log.Printf("Waiting for command to finish...")
-	err = cmd.Wait()
-}
-
-func main() {
-	RunCmd("sleep")
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// syscall.Exec function called with harcoded arguments
-// shouldn't be consider as a command injection
-package main
-import (
-	"fmt"
-	"syscall"
-)
-func main() {
-	err := syscall.Exec("/bin/cat", []string{"/etc/passwd"}, nil)
-	if err != nil {
-		fmt.Printf("Error: %v\n", err)
-	}
-}`}, 0, gosec.NewConfig()},
-		{[]string{`
-package main
-
-import (
-	"fmt"
-	"syscall"
-)
-
-func RunCmd(command string) {
-	_, err := syscall.ForkExec(command, []string{}, nil)
-	if err != nil {
-	    fmt.Printf("Error: %v\n", err)
-	}
-}
-
-func main() {
-	RunCmd("sleep")
-}`}, 1, gosec.NewConfig(),
-		},
-		{[]string{`
-package main
-
-import (
-	"fmt"
-	"syscall"
-)
-
-func RunCmd(command string) {
-	_, _, err := syscall.StartProcess(command, []string{}, nil)
-	if err != nil {
-	    fmt.Printf("Error: %v\n", err)
-	}
-}
-
-func main() {
-	RunCmd("sleep")
-}`}, 1, gosec.NewConfig(),
-		},
-		{[]string{`
-// starting a process with a variable as an argument
-// even if not constant is not considered as dangerous
-// because it has harcoded value
-package main
-import (
-	"log"
-	"os/exec"
-)
-func main() {
-	run := "sleep"
-	cmd := exec.Command(run, "5")
-	err := cmd.Start()
-	if err != nil {
-		log.Fatal(err)
-	}
-	log.Printf("Waiting for command to finish...")
-	err = cmd.Wait()
-	log.Printf("Command finished with error: %v", err)
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG301 - mkdir permission check
-	SampleCodeG301 = []CodeSample{{[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	err := os.Mkdir("/tmp/mydir", 0777)
-	if err != nil {
-		fmt.Println("Error when creating a directory!")
-		return
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	err := os.MkdirAll("/tmp/mydir", 0777)
-	if err != nil {
-		fmt.Println("Error when creating a directory!")
-		return
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	err := os.Mkdir("/tmp/mydir", 0600)
-	if err != nil {
-		fmt.Println("Error when creating a directory!")
-		return
-	}
-}`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG302 - file create / chmod permissions check
-	SampleCodeG302 = []CodeSample{{[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	err := os.Chmod("/tmp/somefile", 0777)
-	if err != nil {
-		fmt.Println("Error when changing file permissions!")
-		return
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	_, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0666)
-	if err != nil {
-		fmt.Println("Error opening a file!")
-		return
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	err := os.Chmod("/tmp/mydir", 0400)
-	if err != nil {
-		fmt.Println("Error")
-		return
-	}
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"os"
-)
-
-func main() {
-	_, err := os.OpenFile("/tmp/thing", os.O_CREATE|os.O_WRONLY, 0600)
-	if err != nil {
-		fmt.Println("Error opening a file!")
-		return
-	}
-}
-`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG303 - bad tempfile permissions & hardcoded shared path
-	SampleCodeG303 = []CodeSample{{[]string{`
-package samples
-
-import (
-	"fmt"
-	"io/ioutil"
-)
-
-func main() {
-	err := ioutil.WriteFile("/tmp/demo2", []byte("This is some data"), 0644)
-	if err != nil {
-		fmt.Println("Error while writing!")
-	}
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG304 - potential file inclusion vulnerability
-	SampleCodeG304 = []CodeSample{{[]string{`
-package main
-import (
-"os"
-"io/ioutil"
-"log"
-)
-func main() {
-	f := os.Getenv("tainted_file")
-	body, err := ioutil.ReadFile(f)
-	if err != nil {
-	log.Printf("Error: %v\n", err)
-	}
-	log.Print(body)
-
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"log"
-	"net/http"
-	"os"
-)
-
-func main() {
-	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
-  		title := r.URL.Query().Get("title")
-		f, err := os.Open(title)
-		if err != nil {
-			fmt.Printf("Error: %v\n", err)
-		}
-		body := make([]byte, 5)
-		if _, err = f.Read(body); err != nil {
-			fmt.Printf("Error: %v\n", err)
-		}
-		fmt.Fprintf(w, "%s", body)
-	})
-	log.Fatal(http.ListenAndServe(":3000", nil))
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"fmt"
-	"log"
-	"net/http"
-	"os"
-)
-
-func main() {
-	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
-  		title := r.URL.Query().Get("title")
-		f, err := os.OpenFile(title, os.O_RDWR|os.O_CREATE, 0755)
-		if err != nil {
-			fmt.Printf("Error: %v\n", err)
-		}
-		body := make([]byte, 5)
-		if _, err = f.Read(body); err != nil {
-			fmt.Printf("Error: %v\n", err)
-		}
-		fmt.Fprintf(w, "%s", body)
-	})
-	log.Fatal(http.ListenAndServe(":3000", nil))
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"log"
-	"os"
-	"io/ioutil"
-)
-
-	func main() {
-		f2 := os.Getenv("tainted_file2")
-		body, err := ioutil.ReadFile("/tmp/" + f2)
-		if err != nil {
-		log.Printf("Error: %v\n", err)
-	  }
-		log.Print(body)
- }`}, 1, gosec.NewConfig()}, {[]string{`
- package main
-
- import (
-	 "bufio"
-	 "fmt"
-	 "os"
-	 "path/filepath"
- )
-
-func main() {
-	reader := bufio.NewReader(os.Stdin)
-  fmt.Print("Please enter file to read: ")
-	file, _ := reader.ReadString('\n')
-	file = file[:len(file)-1]
-	f, err := os.Open(filepath.Join("/tmp/service/", file))
-	if err != nil {
-		fmt.Printf("Error: %v\n", err)
-	}
-	contents := make([]byte, 15)
-  if _, err = f.Read(contents); err != nil {
-		fmt.Printf("Error: %v\n", err)
-	}
-  fmt.Println(string(contents))
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-	"log"
-	"os"
-	"io/ioutil"
-	"path/filepath"
-)
-
-func main() {
-	dir := os.Getenv("server_root")
-	f3 := os.Getenv("tainted_file3")
-	// edge case where both a binary expression and file Join are used.
-	body, err := ioutil.ReadFile(filepath.Join("/var/"+dir, f3))
-	if err != nil {
-		log.Printf("Error: %v\n", err)
-	}
-	log.Print(body)
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-    "os"
-    "path/filepath"
-)
-
-func main() {
-    repoFile := "path_of_file"
-    cleanRepoFile := filepath.Clean(repoFile)
-    _, err := os.OpenFile(cleanRepoFile, os.O_RDONLY, 0600)
-    if err != nil {
-        panic(err)
-    }
-}
-`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-    "os"
-    "path/filepath"
-)
-
-func openFile(filePath string) {
-	_, err := os.OpenFile(filepath.Clean(filePath), os.O_RDONLY, 0600)
-	if err != nil {
-		panic(err)
-	}
-}
-
-func main() {
-    repoFile := "path_of_file"
-	openFile(repoFile)
-}
-`}, 0, gosec.NewConfig()}, {[]string{`
-package main
-
-import (
-    "os"
-    "path/filepath"
-)
-
-func main() {
-    repoFile := "path_of_file"
-	relFile, err := filepath.Rel("./", repoFile)
-	if err != nil {
-		panic(err)
-	}
-    _, err = os.OpenFile(relFile, os.O_RDONLY, 0600)
-    if err != nil {
-        panic(err)
-    }
-}
-
-`}, 0, gosec.NewConfig()}}
-
-	// SampleCodeG305 - File path traversal when extracting zip/tar archives
-	SampleCodeG305 = []CodeSample{{[]string{`
-package unzip
-
-import (
-	"archive/zip"
-	"io"
-	"os"
-	"path/filepath"
-)
-
-func unzip(archive, target string) error {
-	reader, err := zip.OpenReader(archive)
-	if err != nil {
-		return err
-	}
-
-	if err := os.MkdirAll(target, 0750); err != nil {
-		return err
-	}
-
-	for _, file := range reader.File {
-		path := filepath.Join(target, file.Name)
-		if file.FileInfo().IsDir() {
-			os.MkdirAll(path, file.Mode()) // #nosec
-			continue
-		}
-
-		fileReader, err := file.Open()
-		if err != nil {
-			return err
-		}
-		defer fileReader.Close()
-
-		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
-		if err != nil {
-			return err
-		}
-		defer targetFile.Close()
-
-		if _, err := io.Copy(targetFile, fileReader); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package unzip
-
-import (
-	"archive/zip"
-	"io"
-	"os"
-	"path/filepath"
-)
-
-func unzip(archive, target string) error {
-	reader, err := zip.OpenReader(archive)
-	if err != nil {
-		return err
-	}
-
-	if err := os.MkdirAll(target, 0750); err != nil {
-		return err
-	}
-
-	for _, file := range reader.File {
-                archiveFile := file.Name
-		path := filepath.Join(target, archiveFile)
-		if file.FileInfo().IsDir() {
-			os.MkdirAll(path, file.Mode()) // #nosec
-			continue
-		}
-
-		fileReader, err := file.Open()
-		if err != nil {
-			return err
-		}
-		defer fileReader.Close()
-
-		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
-		if err != nil {
-			return err
-		}
-		defer targetFile.Close()
-
-		if _, err := io.Copy(targetFile, fileReader); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package zip
-
-import (
-    "archive/zip"
-    "io"
-    "os"
-    "path"
-)
-
-func extractFile(f *zip.File, destPath string) error {
-    filePath := path.Join(destPath, f.Name)
-    os.MkdirAll(path.Dir(filePath), os.ModePerm)
-
-    rc, err := f.Open()
-    if err != nil {
-        return err
-    }
-    defer rc.Close()
-
-    fw, err := os.Create(filePath)
-    if err != nil {
-        return err
-    }
-    defer fw.Close()
-
-    if _, err = io.Copy(fw, rc); err != nil {
-        return err
-    }
-
-    if f.FileInfo().Mode()&os.ModeSymlink != 0 {
-        return nil
-    }
-
-    if err = os.Chtimes(filePath, f.ModTime(), f.ModTime()); err != nil {
-        return err
-    }
-    return os.Chmod(filePath, f.FileInfo().Mode())
-}`}, 1, gosec.NewConfig()}, {[]string{`
-package tz
-
-import (
-    "archive/tar"
-    "io"
-    "os"
-    "path"
-)
-
-func extractFile(f *tar.Header, tr *tar.Reader, destPath string) error {
-    filePath := path.Join(destPath, f.Name)
-    os.MkdirAll(path.Dir(filePath), os.ModePerm)
-
-    fw, err := os.Create(filePath)
-    if err != nil {
-        return err
-    }
-    defer fw.Close()
-
-    if _, err = io.Copy(fw, tr); err != nil {
-        return err
-    }
-
-    if f.FileInfo().Mode()&os.ModeSymlink != 0 {
-        return nil
-    }
-
-    if err = os.Chtimes(filePath, f.FileInfo().ModTime(), f.FileInfo().ModTime()); err != nil {
-        return err
-    }
-    return os.Chmod(filePath, f.FileInfo().Mode())
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG306 - Poor permissions for WriteFile
-	SampleCodeG306 = []CodeSample{
-		{[]string{`package main
-
-import (
-	"bufio"
-	"fmt"
-	"io/ioutil"
-	"os"
-)
-
-func check(e error) {
-	if e != nil {
-		panic(e)
-	}
-}
-
-func main() {
-
-	d1 := []byte("hello\ngo\n")
-	err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
-	check(err)
-
-	allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
-	check(allowed)
-
-	f, err := os.Create("/tmp/dat2")
-	check(err)
-
-	defer f.Close()
-
-	d2 := []byte{115, 111, 109, 101, 10}
-	n2, err := f.Write(d2)
-
-	defer check(err)
-	fmt.Printf("wrote %d bytes\n", n2)
-
-	n3, err := f.WriteString("writes\n")
-	fmt.Printf("wrote %d bytes\n", n3)
-
-	f.Sync()
-
-	w := bufio.NewWriter(f)
-	n4, err := w.WriteString("buffered\n")
-	fmt.Printf("wrote %d bytes\n", n4)
-
-	w.Flush()
-
-}`}, 1, gosec.NewConfig()}}
-	// SampleCodeG307 - Unsafe defer of os.Close
-	SampleCodeG307 = []CodeSample{
-		{[]string{`package main
-
-import (
-	"bufio"
-	"fmt"
-	"io/ioutil"
-	"os"
-)
-
-func check(e error) {
-	if e != nil {
-		panic(e)
-	}
-}
-
-func main() {
-
-	d1 := []byte("hello\ngo\n")
-	err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
-	check(err)
-
-	allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
-	check(allowed)
-
-	f, err := os.Create("/tmp/dat2")
-	check(err)
-
-	defer f.Close()
-
-	d2 := []byte{115, 111, 109, 101, 10}
-	n2, err := f.Write(d2)
-
-	defer check(err)
-	fmt.Printf("wrote %d bytes\n", n2)
-
-	n3, err := f.WriteString("writes\n")
-	fmt.Printf("wrote %d bytes\n", n3)
-
-	f.Sync()
-
-	w := bufio.NewWriter(f)
-	n4, err := w.WriteString("buffered\n")
-	fmt.Printf("wrote %d bytes\n", n4)
-
-	w.Flush()
-
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG401 - Use of weak crypto MD5
-	SampleCodeG401 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/md5"
-	"fmt"
-	"io"
-	"log"
-	"os"
-)
-
-func main() {
-	f, err := os.Open("file.txt")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer f.Close()
-
-	defer func() {
-	  err := f.Close()
-	  if err != nil {
-		 log.Printf("error closing the file: %s", err)
-	  }
-	}()
-
-	h := md5.New()
-	if _, err := io.Copy(h, f); err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%x", h.Sum(nil))
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG401b - Use of weak crypto SHA1
-	SampleCodeG401b = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/sha1"
-	"fmt"
-	"io"
-	"log"
-	"os"
-)
-func main() {
-	f, err := os.Open("file.txt")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer f.Close()
-
-	h := sha1.New()
-	if _, err := io.Copy(h, f); err != nil {
-		log.Fatal(err)
-	}
-	fmt.Printf("%x", h.Sum(nil))
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG402 - TLS settings
-	SampleCodeG402 = []CodeSample{{[]string{`
-// InsecureSkipVerify
-package main
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-func main() {
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-	}
-
-	client := &http.Client{Transport: tr}
-	_, err := client.Get("https://golang.org/")
-	if err != nil {
-		fmt.Println(err)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{
-		`
-// Insecure minimum version
-package main
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-func main() {
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{MinVersion: 0},
-	}
-	client := &http.Client{Transport: tr}
-	_, err := client.Get("https://golang.org/")
-	if err != nil {
-		fmt.Println(err)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// Insecure max version
-package main
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-func main() {
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{MaxVersion: 0},
-	}
-	client := &http.Client{Transport: tr}
-	_, err := client.Get("https://golang.org/")
-	if err != nil {
-		fmt.Println(err)
-	}
-}
-`}, 1, gosec.NewConfig()}, {
-		[]string{`
-// Insecure ciphersuite selection
-package main
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-func main() {
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{CipherSuites: []uint16{
-						tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
-						tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-						},},
-	}
-	client := &http.Client{Transport: tr}
-	_, err := client.Get("https://golang.org/")
-	if err != nil {
-		fmt.Println(err)
-	}
-}`}, 1, gosec.NewConfig()}, {[]string{`
-// secure max version when min version is specified
-package main
-import (
-	"crypto/tls"
-	"fmt"
-	"net/http"
-)
-func main() {
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{MaxVersion: 0, MinVersion: tls.VersionTLS13},
-	}
-	client := &http.Client{Transport: tr}
-	_, err := client.Get("https://golang.org/")
-	if err != nil {
-		fmt.Println(err)
-	}
-}`}, 0, gosec.NewConfig()}, {[]string{`
-package p0
-
-import "crypto/tls"
-
-func TlsConfig0() *tls.Config {
-	var v uint16 = 0
-	return &tls.Config{MinVersion: v}
-}
-`, `
-package p0
-
-import "crypto/tls"
-
-func TlsConfig1() *tls.Config {
-   return &tls.Config{MinVersion: 0x0304}
-}
-`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG403 - weak key strength
-	SampleCodeG403 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/rand"
-	"crypto/rsa"
-	"fmt"
-)
-func main() {
-	//Generate Private Key
-	pvk, err := rsa.GenerateKey(rand.Reader, 1024)
-	if err != nil {
-		fmt.Println(err)
-	}
-	fmt.Println(pvk)
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG404 - weak random number
-	SampleCodeG404 = []CodeSample{
-		{[]string{`
-package main
-import "crypto/rand"
-func main() {
-	good, _ := rand.Read(nil)
-	println(good)
-}`}, 0, gosec.NewConfig()},
-		{[]string{`
-package main
-import "math/rand"
-func main() {
-	bad := rand.Int()
-	println(bad)
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import (
-	"crypto/rand"
-	mrand "math/rand"
-)
-func main() {
-	good, _ := rand.Read(nil)
-	println(good)
-	bad := mrand.Int31()
-	println(bad)
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import (
-	"math/rand"
-)
-func main() {
-	gen := rand.New(rand.NewSource(10))
-	bad := gen.Int()
-	println(bad)
-}`}, 1, gosec.NewConfig()},
-		{[]string{`
-package main
-import (
-	"math/rand"
-)
-func main() {
-	bad := rand.Intn(10)
-	println(bad)
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG501 - Blocklisted import MD5
-	SampleCodeG501 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/md5"
-	"fmt"
-	"os"
-)
-func main() {
-	for _, arg := range os.Args {
-		fmt.Printf("%x - %s\n", md5.Sum([]byte(arg)), arg)
-	}
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG502 - Blocklisted import DES
-	SampleCodeG502 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/cipher"
-	"crypto/des"
-	"crypto/rand"
-	"encoding/hex"
-	"fmt"
-	"io"
-)
-func main() {
-	block, err := des.NewCipher([]byte("sekritz"))
-	if err != nil {
-		panic(err)
-	}
-	plaintext := []byte("I CAN HAZ SEKRIT MSG PLZ")
-	ciphertext := make([]byte, des.BlockSize+len(plaintext))
-	iv := ciphertext[:des.BlockSize]
-	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
-		panic(err)
-	}
-	stream := cipher.NewCFBEncrypter(block, iv)
-	stream.XORKeyStream(ciphertext[des.BlockSize:], plaintext)
-	fmt.Println("Secret message is: %s", hex.EncodeToString(ciphertext))
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG503 - Blocklisted import RC4
-	SampleCodeG503 = []CodeSample{{[]string{`
-package main
-import (
-	"crypto/rc4"
-	"encoding/hex"
-	"fmt"
-)
-func main() {
-	cipher, err := rc4.NewCipher([]byte("sekritz"))
-	if err != nil {
-		panic(err)
-	}
-	plaintext := []byte("I CAN HAZ SEKRIT MSG PLZ")
-	ciphertext := make([]byte, len(plaintext))
-	cipher.XORKeyStream(ciphertext, plaintext)
-	fmt.Println("Secret message is: %s", hex.EncodeToString(ciphertext))
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG504 - Blocklisted import CGI
-	SampleCodeG504 = []CodeSample{{[]string{`
-package main
-import (
-	"net/http/cgi"
-	"net/http"
- )
-func main() {
-	cgi.Serve(http.FileServer(http.Dir("/usr/share/doc")))
-}`}, 1, gosec.NewConfig()}}
-	// SampleCodeG505 - Blocklisted import SHA1
-	SampleCodeG505 = []CodeSample{
-		{[]string{`
-package main
-import (
-	"crypto/sha1"
-	"fmt"
-	"os"
-)
-func main() {
-	for _, arg := range os.Args {
-		fmt.Printf("%x - %s\n", sha1.Sum([]byte(arg)), arg)
-	}
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeG601 - Implicit aliasing over range statement
-	SampleCodeG601 = []CodeSample{
-		{[]string{`
-package main
-
-import "fmt"
-
-var vector []*string
-func appendVector(s *string) {
-	vector = append(vector, s)
-}
-
-func printVector() {
-	for _, item := range vector {
-		fmt.Printf("%s", *item)
-	}
-	fmt.Println()
-}
-
-func foo() (int, **string, *string) {
-	for _, item := range vector {
-		return 0, &item, item
-	}
-	return 0, nil, nil
-}
-
-func main() {
-	for _, item := range []string{"A", "B", "C"} {
-		appendVector(&item)
-	}
-
-	printVector()
-
-	zero, c_star, c := foo()
-	fmt.Printf("%d %v %s", zero, c_star, c)
-}`,
-		}, 1, gosec.NewConfig()},
-		{[]string{`
-// see: github.com/securego/gosec/issues/475
-package main
-import (
-    "fmt"
-)
-func main() {
-    sampleMap := map[string]string{}
-    sampleString := "A string"
-    for sampleString, _ = range sampleMap {
-        fmt.Println(sampleString)
-    }
-}`}, 0, gosec.NewConfig()},
-	}
-
-	// SampleCodeBuildTag - G601 build tags
-	SampleCodeBuildTag = []CodeSample{{[]string{`
-// +build tag
-package main
-func main() {
-  fmt.Println("no package imported error")
-}`}, 1, gosec.NewConfig()}}
-
-	// SampleCodeCgo - Cgo file sample
-	SampleCodeCgo = []CodeSample{{[]string{`
-package main
-
-import (
-        "fmt"
-        "unsafe"
-)
-
-/*
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-int printData(unsigned char *data) {
-    return printf("cData: %lu \"%s\"\n", (long unsigned int)strlen(data), data);
-}
-*/
-import "C"
-
-func main() {
-        // Allocate C data buffer.
-        width, height := 8, 2
-        lenData := width * height
-        // add string terminating null byte
-        cData := (*C.uchar)(C.calloc(C.size_t(lenData+1), C.sizeof_uchar))
-
-        // When no longer in use, free C allocations.
-        defer C.free(unsafe.Pointer(cData))
-
-        // Go slice reference to C data buffer,
-        // minus string terminating null byte
-        gData := (*[1 << 30]byte)(unsafe.Pointer(cData))[:lenData:lenData]
-
-        // Write and read cData via gData.
-        for i := range gData {
-                gData[i] = '.'
-        }
-        copy(gData[0:], "Data")
-        gData[len(gData)-1] = 'X'
-        fmt.Printf("gData: %d %q\n", len(gData), gData)
-        C.printData(cData)
-}
-`}, 0, gosec.NewConfig()}}
-)
diff --git a/tools/tools.go b/tools/tools.go
new file mode 100644
index 0000000000..1c3d45f227
--- /dev/null
+++ b/tools/tools.go
@@ -0,0 +1,11 @@
+//go:build tools
+// +build tools
+
+package tools
+
+// nolint
+import (
+	_ "github.com/lib/pq"
+	_ "golang.org/x/crypto/ssh"
+	_ "golang.org/x/text"
+)
