File gosec-2.18.1.obscpio of Package gosec

07070100000000000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001500000000gosec-2.18.1/.github0707010000000100008180000000000000000000000001652CE6670000004C000000000000000000000000000000000000002100000000gosec-2.18.1/.github/FUNDING.yml# These are supported funding model platforms

github: [ccojocar, gcmurphy]
0707010000000200008180000000000000000000000001652CE667000000BE000000000000000000000000000000000000002700000000gosec-2.18.1/.github/issue_template.md### Summary 

### Steps to reproduce the behavior

### gosec version

### Go version (output of 'go version')

### Operating system / Environment

### Expected behavior

### Actual behavior
07070100000003000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001F00000000gosec-2.18.1/.github/workflows07070100000004000081A4000000000000000000000001652CE6670000069C000000000000000000000000000000000000002600000000gosec-2.18.1/.github/workflows/ci.ymlname: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  test:
    strategy:
      matrix:
        version: [{go: '1.20.10', golangci: 'latest'}, {go: '1.21.3', golangci: 'latest'}]
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
    steps:
      - name: Setup go ${{ matrix.version.go }} 
        uses: actions/setup-go@v4
        with:
          go-version: ${{ matrix.version.go }}
      - name: Checkout Source 
        uses: actions/checkout@v4
      - uses: actions/cache@v3
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-
      - name: lint
        uses: golangci/golangci-lint-action@v3
        with:
         version: ${{ matrix.version.golangci }}
      - name: Run Gosec Security Scanner
        uses: securego/gosec@master
        with:
          args: ./...
      - name: Run Tests
        run: make test
  coverage:
    needs: [test]
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
    steps:
      - name: Setup go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21.3'
      - name: Checkout Source 
        uses: actions/checkout@v4
      - uses: actions/cache@v3
        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@v3
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: true
07070100000005000081A4000000000000000000000001652CE66700000948000000000000000000000000000000000000002B00000000gosec-2.18.1/.github/workflows/release.ymlname: Release
on:
  push:
    tags:
      - 'v*'
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
      ACTIONS_ALLOW_UNSECURE_COMMANDS: true
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4
      - name: Unshallow
        run: git fetch --prune --unshallow
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21.3'
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3
        with:
          cosign-release: 'v2.2.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@v5
        with:
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
          COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
      - name: Release Docker Image
        uses: docker/build-push-action@v5
        id: relimage
        with:
          platforms: linux/amd64,linux/arm/v7,linux/arm64
          tags: ${{steps.meta.outputs.tags}}
          labels: ${{steps.meta.outputs.labels}}
          push: true
          build-args: GO_VERSION=1.21
      - name: Sign Docker Image
        run: cosign sign --yes --key /tmp/cosign.key ${DIGEST}
        env:
          TAGS: ${{steps.meta.outputs.tags}}
          COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}}
          COSIGN_PRIVATE_KEY: /tmp/cosign.key
          DIGEST: ${{steps.relimage.outputs.digest}}
07070100000006000081A4000000000000000000000001652CE6670000031A000000000000000000000000000000000000002800000000gosec-2.18.1/.github/workflows/scan.ymlname: "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@v2
      with:
        # Path to SARIF file relative to the root of the repository
        sarif_file: results.sarif
0707010000000700008180000000000000000000000001652CE6670000017E000000000000000000000000000000000000001800000000gosec-2.18.1/.gitignore# transient files
/image

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.swp
/gosec

# Folders
_obj
_test
vendor
dist

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

.DS_Store

.vscode
.idea

# SBOMs generated during CI
/bom.json
07070100000008000081A4000000000000000000000001652CE6670000025A000000000000000000000000000000000000001B00000000gosec-2.18.1/.golangci.ymllinters:
  enable:
    - asciicheck
    - bodyclose
    - dogsled
    - durationcheck
    - errcheck
    - errorlint
    - exportloopref
    - gci
    - ginkgolinter
    - gochecknoinits
    - gofmt
    - gofumpt
    - goimports
    - gosec
    - gosimple
    - govet
    - importas
    - ineffassign
    - megacheck
    - misspell
    - nakedret
    - nolintlint
    - revive
    - staticcheck
    - typecheck
    - unconvert
    - unparam
    - unused
    - wastedassign

linters-settings:
  gci:
    sections:
      - standard
      - default
      - prefix(github.com/securego)

run:
  timeout: 5m
07070100000009000081A4000000000000000000000001652CE6670000024D000000000000000000000000000000000000001D00000000gosec-2.18.1/.goreleaser.yml---
project_name: gosec

release:
  extra_files:
    - glob: ./bom.json
  github:
    owner: securego
    name: gosec

builds:
  - main: ./cmd/gosec/
    binary: gosec
    goos:
      - darwin
      - linux
      - windows
    goarch:
      - amd64
      - arm64
      - s390x
    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

0707010000000A000081A4000000000000000000000001652CE667000001D1000000000000000000000000000000000000001800000000gosec-2.18.1/DockerfileARG GO_VERSION
FROM golang:${GO_VERSION}-alpine AS builder
RUN apk add --no-cache ca-certificates make git curl gcc libc-dev \
    && mkdir -p /build
WORKDIR /build
COPY . /build/
RUN go mod download \
    && make build-linux

FROM golang:${GO_VERSION}-alpine 
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
ENTRYPOINT ["/bin/entrypoint.sh"]
0707010000000B00008180000000000000000000000001652CE667000023B2000000000000000000000000000000000000001900000000gosec-2.18.1/LICENSE.txtApache License

Version 2.0, January 2004

http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.

"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.

3. Grant of Patent License. Subject to the terms and conditions of this License,
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise
transfer the Work, where such license applies only to those patent claims
licensable by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s) with the Work
to which such Contribution(s) was submitted. If You institute patent litigation
against any entity (including a cross-claim or counterclaim in a lawsuit)
alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate as of the date
such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:

You must give any other recipients of the Work or Derivative Works a copy of
this License; and You must cause any modified files to carry prominent notices
stating that You changed the files; and You must retain, in the Source form of
any Derivative Works that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work, excluding those notices
that do not pertain to any part of the Derivative Works; and If the Work
includes a "NOTICE" text file as part of its distribution, then any Derivative
Works that You distribute must include a readable copy of the attribution
notices contained within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one of the following
places: within a NOTICE text file distributed as part of the Derivative Works;
within the Source form or documentation, if provided along with the Derivative
Works; or, within a display generated by the Derivative Works, if and wherever
such third-party notices normally appear. The contents of the NOTICE file are
for informational purposes only and do not modify the License. You may add Your
own attribution notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided that such
additional attribution notices cannot be construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License. 5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.

6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any character
arising as a result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill, work stoppage,
computer failure or malfunction, or any and all other commercial damages or
losses), even if such Contributor has been advised of the possibility of such
damages.

9. Accepting Warranty or Additional Liability. While redistributing the Work or
Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree to
indemnify, defend, and hold each Contributor harmless for any liability incurred
by, or claims asserted against, such Contributor by reason of your accepting any
such warranty or additional liability.

END OF TERMS AND CONDITIONS
0707010000000C000081A4000000000000000000000001652CE66700000A03000000000000000000000000000000000000001600000000gosec-2.18.1/MakefileGIT_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
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
GOPATH ?= $(shell $(GO) env GOPATH)
GOBIN ?= $(GOPATH)/bin
GOSEC ?= $(GOBIN)/gosec
GINKGO ?= $(GOBIN)/ginkgo
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
GOVULN_MIN_VERSION = 17
GO_VERSION = 1.20

default:
	$(MAKE) build

install-test-deps:
	go install github.com/onsi/ginkgo/v2/ginkgo@latest
	go install golang.org/x/crypto/...@latest
	go install github.com/lib/pq/...@latest

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 fmt vet sec govulncheck
	$(GINKGO) -v --fail-fast

fmt:
	@echo "FORMATTING"
	@FORMATTED=`$(GO) fmt ./...`
	@([ ! -z "$(FORMATTED)" ] && printf "Fixed unformatted files:\n$(FORMATTED)") || true

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 -v -count=1 -coverprofile=coverage.out ./...

build:
	go build -o $(BIN) ./cmd/gosec/

clean:
	rm -rf build vendor dist coverage.txt
	rm -f release image $(BIN)

release:
	@echo "Releasing the gosec binary..."
	goreleaser release

build-linux:
	CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build -ldflags=$(BUILDFLAGS) -o $(BIN) ./cmd/gosec/

image:
	@echo "Building the Docker image..."
	docker build -t $(IMAGE_REPO)/$(BIN):$(GIT_TAG) --build-arg GO_VERSION=$(GO_VERSION) .
	docker tag $(IMAGE_REPO)/$(BIN):$(GIT_TAG) $(IMAGE_REPO)/$(BIN):latest
	touch image

image-push: image
	@echo "Pushing the Docker image..."
	docker push $(IMAGE_REPO)/$(BIN):$(GIT_TAG)
	docker push $(IMAGE_REPO)/$(BIN):latest

tlsconfig:
	go generate ./...

.PHONY: test build clean release image image-push tlsconfig
0707010000000D000081A4000000000000000000000001652CE66700003F62000000000000000000000000000000000000001700000000gosec-2.18.1/README.md
# gosec - Golang Security Checker

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">

## License

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 [here](http://www.apache.org/licenses/LICENSE-2.0).

## Project status

[![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/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](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](http://securego.slack.com)

## Install

### CI Installation

```bash
# binary will be $(go env GOPATH)/bin/gosec
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z

# or install it into ./bin/
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s vX.Y.Z

# In alpine linux (as it does not come with curl by default)
wget -O - -q https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s vX.Y.Z

# If you want to use the checksums provided on the "Releases" page
# then you will have to download a tar.gz file for your operating system instead of a binary file
wget https://github.com/securego/gosec/releases/download/vX.Y.Z/gosec_vX.Y.Z_OS.tar.gz

# The file will be in the current folder where you run the command
# and you can check the checksum like this
echo "<check sum from the check sum file>  gosec_vX.Y.Z_OS.tar.gz" | sha256sum -c -

gosec --help
```

### GitHub Action

You can run `gosec` as a GitHub action as follows:

```yaml
name: Run Gosec
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  tests:
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
    steps:
      - name: Checkout Source
        uses: actions/checkout@v3
      - name: Run Gosec Security Scanner
        uses: securego/gosec@master
        with:
          args: ./...
```

### Integrating with code scanning

You can [integrate third-party code analysis tools](https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/integrating-with-code-scanning) with GitHub code scanning by uploading data as SARIF files.

The workflow shows an example of running the `gosec` as a step in a GitHub action workflow which outputs the `results.sarif` file. The workflow then uploads the `results.sarif` file to GitHub using the `upload-sarif` action.

```yaml
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:
  schedule:
  - cron: '0 0 * * 0'

jobs:
  tests:
    runs-on: ubuntu-latest
    env:
      GO111MODULE: on
    steps:
      - name: Checkout Source
        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
        with:
          # Path to SARIF file relative to the root of the repository
          sarif_file: results.sarif
```

### Local Installation

#### Go 1.16+

```bash
go install github.com/securego/gosec/v2/cmd/gosec@latest
```

#### Go version < 1.16

```bash
go get -u github.com/securego/gosec/v2/cmd/gosec
```

## Usage

Gosec can be configured to only run a subset of rules, to exclude certain file
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
- G102: Bind to all interfaces
- G103: Audit the use of unsafe block
- G104: Audit errors not checked
- G106: Audit the use of ssh.InsecureIgnoreHostKey
- G107: Url provided to HTTP request as taint input
- 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
- G113: Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772)
- G114: Use of net/http serve function that has no support for setting timeouts
- G201: SQL query construction using format string
- G202: SQL query construction using string concatenation
- G203: Use of unescaped data in HTML templates
- G204: Audit use of command execution
- G301: Poor file permissions used when creating a directory
- G302: Poor file permissions used with chmod
- G303: Creating tempfile using a predictable path
- 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: Poor file permissions used when creating a file with os.Create
- G401: Detect the usage of DES, RC4, 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)
- 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
- 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)
- 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

By default, gosec will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the `-include=` flag,
or to specify a set of rules to explicitly exclude using the `-exclude=` flag.

```bash
# Run a specific set of rules
$ 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/issue.go#L50).

### Configuration

A number of global settings can be provided in a configuration file as follows:

```JSON
{
    "global": {
        "nosec": "enabled",
        "audit": "enabled"
    }
}
```

- `nosec`: this setting will overwrite all `#nosec` directives defined throughout the code base
- `audit`: runs in audit mode which enables addition checks that for normal code analysis might be too nosy

```bash
# 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": {
        "ioutil": ["WriteFile"]
    }
}
```

You can also configure the hard-coded credentials rule `G101` with additional patterns, or adjust the entropy threshold:

```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"
    }
}
```

### 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,
the dependencies need to be explicitly downloaded by running the `go get -d` command before the scan.

### Excluding test files and folders

gosec will ignore test files across all packages and any dependencies in your vendor directory.

The scanning of test files can be enabled with the following flag:

```bash
gosec -tests ./...
```

Also additional folders can be excluded as follows:

```bash
 gosec -exclude-dir=rules -exclude-dir=cmd ./...
```

### Excluding generated files

gosec can ignore generated go files with default generated code comment.

```
// Code generated by some generator DO NOT EDIT.
```

```bash
gosec -exclude-generated ./...
```


### 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 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.

```go

import "md5" //#nosec


func main(){

    /* #nosec */
    if x > y {
        h := md5.New() // this will also be ignored
    }

}

```

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`

In some cases you may also want to revisit places where `#nosec` annotations
have been used. To run the scanner and ignore any `#nosec` annotations you
can do the following:

```bash
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.
They can be provided as a comma separated list as follows:

```bash
gosec -tags debug,ignore ./...
```

### Output formats

gosec currently supports `text`, `json`, `yaml`, `csv`, `sonarqube`, `JUnit XML`, `html` and `golint` output formats. By default
results will be reported to stdout, but can also be written to an output
file. The output format is controlled by the `-fmt` flag, and the output file is controlled by the `-out` flag as follows:

```bash
# Write output in json format to results.json
$ 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

### 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 Golang convention defined [here](https://github.com/golang/lint/blob/master/lint.go#L700)

### Tests

You can run all unit tests using:

```bash
make test
```

### Release

You can create a release by tagging the version as follows:

``` bash
git tag v1.0.0 -m "Release version v1.0.0"
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:

```bash
make image
```

You can run the `gosec` tool in a container against your local Go project. You only have to mount the project
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

### Generate TLS rule

The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recommendation](https://statics.tls.security.mozilla.org/server-side-tls-conf.json).

First you need to install the generator tool:

```bash
go get github.com/securego/gosec/v2/cmd/tlsconfig/...
```

You can invoke now the `go generate` in the root of the project:

```bash
go generate ./...
```

This will generate the `rules/tls_config.go` file which will contain the current ciphers recommendation from Mozilla.

## 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>
0707010000000E000081A4000000000000000000000001652CE66700000535000000000000000000000000000000000000001600000000gosec-2.18.1/USERS.md# Users

This is a list of gosec's users. Please send a pull request with your organisation or project name if you are using gosec. 

## Companies

1. [Gitlab](https://docs.gitlab.com/ee/user/application_security/sast/)
2. [CloudBees](https://cloudbees.com)
3. [VMware](https://www.vmware.com)
4. [Codacy](https://support.codacy.com/hc/en-us/articles/213632009-Engines)
5. [Coinbase](https://github.com/coinbase/watchdog/blob/master/Makefile#L12)
6. [RedHat/OpenShift](https://github.com/openshift/openshift-azure)
7. [Guardalis](https://www.guardrails.io/)
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)

## Projects

1. [golangci-lint](https://github.com/golangci/golangci-lint)
2. [Kubernetes](https://github.com/kubernetes/kubernetes) (via golangci)
3. [caddy](https://github.com/caddyserver/caddy) (via golangci)
4. [Jenkins X](https://github.com/jenkins-x/jx/blob/bdc51840a41b75776159c1c7b7faa1cf477be473/hack/linter.sh#L25)
5. [HuskyCI](https://huskyci.opensource.globo.com/)
6. [GolangCI](https://golangci.com/)
7. [semgrep.live](https://semgrep.live/)
8. [gofiber](https://github.com/gofiber/fiber)
9. [KICS](https://github.com/Checkmarx/kics)
0707010000000F000081A4000000000000000000000001652CE66700000157000000000000000000000000000000000000001800000000gosec-2.18.1/action.ymlname: 'Gosec Security Checker'
description: 'Runs the gosec security checker'
author: '@ccojocar'

inputs:
  args:
    description: 'Arguments for gosec'
    required: true
    default: '-h'

runs:
    using: 'docker'
    image: 'docker://securego/gosec:2.18.1'
    args:
      - ${{ inputs.args }}

branding:
  icon: 'shield'
  color: 'blue'
07070100000010000081A4000000000000000000000001652CE66700004A01000000000000000000000000000000000000001900000000gosec-2.18.1/analyzer.go// (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 gosec holds the central scanning logic used by gosec security scanner
package gosec

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/token"
	"go/types"
	"log"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/securego/gosec/v2/analyzers"
	"github.com/securego/gosec/v2/issue"
	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/buildssa"
	"golang.org/x/tools/go/packages"
)

// LoadMode controls the amount of details to return when loading the packages
const LoadMode = packages.NeedName |
	packages.NeedFiles |
	packages.NeedCompiledGoFiles |
	packages.NeedImports |
	packages.NeedTypes |
	packages.NeedTypesSizes |
	packages.NeedTypesInfo |
	packages.NeedSyntax |
	packages.NeedModule |
	packages.NeedEmbedFiles |
	packages.NeedEmbedPatterns

const externalSuppressionJustification = "Globally suppressed."

const aliasOfAllRules = "*"

var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)

// ignoreLocation keeps the location of an ignored rule
type ignoreLocation struct {
	file string
	line string
}

// 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 with the encountered AST node.
type Context struct {
	FileSet      *token.FileSet
	Comments     ast.CommentMap
	Info         *types.Info
	Pkg          *types.Package
	PkgFiles     []*ast.File
	Root         *ast.File
	Imports      *ImportTracker
	Config       Config
	Ignores      map[ignoreLocation]map[string][]issue.SuppressionInfo
	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"`
	NumLines int `json:"lines"`
	NumNosec int `json:"nosec"`
	NumFound int `json:"found"`
}

// 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.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
	analyzerList      []*analysis.Analyzer
}

// NewAnalyzer builds a new 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,
		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,
		analyzerList:      analyzers.BuildDefaultAnalyzers(),
	}
}

// SetConfig updates the analyzer configuration
func (gosec *Analyzer) SetConfig(conf Config) {
	gosec.config = conf
}

// Config returns the current configuration
func (gosec *Analyzer) Config() Config {
	return gosec.config
}

// LoadRules instantiates all the rules to be used when analyzing source
// packages
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, ruleSuppressed[id], nodes...)
	}
}

// Process kicks off the analysis process for a given package
func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error {
	config := &packages.Config{
		Mode:       LoadMode,
		BuildFlags: buildTags,
		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 {
		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 r.pkgs {
			if pkg.Name != "" {
				err := gosec.ParseErrors(pkg)
				if err != nil {
					close(quit)
					wg.Wait() // wait for the goroutines to stop
					return fmt.Errorf("parsing errors in pkg %q: %w", pkg.Name, err)
				}
				gosec.CheckRules(pkg)
				gosec.CheckAnalyzers(pkg)
			}
		}
	}
	sortErrors(gosec.errors)
	return nil
}

func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.Package, error) {
	abspath, err := GetPkgAbsPath(pkgPath)
	if err != nil {
		gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
		return []*packages.Package{}, nil
	}

	gosec.logger.Println("Import directory:", abspath)
	// step 1/3 create build context.
	buildD := build.Default
	// step 2/3: add build tags to get env dependent files into basePackage.
	buildD.BuildTags = conf.BuildFlags
	basePackage, err := buildD.ImportDir(pkgPath, build.ImportComment)
	if err != nil {
		return []*packages.Package{}, fmt.Errorf("importing dir %q: %w", pkgPath, err)
	}

	var packageFiles []string
	for _, filename := range basePackage.GoFiles {
		packageFiles = append(packageFiles, path.Join(pkgPath, filename))
	}
	for _, filename := range basePackage.CgoFiles {
		packageFiles = append(packageFiles, path.Join(pkgPath, filename))
	}

	if gosec.tests {
		testsFiles := make([]string, 0)
		testsFiles = append(testsFiles, basePackage.TestGoFiles...)
		testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
		for _, filename := range testsFiles {
			packageFiles = append(packageFiles, path.Join(pkgPath, filename))
		}
	}

	// step 3/3 remove build tags from conf to proceed build correctly.
	conf.BuildFlags = nil
	pkgs, err := packages.Load(conf, packageFiles...)
	if err != nil {
		return []*packages.Package{}, fmt.Errorf("loading files from package %q: %w", pkgPath, err)
	}
	return pkgs, nil
}

// 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 {
		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 && isGeneratedFile(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
		gosec.context.Comments = ast.NewCommentMap(gosec.context.FileSet, file, file.Comments)
		gosec.context.Root = file
		gosec.context.Info = pkg.TypesInfo
		gosec.context.Pkg = pkg.Types
		gosec.context.PkgFiles = pkg.Syntax
		gosec.context.Imports = NewImportTracker()
		gosec.context.PassedValues = make(map[string]interface{})
		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),
		},
	}
	for _, analyzer := range gosec.analyzerList {
		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 {
					gosec.updateIssues(iss)
				}
			}
		}
	}
}

// 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)
}

func isGeneratedFile(file *ast.File) bool {
	for _, comment := range file.Comments {
		for _, row := range comment.List {
			if generatedCodePattern.MatchString(row.Text) {
				return true
			}
		}
	}
	return false
}

// ParseErrors parses the errors from given package
func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
	if len(pkg.Errors) == 0 {
		return nil
	}
	for _, pkgErr := range pkg.Errors {
		parts := strings.Split(pkgErr.Pos, ":")
		file := parts[0]
		var err error
		var line int
		if len(parts) > 1 {
			if line, err = strconv.Atoi(parts[1]); err != nil {
				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: %w", err)
			}
		}
		msg := strings.TrimSpace(pkgErr.Msg)
		newErr := NewError(line, column, msg)
		if errSlice, ok := gosec.errors[file]; ok {
			gosec.errors[file] = append(errSlice, *newErr)
		} else {
			errSlice = []Error{}
			gosec.errors[file] = append(errSlice, *newErr)
		}
	}
	return nil
}

// 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()) {
		return
	}
	errors := make([]Error, 0)
	if ferrs, ok := gosec.errors[file]; ok {
		errors = ferrs
	}
	ferr := NewError(0, 0, err.Error())
	errors = append(errors, *ferr)
	gosec.errors[file] = errors
}

// ignore a node (and sub-tree) if it is tagged with a nosec tag comment
func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo {
	if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec {

		// Checks if an alternative for #nosec is set and, if not, uses the default.
		noSecDefaultTag := NoSecTag(string(Nosec))
		noSecAlternativeTag, err := gosec.config.GetGlobal(NoSecAlternative)
		if err != nil {
			noSecAlternativeTag = noSecDefaultTag
		} else {
			noSecAlternativeTag = NoSecTag(noSecAlternativeTag)
		}

		for _, group := range groups {
			comment := strings.TrimSpace(group.Text())
			foundDefaultTag := strings.HasPrefix(comment, noSecDefaultTag) || regexp.MustCompile("\n *"+noSecDefaultTag).Match([]byte(comment))
			foundAlternativeTag := strings.HasPrefix(comment, noSecAlternativeTag) || regexp.MustCompile("\n *"+noSecAlternativeTag).Match([]byte(comment))

			if foundDefaultTag || foundAlternativeTag {
				gosec.stats.NumNosec++

				// Discard what's in front of the nosec tag.
				if foundDefaultTag {
					comment = strings.SplitN(comment, noSecDefaultTag, 2)[1]
				} else {
					comment = strings.SplitN(comment, noSecAlternativeTag, 2)[1]
				}

				// Extract the directive and the justification.
				justification := ""
				commentParts := regexp.MustCompile(`-{2,}`).Split(comment, 2)
				directive := commentParts[0]
				if len(commentParts) > 1 {
					justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n"))
				}

				// Pull out the specific rules that are listed to be ignored.
				re := regexp.MustCompile(`(G\d{3})`)
				matches := re.FindAllStringSubmatch(directive, -1)

				suppression := issue.SuppressionInfo{
					Kind:          "inSource",
					Justification: justification,
				}

				// Find the rule IDs to ignore.
				ignores := make(map[string]issue.SuppressionInfo)
				for _, v := range matches {
					ignores[v[1]] = suppression
				}

				// If no specific rules were given, ignore everything.
				if len(matches) == 0 {
					ignores[aliasOfAllRules] = suppression
				}
				return ignores
			}
		}
	}
	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 {
	// Update any potentially ignored rules at the node location
	gosec.updateIgnoredRules(n)

	// 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) {
		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)
		}
		gosec.updateIssues(issue)
	}
	return gosec
}

func (gosec *Analyzer) updateIgnoredRules(n ast.Node) {
	ignoredRules := gosec.ignore(n)
	if len(ignoredRules) > 0 {
		if gosec.context.Ignores == nil {
			gosec.context.Ignores = make(map[ignoreLocation]map[string][]issue.SuppressionInfo)
		}
		line := issue.GetLine(gosec.context.FileSet.File(n.Pos()), n)
		ignoreLocation := ignoreLocation{
			file: gosec.context.FileSet.File(n.Pos()).Name(),
			line: line,
		}
		current, ok := gosec.context.Ignores[ignoreLocation]
		if !ok {
			current = map[string][]issue.SuppressionInfo{}
		}
		for r, s := range ignoredRules {
			if current[r] == nil {
				current[r] = []issue.SuppressionInfo{}
			}
			current[r] = append(current[r], s)
		}
		gosec.context.Ignores[ignoreLocation] = current
	}
}

func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id string) ([]issue.SuppressionInfo, bool) {
	ignores, ok := gosec.context.Ignores[ignoreLocation{file: file, line: line}]
	if !ok {
		ignores = make(map[string][]issue.SuppressionInfo)
	}

	// Check if the rule was specifically suppressed at this location.
	generalSuppressions, generalIgnored := ignores[aliasOfAllRules]
	ruleSuppressions, ruleIgnored := ignores[id]
	ignored := generalIgnored || ruleIgnored
	suppressions := append(generalSuppressions, ruleSuppressions...)

	// Track external suppressions of this rule.
	if gosec.ruleset.IsRuleSuppressed(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)
		}
	}
}

// Report returns the current issues discovered and the metrics about the scan
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.Issue, 0, 16)
	gosec.stats = &Metrics{}
	gosec.ruleset = NewRuleSet()
}
07070100000011000081A4000000000000000000000001652CE66700006F67000000000000000000000000000000000000001E00000000gosec-2.18.1/analyzer_test.gopackage gosec_test

import (
	"errors"
	"log"
	"os"
	"regexp"
	"strings"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/rules"
	"github.com/securego/gosec/v2/testutils"
	"golang.org/x/tools/go/packages"
)

var _ = Describe("Analyzer", func() {
	var (
		analyzer  *gosec.Analyzer
		logger    *log.Logger
		buildTags []string
		tests     bool
	)
	BeforeEach(func() {
		logger, _ = testutils.NewLogger()
		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(false).RulesInfo())
			dir, err := os.MkdirTemp("", "empty")
			defer os.RemoveAll(dir)
			Expect(err).ShouldNot(HaveOccurred())
			err = analyzer.Process(buildTags, dir)
			Expect(err).ShouldNot(HaveOccurred())
			_, _, errors := analyzer.Report()
			Expect(errors).To(BeEmpty())
		})

		It("should report an error if the package fails to build", func() {
			analyzer.LoadRules(rules.Generate(false).RulesInfo())
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("wonky.go", `func main(){ println("forgot the package")}`)
			err := pkg.Build()
			Expect(err).Should(HaveOccurred())
			err = analyzer.Process(buildTags, pkg.Path)
			Expect(err).ShouldNot(HaveOccurred())
			_, _, errors := analyzer.Report()
			Expect(errors).To(HaveLen(1))
			for _, ferr := range errors {
				Expect(ferr).To(HaveLen(1))
			}
		})

		It("should be able to analyze multiple Go files", func() {
			analyzer.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 = analyzer.Process(buildTags, pkg.Path)
			Expect(err).ShouldNot(HaveOccurred())
			_, metrics, _ := analyzer.Report()
			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(false).RulesInfo())
			pkg1 := testutils.NewTestPackage()
			pkg2 := testutils.NewTestPackage()
			defer pkg1.Close()
			defer pkg2.Close()
			pkg1.AddFile("foo.go", `
				package main
				func main(){
				}`)
			pkg2.AddFile("bar.go", `
				package main
				func bar(){
				}`)
			err := pkg1.Build()
			Expect(err).ShouldNot(HaveOccurred())
			err = pkg2.Build()
			Expect(err).ShouldNot(HaveOccurred())
			err = analyzer.Process(buildTags, pkg1.Path, pkg2.Path)
			Expect(err).ShouldNot(HaveOccurred())
			_, metrics, _ := analyzer.Report()
			Expect(metrics.NumFiles).To(Equal(2))
		})

		It("should find errors when nosec is not in use", func() {
			sample := testutils.SampleCodeG401[0]
			source := sample.Code[0]
			analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())

			controlPackage := testutils.NewTestPackage()
			defer controlPackage.Close()
			controlPackage.AddFile("md5.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(false).RulesInfo())
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `
				package main
				func main()
				}`)
			err := pkg.Build()
			Expect(err).ShouldNot(HaveOccurred())
			err = analyzer.Process(buildTags, pkg.Path)
			Expect(err).ShouldNot(HaveOccurred())
			_, _, errors := analyzer.Report()
			foundErr := false
			for _, ferr := range errors {
				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 line comment is present", 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())
			nosecIssues, _, _ := analyzer.Report()
			Expect(nosecIssues).Should(BeEmpty())
		})

		It("should not report errors when a nosec block comment is present", 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())
			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]
			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)
			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 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 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 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))
		})

		XIt("should be possible to overwrite nosec comments, and report issues but the 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, "true")
			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() //#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, 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 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 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 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 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 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 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", 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", 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", `
				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(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{
					{
						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 return error when line cannot be parsed", func() {
			pkg := &packages.Package{
				Errors: []packages.Error{
					{
						Pos: "file:line",
						Msg: "build error",
					},
				},
			}
			err := analyzer.ParseErrors(pkg)
			Expect(err).Should(HaveOccurred())
		})

		It("should return error when column cannot be parsed", func() {
			pkg := &packages.Package{
				Errors: []packages.Error{
					{
						Pos: "file:1:column",
						Msg: "build error",
					},
				},
			}
			err := analyzer.ParseErrors(pkg)
			Expect(err).Should(HaveOccurred())
		})

		It("should append  error to the same file", func() {
			pkg := &packages.Package{
				Errors: []packages.Error{
					{
						Pos: "file:1:2",
						Msg: "error1",
					},
					{
						Pos: "file:3:4",
						Msg: "error2",
					},
				},
			}
			err := analyzer.ParseErrors(pkg)
			Expect(err).ShouldNot(HaveOccurred())
			_, _, errors := analyzer.Report()
			Expect(errors).To(HaveLen(1))
			for _, ferr := range errors {
				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`))
				Expect(ferr[1].Line).To(Equal(3))
				Expect(ferr[1].Column).To(Equal(4))
				Expect(ferr[1].Err).Should(MatchRegexp(`error2`))
			}
		})

		It("should set the config", func() {
			config := gosec.NewConfig()
			config["test"] = "test"
			analyzer.SetConfig(config)
			found := analyzer.Config()
			Expect(config).To(Equal(found))
		})

		It("should reset the analyzer", func() {
			analyzer.Reset()
			issues, metrics, errors := analyzer.Report()
			Expect(issues).To(BeEmpty())
			Expect(*metrics).To(Equal(gosec.Metrics{}))
			Expect(errors).To(BeEmpty())
		})
	})

	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`))
			_, _, errors := analyzer.Report()
			Expect(errors).To(BeEmpty())
		})

		It("should add a new error", func() {
			pkg := &packages.Package{
				Errors: []packages.Error{
					{
						Pos: "file:1:2",
						Msg: "build error",
					},
				},
			}
			err := analyzer.ParseErrors(pkg)
			Expect(err).ShouldNot(HaveOccurred())
			analyzer.AppendError("file", errors.New("file build error"))
			_, _, errors := analyzer.Report()
			Expect(errors).To(HaveLen(1))
			for _, ferr := range errors {
				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 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 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 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))
		})
	})
})
07070100000012000041ED000000000000000000000002652CE66700000000000000000000000000000000000000000000001700000000gosec-2.18.1/analyzers07070100000013000081A4000000000000000000000001652CE66700002553000000000000000000000000000000000000002700000000gosec-2.18.1/analyzers/slice_bounds.go// (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)
			}
			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)
							}
						}
					}
				}
			}
		}
	}

	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")
}
07070100000014000081A4000000000000000000000001652CE66700000A93000000000000000000000000000000000000001F00000000gosec-2.18.1/analyzers/util.go// (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{
		newSliceBoundsAnalyzer("G602", "Possible slice bounds out of range"),
	}
}

// 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)
	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
}
0707010000001500008180000000000000000000000001652CE66700000D24000000000000000000000000000000000000001A00000000gosec-2.18.1/call_list.go//
// 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

import (
	"go/ast"
	"strings"
)

const vendorPath = "vendor/"

type set map[string]bool

// CallList is used to check for usage of specific packages
// and functions.
type CallList map[string]set

// NewCallList creates a new empty CallList
func NewCallList() CallList {
	return make(CallList)
}

// AddAll will add several calls to the call list at once
func (c CallList) AddAll(selector string, idents ...string) {
	for _, ident := range idents {
		c.Add(selector, ident)
	}
}

// Add a selector and call to the call list
func (c CallList) Add(selector, ident string) {
	if _, ok := c[selector]; !ok {
		c[selector] = make(set)
	}
	c[selector][ident] = true
}

// Contains returns true if the package and function are
// members of this call list.
func (c CallList) Contains(selector, ident string) bool {
	if idents, ok := c[selector]; ok {
		_, found := idents[ident]
		return found
	}
	return false
}

// ContainsPointer returns true if a pointer to the selector type or the type
// itself is a members of this call list.
func (c CallList) ContainsPointer(selector, indent string) bool {
	if strings.HasPrefix(selector, "*") {
		if c.Contains(selector, indent) {
			return true
		}
		s := strings.TrimPrefix(selector, "*")
		return c.Contains(s, indent)
	}
	return false
}

// ContainsPkgCallExpr resolves the call expression name and type, and then further looks
// up the package path for that type. Finally, it determines if the call exists within the call list
func (c CallList) ContainsPkgCallExpr(n ast.Node, ctx *Context, stripVendor bool) *ast.CallExpr {
	selector, ident, err := GetCallInfo(n, ctx)
	if err != nil {
		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(selector, vendorPath); vendorIdx >= 0 {
			selector = selector[vendorIdx+len(vendorPath):]
		}
	}
	if !c.Contains(selector, ident) {
		return nil
	}

	return n.(*ast.CallExpr)
}

// ContainsCallExpr resolves the call expression name and type, and then determines
// if the call exists with the call list
func (c CallList) ContainsCallExpr(n ast.Node, ctx *Context) *ast.CallExpr {
	selector, ident, err := GetCallInfo(n, ctx)
	if err != nil {
		return nil
	}
	if !c.Contains(selector, ident) && !c.ContainsPointer(selector, ident) {
		return nil
	}

	return n.(*ast.CallExpr)
}
07070100000016000081A4000000000000000000000001652CE66700000CFF000000000000000000000000000000000000001F00000000gosec-2.18.1/call_list_test.gopackage gosec_test

import (
	"go/ast"

	. "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
	BeforeEach(func() {
		calls = gosec.NewCallList()
	})

	It("should not return any matches when empty", func() {
		Expect(calls.Contains("foo", "bar")).Should(BeFalse())
	})

	It("should be possible to add a single call", func() {
		Expect(calls).Should(BeEmpty())
		calls.Add("foo", "bar")
		Expect(calls).Should(HaveLen(1))

		expected := make(map[string]bool)
		expected["bar"] = true
		actual := map[string]bool(calls["foo"])
		Expect(actual).Should(Equal(expected))
	})

	It("should be possible to add multiple calls at once", func() {
		Expect(calls).Should(BeEmpty())
		calls.AddAll("fmt", "Sprint", "Sprintf", "Printf", "Println")

		expected := map[string]bool{
			"Sprint":  true,
			"Sprintf": true,
			"Printf":  true,
			"Println": true,
		}
		actual := map[string]bool(calls["fmt"])
		Expect(actual).Should(Equal(expected))
	})

	It("should be possible to add pointer call", func() {
		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(BeEmpty())
		calls.Add("bytes.Buffer", "WriteString")
		actual := calls.ContainsPointer("*bytes.Buffer", "WriteString")
		Expect(actual).Should(BeTrue())
	})

	It("should not return a match if none are present", func() {
		calls.Add("ioutil", "Copy")
		Expect(calls.Contains("fmt", "Println")).Should(BeFalse())
	})

	It("should match a call based on selector and ident", func() {
		calls.Add("ioutil", "Copy")
		Expect(calls.Contains("ioutil", "Copy")).Should(BeTrue())
	})

	It("should match a package call expression", func() {
		// Create file to be scanned
		pkg := testutils.NewTestPackage()
		defer pkg.Close()
		pkg.AddFile("md5.go", testutils.SampleCodeG401[0].Code[0])

		ctx := pkg.CreateContext("md5.go")

		// Search for md5.New()
		calls.Add("crypto/md5", "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()
		defer pkg.Close()
		pkg.AddFile("main.go", testutils.SampleCodeG104[6].Code[0])

		ctx := pkg.CreateContext("main.go")

		calls.Add("bytes.Buffer", "WriteString")
		calls.Add("strings.Builder", "WriteString")
		calls.Add("io.Pipe", "CloseWithError")
		calls.Add("fmt", "Fprintln")

		// 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.ContainsCallExpr(n, ctx) != nil {
				matched++
			}
			return true
		}
		ast.Walk(v, ctx.Root)
		Expect(matched).Should(Equal(5))
	})
})
07070100000017000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001100000000gosec-2.18.1/cmd07070100000018000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001700000000gosec-2.18.1/cmd/gosec07070100000019000081A4000000000000000000000001652CE6670000351D000000000000000000000000000000000000001F00000000gosec-2.18.1/cmd/gosec/main.go// (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 main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sort"
	"strings"

	"github.com/securego/gosec/v2"
	"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"
)

const (
	usageText = `
gosec - Golang security checker

gosec analyzes Go source code to look for common programming mistakes that
can lead to security problems.

VERSION: %s
GIT TAG: %s
BUILD DATE: %s

USAGE:

	# Check a single package
	$ gosec $GOPATH/src/github.com/example/project

	# Check all packages under the current directory and save results in
	# json format.
	$ gosec -fmt=json -out=results.json ./...

	# Run a specific set of rules (by default all rules will be run):
	$ gosec -include=G101,G203,G401  ./...

	# Run all rules except the provided
	$ gosec -exclude=G101 $GOPATH/src/github.com/example/project/...

`
)

type arrayFlags []string

func (a *arrayFlags) String() string {
	return strings.Join(*a, " ")
}

func (a *arrayFlags) Set(value string) error {
	*a = append(*a, value)
	return nil
}

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")

	// output file
	flagOutput = flag.String("out", "", "Set output file for results")

	// config file
	flagConfig = flag.String("conf", "", "Path to optional config file")

	// quiet
	flagQuiet = flag.Bool("quiet", false, "Only show output when errors are found")

	// rules to explicitly include
	flagRulesInclude = flag.String("include", "", "Comma separated list of rules IDs to include. (see rule list)")

	// rules to explicitly exclude
	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")

	// go build tags
	flagBuildTags = flag.String("tags", "", "Comma separated list of build tags")

	// fail by severity
	flagSeverity = flag.String("severity", "low", "Filter out the issues with a lower severity than the given value. Valid options are: low, medium, high")

	// 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")

	// scan tests files
	flagScanTests = flag.Bool("tests", false, "Scan tests files")

	// print version and quit with exit code 0
	flagVersion = flag.Bool("version", false, "Print version and quit with exit code 0")

	// 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")

	// exclude the folders from scan
	flagDirsExclude arrayFlags

	logger *log.Logger
)

// #nosec
func usage() {
	usageText := fmt.Sprintf(usageText, Version, GitTag, BuildDate)
	fmt.Fprintln(os.Stderr, usageText)
	fmt.Fprint(os.Stderr, "OPTIONS:\n\n")
	flag.PrintDefaults()
	fmt.Fprint(os.Stderr, "\n\nRULES:\n\n")

	// sorted rule list for ease of reading
	rl := rules.Generate(*flagTrackSuppressions)
	keys := make([]string, 0, len(rl.Rules))
	for key := range rl.Rules {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := rl.Rules[k]
		fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, v.Description)
	}
	fmt.Fprint(os.Stderr, "\n")
}

func loadConfig(configFile string) (gosec.Config, error) {
	config := gosec.NewConfig()
	if configFile != "" {
		// #nosec
		file, err := os.Open(configFile)
		if err != nil {
			return nil, err
		}
		defer file.Close() // #nosec G307
		if _, err := config.ReadFrom(file); err != nil {
			return nil, err
		}
	}
	if *flagIgnoreNoSec {
		config.SetGlobal(gosec.Nosec, "true")
	}
	if *flagShowIgnored {
		config.SetGlobal(gosec.ShowIgnored, "true")
	}
	if *flagAlternativeNoSec != "" {
		config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
	}
	// 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 IncludeRules  is nil
	if v, _ := config.GetGlobal(gosec.ExcludeRules); flagRulesExclude.String() != "" || v == "" {
		config.SetGlobal(gosec.ExcludeRules, flagRulesExclude.String())
	}
	return config, nil
}

func loadRules(include, exclude string) rules.RuleList {
	var filters []rules.RuleFilter
	if include != "" {
		logger.Printf("Including rules: %s", include)
		including := strings.Split(include, ",")
		filters = append(filters, rules.NewRuleFilter(false, including...))
	} else {
		logger.Println("Including rules: default")
	}

	if exclude != "" {
		logger.Printf("Excluding rules: %s", exclude)
		excluding := strings.Split(exclude, ",")
		filters = append(filters, rules.NewRuleFilter(true, excluding...))
	} else {
		logger.Println("Excluding rules: default")
	}
	return rules.Generate(*flagTrackSuppressions, filters...)
}

func getRootPaths(paths []string) []string {
	rootPaths := make([]string, 0)
	for _, path := range paths {
		rootPath, err := gosec.RootPath(path)
		if err != nil {
			logger.Fatal(fmt.Errorf("failed to get the root path of the projects: %w", err))
		}
		rootPaths = append(rootPaths, rootPath)
	}
	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 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 issue.Low, nil
	case "medium":
		return issue.Medium, nil
	case "high":
		return issue.High, nil
	default:
		return issue.Low, fmt.Errorf("provided value '%s' not valid. Valid options: low, medium, high", value)
	}
}

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, 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() {
	// Makes sure some version information is set
	prepareVersionInfo()

	// Setup usage description
	flag.Usage = usage

	// Setup the excluded folders from scan
	flag.Var(&flagDirsExclude, "exclude-dir", "Exclude folder from scan (can be specified multiple times)")
	err := flag.Set("exclude-dir", "vendor")
	if err != nil {
		fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", "vendor")
	}
	err = flag.Set("exclude-dir", "\\.git/")
	if err != nil {
		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()

	if *flagVersion {
		fmt.Printf("Version: %s\nGit tag: %s\nBuild date: %s\n", Version, GitTag, BuildDate)
		os.Exit(0)
	}

	// 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)
	}

	// Setup logging
	logWriter := os.Stderr
	if *flagLogfile != "" {
		var e error
		logWriter, e = os.Create(*flagLogfile)
		if e != nil {
			flag.Usage()
			log.Fatal(e)
		}
	}

	if *flagQuiet || *flagTerse {
		logger = log.New(io.Discard, "", 0)
	} else {
		logger = log.New(logWriter, "[gosec] ", log.LstdFlags)
	}

	failSeverity, err := convertToScore(*flagSeverity)
	if err != nil {
		logger.Fatalf("Invalid severity value: %v", err)
	}

	failConfidence, err := convertToScore(*flagConfidence)
	if err != nil {
		logger.Fatalf("Invalid confidence value: %v", err)
	}

	// Load the analyzer configuration
	config, err := loadConfig(*flagConfig)
	if err != nil {
		logger.Fatal(err)
	}

	// Load enabled rule definitions
	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)
	if len(ruleList.Rules) == 0 {
		logger.Fatal("No rules are configured")
	}

	// Create the analyzer
	analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, *flagTrackSuppressions, *flagConcurrency, logger)
	analyzer.LoadRules(ruleList.RulesInfo())

	excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude)
	var packages []string

	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")
	}

	var buildTags []string
	if *flagBuildTags != "" {
		buildTags = strings.Split(*flagBuildTags, ",")
	}

	if err := analyzer.Process(buildTags, packages...); err != nil {
		logger.Fatal(err)
	}

	// Collect the results
	issues, metrics, errors := analyzer.Report()

	// Sort the issue by severity
	if *flagSortIssues {
		sortIssues(issues)
	}

	// Filter the issues by severity and confidence
	var trueIssues int
	issues, trueIssues = filterIssues(issues, failSeverity, failConfidence)
	if metrics.NumFound != trueIssues {
		metrics.NumFound = trueIssues
	}

	// Exit quietly if nothing was found
	if len(issues) == 0 && *flagQuiet {
		os.Exit(0)
	}

	// Create output report
	rootPaths := getRootPaths(flag.Args())

	reportInfo := gosec.NewReportInfo(issues, metrics, errors).WithVersion(Version)

	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

	exit(issues, errors, *flagNoFail)
}
0707010000001A000081A4000000000000000000000001652CE6670000035B000000000000000000000000000000000000002600000000gosec-2.18.1/cmd/gosec/sort_issues.gopackage main

import (
	"sort"
	"strconv"
	"strings"

	"github.com/securego/gosec/v2/issue"
)

// handle ranges
func extractLineNumber(s string) int {
	lineNumber, _ := strconv.Atoi(strings.Split(s, "-")[0])
	return lineNumber
}

type sortBySeverity []*issue.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 []*issue.Issue) {
	sort.Sort(sortBySeverity(issues))
}
0707010000001B000081A4000000000000000000000001652CE66700000661000000000000000000000000000000000000002B00000000gosec-2.18.1/cmd/gosec/sort_issues_test.gopackage main

import (
	"testing"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2/issue"
)

var defaultIssue = 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:        issue.GetCweByRule("G101"),
}

func createIssue() issue.Issue {
	return defaultIssue
}

func TestRules(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Sort issues Suite")
}

func firstIsGreater(less, greater *issue.Issue) {
	slice := []*issue.Issue{less, greater}

	sortIssues(slice)

	ExpectWithOffset(0, slice[0]).To(Equal(greater))
}

var _ = Describe("Sorting by Severity", func() {
	It("sorts by severity", func() {
		less := createIssue()
		less.Severity = issue.Low
		greater := createIssue()
		less.Severity = issue.High
		firstIsGreater(&less, &greater)
	})

	Context("Severity is same", func() {
		It("sorts by What", func() {
			less := createIssue()
			less.What = "test1"
			greater := createIssue()
			greater.What = "test2"
			firstIsGreater(&less, &greater)
		})
	})

	Context("Severity and What is same", func() {
		It("sorts by File", func() {
			less := createIssue()
			less.File = "test1"
			greater := createIssue()
			greater.File = "test2"

			firstIsGreater(&less, &greater)
		})
	})

	Context("Severity, What and File is same", func() {
		It("sorts by line number", func() {
			less := createIssue()
			less.Line = "1"
			greater := createIssue()
			greater.Line = "2"

			firstIsGreater(&less, &greater)
		})
	})
})
0707010000001C00008180000000000000000000000001652CE667000002C4000000000000000000000000000000000000002200000000gosec-2.18.1/cmd/gosec/version.gopackage main

// Version is the build version
var Version string

// GitTag is the git tag of the build
var GitTag string

// BuildDate is the date when the build was created
var BuildDate string

// prepareVersionInfo sets some runtime version when the version value
// was not injected by the build into the binary (e.g. go get).
// This returns currently "(devel)" but not an effective version until
// https://github.com/golang/go/issues/29814 gets resolved.
func prepareVersionInfo() {
	if Version == "" {
		// bi, _ := debug.ReadBuildInfo()
		// Version = bi.Main.Version
		// TODO use the debug information when it will provide more details
		// It seems to panic with Go 1.13.
		Version = "dev"
	}
}
0707010000001D000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001B00000000gosec-2.18.1/cmd/gosecutil0707010000001E00008180000000000000000000000001652CE6670000199C000000000000000000000000000000000000002400000000gosec-2.18.1/cmd/gosecutil/tools.go// (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 main

import (
	"flag"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"os"
	"strings"
)

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 {
	utils := make(map[string]command)
	utils["ast"] = dumpAst
	utils["callobj"] = dumpCallObj
	utils["uses"] = dumpUses
	utils["types"] = dumpTypes
	utils["defs"] = dumpDefs
	utils["comments"] = dumpComments
	utils["imports"] = dumpImports
	return &utilities{utils, make([]string, 0)}
}

func (u *utilities) String() string {
	i := 0
	keys := make([]string, len(u.commands))
	for k := range u.commands {
		keys[i] = k
		i++
	}
	return strings.Join(keys, ", ")
}

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
}

func (u *utilities) run(args ...string) {
	for _, util := range u.call {
		if cmd, ok := u.commands[util]; ok {
			cmd(args...)
		}
	}
}

func shouldSkip(path string) bool {
	st, e := os.Stat(path)
	if e != nil {
		//#nosec
		fmt.Fprintf(os.Stderr, "Skipping: %s - %s\n", path, e)
		return true
	}
	if st.IsDir() {
		//#nosec
		fmt.Fprintf(os.Stderr, "Skipping: %s - directory\n", path)
		return true
	}
	return false
}

func dumpAst(files ...string) {
	for _, arg := range files {
		// Ensure file exists and not a directory
		if shouldSkip(arg) {
			continue
		}

		// Create the AST by parsing src.
		fset := token.NewFileSet() // positions are relative to fset
		f, err := parser.ParseFile(fset, arg, nil, 0)
		if err != nil {
			//#nosec
			fmt.Fprintf(os.Stderr, "Unable to parse file %s\n", err)
			continue
		}

		//#nosec -- Print the AST.
		ast.Print(fset, f)
	}
}

type context struct {
	fileset  *token.FileSet
	comments ast.CommentMap
	info     *types.Info
	pkg      *types.Package
	config   *types.Config
	root     *ast.File
}

func createContext(filename string) *context {
	fileset := token.NewFileSet()
	root, e := parser.ParseFile(fileset, filename, nil, parser.ParseComments)
	if e != nil {
		//#nosec
		fmt.Fprintf(os.Stderr, "Unable to parse file: %s. Reason: %s\n", filename, e)
		return nil
	}
	comments := ast.NewCommentMap(fileset, root, root.Comments)
	info := &types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
		Scopes:     make(map[ast.Node]*types.Scope),
		Implicits:  make(map[ast.Node]types.Object),
	}
	config := types.Config{Importer: importer.Default()}
	pkg, e := config.Check("main.go", fileset, []*ast.File{root}, info)
	if e != nil {
		//#nosec
		fmt.Fprintf(os.Stderr, "Type check failed for file: %s. Reason: %s\n", filename, e)
		return nil
	}
	return &context{fileset, comments, info, pkg, &config, root}
}

func printObject(obj types.Object) {
	fmt.Println("OBJECT")
	if obj == nil {
		fmt.Println("object is nil")
		return
	}
	fmt.Printf("   Package = %v\n", obj.Pkg())
	if obj.Pkg() != nil {
		fmt.Println("   Path = ", obj.Pkg().Path())
		fmt.Println("   Name = ", obj.Pkg().Name())
		fmt.Println("   String = ", obj.Pkg().String())
	}
	fmt.Printf("   Name = %v\n", obj.Name())
	fmt.Printf("   Type = %v\n", obj.Type())
	fmt.Printf("   Id = %v\n", obj.Id())
}

func checkContext(ctx *context, file string) bool {
	//#nosec
	if ctx == nil {
		fmt.Fprintln(os.Stderr, "Failed to create context for file: ", file)
		return false
	}
	return true
}

func dumpCallObj(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		ast.Inspect(context.root, func(n ast.Node) bool {
			var obj types.Object
			switch node := n.(type) {
			case *ast.Ident:
				obj = context.info.ObjectOf(node) // context.info.Uses[node]
			case *ast.SelectorExpr:
				obj = context.info.ObjectOf(node.Sel) // context.info.Uses[node.Sel]
			default:
				obj = nil
			}
			if obj != nil {
				printObject(obj)
			}
			return true
		})
	}
}

func dumpUses(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		for ident, obj := range context.info.Uses {
			fmt.Printf("IDENT: %v, OBJECT: %v\n", ident, obj)
		}
	}
}

func dumpTypes(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		for expr, tv := range context.info.Types {
			fmt.Printf("EXPR: %v, TYPE: %v\n", expr, tv)
		}
	}
}

func dumpDefs(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		for ident, obj := range context.info.Defs {
			fmt.Printf("IDENT: %v, OBJ: %v\n", ident, obj)
		}
	}
}

func dumpComments(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		for _, group := range context.comments.Comments() {
			fmt.Println(group.Text())
		}
	}
}

func dumpImports(files ...string) {
	for _, file := range files {
		if shouldSkip(file) {
			continue
		}
		context := createContext(file)
		if !checkContext(context, file) {
			return
		}
		for _, pkg := range context.pkg.Imports() {
			fmt.Println(pkg.Path(), pkg.Name())
			for _, name := range pkg.Scope().Names() {
				fmt.Println("  => ", name)
			}
		}
	}
}

func main() {
	tools := newUtils()
	flag.Var(tools, "tool", "Utils to assist with rule development")
	flag.Parse()

	if len(tools.call) > 0 {
		tools.run(flag.Args()...)
		os.Exit(0)
	}
}
0707010000001F000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001B00000000gosec-2.18.1/cmd/tlsconfig07070100000020000081A4000000000000000000000001652CE667000000DF000000000000000000000000000000000000002E00000000gosec-2.18.1/cmd/tlsconfig/header_template.gopackage main

import "text/template"

var generatedHeaderTmpl = template.Must(template.New("generated").Parse(`
package {{.}}

import (
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)
`))
07070100000021000081A4000000000000000000000001652CE6670000028C000000000000000000000000000000000000002C00000000gosec-2.18.1/cmd/tlsconfig/rule_template.gopackage main

import "text/template"

var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
// New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers
// DO NOT EDIT - generated by tlsconfig tool
func New{{.Name}}TLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return &insecureConfigTLS{
                MetaData: issue.MetaData{ID: id},
		requiredType: "crypto/tls.Config",
		MinVersion:   {{ .MinVersion }},
		MaxVersion:   {{ .MaxVersion }},
		goodCiphers: []string{
{{range $cipherName := .Ciphers }} "{{$cipherName}}",
{{end}}
		},
	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
}
`))
0707010000002200008180000000000000000000000001652CE66700000206000000000000000000000000000000000000002A00000000gosec-2.18.1/cmd/tlsconfig/tls_version.gopackage 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)
		default:
			continue
		}
	}
	sort.Ints(versions)
	return versions
}
07070100000023000081A4000000000000000000000001652CE6670000165C000000000000000000000000000000000000002800000000gosec-2.18.1/cmd/tlsconfig/tlsconfig.gopackage main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"go/format"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mozilla/tls-observatory/constants"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

var (
	pkg        = flag.String("pkg", "rules", "package name to be added to the output file")
	outputFile = flag.String("outputFile", "tls_config.go", "name of the output file")
)

// TLSConfURL url where Mozilla publishes the TLS ciphers recommendations
const TLSConfURL = "https://statics.tls.security.mozilla.org/server-side-tls-conf.json"

// ServerSideTLSJson contains all the available configurations and the version of the current document.
type ServerSideTLSJson struct {
	Configurations map[string]Configuration `json:"configurations"`
	Version        float64                  `json:"version"`
}

// Configuration represents configurations levels declared by the Mozilla server-side-tls
// see https://wiki.mozilla.org/Security/Server_Side_TLS
type Configuration struct {
	OpenSSLCiphersuites   []string `json:"openssl_ciphersuites"`
	OpenSSLCiphers        []string `json:"openssl_ciphers"`
	TLSVersions           []string `json:"tls_versions"`
	TLSCurves             []string `json:"tls_curves"`
	CertificateTypes      []string `json:"certificate_types"`
	CertificateCurves     []string `json:"certificate_curves"`
	CertificateSignatures []string `json:"certificate_signatures"`
	RsaKeySize            float64  `json:"rsa_key_size"`
	DHParamSize           float64  `json:"dh_param_size"`
	ECDHParamSize         float64  `json:"ecdh_param_size"`
	HstsMinAge            float64  `json:"hsts_min_age"`
	OldestClients         []string `json:"oldest_clients"`
	OCSPStaple            bool     `json:"ocsp_staple"`
	ServerPreferredOrder  bool     `json:"server_preferred_order"`
	MaxCertLifespan       float64  `json:"maximum_certificate_lifespan"`
}

type goCipherConfiguration struct {
	Name       string
	Ciphers    []string
	MinVersion string
	MaxVersion string
}

type goTLSConfiguration struct {
	cipherConfigs []goCipherConfiguration
}

// 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
	if err != nil {
		return nil, err
	}
	defer r.Body.Close() //#nosec G307

	var sstls ServerSideTLSJson
	err = json.NewDecoder(r.Body).Decode(&sstls)
	if err != nil {
		return nil, err
	}

	return &sstls, nil
}

func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfiguration, error) {
	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)
	}

	// These ciphers are already defined in IANA format
	cipherConf.Ciphers = append(cipherConf.Ciphers, conf.OpenSSLCiphersuites...)

	for _, cipherName := range conf.OpenSSLCiphers {
		cipherSuite, ok := constants.CipherSuites[cipherName]
		if !ok {
			log.Printf("'%s' cipher is not available in crypto/tls package\n", cipherName)
		}
		if len(cipherSuite.IANAName) > 0 {
			cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.IANAName)
			if len(cipherSuite.NSSName) > 0 && cipherSuite.NSSName != cipherSuite.IANAName {
				cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.NSSName)
			}
		}
	}

	versions := mapTLSVersions(conf.TLSVersions)
	if len(versions) > 0 {
		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, nil
}

func getGoTLSConf() (goTLSConfiguration, error) {
	sstls, err := getTLSConfFromURL(TLSConfURL)
	if err != nil || sstls == nil {
		msg := fmt.Sprintf("Could not load the Server Side TLS configuration from Mozilla's website. Check the URL: %s. Error: %v\n",
			TLSConfURL, err)
		panic(msg)
	}

	tlsConfig := goTLSConfiguration{}

	modern, err := getGoCipherConfig("modern", *sstls)
	if err != nil {
		return tlsConfig, err
	}
	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, modern)

	intermediate, err := getGoCipherConfig("intermediate", *sstls)
	if err != nil {
		return tlsConfig, err
	}
	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, intermediate)

	old, err := getGoCipherConfig("old", *sstls)
	if err != nil {
		return tlsConfig, err
	}
	tlsConfig.cipherConfigs = append(tlsConfig.cipherConfigs, old)

	return tlsConfig, nil
}

func getCurrentDir() (string, error) {
	dir := "."
	if args := flag.Args(); len(args) == 1 {
		dir = args[0]
	} else if len(args) > 1 {
		return "", errors.New("only one directory at a time")
	}
	dir, err := filepath.Abs(dir)
	if err != nil {
		return "", err
	}
	return dir, nil
}

func main() {
	dir, err := getCurrentDir()
	if err != nil {
		log.Fatalln(err)
	}
	tlsConfig, err := getGoTLSConf()
	if err != nil {
		log.Fatalln(err)
	}

	var buf bytes.Buffer
	err = generatedHeaderTmpl.Execute(&buf, *pkg)
	if err != nil {
		log.Fatalf("Failed to generate the header: %v", err)
	}
	for _, cipherConfig := range tlsConfig.cipherConfigs {
		err := generatedRuleTmpl.Execute(&buf, cipherConfig)
		if err != nil {
			log.Fatalf("Failed to generated the cipher config: %v", err)
		}
	}

	src, err := format.Source(buf.Bytes())
	if err != nil {
		log.Printf("warnings: Failed to format the code: %v", err)
		src = buf.Bytes()
	}

	outputPath := filepath.Join(dir, *outputFile)
	if err := os.WriteFile(outputPath, src, 0o644); err != nil /*#nosec G306*/ {
		log.Fatalf("Writing output: %s", err)
	}
}
07070100000024000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001700000000gosec-2.18.1/cmd/vflag0707010000002500008180000000000000000000000001652CE667000001A5000000000000000000000000000000000000001F00000000gosec-2.18.1/cmd/vflag/flag.gopackage 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
}
07070100000026000081A4000000000000000000000001652CE66700000FAD000000000000000000000000000000000000001700000000gosec-2.18.1/config.gopackage gosec

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
)

const (
	// Globals are applicable to all rules and used for general
	// configuration settings for gosec.
	Globals = "global"
)

// GlobalOption defines the name of the global options
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{}

// NewConfig initializes a new configuration instance. The configuration data then
// needs to be loaded via c.ReadFrom(strings.NewReader("config data"))
// or from a *os.File.
func NewConfig() Config {
	cfg := make(Config)
	cfg[Globals] = make(map[GlobalOption]string)
	return cfg
}

func (c Config) keyToGlobalOptions(key string) GlobalOption {
	return GlobalOption(key)
}

func (c Config) convertGlobals() {
	if globals, ok := c[Globals]; ok {
		if settings, ok := globals.(map[string]interface{}); ok {
			validGlobals := map[GlobalOption]string{}
			for k, v := range settings {
				validGlobals[c.keyToGlobalOptions(k)] = fmt.Sprintf("%v", v)
			}
			c[Globals] = validGlobals
		}
	}
}

// ReadFrom implements the io.ReaderFrom interface. This
// should be used with io.Reader to load configuration from
// file or from string etc.
func (c Config) ReadFrom(r io.Reader) (int64, error) {
	data, err := io.ReadAll(r)
	if err != nil {
		return int64(len(data)), err
	}
	if err = json.Unmarshal(data, &c); err != nil {
		return int64(len(data)), err
	}
	c.convertGlobals()
	return int64(len(data)), nil
}

// WriteTo implements the io.WriteTo interface. This should
// be used to save or print out the configuration information.
func (c Config) WriteTo(w io.Writer) (int64, error) {
	data, err := json.Marshal(c)
	if err != nil {
		return int64(len(data)), err
	}
	return io.Copy(w, bytes.NewReader(data))
}

// Get returns the configuration section for the supplied key
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 settings, nil
}

// Set section in the configuration to specified value
func (c Config) Set(section string, value interface{}) {
	c[section] = value
}

// GetGlobal returns value associated with global configuration option
func (c Config) GetGlobal(option GlobalOption) (string, error) {
	if globals, ok := c[Globals]; ok {
		if settings, ok := globals.(map[GlobalOption]string); ok {
			if value, ok := settings[option]; ok {
				return value, nil
			}
			return "", fmt.Errorf("global setting for %s not found", option)
		}
	}
	return "", fmt.Errorf("no global config options found")
}

// SetGlobal associates a value with a global configuration option
func (c Config) SetGlobal(option GlobalOption, value string) {
	if globals, ok := c[Globals]; ok {
		if settings, ok := globals.(map[GlobalOption]string); ok {
			settings[option] = value
		}
	}
}

// IsGlobalEnabled checks if a global option is enabled
func (c Config) IsGlobalEnabled(option GlobalOption) (bool, error) {
	value, err := c.GetGlobal(option)
	if err != nil {
		return false, err
	}
	return (value == "true" || value == "enabled"), nil
}
07070100000027000081A4000000000000000000000001652CE6670000103C000000000000000000000000000000000000001C00000000gosec-2.18.1/config_test.gopackage gosec_test

import (
	"bytes"
	"strings"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2"
)

var _ = Describe("Configuration", func() {
	var configuration gosec.Config
	BeforeEach(func() {
		configuration = gosec.NewConfig()
	})

	Context("when loading from disk", func() {
		It("should be possible to load configuration from a file", func() {
			json := `{"G101": {}}`
			buffer := bytes.NewBufferString(json)
			nread, err := configuration.ReadFrom(buffer)
			Expect(nread).Should(Equal(int64(len(json))))
			Expect(err).ShouldNot(HaveOccurred())
		})

		It("should return an error if configuration file is invalid", func() {
			var err error
			invalidBuffer := bytes.NewBuffer([]byte{0xc0, 0xff, 0xee})
			_, err = configuration.ReadFrom(invalidBuffer)
			Expect(err).Should(HaveOccurred())

			emptyBuffer := bytes.NewBuffer([]byte{})
			_, err = configuration.ReadFrom(emptyBuffer)
			Expect(err).Should(HaveOccurred())
		})
	})

	Context("when saving to disk", func() {
		It("should be possible to save an empty configuration to file", func() {
			expected := `{"global":{}}`
			buffer := bytes.NewBuffer([]byte{})
			nbytes, err := configuration.WriteTo(buffer)
			Expect(int(nbytes)).Should(Equal(len(expected)))
			Expect(err).ShouldNot(HaveOccurred())
			Expect(buffer.String()).Should(Equal(expected))
		})

		It("should be possible to save configuration to file", func() {
			configuration.Set("G101", map[string]string{
				"mode": "strict",
			})

			buffer := bytes.NewBuffer([]byte{})
			nbytes, err := configuration.WriteTo(buffer)
			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",
			}
			configuration.Set("G101", settings)

			retrieved, err := configuration.Get("G101")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(retrieved).Should(HaveKeyWithValue("ciphers", "AES256-GCM"))
			Expect(retrieved).ShouldNot(HaveKey("foobar"))
		})
	})

	Context("when using global configuration options", func() {
		It("should have a default global section", func() {
			settings, err := configuration.Get("global")
			Expect(err).ShouldNot(HaveOccurred())
			expectedType := make(map[gosec.GlobalOption]string)
			Expect(settings).Should(BeAssignableToTypeOf(expectedType))
		})

		It("should save global settings to correct section", func() {
			configuration.SetGlobal(gosec.Nosec, "enabled")
			settings, err := configuration.Get("global")
			Expect(err).ShouldNot(HaveOccurred())
			if globals, ok := settings.(map[gosec.GlobalOption]string); ok {
				Expect(globals["nosec"]).Should(MatchRegexp("enabled"))
			} else {
				Fail("globals are not defined as map")
			}

			setValue, err := configuration.GetGlobal(gosec.Nosec)
			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).ShouldNot(HaveOccurred())
			Expect(enabled).Should(BeTrue())
		})

		It("should parse the global settings of type string from file", func() {
			config := `
			{
				"global": {
					"nosec": "enabled"
				}
			}`
			cfg := gosec.NewConfig()
			_, err := cfg.ReadFrom(strings.NewReader(config))
			Expect(err).ShouldNot(HaveOccurred())

			value, err := cfg.GetGlobal(gosec.Nosec)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(value).Should(Equal("enabled"))
		})
		It("should parse the global settings of other types from file", func() {
			config := `
			{
				"global": {
					"nosec": true
				}
			}`
			cfg := gosec.NewConfig()
			_, err := cfg.ReadFrom(strings.NewReader(config))
			Expect(err).ShouldNot(HaveOccurred())

			value, err := cfg.GetGlobal(gosec.Nosec)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(value).Should(Equal("true"))
		})
	})
})
0707010000002800008180000000000000000000000001652CE667000000B2000000000000000000000000000000000000001800000000gosec-2.18.1/cosign.pub-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFphl7f2VuFRfsi4wqiLUCQ9xHQgV
O2VMDNcvh+kxiymLXa+GkPzSKExFYIlVwfg13URvCiB+kFvITmLzuLiGQg==
-----END PUBLIC KEY-----
07070100000029000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001100000000gosec-2.18.1/cwe0707010000002A00008180000000000000000000000001652CE667000000B9000000000000000000000000000000000000002300000000gosec-2.18.1/cwe/cwe_suite_test.gopackage cwe_test

import (
	"testing"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

func TestCwe(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Cwe Suite")
}
0707010000002B000081A4000000000000000000000001652CE66700001D2C000000000000000000000000000000000000001900000000gosec-2.18.1/cwe/data.gopackage 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{
	"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",
	},
	"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')",
	},
	"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",
	},
	"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)",
	},
	"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",
	},
	"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')",
	},
	"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",
	},
	"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')",
	},
	"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",
	},
}

// 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
}
0707010000002C000081A4000000000000000000000001652CE667000001F7000000000000000000000000000000000000001E00000000gosec-2.18.1/cwe/data_test.gopackage 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())
		})
	})
})
0707010000002D00008180000000000000000000000001652CE667000002F1000000000000000000000000000000000000001A00000000gosec-2.18.1/cwe/types.gopackage 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(),
	})
}
0707010000002E000081A4000000000000000000000001652CE6670000031A000000000000000000000000000000000000001F00000000gosec-2.18.1/cwe/types_test.gopackage 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"))
		})
	})
})
0707010000002F000081C0000000000000000000000001652CE6670000018B000000000000000000000000000000000000001B00000000gosec-2.18.1/entrypoint.sh#!/usr/bin/env bash

# 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[*]}
0707010000003000008180000000000000000000000001652CE667000002B4000000000000000000000000000000000000001700000000gosec-2.18.1/errors.gopackage gosec

import (
	"sort"
)

// Error is used when there are golang errors while parsing the AST
type Error struct {
	Line   int    `json:"line"`
	Column int    `json:"column"`
	Err    string `json:"error"`
}

// NewError creates Error object
func NewError(line, column int, err string) *Error {
	return &Error{
		Line:   line,
		Column: column,
		Err:    err,
	}
}

// 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 {
			if errors[i].Line == errors[j].Line {
				return errors[i].Column <= errors[j].Column
			}
			return errors[i].Line < errors[j].Line
		})
	}
}
07070100000031000081A4000000000000000000000001652CE66700000511000000000000000000000000000000000000001A00000000gosec-2.18.1/flag_test.gopackage 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", "-test1=-incorrect"}
			f := vflag.ValidatedFlag{}
			flag.Var(&f, "test1", "")
			flag.CommandLine.Init("test1", 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.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`))
		})
	})
})
07070100000032000081A4000000000000000000000001652CE6670000039C000000000000000000000000000000000000001400000000gosec-2.18.1/go.modmodule github.com/securego/gosec/v2

require (
	github.com/ccojocar/zxcvbn-go v1.0.1
	github.com/google/uuid v1.3.1
	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.13.0
	github.com/onsi/gomega v1.28.0
	golang.org/x/crypto v0.14.0
	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
	golang.org/x/text v0.13.0
	golang.org/x/tools v0.14.0
	gopkg.in/yaml.v3 v3.0.1
)

require (
	github.com/go-logr/logr v1.2.4 // indirect
	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
	github.com/google/go-cmp v0.5.9 // indirect
	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
	golang.org/x/mod v0.13.0 // indirect
	golang.org/x/net v0.17.0 // indirect
	golang.org/x/sys v0.13.0 // indirect
)

go 1.20
07070100000033000081A4000000000000000000000001652CE667000105B7000000000000000000000000000000000000001400000000gosec-2.18.1/go.sumbitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
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/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/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/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=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/pubsub v1.5.0/go.mod h1:ZEwJccE3z93Z2HWvstpri00jOg7oO4UZDtKhwDwqF0w=
cloud.google.com/go/spanner v1.7.0/go.mod h1:sd3K2gZ9Fd0vMPLXzeCrF6fq4i63Q7aTLW/lBIfBkIk=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
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.1 h1:+sxrANSCj6CdadkcMnvde/GWU1vZiiXRbqYSCalV4/4=
github.com/ccojocar/zxcvbn-go v1.0.1/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
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=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
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.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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 v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
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=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
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.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
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/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
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-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
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.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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.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=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/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/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.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=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
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-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/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
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/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.28.0 h1:i2rg/p9n/UqIDAMFUJ6qIUUMcsqOuUHgbpbu235Vr1c=
github.com/onsi/gomega v1.28.0/go.mod h1:A1H2JE76sI14WIP57LMKj7FVfCHx3g3BcZVjJG8bjX8=
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=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/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=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
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/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=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/testify v0.0.0-20170130113145-4d4bfba8f1d1/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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
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=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
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-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
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=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
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=
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=
go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
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.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/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=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
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=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
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/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=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
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/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=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
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=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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-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-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=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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/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=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
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.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
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=
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=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
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/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=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181107211654-5fc9ac540362/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
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/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=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
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/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=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
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/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/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=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
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=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
0707010000003400008180000000000000000000000001652CE667000000BF000000000000000000000000000000000000002100000000gosec-2.18.1/gosec_suite_test.gopackage gosec_test

import (
	"testing"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

func TestGosec(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "gosec Suite")
}
07070100000035000081A4000000000000000000000001652CE66700003B2E000000000000000000000000000000000000001800000000gosec-2.18.1/helpers.go// (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 gosec

import (
	"errors"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"os"
	"os/user"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

// 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")
func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*ast.CallExpr, bool) {
	importedNames, found := GetImportedNames(pkg, c)
	if !found {
		return nil, false
	}

	if callExpr, ok := n.(*ast.CallExpr); ok {
		packageName, callName, err := GetCallInfo(callExpr, c)
		if err != nil {
			return nil, false
		}
		for _, in := range importedNames {
			if packageName != in {
				continue
			}
			for _, name := range names {
				if callName == name {
					return callExpr, true
				}
			}
		}
	}
	return nil, false
}

// MatchCompLit will match an ast.CompositeLit based on the supplied type
func MatchCompLit(n ast.Node, ctx *Context, required string) *ast.CompositeLit {
	if complit, ok := n.(*ast.CompositeLit); ok {
		typeOf := ctx.Info.TypeOf(complit)
		if typeOf.String() == required {
			return complit
		}
	}
	return nil
}

// GetInt will read and return an integer value from an ast.BasicLit
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)
}

// GetFloat will read and return a float value from an ast.BasicLit
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)
}

// GetChar will read and return a char value from an ast.BasicLit
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)
}

// 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
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)
}

// GetCallObject returns the object and call expression and associated
// object for a given AST node. nil, nil will be returned if the
// object cannot be resolved.
func GetCallObject(n ast.Node, ctx *Context) (*ast.CallExpr, types.Object) {
	switch node := n.(type) {
	case *ast.CallExpr:
		switch fn := node.Fun.(type) {
		case *ast.Ident:
			return node, ctx.Info.Uses[fn]
		case *ast.SelectorExpr:
			return node, ctx.Info.Uses[fn.Sel]
		}
	}
	return nil, nil
}

// GetCallInfo returns the package or type and name  associated with a
// call expression.
func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
	switch node := n.(type) {
	case *ast.CallExpr:
		switch fn := node.Fun.(type) {
		case *ast.SelectorExpr:
			switch expr := fn.X.(type) {
			case *ast.Ident:
				if expr.Obj != nil && expr.Obj.Kind == ast.Var {
					t := ctx.Info.TypeOf(expr)
					if t != nil {
						return t.String(), fn.Sel.Name, nil
					}
					return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
				}
				return expr.Name, fn.Sel.Name, nil
			case *ast.SelectorExpr:
				if expr.Sel != nil {
					t := ctx.Info.TypeOf(expr.Sel)
					if t != nil {
						return t.String(), fn.Sel.Name, nil
					}
					return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
				}
			case *ast.CallExpr:
				switch call := expr.Fun.(type) {
				case *ast.Ident:
					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
						}
						return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
					}
					if call.Obj != nil {
						switch decl := call.Obj.Decl.(type) {
						case *ast.FuncDecl:
							ret := decl.Type.Results
							if ret != nil && len(ret.List) > 0 {
								ret1 := ret.List[0]
								if ret1 != nil {
									t := ctx.Info.TypeOf(ret1.Type)
									if t != nil {
										return t.String(), fn.Sel.Name, nil
									}
									return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
								}
							}
						}
					}
				}
			}
		case *ast.Ident:
			return ctx.Pkg.Name(), fn.Name, nil
		}
	}

	return "", "", fmt.Errorf("unable to determine call info")
}

// GetCallStringArgsValues returns the values of strings arguments if they can be resolved
func GetCallStringArgsValues(n ast.Node, _ *Context) []string {
	values := []string{}
	switch node := n.(type) {
	case *ast.CallExpr:
		for _, arg := range node.Args {
			switch param := arg.(type) {
			case *ast.BasicLit:
				value, err := GetString(param)
				if err == nil {
					values = append(values, value)
				}
			case *ast.Ident:
				values = append(values, GetIdentStringValues(param)...)
			}
		}
	}
	return values
}

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 := stringFinder(v)
				if err == nil {
					values = append(values, value)
				}
			}
		case *ast.AssignStmt:
			for _, v := range decl.Rhs {
				value, err := stringFinder(v)
				if err == nil {
					values = append(values, value)
				}
			}
		}
	}
	return values
}

// getIdentStringRecursive 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 {
	var traverse func(be *ast.BinaryExpr)
	result := []ast.Node{}
	traverse = func(be *ast.BinaryExpr) {
		if lhs, ok := be.X.(*ast.BinaryExpr); ok {
			traverse(lhs)
		} else {
			result = append(result, be.X)
		}
		if rhs, ok := be.Y.(*ast.BinaryExpr); ok {
			traverse(rhs)
		} else {
			result = append(result, be.Y)
		}
	}
	traverse(be)
	return result
}

// 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(including aliases).
func GetImportPath(name string, ctx *Context) (string, bool) {
	for path := range ctx.Imports.Imported {
		if imported, ok := GetImportedNames(path, ctx); ok {
			for _, n := range imported {
				if n == name {
					return path, true
				}
			}
		}
	}

	return "", false
}

// GetLocation returns the filename and line number of an ast.Node
func GetLocation(n ast.Node, ctx *Context) (string, int) {
	fobj := ctx.FileSet.File(n.Pos())
	return fobj.Name(), fobj.Line(n.Pos())
}

// Gopath returns all GOPATHs
func Gopath() []string {
	defaultGoPath := runtime.GOROOT()
	if u, err := user.Current(); err == nil {
		defaultGoPath = filepath.Join(u.HomeDir, "go")
	}
	path := Getenv("GOPATH", defaultGoPath)
	paths := strings.Split(path, string(os.PathListSeparator))
	for idx, path := range paths {
		if abs, err := filepath.Abs(path); err == nil {
			paths[idx] = abs
		}
	}
	return paths
}

// Getenv returns the values of the environment variable, otherwise
// returns the default if variable is not set
func Getenv(key, userDefault string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return userDefault
}

// GetPkgRelativePath returns the Go relative path derived
// form the given path
func GetPkgRelativePath(path string) (string, error) {
	abspath, err := filepath.Abs(path)
	if err != nil {
		abspath = path
	}
	if strings.HasSuffix(abspath, ".go") {
		abspath = filepath.Dir(abspath)
	}
	for _, base := range Gopath() {
		projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base))
		if strings.HasPrefix(abspath, projectRoot) {
			return strings.TrimPrefix(abspath, projectRoot), nil
		}
	}
	return "", errors.New("no project relative path found")
}

// GetPkgAbsPath returns the Go package absolute path derived from
// the given path
func GetPkgAbsPath(pkgPath string) (string, error) {
	absPath, err := filepath.Abs(pkgPath)
	if err != nil {
		return "", err
	}
	if _, err := os.Stat(absPath); os.IsNotExist(err) {
		return "", errors.New("no project absolute path found")
	}
	return absPath, nil
}

// ConcatString recursively concatenates strings from a binary expression
func ConcatString(n *ast.BinaryExpr) (string, bool) {
	var s string
	// sub expressions are found in X object, Y object is always last BasicLit
	if rightOperand, ok := n.Y.(*ast.BasicLit); ok {
		if str, err := GetString(rightOperand); err == nil {
			s = str + s
		}
	} else {
		return "", false
	}
	if leftOperand, ok := n.X.(*ast.BinaryExpr); ok {
		if recursion, ok := ConcatString(leftOperand); ok {
			s = recursion + s
		}
	} else if leftOperand, ok := n.X.(*ast.BasicLit); ok {
		if str, err := GetString(leftOperand); err == nil {
			s = str + s
		}
	} else {
		return "", false
	}
	return s, true
}

// FindVarIdentities returns array of all variable identities in a given binary expression
func FindVarIdentities(n *ast.BinaryExpr, c *Context) ([]*ast.Ident, bool) {
	identities := []*ast.Ident{}
	// sub expressions are found in X object, Y object is always the last term
	if rightOperand, ok := n.Y.(*ast.Ident); ok {
		obj := c.Info.ObjectOf(rightOperand)
		if _, ok := obj.(*types.Var); ok && !TryResolve(rightOperand, c) {
			identities = append(identities, rightOperand)
		}
	}
	if leftOperand, ok := n.X.(*ast.BinaryExpr); ok {
		if leftIdentities, ok := FindVarIdentities(leftOperand, c); ok {
			identities = append(identities, leftIdentities...)
		}
	} else {
		if leftOperand, ok := n.X.(*ast.Ident); ok {
			obj := c.Info.ObjectOf(leftOperand)
			if _, ok := obj.(*types.Var); ok && !TryResolve(leftOperand, c) {
				identities = append(identities, leftOperand)
			}
		}
	}

	if len(identities) > 0 {
		return identities, true
	}
	// if nil or error, return false
	return nil, false
}

// PackagePaths returns a slice with all packages path at given root directory
func PackagePaths(root string, excludes []*regexp.Regexp) ([]string, error) {
	if strings.HasSuffix(root, "...") {
		root = root[0 : len(root)-3]
	} else {
		return []string{root}, nil
	}
	paths := map[string]bool{}
	err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
		if filepath.Ext(path) == ".go" {
			path = filepath.Dir(path)
			if isExcluded(filepath.ToSlash(path), excludes) {
				return nil
			}
			paths[path] = true
		}
		return nil
	})
	if err != nil {
		return []string{}, err
	}

	result := []string{}
	for path := range paths {
		result = append(result, path)
	}
	return result, nil
}

// isExcluded checks if a string matches any of the exclusion regexps
func isExcluded(str string, excludes []*regexp.Regexp) bool {
	if excludes == nil {
		return false
	}
	for _, exclude := range excludes {
		if exclude != nil && exclude.MatchString(str) {
			return true
		}
	}
	return false
}

// ExcludedDirsRegExp builds the regexps for a list of excluded dirs provided as strings
func ExcludedDirsRegExp(excludedDirs []string) []*regexp.Regexp {
	var exps []*regexp.Regexp
	for _, excludedDir := range excludedDirs {
		str := fmt.Sprintf(`([\\/])?%s([\\/])?`, strings.ReplaceAll(filepath.ToSlash(excludedDir), "/", `\/`))
		r := regexp.MustCompile(str)
		exps = append(exps, r)
	}
	return exps
}

// RootPath returns the absolute root path of a scan
func RootPath(root string) (string, error) {
	root = strings.TrimSuffix(root, "...")
	return filepath.Abs(root)
}

// GoVersion returns parsed version of Go from runtime
func GoVersion() (int, int, int) {
	return parseGoVersion(runtime.Version())
}

// parseGoVersion parses Go version.
// example:
// - go1.19rc2
// - go1.19beta2
// - go1.19.4
// - go1.19
func parseGoVersion(version string) (int, int, int) {
	exp := regexp.MustCompile(`go(\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
}
07070100000036000081A4000000000000000000000001652CE667000024DE000000000000000000000000000000000000001D00000000gosec-2.18.1/helpers_test.gopackage gosec_test

import (
	"go/ast"
	"os"
	"path/filepath"
	"regexp"

	. "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 package paths", func() {
		var dir string
		JustBeforeEach(func() {
			var err error
			dir, err = os.MkdirTemp("", "gosec")
			Expect(err).ShouldNot(HaveOccurred())
			_, err = os.MkdirTemp(dir, "test*.go")
			Expect(err).ShouldNot(HaveOccurred())
		})
		AfterEach(func() {
			err := os.RemoveAll(dir)
			Expect(err).ShouldNot(HaveOccurred())
		})
		It("should return the root directory as package path", func() {
			paths, err := gosec.PackagePaths(dir, nil)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(paths).Should(Equal([]string{dir}))
		})
		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, 0o755)
			Expect(err).ShouldNot(HaveOccurred())
			_, err = os.Create(nested + "/test.go")
			Expect(err).ShouldNot(HaveOccurred())
			exclude, err := regexp.Compile(`([\\/])?vendor([\\/])?`)
			Expect(err).ShouldNot(HaveOccurred())
			paths, err := gosec.PackagePaths(dir+"/...", []*regexp.Regexp{exclude})
			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)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(paths).Should(BeEmpty())
		})
	})

	Context("when getting the root path", func() {
		It("should return the absolute path from relative path", func() {
			base := "test"
			cwd, err := os.Getwd()
			Expect(err).ShouldNot(HaveOccurred())
			root, err := gosec.RootPath(base)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(root).Should(Equal(filepath.Join(cwd, base)))
		})
		It("should return the absolute path from ellipsis path", func() {
			base := "test"
			cwd, err := os.Getwd()
			Expect(err).ShouldNot(HaveOccurred())
			root, err := gosec.RootPath(filepath.Join(base, "..."))
			Expect(err).ShouldNot(HaveOccurred())
			Expect(root).Should(Equal(filepath.Join(cwd, base)))
		})
	})

	Context("when excluding the dirs", func() {
		It("should create a proper regexp", func() {
			r := gosec.ExcludedDirsRegExp([]string{"test"})
			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(r).Should(BeEmpty())
			r = gosec.ExcludedDirsRegExp([]string{})
			Expect(r).Should(BeEmpty())
		})
	})

	Context("when getting call info", func() {
		It("should return the type and call name for selector expression", func() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "bytes"
			)

			func main() {
			    b := new(bytes.Buffer)
				_, err := b.WriteString("test")
				if err != nil {
				    panic(err)
				}
			}
			`)
			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("*bytes.Buffer", "WriteString"))
		})

		It("should return the type and call name for new selector expression", func() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "bytes"
			)

			func main() {
				_, err := new(bytes.Buffer).WriteString("test")
				if err != nil {
				    panic(err)
				}
			}
			`)
			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("bytes.Buffer", "WriteString"))
		})

		It("should return the type and call name for function selector expression", func() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "bytes"
			)

			func createBuffer() *bytes.Buffer {
			    return new(bytes.Buffer)
			}

			func main() {
				_, err := createBuffer().WriteString("test")
				if err != nil {
				    panic(err)
				}
			}
			`)
			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("*bytes.Buffer", "WriteString"))
		})

		It("should return the type and call name for package function", func() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "fmt"
			)

			func main() {
			    fmt.Println("test")
			}
			`)
			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("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() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "fmt"
			)

			func main() {
				be := "test1" + "test2"
				fmt.Println(be)
			}
			`)
			ctx := pkg.CreateContext("main.go")
			var be *ast.BinaryExpr
			visitor := testutils.NewMockVisitor()
			visitor.Context = ctx
			visitor.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if expr, ok := n.(*ast.BinaryExpr); ok {
					be = expr
				}
				return true
			}
			ast.Walk(visitor, ctx.Root)

			operands := gosec.GetBinaryExprOperands(be)
			Expect(operands).Should(HaveLen(2))
		})
		It("should return all operands of complex binary expression", func() {
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("main.go", `
			package main

			import(
			    "fmt"
			)

			func main() {
				be := "test1" + "test2" + "test3" + "test4"
				fmt.Println(be)
			}
			`)
			ctx := pkg.CreateContext("main.go")
			var be *ast.BinaryExpr
			visitor := testutils.NewMockVisitor()
			visitor.Context = ctx
			visitor.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if expr, ok := n.(*ast.BinaryExpr); ok {
					if be == nil {
						be = expr
					}
				}
				return true
			}
			ast.Walk(visitor, ctx.Root)

			operands := gosec.GetBinaryExprOperands(be)
			Expect(operands).Should(HaveLen(4))
		})
	})
})
07070100000037000081A4000000000000000000000001652CE66700000828000000000000000000000000000000000000001F00000000gosec-2.18.1/import_tracker.go// 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

import (
	"go/ast"
	"go/types"
	"strings"
)

// 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 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{
		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 {
		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()] = []string{pkg.Name()}
	}
}

// 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]
	}
	return name
}
07070100000038000081A4000000000000000000000001652CE66700000562000000000000000000000000000000000000002400000000gosec-2.18.1/import_tracker_test.gopackage gosec_test

import (
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/testutils"
)

var _ = Describe("Import Tracker", func() {
	Context("when tracking a file", func() {
		It("should parse the imports from file", func() {
			tracker := gosec.NewImportTracker()
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `
				package foo
				import "fmt"
				func foo() {
				  fmt.Println()
				}
			`)
			err := pkg.Build()
			Expect(err).ShouldNot(HaveOccurred())
			pkgs := pkg.Pkgs()
			Expect(pkgs).Should(HaveLen(1))
			files := pkgs[0].Syntax
			Expect(files).Should(HaveLen(1))
			tracker.TrackFile(files[0])
			Expect(tracker.Imported).Should(Equal(map[string][]string{"fmt": {"fmt"}}))
		})
		It("should parse the named imports from file", func() {
			tracker := gosec.NewImportTracker()
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `
				package foo
				import fm "fmt"
				func foo() {
				  fm.Println()
				}
			`)
			err := pkg.Build()
			Expect(err).ShouldNot(HaveOccurred())
			pkgs := pkg.Pkgs()
			Expect(pkgs).Should(HaveLen(1))
			files := pkgs[0].Syntax
			Expect(files).Should(HaveLen(1))
			tracker.TrackFile(files[0])
			Expect(tracker.Imported).Should(Equal(map[string][]string{"fmt": {"fm"}}))
		})
	})
})
0707010000003900008180000000000000000000000001652CE66700002484000000000000000000000000000000000000001800000000gosec-2.18.1/install.sh#!/bin/sh
set -e
# Code generated by godownloader. DO NOT EDIT.
#

usage() {
  this=$1
  cat <<EOF
$this: download go binaries for securego/gosec

Usage: $this [-b] bindir [-d] [tag]
  -b sets bindir or installation directory, Defaults to ./bin
  -d turns on debug logging
   [tag] is a tag from
   https://github.com/securego/gosec/releases
   If tag is missing, then the latest will be used.

 Generated by godownloader
  https://github.com/goreleaser/godownloader

EOF
  exit 2
}

parse_args() {
  #BINDIR is ./bin unless set be ENV
  # over-ridden by flag below

  BINDIR=${BINDIR:-./bin}
  while getopts "b:dh?x" arg; do
    case "$arg" in
      b) BINDIR="$OPTARG" ;;
      d) log_set_priority 10 ;;
      h | \?) usage "$0" ;;
      x) set -x ;;
    esac
  done
  shift $((OPTIND - 1))
  TAG=$1
}
# this function wraps all the destructive operations
# if a curl|bash cuts off the end of the script due to
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
  tmpdir=$(mktemp -d)
  log_debug "downloading files into ${tmpdir}"
  http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
  http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
  hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
  srcdir="${tmpdir}"
  (cd "${tmpdir}" && untar "${TARBALL}")
  test ! -d "${BINDIR}" && install -d "${BINDIR}"
  for binexe in $BINARIES; do
    if [ "$OS" = "windows" ]; then
      binexe="${binexe}.exe"
    fi
    install "${srcdir}/${binexe}" "${BINDIR}/"
    log_info "installed ${BINDIR}/${binexe}"
  done
  rm -rf "${tmpdir}"
}
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
      ;;
  esac
}
tag_to_version() {
  if [ -z "${TAG}" ]; then
    log_info "checking GitHub for latest tag"
  else
    log_info "checking GitHub for tag '${TAG}'"
  fi
  REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
  if test -z "$REALTAG"; then
    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
    exit 1
  fi
  # if version starts with 'v', remove it
  TAG="$REALTAG"
  VERSION=${TAG#v}
}
adjust_format() {
  # change format (tar.gz or zip) based on OS
  true
}
adjust_os() {
  # adjust archive name based on OS
  true
}
adjust_arch() {
  # adjust archive name based on ARCH
  true
}

cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
  command -v "$1" >/dev/null
}
echoerr() {
  echo "$@" 1>&2
}
log_prefix() {
  echo "$0"
}
_logp=6
log_set_priority() {
  _logp="$1"
}
log_priority() {
  if test -z "$1"; then
    echo "$_logp"
    return
  fi
  [ "$1" -le "$_logp" ]
}
log_tag() {
  case $1 in
    0) echo "emerg" ;;
    1) echo "alert" ;;
    2) echo "crit" ;;
    3) echo "err" ;;
    4) echo "warning" ;;
    5) echo "notice" ;;
    6) echo "info" ;;
    7) echo "debug" ;;
    *) echo "$1" ;;
  esac
}
log_debug() {
  log_priority 7 || return 0
  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
  log_priority 6 || return 0
  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
  log_priority 3 || return 0
  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
  log_priority 2 || return 0
  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
  os=$(uname -s | tr '[:upper:]' '[:lower:]')
  case "$os" in
    cygwin_nt*) os="windows" ;;
    mingw*) os="windows" ;;
    msys_nt*) os="windows" ;;
  esac
  echo "$os"
}
uname_arch() {
  arch=$(uname -m)
  case $arch in
    x86_64) arch="amd64" ;;
    x86) arch="386" ;;
    i686) arch="386" ;;
    i386) arch="386" ;;
    aarch64) arch="arm64" ;;
    armv5*) arch="armv5" ;;
    armv6*) arch="armv6" ;;
    armv7*) arch="armv7" ;;
  esac
  echo ${arch}
}
uname_os_check() {
  os=$(uname_os)
  case "$os" in
    darwin) return 0 ;;
    dragonfly) return 0 ;;
    freebsd) return 0 ;;
    linux) return 0 ;;
    android) return 0 ;;
    nacl) return 0 ;;
    netbsd) return 0 ;;
    openbsd) return 0 ;;
    plan9) return 0 ;;
    solaris) return 0 ;;
    windows) return 0 ;;
  esac
  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
  return 1
}
uname_arch_check() {
  arch=$(uname_arch)
  case "$arch" in
    386) return 0 ;;
    amd64) return 0 ;;
    arm64) return 0 ;;
    armv5) return 0 ;;
    armv6) return 0 ;;
    armv7) return 0 ;;
    ppc64) return 0 ;;
    ppc64le) return 0 ;;
    mips) return 0 ;;
    mipsle) return 0 ;;
    mips64) return 0 ;;
    mips64le) return 0 ;;
    s390x) return 0 ;;
    amd64p32) return 0 ;;
  esac
  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
  return 1
}
untar() {
  tarball=$1
  case "${tarball}" in
    *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;;
    *.tar) tar --no-same-owner -xf "${tarball}" ;;
    *.zip) unzip "${tarball}" ;;
    *)
      log_err "untar unknown archive format for ${tarball}"
      return 1
      ;;
  esac
}
http_download_curl() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
  else
    code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
  fi
  if [ "$code" != "200" ]; then
    log_debug "http_download_curl received HTTP status $code"
    return 1
  fi
  return 0
}
http_download_wget() {
  local_file=$1
  source_url=$2
  header=$3
  if [ -z "$header" ]; then
    wget -q -O "$local_file" "$source_url"
  else
    wget -q --header "$header" -O "$local_file" "$source_url"
  fi
}
http_download() {
  log_debug "http_download $2"
  if is_command curl; then
    http_download_curl "$@"
    return
  elif is_command wget; then
    http_download_wget "$@"
    return
  fi
  log_crit "http_download unable to find wget or curl"
  return 1
}
http_copy() {
  tmp=$(mktemp)
  http_download "${tmp}" "$1" "$2" || return 1
  body=$(cat "$tmp")
  rm -f "${tmp}"
  echo "$body"
}
github_release() {
  owner_repo=$1
  version=$2
  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/".*//')
  test -z "$version" && return 1
  echo "$version"
}
hash_sha256() {
  TARGET=${1:-/dev/stdin}
  if is_command gsha256sum; then
    hash=$(gsha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command sha256sum; then
    hash=$(sha256sum "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command shasum; then
    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
    echo "$hash" | cut -d ' ' -f 1
  elif is_command openssl; then
    hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
    echo "$hash" | cut -d ' ' -f a
  else
    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
    return 1
  fi
}
hash_sha256_verify() {
  TARGET=$1
  checksums=$2
  if [ -z "$checksums" ]; then
    log_err "hash_sha256_verify checksum file not specified in arg2"
    return 1
  fi
  BASENAME=${TARGET##*/}
  want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
  if [ -z "$want" ]; then
    log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
    return 1
  fi
  got=$(hash_sha256 "$TARGET")
  if [ "$want" != "$got" ]; then
    log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
    return 1
  fi
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF

PROJECT_NAME="gosec"
OWNER=securego
REPO="gosec"
BINARY=gosec
FORMAT=tar.gz
OS=$(uname_os)
ARCH=$(uname_arch)
PREFIX="$OWNER/$REPO"

# use in logging routines
log_prefix() {
	echo "$PREFIX"
}
PLATFORM="${OS}/${ARCH}"
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download

uname_os_check "$OS"
uname_arch_check "$ARCH"

parse_args "$@"

get_binaries

tag_to_version

adjust_format

adjust_os

adjust_arch

log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"

NAME=${PROJECT_NAME}_${VERSION}_${OS}_${ARCH}
TARBALL=${NAME}.${FORMAT}
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
CHECKSUM=${PROJECT_NAME}_${VERSION}_checksums.txt
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}


execute
0707010000003A000041ED000000000000000000000002652CE66700000000000000000000000000000000000000000000001300000000gosec-2.18.1/issue0707010000003B000081A4000000000000000000000001652CE66700001736000000000000000000000000000000000000001C00000000gosec-2.18.1/issue/issue.go// (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 issue

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/token"
	"os"
	"strconv"

	"github.com/securego/gosec/v2/cwe"
)

// Score type used by severity and confidence values
type Score int

const (
	// Low severity or confidence
	Low Score = iota
	// Medium severity or confidence
	Medium
	// High severity or confidence
	High
)

// SnippetOffset defines the number of lines captured before
// the beginning and after the end of a code snippet
const SnippetOffset = 1

// 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
}

// 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",
	"G113": "190",
	"G114": "676",
	"G201": "89",
	"G202": "89",
	"G203": "79",
	"G204": "78",
	"G301": "276",
	"G302": "276",
	"G303": "377",
	"G304": "22",
	"G305": "22",
	"G306": "276",
	"G401": "326",
	"G402": "295",
	"G403": "310",
	"G404": "338",
	"G501": "327",
	"G502": "327",
	"G503": "327",
	"G504": "327",
	"G505": "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.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
}

// 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 {
	return fmt.Sprintf("%s:%s", i.File, i.Line)
}

// MetaData is embedded in all gosec rules. The Severity, Confidence and What message
// will be passed through to reported issues.
type MetaData struct {
	ID         string
	Severity   Score
	Confidence Score
	What       string
}

// MarshalJSON is used convert a Score object into a JSON representation
func (c Score) MarshalJSON() ([]byte, error) {
	return json.Marshal(c.String())
}

// String converts a Score into a string
func (c Score) String() string {
	switch c {
	case High:
		return "HIGH"
	case Medium:
		return "MEDIUM"
	case Low:
		return "LOW"
	}
	return "UNDEFINED"
}

// 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)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		pos++
		if pos > end {
			break
		} else if pos >= start && pos <= end {
			code := fmt.Sprintf("%d: %s\n", pos, scanner.Text())
			buf.WriteString(code)
		}
	}
	return buf.String(), nil
}

func codeSnippetStartLine(node ast.Node, fobj *token.File) int64 {
	s := (int64)(fobj.Line(node.Pos()))
	if s-SnippetOffset > 0 {
		return s - SnippetOffset
	}
	return s
}

func codeSnippetEndLine(node ast.Node, fobj *token.File) int64 {
	e := (int64)(fobj.Line(node.End()))
	return e + SnippetOffset
}

// New creates a new Issue
func New(fobj *token.File, node ast.Node, ruleID, desc string, severity, confidence Score) *Issue {
	name := fobj.Name()
	line := GetLine(fobj, node)
	col := strconv.Itoa(fobj.Position(node.Pos()).Column)

	var code string
	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)
		if err != nil {
			code = err.Error()
		}
	}

	return &Issue{
		File:       name,
		Line:       line,
		Col:        col,
		RuleID:     ruleID,
		What:       desc,
		Confidence: confidence,
		Severity:   severity,
		Code:       code,
		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
}
0707010000003C000081A4000000000000000000000001652CE66700000ED9000000000000000000000000000000000000002100000000gosec-2.18.1/issue/issue_test.gopackage issue_test

import (
	"go/ast"

	. "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
			source := `package main
			const foo = "bar"
			func main(){
				println(foo)
			}
			`
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", source)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.BasicLit); ok {
					target = node
					return false
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(target).ShouldNot(BeNil())

			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).Should(BeNil())
		})

		It("should return an error if specific context is not able to be obtained", func() {
			Skip("Not implemented")
		})

		It("should construct file path based on line and file information", func() {
			var target *ast.AssignStmt

			source := `package main
			import "fmt"
			func main() {
				username := "admin"
				password := "f62e5bcda4fae4f82370da0c6f20697b8f8447ef"
				fmt.Println("Doing something with: ", username, password)
			}`

			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", source)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.AssignStmt); ok {
					if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "password" {
						target = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(target).ShouldNot(BeNil())

			// Use hardcoded rule to check assignment
			cfg := gosec.NewConfig()
			rule, _ := rules.NewHardcodedCredentials("TEST", cfg)
			foundIssue, err := rule.Match(target, ctx)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(foundIssue).ShouldNot(BeNil())
			Expect(foundIssue.FileLocation()).Should(MatchRegexp("foo.go:5"))
		})

		It("should provide accurate line and file information", func() {
			Skip("Not implemented")
		})

		It("should provide accurate line and file information for multi-line statements", func() {
			var target *ast.CallExpr
			source := `
package main
import (
   	"net"
)
func main() {
	_, _ := net.Listen("tcp", 
	"0.0.0.0:2000")
}
`
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", source)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.CallExpr); ok {
					target = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(target).ShouldNot(BeNil())

			cfg := gosec.NewConfig()
			rule, _ := rules.NewBindsToAllNetworkInterfaces("TEST", cfg)
			issue, err := rule.Match(target, ctx)
			Expect(err).ShouldNot(HaveOccurred())
			Expect(issue).ShouldNot(BeNil())
			Expect(issue.File).Should(MatchRegexp("foo.go"))
			Expect(issue.Line).Should(MatchRegexp("7-8"))
			Expect(issue.Col).Should(Equal("10"))
		})

		It("should maintain the provided severity score", func() {
			Skip("Not implemented")
		})

		It("should maintain the provided confidence score", func() {
			Skip("Not implemented")
		})
	})
})
0707010000003D00008180000000000000000000000001652CE667000001FC000000000000000000000000000000000000001B00000000gosec-2.18.1/renovate.json{
  "dependencyDashboard": true,
  "dependencyDashboardTitle" : "Renovate(bot) : dependency dashboard",
  "vulnerabilityAlerts": {
    "enabled": true
  },
  "extends": [
    ":preserveSemverRanges",
    "group:all",
    "schedule:weekly"
  ],
  "lockFileMaintenance": {
    "commitMessageAction": "Update",
    "enabled": true,
    "extends": [
      "group:all",
      "schedule:weekly"
    ]
  },
  "postUpdateOptions": [
    "gomodTidy",
    "gomodUpdateImportPaths"
  ],
  "separateMajorMinor": false
}
0707010000003E000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001400000000gosec-2.18.1/report0707010000003F000081A4000000000000000000000001652CE66700000295000000000000000000000000000000000000001700000000gosec-2.18.1/report.gopackage 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
}
07070100000040000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001800000000gosec-2.18.1/report/csv0707010000004100008180000000000000000000000001652CE66700000206000000000000000000000000000000000000002200000000gosec-2.18.1/report/csv/writer.gopackage 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
}
07070100000042000081A4000000000000000000000001652CE66700000B6A000000000000000000000000000000000000002100000000gosec-2.18.1/report/formatter.go// (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
}
0707010000004300008180000000000000000000000001652CE667000000C0000000000000000000000000000000000000002C00000000gosec-2.18.1/report/formatter_suite_test.gopackage report

import (
	"testing"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

func TestRules(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Formatters Suite")
}
07070100000044000081A4000000000000000000000001652CE667000041FD000000000000000000000000000000000000002600000000gosec-2.18.1/report/formatter_test.gopackage report

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
	"gopkg.in/yaml.v3"

	"github.com/securego/gosec/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) *issue.Issue {
	issue := createIssue("i1", issue.GetCweByRule("G101"))
	issue.File = file
	issue.What = what
	return &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: issue.High,
		Severity:   issue.High,
		Code:       "1: testcode",
		Cwe:        weakness,
	}
}

func createReportInfo(rule string, weakness *cwe.Weakness) gosec.ReportInfo {
	newissue := createIssue(rule, weakness)
	metrics := gosec.Metrics{}
	return gosec.ReportInfo{
		Errors: map[string][]gosec.Error{},
		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)
	return ret
}

var _ = Describe("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))
		})
	})

	Context("When using junit", func() {
		It("preserves order of issues", func() {
			issues := []*issue.Issue{createIssueWithFileWhat("i1", "1"), createIssueWithFileWhat("i2", "2"), createIssueWithFileWhat("i3", "1")}

			junitReport := junit.GenerateReport(&gosec.ReportInfo{Issues: issues})

			testSuite := junitReport.Testsuites[0]

			Expect(testSuite.Testcases[0].Name).To(Equal(issues[0].File))
			Expect(testSuite.Testcases[1].Name).To(Equal(issues[2].File))

			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", "G111", "G112", "G113", "G201", "G202", "G203",
			"G204", "G301", "G302", "G303", "G304", "G305", "G401",
			"G402", "G403", "G404", "G501", "G502", "G503", "G504",
			"G505", "G601",
		}

		It("csv formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				buf := new(bytes.Buffer)
				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(buf.String()).To(Equal(expect))
			}
		})
		It("xml formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				buf := new(bytes.Buffer)
				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  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(buf.String()).To(Equal(expect))
			}
		})
		It("json formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				data := createReportInfo(rule, cwe)

				expect := new(bytes.Buffer)
				enc := json.NewEncoder(expect)
				err := enc.Encode(data)
				Expect(err).ShouldNot(HaveOccurred())
				buf := new(bytes.Buffer)
				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())
				Expect(result).To(Equal(expectation))
			}
		})
		It("html formatted report should  contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				data := createReportInfo(rule, cwe)

				expect := new(bytes.Buffer)
				enc := json.NewEncoder(expect)
				err := enc.Encode(data)
				Expect(err).ShouldNot(HaveOccurred())
				buf := new(bytes.Buffer)
				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())
				Expect(result).To(ContainSubstring(expectation))
			}
		})
		It("yaml formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				data := createReportInfo(rule, cwe)

				expect := new(bytes.Buffer)
				enc := yaml.NewEncoder(expect)
				err := enc.Encode(data)
				Expect(err).ShouldNot(HaveOccurred())
				buf := new(bytes.Buffer)
				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())
				Expect(result).To(ContainSubstring(expectation))
			}
		})
		It("junit-xml formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				data := createReportInfo(rule, cwe)

				expect := new(bytes.Buffer)
				enc := yaml.NewEncoder(expect)
				err := enc.Encode(data)
				Expect(err).ShouldNot(HaveOccurred())
				buf := new(bytes.Buffer)
				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())
				Expect(result).To(ContainSubstring(expectation))
			}
		})
		It("text formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				data := createReportInfo(rule, cwe)

				expect := new(bytes.Buffer)
				enc := yaml.NewEncoder(expect)
				err := enc.Encode(data)
				Expect(err).ShouldNot(HaveOccurred())
				buf := new(bytes.Buffer)
				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 shouldn't contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}
				buf := new(bytes.Buffer)
				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())

				expect := new(bytes.Buffer)
				enc := json.NewEncoder(expect)
				err = enc.Encode(cwe)
				Expect(err).ShouldNot(HaveOccurred())

				expectation := stripString(expect.String())
				Expect(result).ShouldNot(ContainSubstring(expectation))
			}
		})
		It("golint formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				buf := new(bytes.Buffer)
				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(buf.String()).To(Equal(expect))
			}
		})
		It("sarif formatted report should contain the CWE mapping", func() {
			for _, rule := range grules {
				cwe := issue.GetCweByRule(rule)
				newissue := createIssue(rule, cwe)
				errors := map[string][]gosec.Error{}

				buf := new(bytes.Buffer)
				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())

				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(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":[{`))
		})
	})
})
07070100000045000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001B00000000gosec-2.18.1/report/golint0707010000004600008180000000000000000000000001652CE6670000039C000000000000000000000000000000000000002500000000gosec-2.18.1/report/golint/writer.gopackage 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
}
07070100000047000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001900000000gosec-2.18.1/report/html07070100000048000081A4000000000000000000000001652CE667000044DF000000000000000000000000000000000000002700000000gosec-2.18.1/report/html/template.html<!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/0.9.4/css/bulma.min.css" integrity="sha512-HqxHUkJM0SYcbvxUw5P60SzdOTy/QVwA1JJrvaXJv4q7lmbDZCmZaqz01UPOaQveoxfYRv1tHozWGPMcuTBuvQ==" crossorigin="anonymous"/>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous"/>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin="anonymous"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/go.min.js" integrity="sha512-wsnZc3vH14xwbbaoAwkar86729DTpz6wx48ABISfmaKLZwP/lm8d7Z+Hmr9JKobAENs0qO/cGounL7LUEg10Pg==" 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.23.2/babel.min.js" integrity="sha512-ND11pbmp3M3Wldj90In1EUlHIt8T7O6FykizQ/yXrFjHtpX+D0SX+/IVeLjqAV91r/Lavq4BpWJIC0nUtw5Kvw==" 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>0707010000004900008180000000000000000000000001652CE66700000183000000000000000000000000000000000000002300000000gosec-2.18.1/report/html/writer.gopackage 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)
}
0707010000004A000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001900000000gosec-2.18.1/report/json0707010000004B00008180000000000000000000000001652CE66700000149000000000000000000000000000000000000002300000000gosec-2.18.1/report/json/writer.gopackage 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
}
0707010000004C000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001A00000000gosec-2.18.1/report/junit0707010000004D00008180000000000000000000000001652CE667000001C3000000000000000000000000000000000000002500000000gosec-2.18.1/report/junit/builder.gopackage 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,
	}
}
0707010000004E000081A4000000000000000000000001652CE667000004CC000000000000000000000000000000000000002700000000gosec-2.18.1/report/junit/formatter.gopackage 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)
}

// 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
}
0707010000004F00008180000000000000000000000001652CE667000002F5000000000000000000000000000000000000002300000000gosec-2.18.1/report/junit/types.gopackage 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"`
}
0707010000005000008180000000000000000000000001652CE66700000203000000000000000000000000000000000000002400000000gosec-2.18.1/report/junit/writer.gopackage 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
}
07070100000051000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001A00000000gosec-2.18.1/report/sarif07070100000052000081A4000000000000000000000001652CE667000016C5000000000000000000000000000000000000002500000000gosec-2.18.1/report/sarif/builder.gopackage 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) *Result {
	return &Result{
		RuleID:       ruleID,
		RuleIndex:    ruleIndex,
		Level:        level,
		Message:      NewMessage(message),
		Suppressions: suppressions,
	}
}

// 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,
	}
}
0707010000005300008180000000000000000000000001652CE6670000037A000000000000000000000000000000000000002200000000gosec-2.18.1/report/sarif/data.gopackage 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/master/Schemata/sarif-schema-2.1.0.json"
)
07070100000054000081A4000000000000000000000001652CE66700001A9C000000000000000000000000000000000000002700000000gosec-2.18.1/report/sarif/formatter.gopackage 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),
		).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
}
0707010000005500008180000000000000000000000001652CE667000000CA000000000000000000000000000000000000002E00000000gosec-2.18.1/report/sarif/sarif_suite_test.gopackage 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")
}
07070100000056000081A4000000000000000000000001652CE667000016B0000000000000000000000000000000000000002800000000gosec-2.18.1/report/sarif/sarif_test.gopackage 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))
		})
	})
})
0707010000005700008180000000000000000000000001652CE66700010376000000000000000000000000000000000000002300000000gosec-2.18.1/report/sarif/types.go// 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"`
}
0707010000005800008180000000000000000000000001652CE667000001AA000000000000000000000000000000000000002400000000gosec-2.18.1/report/sarif/writer.gopackage 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
}
07070100000059000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001A00000000gosec-2.18.1/report/sonar0707010000005A00008180000000000000000000000001652CE66700000309000000000000000000000000000000000000002500000000gosec-2.18.1/report/sonar/builder.gopackage 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,
	}
}
0707010000005B000081A4000000000000000000000001652CE66700000697000000000000000000000000000000000000002700000000gosec-2.18.1/report/sonar/formatter.gopackage 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"
	}
}
0707010000005C00008180000000000000000000000001652CE667000000CA000000000000000000000000000000000000002E00000000gosec-2.18.1/report/sonar/sonar_suite_test.gopackage 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")
}
0707010000005D000081A4000000000000000000000001652CE667000013BB000000000000000000000000000000000000002800000000gosec-2.18.1/report/sonar/sonar_test.gopackage 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))
		})
	})
})
0707010000005E00008180000000000000000000000001652CE667000003D9000000000000000000000000000000000000002300000000gosec-2.18.1/report/sonar/types.gopackage 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"`
}
0707010000005F00008180000000000000000000000001652CE667000001A9000000000000000000000000000000000000002400000000gosec-2.18.1/report/sonar/writer.gopackage 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
}
07070100000060000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001900000000gosec-2.18.1/report/text0707010000006100008180000000000000000000000001652CE6670000036C000000000000000000000000000000000000002600000000gosec-2.18.1/report/text/template.txtResults:
{{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 }}

{{ 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 }}

07070100000062000081A4000000000000000000000001652CE66700000A33000000000000000000000000000000000000002300000000gosec-2.18.1/report/text/writer.gopackage 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
}
07070100000063000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001900000000gosec-2.18.1/report/yaml07070100000064000081A4000000000000000000000001652CE6670000013C000000000000000000000000000000000000002300000000gosec-2.18.1/report/yaml/writer.gopackage 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
}
07070100000065000081A4000000000000000000000001652CE66700000935000000000000000000000000000000000000001800000000gosec-2.18.1/resolve.go// (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 gosec

import "go/ast"

func resolveIdent(n *ast.Ident, c *Context) bool {
	if n.Obj == nil || n.Obj.Kind != ast.Var {
		return true
	}
	if node, ok := n.Obj.Decl.(ast.Node); ok {
		return TryResolve(node, c)
	}
	return false
}

func resolveValueSpec(n *ast.ValueSpec, c *Context) bool {
	if len(n.Values) == 0 {
		return false
	}
	for _, value := range n.Values {
		if !TryResolve(value, c) {
			return false
		}
	}
	return true
}

func resolveAssign(n *ast.AssignStmt, c *Context) bool {
	if len(n.Rhs) == 0 {
		return false
	}
	for _, arg := range n.Rhs {
		if !TryResolve(arg, c) {
			return false
		}
	}
	return true
}

func resolveCompLit(n *ast.CompositeLit, c *Context) bool {
	if len(n.Elts) == 0 {
		return false
	}
	for _, arg := range n.Elts {
		if !TryResolve(arg, c) {
			return false
		}
	}
	return true
}

func resolveBinExpr(n *ast.BinaryExpr, c *Context) bool {
	return (TryResolve(n.X, c) && TryResolve(n.Y, c))
}

func resolveCallExpr(_ *ast.CallExpr, _ *Context) bool {
	// TODO(tkelsey): next step, full function resolution
	return false
}

// TryResolve will attempt, given a subtree starting at some AST node, to resolve
// all values contained within to a known constant. It is used to check for any
// unknown values in compound expressions.
func TryResolve(n ast.Node, c *Context) bool {
	switch node := n.(type) {
	case *ast.BasicLit:
		return true
	case *ast.CompositeLit:
		return resolveCompLit(node, c)
	case *ast.Ident:
		return resolveIdent(node, c)
	case *ast.ValueSpec:
		return resolveValueSpec(node, c)
	case *ast.AssignStmt:
		return resolveAssign(node, c)
	case *ast.CallExpr:
		return resolveCallExpr(node, c)
	case *ast.BinaryExpr:
		return resolveBinExpr(node, c)
	}
	return false
}
07070100000066000081A4000000000000000000000001652CE66700002B5A000000000000000000000000000000000000001D00000000gosec-2.18.1/resolve_test.gopackage gosec_test

import (
	"go/ast"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/testutils"
)

var _ = Describe("Resolve ast node to concrete value", func() {
	Context("when attempting to resolve an ast node", func() {
		It("should successfully resolve basic literal", func() {
			var basicLiteral *ast.BasicLit
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const foo = "bar"; func main(){}`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.BasicLit); ok {
					basicLiteral = node
					return false
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(basicLiteral).ShouldNot(BeNil())
			Expect(gosec.TryResolve(basicLiteral, ctx)).Should(BeTrue())
		})

		It("should successfully resolve identifier", func() {
			var ident *ast.Ident
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; var foo string = "bar"; func main(){}`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.Ident); ok {
					ident = node
					return false
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(ident).ShouldNot(BeNil())
			Expect(gosec.TryResolve(ident, ctx)).Should(BeTrue())
		})

		It("should successfully resolve variable identifier", func() {
			var ident *ast.Ident
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; import "fmt"; func main(){ x := "test"; y := x; fmt.Println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.Ident); ok && node.Name == "y" {
					ident = node
					return false
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(ident).ShouldNot(BeNil())
			Expect(gosec.TryResolve(ident, ctx)).Should(BeTrue())
		})

		It("should successfully not resolve variable identifier with no declaration", func() {
			var ident *ast.Ident
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; import "fmt"; func main(){ x := "test"; y := x; fmt.Println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.Ident); ok && node.Name == "y" {
					ident = node
					return false
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(ident).ShouldNot(BeNil())
			ident.Obj.Decl = nil
			Expect(gosec.TryResolve(ident, ctx)).Should(BeFalse())
		})

		It("should successfully resolve assign statement", func() {
			var assign *ast.AssignStmt
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.AssignStmt); ok {
					if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" {
						assign = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(assign).ShouldNot(BeNil())
			Expect(gosec.TryResolve(assign, ctx)).Should(BeTrue())
		})

		It("should successfully not resolve assign statement without rhs", func() {
			var assign *ast.AssignStmt
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.AssignStmt); ok {
					if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" {
						assign = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(assign).ShouldNot(BeNil())
			assign.Rhs = []ast.Expr{}
			Expect(gosec.TryResolve(assign, ctx)).Should(BeFalse())
		})

		It("should successfully not resolve assign statement with unsolvable rhs", func() {
			var assign *ast.AssignStmt
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.AssignStmt); ok {
					if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" {
						assign = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(assign).ShouldNot(BeNil())
			assign.Rhs = []ast.Expr{&ast.CallExpr{}}
			Expect(gosec.TryResolve(assign, ctx)).Should(BeFalse())
		})

		It("should successfully resolve a binary statement", func() {
			var target *ast.BinaryExpr
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const (x = "bar"; y = "baz"); func main(){ z := x + y; println(z) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.BinaryExpr); ok {
					target = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(target).ShouldNot(BeNil())
			Expect(gosec.TryResolve(target, ctx)).Should(BeTrue())
		})

		It("should successfully resolve value spec", func() {
			var value *ast.ValueSpec
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ var y string = x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.ValueSpec); ok {
					if len(node.Names) == 1 && node.Names[0].Name == "y" {
						value = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			Expect(gosec.TryResolve(value, ctx)).Should(BeTrue())
		})
		It("should successfully not resolve value spec without values", func() {
			var value *ast.ValueSpec
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ var y string = x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.ValueSpec); ok {
					if len(node.Names) == 1 && node.Names[0].Name == "y" {
						value = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			value.Values = []ast.Expr{}
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})

		It("should successfully not resolve value spec with unsolvable value", func() {
			var value *ast.ValueSpec
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ var y string = x; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.ValueSpec); ok {
					if len(node.Names) == 1 && node.Names[0].Name == "y" {
						value = node
					}
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			value.Values = []ast.Expr{&ast.CallExpr{}}
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})

		It("should successfully resolve composite literal", func() {
			var value *ast.CompositeLit
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; func main(){ y := []string{"value1", "value2"}; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.CompositeLit); ok {
					value = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			Expect(gosec.TryResolve(value, ctx)).Should(BeTrue())
		})

		It("should successfully not resolve composite literal without elst", func() {
			var value *ast.CompositeLit
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; func main(){ y := []string{"value1", "value2"}; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.CompositeLit); ok {
					value = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			value.Elts = []ast.Expr{}
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})

		It("should successfully not resolve composite literal with unsolvable elst", func() {
			var value *ast.CompositeLit
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; func main(){ y := []string{"value1", "value2"}; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.CompositeLit); ok {
					value = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			value.Elts = []ast.Expr{&ast.CallExpr{}}
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})

		It("should successfully not resolve call expressions", func() {
			var value *ast.CallExpr
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; func main(){ y := []string{"value1", "value2"}; println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.CallExpr); ok {
					value = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})

		It("should successfully not resolve call expressions", func() {
			var value *ast.ImportSpec
			pkg := testutils.NewTestPackage()
			defer pkg.Close()
			pkg.AddFile("foo.go", `package main; import "fmt"; func main(){ y := []string{"value1", "value2"}; fmt.Println(y) }`)
			ctx := pkg.CreateContext("foo.go")
			v := testutils.NewMockVisitor()
			v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
				if node, ok := n.(*ast.ImportSpec); ok {
					value = node
				}
				return true
			}
			v.Context = ctx
			ast.Walk(v, ctx.Root)
			Expect(value).ShouldNot(BeNil())
			Expect(gosec.TryResolve(value, ctx)).Should(BeFalse())
		})
	})
})
07070100000067000081A4000000000000000000000001652CE667000008B5000000000000000000000000000000000000001500000000gosec-2.18.1/rule.go// 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

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.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 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 struct {
	Rules             map[reflect.Type][]Rule
	RuleSuppressedMap map[string]bool
}

// NewRuleSet constructs a new RuleSet
func NewRuleSet() RuleSet {
	return RuleSet{make(map[reflect.Type][]Rule), make(map[string]bool)}
}

// Register adds a trigger for the supplied rule for the
// specified ast nodes.
func (r RuleSet) Register(rule Rule, isSuppressed bool, nodes ...ast.Node) {
	for _, n := range nodes {
		t := reflect.TypeOf(n)
		if rules, ok := r.Rules[t]; ok {
			r.Rules[t] = append(rules, rule)
		} else {
			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.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]
}
07070100000068000081A4000000000000000000000001652CE66700000C57000000000000000000000000000000000000001A00000000gosec-2.18.1/rule_test.gopackage gosec_test

import (
	"fmt"
	"go/ast"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type mockrule struct {
	issue    *issue.Issue
	err      error
	callback func(n ast.Node, ctx *gosec.Context) bool
}

func (m *mockrule) ID() string {
	return "MOCK"
}

func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
	if m.callback(n, ctx) {
		return m.issue, nil
	}
	return nil, m.err
}

var _ = Describe("Rule", func() {
	Context("when using a ruleset", func() {
		var (
			ruleset        gosec.RuleSet
			dummyErrorRule gosec.Rule
			dummyIssueRule gosec.Rule
		)

		JustBeforeEach(func() {
			ruleset = gosec.NewRuleSet()
			dummyErrorRule = &mockrule{
				issue:    nil,
				err:      fmt.Errorf("An unexpected error occurred"),
				callback: func(n ast.Node, ctx *gosec.Context) bool { return false },
			}
			dummyIssueRule = &mockrule{
				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"); }`,
					Line:       "42",
				},
				err:      nil,
				callback: func(n ast.Node, ctx *gosec.Context) bool { return true },
			}
		})
		It("should be possible to register a rule for multiple ast.Node", func() {
			registeredNodeA := (*ast.CallExpr)(nil)
			registeredNodeB := (*ast.AssignStmt)(nil)
			unregisteredNode := (*ast.BinaryExpr)(nil)

			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, 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, 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())
		})
	})
})
07070100000069000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001300000000gosec-2.18.1/rules0707010000006A000081A4000000000000000000000001652CE667000006FA000000000000000000000000000000000000001E00000000gosec-2.18.1/rules/archive.gopackage rules

import (
	"go/ast"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type archive struct {
	issue.MetaData
	calls    gosec.CallList
	argTypes []string
}

func (a *archive) ID() string {
	return a.MetaData.ID
}

// 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) (*issue.Issue, error) {
	if node := a.calls.ContainsPkgCallExpr(n, c, false); node != nil {
		for _, arg := range node.Args {
			var argType types.Type
			if selector, ok := arg.(*ast.SelectorExpr); ok {
				argType = c.Info.TypeOf(selector.X)
			} else if ident, ok := arg.(*ast.Ident); ok {
				if ident.Obj != nil && ident.Obj.Kind == ast.Var {
					decl := ident.Obj.Decl
					if assign, ok := decl.(*ast.AssignStmt); ok {
						if selector, ok := assign.Rhs[0].(*ast.SelectorExpr); ok {
							argType = c.Info.TypeOf(selector.X)
						}
					}
				}
			}

			if argType != nil {
				for _, t := range a.argTypes {
					if argType.String() == t {
						return c.NewIssue(n, a.ID(), a.What, a.Severity, a.Confidence), nil
					}
				}
			}
		}
	}
	return nil, nil
}

// NewArchive creates a new rule which detects the file traversal when extracting zip/tar archives
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: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       "File traversal when extracting zip/tar archive",
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
0707010000006B000081A4000000000000000000000001652CE66700000A12000000000000000000000000000000000000001B00000000gosec-2.18.1/rules/bind.go// (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"
	"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 {
	issue.MetaData
	calls   gosec.CallList
	pattern *regexp.Regexp
}

func (r *bindsToAllNetworkInterfaces) ID() string {
	return r.MetaData.ID
}

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
	}
	if len(callExpr.Args) > 1 {
		arg := callExpr.Args[1]
		if bl, ok := arg.(*ast.BasicLit); ok {
			if arg, err := gosec.GetString(bl); err == nil {
				if r.pattern.MatchString(arg) {
					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 c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
				}
			}
		}
	} else if len(callExpr.Args) > 0 {
		values := gosec.GetCallStringArgsValues(callExpr.Args[0], c)
		for _, value := range values {
			if r.pattern.MatchString(value) {
				return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
			}
		}
	}
	return nil, nil
}

// NewBindsToAllNetworkInterfaces detects socket connections that are setup to
// listen on all network interfaces.
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: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       "Binds to all network interfaces",
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
0707010000006C000081A4000000000000000000000001652CE66700000CC6000000000000000000000000000000000000002000000000gosec-2.18.1/rules/blocklist.go// (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"
	"strings"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type blocklistedImport struct {
	issue.MetaData
	Blocklisted map[string]string
}

func unquote(original string) string {
	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) (*issue.Issue, error) {
	if node, ok := n.(*ast.ImportSpec); ok {
		if description, ok := r.Blocklisted[unquote(node.Path.Value)]; ok {
			return c.NewIssue(node, r.ID(), description, r.Severity, r.Confidence), nil
		}
	}
	return nil, nil
}

// NewBlocklistedImports reports when a blocklisted import is being used.
// Typically when a deprecated technology is being used.
func NewBlocklistedImports(id string, _ gosec.Config, blocklist map[string]string) (gosec.Rule, []ast.Node) {
	return &blocklistedImport{
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
		},
		Blocklisted: blocklist,
	}, []ast.Node{(*ast.ImportSpec)(nil)}
}

// NewBlocklistedImportMD5 fails if MD5 is imported
func NewBlocklistedImportMD5(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return NewBlocklistedImports(id, conf, map[string]string{
		"crypto/md5": "Blocklisted import crypto/md5: weak cryptographic primitive",
	})
}

// NewBlocklistedImportDES fails if DES is imported
func NewBlocklistedImportDES(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return NewBlocklistedImports(id, conf, map[string]string{
		"crypto/des": "Blocklisted import crypto/des: weak cryptographic primitive",
	})
}

// NewBlocklistedImportRC4 fails if DES is imported
func NewBlocklistedImportRC4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return NewBlocklistedImports(id, conf, map[string]string{
		"crypto/rc4": "Blocklisted import crypto/rc4: weak cryptographic primitive",
	})
}

// NewBlocklistedImportCGI fails if CGI is imported
func NewBlocklistedImportCGI(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return NewBlocklistedImports(id, conf, map[string]string{
		"net/http/cgi": "Blocklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
	})
}

// NewBlocklistedImportSHA1 fails if SHA1 is imported
func NewBlocklistedImportSHA1(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	return NewBlocklistedImports(id, conf, map[string]string{
		"crypto/sha1": "Blocklisted import crypto/sha1: weak cryptographic primitive",
	})
}
0707010000006D000081A4000000000000000000000001652CE66700000E9D000000000000000000000000000000000000002900000000gosec-2.18.1/rules/decompression-bomb.go// (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 (
	"fmt"
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type decompressionBombCheck struct {
	issue.MetaData
	readerCalls gosec.CallList
	copyCalls   gosec.CallList
}

func (d *decompressionBombCheck) ID() string {
	return d.MetaData.ID
}

func containsReaderCall(node ast.Node, ctx *gosec.Context, list gosec.CallList) bool {
	if list.ContainsPkgCallExpr(node, ctx, false) != nil {
		return true
	}
	// Resolve type info of ident (for *archive/zip.File.Open)
	s, idt, _ := gosec.GetCallInfo(node, ctx)
	return list.Contains(s, idt)
}

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.
	if _, ok := ctx.PassedValues[d.ID()]; !ok {
		readerVarObj = make(map[*ast.Object]struct{})
		ctx.PassedValues[d.ID()] = readerVarObj
	} else if pv, ok := ctx.PassedValues[d.ID()].(map[*ast.Object]struct{}); ok {
		readerVarObj = pv
	} else {
		return nil, fmt.Errorf("PassedValues[%s] of Context is not map[*ast.Object]struct{}, but %T", d.ID(), ctx.PassedValues[d.ID()])
	}

	// io.Copy is a common function.
	// To reduce false positives, This rule detects code which is used for compressed data only.
	switch n := node.(type) {
	case *ast.AssignStmt:
		for _, expr := range n.Rhs {
			if callExpr, ok := expr.(*ast.CallExpr); ok && containsReaderCall(callExpr, ctx, d.readerCalls) {
				if idt, ok := n.Lhs[0].(*ast.Ident); ok && idt.Name != "_" {
					// Example:
					//  r, _ := zlib.NewReader(buf)
					//  Add r's Obj to readerVarObj map
					readerVarObj[idt.Obj] = struct{}{}
				}
			}
		}
	case *ast.CallExpr:
		if d.copyCalls.ContainsPkgCallExpr(n, ctx, false) != nil {
			if idt, ok := n.Args[1].(*ast.Ident); ok {
				if _, ok := readerVarObj[idt.Obj]; ok {
					// Detect io.Copy(x, r)
					return ctx.NewIssue(n, d.ID(), d.What, d.Severity, d.Confidence), nil
				}
			}
		}
	}

	return nil, nil
}

// NewDecompressionBombCheck detects if there is potential DoS vulnerability via decompression bomb
func NewDecompressionBombCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	readerCalls := gosec.NewCallList()
	readerCalls.Add("compress/gzip", "NewReader")
	readerCalls.AddAll("compress/zlib", "NewReader", "NewReaderDict")
	readerCalls.Add("compress/bzip2", "NewReader")
	readerCalls.AddAll("compress/flate", "NewReader", "NewReaderDict")
	readerCalls.Add("compress/lzw", "NewReader")
	readerCalls.Add("archive/tar", "NewReader")
	readerCalls.Add("archive/zip", "NewReader")
	readerCalls.Add("*archive/zip.File", "Open")

	copyCalls := gosec.NewCallList()
	copyCalls.Add("io", "Copy")
	copyCalls.Add("io", "CopyBuffer")

	return &decompressionBombCheck{
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.Medium,
			What:       "Potential DoS vulnerability via decompression bomb",
		},
		readerCalls: readerCalls,
		copyCalls:   copyCalls,
	}, []ast.Node{(*ast.FuncDecl)(nil), (*ast.AssignStmt)(nil), (*ast.CallExpr)(nil)}
}
0707010000006E000081A4000000000000000000000001652CE6670000064A000000000000000000000000000000000000002A00000000gosec-2.18.1/rules/directory-traversal.gopackage 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)}
}
0707010000006F000081A4000000000000000000000001652CE66700000E01000000000000000000000000000000000000001D00000000gosec-2.18.1/rules/errors.go// (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"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type noErrorCheck struct {
	issue.MetaData
	whitelist gosec.CallList
}

func (r *noErrorCheck) ID() string {
	return r.MetaData.ID
}

func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int {
	if tv := ctx.Info.TypeOf(callExpr); tv != nil {
		switch t := tv.(type) {
		case *types.Tuple:
			for pos := 0; pos < t.Len(); pos++ {
				variable := t.At(pos)
				if variable != nil && variable.Type().String() == "error" {
					return pos
				}
			}
		case *types.Named:
			if t.String() == "error" {
				return 0
			}
		}
	}
	return -1
}

func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) {
	switch stmt := n.(type) {
	case *ast.AssignStmt:
		cfg := ctx.Config
		if enabled, err := cfg.IsGlobalEnabled(gosec.Audit); err == nil && enabled {
			for _, expr := range stmt.Rhs {
				if callExpr, ok := expr.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(expr, ctx) == nil {
					pos := returnsError(callExpr, ctx)
					if pos < 0 || pos >= len(stmt.Lhs) {
						return nil, nil
					}
					if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
						return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
					}
				}
			}
		}
	case *ast.ExprStmt:
		if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
			pos := returnsError(callExpr, ctx)
			if pos >= 0 {
				return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
			}
		}
	}
	return nil, nil
}

// NewNoErrorCheck detects if the returned error is unchecked
func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
	// TODO(gm) Come up with sensible defaults here. Or flip it to use a
	// black list instead.
	whitelist := gosec.NewCallList()
	whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString")
	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[id]; ok {
		if whitelisted, ok := configured.(map[string]interface{}); ok {
			for pkg, funcs := range whitelisted {
				if funcs, ok := funcs.([]interface{}); ok {
					whitelist.AddAll(pkg, toStringSlice(funcs)...)
				}
			}
		}
	}

	return &noErrorCheck{
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Low,
			Confidence: issue.High,
			What:       "Errors unhandled.",
		},
		whitelist: whitelist,
	}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
}

func toStringSlice(values []interface{}) []string {
	result := []string{}
	for _, value := range values {
		if value, ok := value.(string); ok {
			result = append(result, value)
		}
	}
	return result
}
07070100000070000081A4000000000000000000000001652CE667000012DF000000000000000000000000000000000000002000000000gosec-2.18.1/rules/fileperms.go// (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 (
	"fmt"
	"go/ast"
	"strconv"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type filePermissions struct {
	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 {
	mode := defaultMode
	if value, ok := conf[configKey]; ok {
		switch value := value.(type) {
		case int64:
			mode = value
		case string:
			if m, e := strconv.ParseInt(value, 0, 64); e != nil {
				mode = defaultMode
			} else {
				mode = m
			}
		}
	}
	return mode
}

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 && !modeIsSubset(mode, r.mode) {
				return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
			}
		}
	}
	return nil, nil
}

// 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, id, 0o600)
	return &filePermissions{
		mode:  mode,
		pkgs:  []string{"io/ioutil", "os"},
		calls: []string{"WriteFile"},
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       fmt.Sprintf("Expect WriteFile permissions to be %#o or less", mode),
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}

// 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, id, 0o600)
	return &filePermissions{
		mode:  mode,
		pkgs:  []string{"os"},
		calls: []string{"OpenFile", "Chmod"},
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       fmt.Sprintf("Expect file permissions to be %#o or less", mode),
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}

// 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, id, 0o750)
	return &filePermissions{
		mode:  mode,
		pkgs:  []string{"os"},
		calls: []string{"Mkdir", "MkdirAll"},
		MetaData: issue.MetaData{
			ID:         id,
			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 reates 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)}
}
0707010000007100008180000000000000000000000001652CE6670000017D000000000000000000000000000000000000002500000000gosec-2.18.1/rules/fileperms_test.gopackage 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())
	})
})
07070100000072000081A4000000000000000000000001652CE66700002E86000000000000000000000000000000000000002C00000000gosec-2.18.1/rules/hardcoded_credentials.go// (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 (
	"fmt"
	"go/ast"
	"go/token"
	"regexp"
	"strconv"

	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 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 {
	issue.MetaData
	pattern          *regexp.Regexp
	entropyThreshold float64
	perCharThreshold float64
	truncate         int
	ignoreEntropy    bool
}

func (r *credentials) ID() string {
	return r.MetaData.ID
}

func truncate(s string, n int) string {
	if n > len(s) {
		return s
	}
	return s[:n]
}

func (r *credentials) isHighEntropyString(str string) bool {
	s := truncate(str, r.truncate)
	info := zxcvbn.PasswordStrength(s, []string{})
	entropyPerChar := info.Entropy / float64(len(s))
	return (info.Entropy >= r.entropyThreshold ||
		(info.Entropy >= (r.entropyThreshold/2) &&
			entropyPerChar >= r.perCharThreshold))
}

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)
	case *ast.ValueSpec:
		return r.matchValueSpec(node, ctx)
	case *ast.BinaryExpr:
		return r.matchEqualityCheck(node, ctx)
	}
	return nil, nil
}

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 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) (*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"
			if len(valueSpec.Values) <= index {
				index = len(valueSpec.Values) - 1
			}
			if val, err := gosec.GetString(valueSpec.Values[index]); err == nil {
				if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
					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) (*issue.Issue, error) {
	if binaryExpr.Op == token.EQL || binaryExpr.Op == token.NEQ {
		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
				}
			}
		}
	}
	return nil, nil
}

// 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|pw|apiKey|bearer|cred`
	entropyThreshold := 80.0
	perCharThreshold := 3.0
	ignoreEntropy := false
	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
			}
		}
		if configEntropyThreshold, ok := conf["entropy_threshold"]; ok {
			if cfgEntropyThreshold, ok := configEntropyThreshold.(string); ok {
				if parsedNum, err := strconv.ParseFloat(cfgEntropyThreshold, 64); err == nil {
					entropyThreshold = parsedNum
				}
			}
		}
		if configCharThreshold, ok := conf["per_char_threshold"]; ok {
			if cfgCharThreshold, ok := configCharThreshold.(string); ok {
				if parsedNum, err := strconv.ParseFloat(cfgCharThreshold, 64); err == nil {
					perCharThreshold = parsedNum
				}
			}
		}
		if configTruncate, ok := conf["truncate"]; ok {
			if cfgTruncate, ok := configTruncate.(string); ok {
				if parsedInt, err := strconv.Atoi(cfgTruncate); err == nil {
					truncateString = parsedInt
				}
			}
		}
	}

	return &credentials{
		pattern:          regexp.MustCompile(pattern),
		entropyThreshold: entropyThreshold,
		perCharThreshold: perCharThreshold,
		ignoreEntropy:    ignoreEntropy,
		truncate:         truncateString,
		MetaData: issue.MetaData{
			ID:         id,
			What:       "Potential hardcoded credentials",
			Confidence: issue.Low,
			Severity:   issue.High,
		},
	}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil), (*ast.BinaryExpr)(nil)}
}
07070100000073000081A4000000000000000000000001652CE6670000045D000000000000000000000000000000000000002100000000gosec-2.18.1/rules/http_serve.gopackage 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)}
}
07070100000074000081A4000000000000000000000001652CE66700001085000000000000000000000000000000000000002800000000gosec-2.18.1/rules/implicit_aliasing.gopackage rules

import (
	"go/ast"
	"go/token"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type implicitAliasing struct {
	issue.MetaData
	aliases         map[*ast.Object]struct{}
	rightBrace      token.Pos
	acceptableAlias []*ast.UnaryExpr
}

func (r *implicitAliasing) ID() string {
	return r.MetaData.ID
}

func containsUnary(exprs []*ast.UnaryExpr, expr *ast.UnaryExpr) bool {
	for _, e := range exprs {
		if e == expr {
			return true
		}
	}
	return false
}

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) {
	switch node := n.(type) {
	case *ast.RangeStmt:
		// When presented with a range statement, get the underlying Object bound to
		// by assignment and add it to our set (r.aliases) of objects to check for.
		if key, ok := node.Value.(*ast.Ident); ok {
			if key.Obj != nil {
				if assignment, ok := key.Obj.Decl.(*ast.AssignStmt); ok {
					if len(assignment.Lhs) < 2 {
						return nil, nil
					}

					if object, ok := assignment.Lhs[1].(*ast.Ident); ok {
						r.aliases[object.Obj] = struct{}{}

						if r.rightBrace < node.Body.Rbrace {
							r.rightBrace = node.Body.Rbrace
						}
					}
				}
			}
		}

	case *ast.UnaryExpr:
		// If this unary expression is outside of the last range statement we were looking at
		// then clear the list of objects we're concerned about because they're no longer in
		// scope
		if node.Pos() > r.rightBrace {
			r.aliases = make(map[*ast.Object]struct{})
			r.acceptableAlias = make([]*ast.UnaryExpr, 0)
		}

		// Short circuit logic to skip checking aliases if we have nothing to check against.
		if len(r.aliases) == 0 {
			return nil, nil
		}

		// If this unary is at the top level of a return statement then it is okay--
		// see *ast.ReturnStmt comment below.
		if containsUnary(r.acceptableAlias, node) {
			return nil, nil
		}

		// If we find a unary op of & (reference) of an object within r.aliases, complain.
		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:
		// Returning a rangeStmt yielded value is acceptable since only one value will be returned
		for _, item := range node.Results {
			if unary, ok := item.(*ast.UnaryExpr); ok && unary.Op.String() == "&" {
				r.acceptableAlias = append(r.acceptableAlias, unary)
			}
		}
	}

	return nil, nil
}

// NewImplicitAliasing detects implicit memory aliasing of type: for blah := SomeCall() {... SomeOtherCall(&blah) ...}
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: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.Medium,
			What:       "Implicit memory aliasing in for loop.",
		},
	}, []ast.Node{(*ast.RangeStmt)(nil), (*ast.UnaryExpr)(nil), (*ast.ReturnStmt)(nil)}
}

/*
This rule is prone to flag false positives.

Within GoSec, the rule is just an AST match-- there are a handful of other
implementation strategies which might lend more nuance to the rule at the
cost of allowing false negatives.

From a tooling side, I'd rather have this rule flag false positives than
potentially have some false negatives-- especially if the sentiment of this
rule (as I understand it, and Go) is that referencing a rangeStmt-yielded
value is kinda strange and does not have a strongly justified use case.

Which is to say-- a false positive _should_ just be changed.
*/
07070100000075000081A4000000000000000000000001652CE66700000B62000000000000000000000000000000000000002700000000gosec-2.18.1/rules/integer_overflow.go// (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 (
	"fmt"
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type integerOverflowCheck struct {
	issue.MetaData
	calls gosec.CallList
}

func (i *integerOverflowCheck) ID() string {
	return i.MetaData.ID
}

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.
	if _, ok := ctx.PassedValues[i.ID()]; !ok {
		atoiVarObj = make(map[*ast.Object]ast.Node)
		ctx.PassedValues[i.ID()] = atoiVarObj
	} else if pv, ok := ctx.PassedValues[i.ID()].(map[*ast.Object]ast.Node); ok {
		atoiVarObj = pv
	} else {
		return nil, fmt.Errorf("PassedValues[%s] of Context is not map[*ast.Object]ast.Node, but %T", i.ID(), ctx.PassedValues[i.ID()])
	}

	// strconv.Atoi is a common function.
	// To reduce false positives, This rule detects code which is converted to int32/int16 only.
	switch n := node.(type) {
	case *ast.AssignStmt:
		for _, expr := range n.Rhs {
			if callExpr, ok := expr.(*ast.CallExpr); ok && i.calls.ContainsPkgCallExpr(callExpr, ctx, false) != nil {
				if idt, ok := n.Lhs[0].(*ast.Ident); ok && idt.Name != "_" {
					// Example:
					//  v, _ := strconv.Atoi("1111")
					// Add v's Obj to atoiVarObj map
					atoiVarObj[idt.Obj] = n
				}
			}
		}
	case *ast.CallExpr:
		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 _, ok := atoiVarObj[idt.Obj]; ok {
						// Detect int32(v) and int16(v)
						return ctx.NewIssue(n, i.ID(), i.What, i.Severity, i.Confidence), nil
					}
				}
			}
		}
	}

	return nil, nil
}

// NewIntegerOverflowCheck detects if there is potential Integer OverFlow
func NewIntegerOverflowCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	calls := gosec.NewCallList()
	calls.Add("strconv", "Atoi")
	return &integerOverflowCheck{
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.High,
			Confidence: issue.Medium,
			What:       "Potential Integer overflow made by strconv.Atoi result conversion to int16/32",
		},
		calls: calls,
	}, []ast.Node{(*ast.FuncDecl)(nil), (*ast.AssignStmt)(nil), (*ast.CallExpr)(nil)}
}
07070100000076000081A4000000000000000000000001652CE6670000048E000000000000000000000000000000000000002300000000gosec-2.18.1/rules/math_big_rat.gopackage rules

import (
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type usingOldMathBig struct {
	issue.MetaData
	calls gosec.CallList
}

func (r *usingOldMathBig) ID() string {
	return r.MetaData.ID
}

func (r *usingOldMathBig) Match(node ast.Node, ctx *gosec.Context) (gi *issue.Issue, err error) {
	if callExpr := r.calls.ContainsPkgCallExpr(node, ctx, false); callExpr == nil {
		return nil, nil
	}

	confidence := issue.Low
	major, minor, build := gosec.GoVersion()
	if major == 1 && (minor == 16 && build < 14 || minor == 17 && build < 7) {
		confidence = issue.Medium
	}

	return ctx.NewIssue(node, r.ID(), r.What, r.Severity, confidence), nil
}

// NewUsingOldMathBig rule detects the use of Rat.SetString from math/big.
func NewUsingOldMathBig(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	calls := gosec.NewCallList()
	calls.Add("math/big.Rat", "SetString")
	return &usingOldMathBig{
		calls: calls,
		MetaData: issue.MetaData{
			ID:       id,
			What:     "Potential uncontrolled memory consumption in Rat.SetString (CVE-2022-23772)",
			Severity: issue.High,
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000077000081A4000000000000000000000001652CE66700000444000000000000000000000000000000000000001C00000000gosec-2.18.1/rules/pprof.gopackage rules

import (
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type pprofCheck struct {
	issue.MetaData
	importPath string
	importName string
}

// ID returns the ID of the check
func (p *pprofCheck) ID() string {
	return p.MetaData.ID
}

// Match checks for pprof imports
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 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &pprofCheck{
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.High,
			Confidence: issue.High,
			What:       "Profiling endpoint is automatically exposed on /debug/pprof",
		},
		importPath: "net/http/pprof",
		importName: "_",
	}, []ast.Node{(*ast.ImportSpec)(nil)}
}
07070100000078000081A4000000000000000000000001652CE66700000704000000000000000000000000000000000000001B00000000gosec-2.18.1/rules/rand.go// (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 weakRand struct {
	issue.MetaData
	funcNames   []string
	packagePath string
}

func (w *weakRand) ID() string {
	return w.MetaData.ID
}

func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
	for _, funcName := range w.funcNames {
		if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
			return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil
		}
	}

	return nil, nil
}

// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &weakRand{
		funcNames: []string{
			"New", "Read", "Float32", "Float64", "Int", "Int31",
			"Int31n", "Int63", "Int63n", "Intn", "NormalFloat64", "Uint32", "Uint64",
		},
		packagePath: "math/rand",
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.High,
			Confidence: issue.Medium,
			What:       "Use of weak random number generator (math/rand instead of crypto/rand)",
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000079000081A4000000000000000000000001652CE66700001289000000000000000000000000000000000000001F00000000gosec-2.18.1/rules/readfile.go// (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"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type readfile struct {
	issue.MetaData
	gosec.CallList
	pathJoin   gosec.CallList
	clean      gosec.CallList
	cleanedVar map[any]ast.Node
}

// ID returns the identifier for this rule
func (r *readfile) ID() string {
	return r.MetaData.ID
}

// isJoinFunc checks if there is a filepath.Join or other join function
func (r *readfile) isJoinFunc(n ast.Node, c *gosec.Context) bool {
	if call := r.pathJoin.ContainsPkgCallExpr(n, c, false); call != nil {
		for _, arg := range call.Args {
			// edge case: check if one of the args is a BinaryExpr
			if binExp, ok := arg.(*ast.BinaryExpr); ok {
				// iterate and resolve all found identities from the BinaryExpr
				if _, ok := gosec.FindVarIdentities(binExp, c); ok {
					return true
				}
			}

			// try and resolve identity
			if ident, ok := arg.(*ast.Ident); ok {
				obj := c.Info.ObjectOf(ident)
				if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
					return true
				}
			}
		}
	}
	return false
}

// 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
	}
	if node, ok := n.Obj.Decl.(*ast.AssignStmt); ok {
		if call, ok := node.Rhs[0].(*ast.CallExpr); ok {
			if clean := r.clean.ContainsPkgCallExpr(call, c, false); clean != nil {
				return true
			}
		}
	}
	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) (*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 c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
				}
			}
			// handles binary string concatenation eg. ioutil.Readfile("/tmp/" + file + "/blob")
			if binExp, ok := arg.(*ast.BinaryExpr); ok {
				// resolve all found identities from the BinaryExpr
				if _, ok := gosec.FindVarIdentities(binExp, c); ok {
					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
				}
			}

			if ident, ok := arg.(*ast.Ident); ok {
				obj := c.Info.ObjectOf(ident)
				if _, ok := obj.(*types.Var); ok &&
					!gosec.TryResolve(ident, c) &&
					!r.isFilepathClean(ident, c) {
					return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil
				}
			}
		}
	}
	return nil, nil
}

// NewReadFile detects cases where we read files
func NewReadFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	rule := &readfile{
		pathJoin: gosec.NewCallList(),
		clean:    gosec.NewCallList(),
		CallList: gosec.NewCallList(),
		MetaData: issue.MetaData{
			ID:         id,
			What:       "Potential file inclusion via variable",
			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.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)}
}
0707010000007A000081A4000000000000000000000001652CE667000006CF000000000000000000000000000000000000001A00000000gosec-2.18.1/rules/rsa.go// (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 (
	"fmt"
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type weakKeyStrength struct {
	issue.MetaData
	calls gosec.CallList
	bits  int
}

func (w *weakKeyStrength) ID() string {
	return w.MetaData.ID
}

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 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	calls := gosec.NewCallList()
	calls.Add("crypto/rsa", "GenerateKey")
	bits := 2048
	return &weakKeyStrength{
		calls: calls,
		bits:  bits,
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       fmt.Sprintf("RSA keys should be at least %d bits", bits),
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
0707010000007B000081A4000000000000000000000001652CE6670000144C000000000000000000000000000000000000001F00000000gosec-2.18.1/rules/rulelist.go// (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 "github.com/securego/gosec/v2"

// RuleDefinition contains the description of a rule and a mechanism to
// create it.
type RuleDefinition struct {
	ID          string
	Description string
	Create      gosec.RuleBuilder
}

// 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
}

// 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.Rules {
		builders[def.ID] = def.Create
	}
	return builders, rl.RuleSuppressed
}

// RuleFilter can be used to include or exclude a rule depending on the return
// value of the function
type RuleFilter func(string) bool

// NewRuleFilter is a closure that will include/exclude the rule ID's based on
// the supplied boolean value.
func NewRuleFilter(action bool, ruleIDs ...string) RuleFilter {
	rulelist := make(map[string]bool)
	for _, rule := range ruleIDs {
		rulelist[rule] = true
	}
	return func(rule string) bool {
		if _, found := rulelist[rule]; found {
			return action
		}
		return !action
	}
}

// Generate the list of rules to use
func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList {
	rules := []RuleDefinition{
		// misc
		{"G101", "Look for hardcoded credentials", NewHardcodedCredentials},
		{"G102", "Bind to all interfaces", NewBindsToAllNetworkInterfaces},
		{"G103", "Audit the use of unsafe block", NewUsingUnsafe},
		{"G104", "Audit errors not checked", NewNoErrorCheck},
		{"G106", "Audit the use of ssh.InsecureIgnoreHostKey function", NewSSHHostKey},
		{"G107", "Url provided to HTTP request as taint input", NewSSRFCheck},
		{"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},
		{"G113", "Usage of Rat.SetString in math/big with an overflow", NewUsingOldMathBig},
		{"G114", "Use of net/http serve function that has no support for setting timeouts", NewHTTPServeWithoutTimeouts},

		// injection
		{"G201", "SQL query construction using format string", NewSQLStrFormat},
		{"G202", "SQL query construction using string concatenation", NewSQLStrConcat},
		{"G203", "Use of unescaped data in HTML templates", NewTemplateCheck},
		{"G204", "Audit use of command execution", NewSubproc},

		// filesystem
		{"G301", "Poor file permissions used when creating a directory", NewMkdirPerms},
		{"G302", "Poor file permissions used when creation file or using chmod", NewFilePerms},
		{"G303", "Creating tempfile using a predictable path", NewBadTempFile},
		{"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", "Poor file permissions used when creating a file with os.Create", NewOsCreatePerms},

		// crypto
		{"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography},
		{"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},

		// blocklist
		{"G501", "Import blocklist: crypto/md5", NewBlocklistedImportMD5},
		{"G502", "Import blocklist: crypto/des", NewBlocklistedImportDES},
		{"G503", "Import blocklist: crypto/rc4", NewBlocklistedImportRC4},
		{"G504", "Import blocklist: net/http/cgi", NewBlocklistedImportCGI},
		{"G505", "Import blocklist: crypto/sha1", NewBlocklistedImportSHA1},

		// 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) {
				ruleSuppressedMap[rule.ID] = true
				if !trackSuppressions {
					continue RULES
				}
			}
		}
		ruleMap[rule.ID] = rule
	}
	return RuleList{ruleMap, ruleSuppressedMap}
}
0707010000007C00008180000000000000000000000001652CE667000000BF000000000000000000000000000000000000002700000000gosec-2.18.1/rules/rules_suite_test.gopackage rules_test

import (
	"testing"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

func TestRules(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Rules Suite")
}
0707010000007D000081A4000000000000000000000001652CE66700001616000000000000000000000000000000000000002100000000gosec-2.18.1/rules/rules_test.gopackage rules_test

import (
	"fmt"
	"log"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/rules"
	"github.com/securego/gosec/v2/testutils"
)

var _ = Describe("gosec rules", 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(rule string, samples []testutils.CodeSample) {
			for n, sample := range samples {
				analyzer.Reset()
				analyzer.SetConfig(sample.Config)
				analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, rule)).RulesInfo())
				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 hardcoded credentials", 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)
		})

		It("should use of unsafe block", func() {
			runner("G103", testutils.SampleCodeG103)
		})

		It("should detect errors not being checked", func() {
			runner("G104", testutils.SampleCodeG104)
		})

		It("should detect errors not being checked in audit mode", func() {
			runner("G104", testutils.SampleCodeG104Audit)
		})

		It("should detect of ssh.InsecureIgnoreHostKey function", func() {
			runner("G106", testutils.SampleCodeG106)
		})

		It("should detect ssrf via http requests with variable url", func() {
			runner("G107", testutils.SampleCodeG107)
		})

		It("should detect pprof endpoint", func() {
			runner("G108", testutils.SampleCodeG108)
		})

		It("should detect integer overflow", func() {
			runner("G109", testutils.SampleCodeG109)
		})

		It("should detect DoS vulnerability via decompression bomb", 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 potential uncontrolled memory consumption in Rat.SetString", func() {
			runner("G113", testutils.SampleCodeG113)
		})

		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)
		})

		It("should detect sql injection via string concatenation", func() {
			runner("G202", testutils.SampleCodeG202)
		})

		It("should detect unescaped html in templates", func() {
			runner("G203", testutils.SampleCodeG203)
		})

		It("should detect command execution", func() {
			runner("G204", testutils.SampleCodeG204)
		})

		It("should detect poor file permissions on mkdir", func() {
			runner("G301", testutils.SampleCodeG301)
		})

		It("should detect poor permissions when creating or chmod a file", func() {
			runner("G302", testutils.SampleCodeG302)
		})

		It("should detect insecure temp file creation", func() {
			runner("G303", testutils.SampleCodeG303)
		})

		It("should detect file path provided as taint input", func() {
			runner("G304", testutils.SampleCodeG304)
		})

		It("should detect file path traversal when extracting zip archive", func() {
			runner("G305", testutils.SampleCodeG305)
		})

		It("should detect poor permissions when writing to a file", func() {
			runner("G306", testutils.SampleCodeG306)
		})

		It("should detect weak crypto algorithms", func() {
			runner("G401", testutils.SampleCodeG401)
		})

		It("should detect weak crypto algorithms", func() {
			runner("G401", testutils.SampleCodeG401b)
		})

		It("should find insecure tls settings", func() {
			runner("G402", testutils.SampleCodeG402)
		})

		It("should detect weak creation of weak rsa keys", func() {
			runner("G403", testutils.SampleCodeG403)
		})

		It("should find non cryptographically secure random number sources", func() {
			runner("G404", testutils.SampleCodeG404)
		})

		It("should detect blocklisted imports - MD5", func() {
			runner("G501", testutils.SampleCodeG501)
		})

		It("should detect blocklisted imports - DES", func() {
			runner("G502", testutils.SampleCodeG502)
		})

		It("should detect blocklisted imports - RC4", func() {
			runner("G503", testutils.SampleCodeG503)
		})

		It("should detect blocklisted imports - CGI (httpoxy)", func() {
			runner("G504", testutils.SampleCodeG504)
		})

		It("should detect blocklisted imports - SHA1", func() {
			runner("G505", testutils.SampleCodeG505)
		})

		It("should detect implicit aliasing in ForRange", func() {
			runner("G601", testutils.SampleCodeG601)
		})

		It("should detect out of bounds slice access", func() {
			runner("G602", testutils.SampleCodeG602)
		})
	})
})
0707010000007E000081A4000000000000000000000001652CE667000007F0000000000000000000000000000000000000002000000000gosec-2.18.1/rules/slowloris.go// (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)}
}
0707010000007F000081A4000000000000000000000001652CE66700002A81000000000000000000000000000000000000001A00000000gosec-2.18.1/rules/sql.go// (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 (
	"fmt"
	"go/ast"
	"regexp"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type sqlStatement struct {
	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.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
}

// See if the string matches the patterns for the statement.
func (s *sqlStatement) MatchPatterns(str string) bool {
	for _, pattern := range s.patterns {
		if !pattern.MatchString(str) {
			return false
		}
	}
	return true
}

type sqlStrConcat struct {
	sqlStatement
}

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 {
		return n.Obj.Kind != ast.Var && n.Obj.Kind != ast.Fun
	}

	// Try to resolve unresolved identifiers using other files in same package
	for _, file := range c.PkgFiles {
		if node, ok := file.Scope.Objects[n.String()]; ok {
			return node.Kind != ast.Var && node.Kind != ast.Fun
		}
	}
	return false
}

// checkQuery verifies if the query parameters is a string concatenation
func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) {
	query, err := findQueryArg(call, ctx)
	if err != nil {
		return nil, err
	}

	if be, ok := query.(*ast.BinaryExpr); ok {
		operands := gosec.GetBinaryExprOperands(be)
		if start, ok := operands[0].(*ast.BasicLit); ok {
			if str, e := gosec.GetString(start); e == nil {
				if !s.MatchPatterns(str) {
					return nil, nil
				}
			}
			for _, op := range operands[1:] {
				if _, ok := op.(*ast.BasicLit); ok {
					continue
				}
				if op, ok := op.(*ast.Ident); ok && s.checkObject(op, ctx) {
					continue
				}
				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
			}
		}
	}

	return nil, nil
}

// Checks SQL query concatenation issues such as "SELECT * FROM table WHERE " + " ' OR 1=1"
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 {
			if sqlQueryCall, ok := expr.(*ast.CallExpr); ok && s.ContainsCallExpr(expr, ctx) != nil {
				return s.checkQuery(sqlQueryCall, ctx)
			}
		}
	case *ast.ExprStmt:
		if sqlQueryCall, ok := stmt.X.(*ast.CallExpr); ok && s.ContainsCallExpr(stmt.X, ctx) != nil {
			return s.checkQuery(sqlQueryCall, ctx)
		}
	}

	return nil, nil
}

// NewSQLStrConcat looks for cases where we are building SQL strings via concatenation
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)( |\n|\r|\t)"),
			},
			MetaData: issue.MetaData{
				ID:         id,
				Severity:   issue.Medium,
				Confidence: issue.High,
				What:       "SQL string concatenation",
			},
			CallList: gosec.NewCallList(),
		},
	}

	for s, si := range sqlCallIdents {
		for i := range si {
			rule.Add(s, i)
		}
	}
	return rule, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
}

type sqlStrFormat struct {
	gosec.CallList
	sqlStatement
	fmtCalls      gosec.CallList
	noIssue       gosec.CallList
	noIssueQuoted gosec.CallList
}

// see if we can figure out what it is
func (s *sqlStrFormat) constObject(e ast.Expr, c *gosec.Context) bool {
	n, ok := e.(*ast.Ident)
	if !ok {
		return false
	}

	if n.Obj != nil {
		return n.Obj.Kind == ast.Con
	}

	// Try to resolve unresolved identifiers using other files in same package
	for _, file := range c.PkgFiles {
		if node, ok := file.Scope.Objects[n.String()]; ok {
			return node.Kind == ast.Con
		}
	}
	return false
}

func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) {
	query, err := findQueryArg(call, ctx)
	if err != nil {
		return nil, err
	}

	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 := s.checkFormatting(expr, ctx)
				if issue != nil {
					return issue, err
				}
			}
		}
	}

	return nil, nil
}

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 {
		// if the function is fmt.Fprintf, search for SQL statement in Args[1] instead
		if sel, ok := node.Fun.(*ast.SelectorExpr); ok {
			if sel.Sel.Name == "Fprintf" {
				// if os.Stderr or os.Stdout is in Arg[0], mark as no issue
				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
						}
					}
				}
				// the function is Fprintf so set argIndex = 1
				argIndex = 1
			}
		}

		// no formatter
		if len(node.Args) == 0 {
			return nil
		}

		var formatter string

		// concats callexpr arg strings together if needed before regex evaluation
		if argExpr, ok := node.Args[argIndex].(*ast.BinaryExpr); ok {
			if fullStr, ok := gosec.ConcatString(argExpr); ok {
				formatter = fullStr
			}
		} else if arg, e := gosec.GetString(node.Args[argIndex]); e == nil {
			formatter = arg
		}
		if len(formatter) <= 0 {
			return nil
		}

		// If all formatter args are quoted or constant, then the SQL construction is safe
		if argIndex+1 < len(node.Args) {
			allSafe := true
			for _, arg := range node.Args[argIndex+1:] {
				if n := s.noIssueQuoted.ContainsPkgCallExpr(arg, ctx, true); n == nil && !s.constObject(arg, ctx) {
					allSafe = false
					break
				}
			}
			if allSafe {
				return nil
			}
		}
		if s.MatchPatterns(formatter) {
			return ctx.NewIssue(n, s.ID(), s.What, s.Severity, s.Confidence)
		}
	}
	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) (*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)
			}
		}
	case *ast.ExprStmt:
		if sqlQueryCall, ok := stmt.X.(*ast.CallExpr); ok && s.ContainsCallExpr(stmt.X, ctx) != nil {
			return s.checkQuery(sqlQueryCall, ctx)
		}
	}
	return nil, nil
}

// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
func NewSQLStrFormat(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
	rule := &sqlStrFormat{
		CallList:      gosec.NewCallList(),
		fmtCalls:      gosec.NewCallList(),
		noIssue:       gosec.NewCallList(),
		noIssueQuoted: gosec.NewCallList(),
		sqlStatement: sqlStatement{
			patterns: []*regexp.Regexp{
				regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE)( |\n|\r|\t)"),
				regexp.MustCompile("%[^bdoxXfFp]"),
			},
			MetaData: issue.MetaData{
				ID:         id,
				Severity:   issue.Medium,
				Confidence: issue.High,
				What:       "SQL string formatting",
			},
		},
	}
	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")

	return rule, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
}
07070100000080000081A4000000000000000000000001652CE667000003C3000000000000000000000000000000000000001A00000000gosec-2.18.1/rules/ssh.gopackage rules

import (
	"go/ast"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type sshHostKey struct {
	issue.MetaData
	pkg   string
	calls []string
}

func (r *sshHostKey) ID() string {
	return r.MetaData.ID
}

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 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &sshHostKey{
		pkg:   "golang.org/x/crypto/ssh",
		calls: []string{"InsecureIgnoreHostKey"},
		MetaData: issue.MetaData{
			ID:         id,
			What:       "Use of ssh InsecureIgnoreHostKey should be audited",
			Severity:   issue.Medium,
			Confidence: issue.High,
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000081000081A4000000000000000000000001652CE667000006EC000000000000000000000000000000000000001B00000000gosec-2.18.1/rules/ssrf.gopackage rules

import (
	"go/ast"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type ssrf struct {
	issue.MetaData
	gosec.CallList
}

// ID returns the identifier for this rule
func (r *ssrf) ID() string {
	return r.MetaData.ID
}

// ResolveVar tries to resolve the first argument of a call expression
// The first argument is the url
func (r *ssrf) ResolveVar(n *ast.CallExpr, c *gosec.Context) bool {
	if len(n.Args) > 0 {
		arg := n.Args[0]
		if ident, ok := arg.(*ast.Ident); ok {
			obj := c.Info.ObjectOf(ident)
			if _, ok := obj.(*types.Var); ok {
				scope := c.Pkg.Scope()
				if scope != nil && scope.Lookup(ident.Name) != nil {
					// a URL defined in a variable at package scope can be changed at any time
					return true
				}
				if !gosec.TryResolve(ident, c) {
					return true
				}
			}
		}
	}
	return false
}

// 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) (*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 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	rule := &ssrf{
		CallList: gosec.NewCallList(),
		MetaData: issue.MetaData{
			ID:         id,
			What:       "Potential HTTP request made with variable url",
			Severity:   issue.Medium,
			Confidence: issue.Medium,
		},
	}
	rule.AddAll("net/http", "Do", "Get", "Head", "Post", "PostForm", "RoundTrip")
	return rule, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000082000081A4000000000000000000000001652CE66700000FA9000000000000000000000000000000000000001E00000000gosec-2.18.1/rules/subproc.go// (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"
	"go/types"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type subprocess struct {
	issue.MetaData
	gosec.CallList
}

func (r *subprocess) ID() string {
	return r.MetaData.ID
}

// TODO(gm) The only real potential for command injection with a Go project
// is something like this:
//
// syscall.Exec("/bin/sh", []string{"-c", tainted})
//
// E.g. Input is correctly escaped but the execution context being used
// is unsafe. For example:
//
// syscall.Exec("echo", "foobar" + tainted)
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) {
			args = args[1:]
		}
		for _, arg := range args {
			if ident, ok := arg.(*ast.Ident); ok {
				obj := c.Info.ObjectOf(ident)

				// 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 c.NewIssue(n, r.ID(), "Subprocess launched with a potential tainted input or cmd arguments", issue.Medium, issue.High), nil
			}
		}
	}
	return nil, nil
}

// isContext checks whether or not the node is a CommandContext call or not
// 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 {
		return false
	}
	if selector == "exec" && indent == "CommandContext" {
		return true
	}
	return false
}

// NewSubproc detects cases where we are forking out to an external process
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)}
}
07070100000083000081A4000000000000000000000001652CE66700000AB5000000000000000000000000000000000000002000000000gosec-2.18.1/rules/tempfiles.go// (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"
	"regexp"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type badTempFile struct {
	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) 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	calls := gosec.NewCallList()
	calls.Add("io/ioutil", "WriteFile")
	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(`^(/(usr|var))?/tmp(/.*)?$`),
		argCalls:    argCalls,
		nestedCalls: nestedCalls,
		MetaData: issue.MetaData{
			ID:         id,
			Severity:   issue.Medium,
			Confidence: issue.High,
			What:       "File creation in shared tmp directory without using ioutil.Tempfile",
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000084000081A4000000000000000000000001652CE667000007B1000000000000000000000000000000000000002000000000gosec-2.18.1/rules/templates.go// (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 templateCheck struct {
	issue.MetaData
	calls gosec.CallList
}

func (t *templateCheck) ID() string {
	return t.MetaData.ID
}

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 c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence), nil
			}
		}
	}
	return nil, nil
}

// 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	calls := gosec.NewCallList()
	calls.Add("html/template", "HTML")
	calls.Add("html/template", "HTMLAttr")
	calls.Add("html/template", "JS")
	calls.Add("html/template", "URL")
	return &templateCheck{
		calls: calls,
		MetaData: issue.MetaData{
			ID:         id,
			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)}
}
07070100000085000081A4000000000000000000000001652CE66700001A2C000000000000000000000000000000000000001A00000000gosec-2.18.1/rules/tls.go// (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.

//go:generate tlsconfig

package rules

import (
	"crypto/tls"
	"fmt"
	"go/ast"
	"go/types"
	"strconv"

	"github.com/securego/gosec/v2"
	"github.com/securego/gosec/v2/issue"
)

type insecureConfigTLS struct {
	issue.MetaData
	MinVersion       int64
	MaxVersion       int64
	requiredType     string
	goodCiphers      []string
	actualMinVersion int64
	actualMaxVersion int64
}

func (t *insecureConfigTLS) ID() string {
	return t.MetaData.ID
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
		}
	}
	return false
}

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 c.NewIssue(ident, t.ID(), err, issue.High, issue.High)
				}
			}
		}
	}
	return nil
}

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 := value.(*ast.Ident); ok {
				if node.Name != "false" {
					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 c.NewIssue(value, t.ID(), "TLS InsecureSkipVerify may be true.", issue.High, issue.Low)
			}

		case "PreferServerCipherSuites":
			if node, ok := value.(*ast.Ident); ok {
				if node.Name == "false" {
					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 c.NewIssue(value, t.ID(), "TLS PreferServerCipherSuites may be false.", issue.Medium, issue.Low)
			}

		case "MinVersion":
			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 := 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(value); ierr == nil {
				t.actualMaxVersion = ival
			} else {
				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(value, c); ret != nil {
				return ret
			}

		}
	}
	return nil
}

func (t *insecureConfigTLS) mapVersion(version string) int64 {
	var v int64
	switch version {
	case "VersionTLS13":
		v = tls.VersionTLS13
	case "VersionTLS12":
		v = tls.VersionTLS12
	case "VersionTLS11":
		v = tls.VersionTLS11
	case "VersionTLS10":
		v = tls.VersionTLS10
	}
	return v
}

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 c.NewIssue(n, t.ID(), "TLS MinVersion too low.", issue.High, issue.High)
	}
	if t.actualMaxVersion < t.MaxVersion {
		return c.NewIssue(n, t.ID(), "TLS MaxVersion too low.", issue.High, issue.High)
	}
	return nil
}

func (t *insecureConfigTLS) resetVersion() {
	t.actualMaxVersion = 0
	t.actualMinVersion = 0
}

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 {
				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
}
07070100000086000081A4000000000000000000000001652CE66700000CDC000000000000000000000000000000000000002100000000gosec-2.18.1/rules/tls_config.gopackage rules

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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &insecureConfigTLS{
		MetaData:     issue.MetaData{ID: id},
		requiredType: "crypto/tls.Config",
		MinVersion:   0x0304,
		MaxVersion:   0x0304,
		goodCiphers: []string{
			"TLS_AES_128_GCM_SHA256",
			"TLS_AES_256_GCM_SHA384",
			"TLS_CHACHA20_POLY1305_SHA256",
		},
	}, []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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &insecureConfigTLS{
		MetaData:     issue.MetaData{ID: id},
		requiredType: "crypto/tls.Config",
		MinVersion:   0x0303,
		MaxVersion:   0x0304,
		goodCiphers: []string{
			"TLS_AES_128_GCM_SHA256",
			"TLS_AES_256_GCM_SHA384",
			"TLS_CHACHA20_POLY1305_SHA256",
			"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
			"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
			"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
			"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
			"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
			"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
			"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
			"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
		},
	}, []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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &insecureConfigTLS{
		MetaData:     issue.MetaData{ID: id},
		requiredType: "crypto/tls.Config",
		MinVersion:   0x0301,
		MaxVersion:   0x0304,
		goodCiphers: []string{
			"TLS_AES_128_GCM_SHA256",
			"TLS_AES_256_GCM_SHA384",
			"TLS_CHACHA20_POLY1305_SHA256",
			"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
			"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
			"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
			"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
			"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
			"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
			"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
			"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
			"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
			"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
			"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
			"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
			"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
			"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
			"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
			"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
			"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
			"TLS_RSA_WITH_AES_128_GCM_SHA256",
			"TLS_RSA_WITH_AES_256_GCM_SHA384",
			"TLS_RSA_WITH_AES_128_CBC_SHA256",
			"TLS_RSA_WITH_AES_256_CBC_SHA256",
			"TLS_RSA_WITH_AES_128_CBC_SHA",
			"TLS_RSA_WITH_AES_256_CBC_SHA",
			"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
		},
	}, []ast.Node{(*ast.CompositeLit)(nil), (*ast.AssignStmt)(nil)}
}
07070100000087000081A4000000000000000000000001652CE66700000662000000000000000000000000000000000000001D00000000gosec-2.18.1/rules/unsafe.go// (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 usingUnsafe struct {
	issue.MetaData
	pkg   string
	calls []string
}

func (r *usingUnsafe) ID() string {
	return r.MetaData.ID
}

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 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, _ gosec.Config) (gosec.Rule, []ast.Node) {
	return &usingUnsafe{
		pkg:   "unsafe",
		calls: []string{"Pointer", "String", "StringData", "Slice", "SliceData"},
		MetaData: issue.MetaData{
			ID:         id,
			What:       "Use of unsafe calls should be audited",
			Severity:   issue.Low,
			Confidence: issue.High,
		},
	}, []ast.Node{(*ast.CallExpr)(nil)}
}
07070100000088000081A4000000000000000000000001652CE66700000734000000000000000000000000000000000000002100000000gosec-2.18.1/rules/weakcrypto.go// (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 usesWeakCryptography struct {
	issue.MetaData
	blocklist map[string][]string
}

func (r *usesWeakCryptography) ID() string {
	return r.MetaData.ID
}

func (r *usesWeakCryptography) 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
}

// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.*
func NewUsesWeakCryptography(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{
		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)}
}
07070100000089000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001700000000gosec-2.18.1/testutils0707010000008A00008180000000000000000000000001652CE667000000EF000000000000000000000000000000000000001E00000000gosec-2.18.1/testutils/log.gopackage testutils

import (
	"bytes"
	"log"
)

// NewLogger returns a logger and the buffer that it will be written to
func NewLogger() (*log.Logger, *bytes.Buffer) {
	var buf bytes.Buffer
	return log.New(&buf, "", log.Lshortfile), &buf
}
0707010000008B000081A4000000000000000000000001652CE66700000C97000000000000000000000000000000000000001E00000000gosec-2.18.1/testutils/pkg.gopackage testutils

import (
	"fmt"
	"go/build"
	"log"
	"os"
	"path"
	"strings"

	"golang.org/x/tools/go/packages"

	"github.com/securego/gosec/v2"
)

type buildObj struct {
	pkg    *build.Package
	config *packages.Config
	pkgs   []*packages.Package
}

// TestPackage is a mock package for testing purposes
type TestPackage struct {
	Path   string
	Files  map[string]string
	onDisk bool
	build  *buildObj
}

// NewTestPackage will create a new and empty package. Must call Close() to cleanup
// auxiliary files
func NewTestPackage() *TestPackage {
	workingDir, err := os.MkdirTemp("", "gosecs_test")
	if err != nil {
		return nil
	}

	return &TestPackage{
		Path:   workingDir,
		Files:  make(map[string]string),
		onDisk: false,
		build:  nil,
	}
}

// AddFile inserts the filename and contents into the package contents
func (p *TestPackage) AddFile(filename, content string) {
	p.Files[path.Join(p.Path, filename)] = content
}

func (p *TestPackage) write() error {
	if p.onDisk {
		return nil
	}
	for filename, content := range p.Files {
		if e := os.WriteFile(filename, []byte(content), 0o644); e != nil /* #nosec G306 */ {
			return e
		}
	}
	p.onDisk = true
	return nil
}

// Build ensures all files are persisted to disk and built
func (p *TestPackage) Build() error {
	if p.build != nil {
		return nil
	}
	if err := p.write(); err != nil {
		return err
	}
	basePackage, err := build.Default.ImportDir(p.Path, build.ImportComment)
	if err != nil {
		return err
	}

	var packageFiles []string
	for _, filename := range basePackage.GoFiles {
		packageFiles = append(packageFiles, path.Join(p.Path, filename))
	}

	conf := &packages.Config{
		Mode:  gosec.LoadMode,
		Tests: false,
	}
	pkgs, err := packages.Load(conf, packageFiles...)
	if err != nil {
		return err
	}
	p.build = &buildObj{
		pkg:    basePackage,
		config: conf,
		pkgs:   pkgs,
	}
	return nil
}

// CreateContext builds a context out of supplied package context
func (p *TestPackage) CreateContext(filename string) *gosec.Context {
	if err := p.Build(); err != nil {
		log.Fatal(err)
		return nil
	}

	for _, pkg := range p.build.pkgs {
		for _, file := range pkg.Syntax {
			pkgFile := pkg.Fset.File(file.Pos()).Name()
			strip := fmt.Sprintf("%s%c", p.Path, os.PathSeparator)
			pkgFile = strings.TrimPrefix(pkgFile, strip)
			if pkgFile == filename {
				ctx := &gosec.Context{
					FileSet:      pkg.Fset,
					Root:         file,
					Config:       gosec.NewConfig(),
					Info:         pkg.TypesInfo,
					Pkg:          pkg.Types,
					Imports:      gosec.NewImportTracker(),
					PassedValues: make(map[string]interface{}),
				}
				ctx.Imports.TrackPackages(ctx.Pkg.Imports()...)
				return ctx
			}
		}
	}
	return nil
}

// Close will delete the package and all files in that directory
func (p *TestPackage) Close() {
	if p.onDisk {
		err := os.RemoveAll(p.Path)
		if err != nil {
			log.Fatal(err)
		}
	}
}

// Pkgs returns the current built packages
func (p *TestPackage) Pkgs() []*packages.Package {
	if p.build != nil {
		return p.build.pkgs
	}
	return []*packages.Package{}
}

// PrintErrors prints to os.Stderr the accumulated errors of built packages
func (p *TestPackage) PrintErrors() int {
	return packages.PrintErrors(p.Pkgs())
}
0707010000008C000081A4000000000000000000000001652CE66700010BA1000000000000000000000000000000000000002100000000gosec-2.18.1/testutils/source.gopackage 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 "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()},
	}

	// 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()},
	}

	// 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)) 
  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()},
	}

	// 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"}}},
	}

	// 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()},
		{[]string{`
package main
import (
    "fmt"
    "strconv"
)
func main() {
    a, err := strconv.Atoi("a")
    b := int32(a) //#nosec G109
    fmt.Println(b, err)
}`}, 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()},
	}

	// SampleCodeG111 - potential directory traversal
	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()},
	}

	// SampleCodeG112 - potential slowloris attack
	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()},
	}

	// SampleCodeG113 - Usage of Rat.SetString in math/big with an overflow
	SampleCodeG113 = []CodeSample{
		{[]string{
			`
package main

import (
	"math/big"
	"fmt"
)

func main() {
	r := big.Rat{}
	r.SetString("13e-9223372036854775808")

	fmt.Println(r)
}`,
		}, 1, gosec.NewConfig()},
	}

	// SampleCodeG114 - Use of net/http serve functions that have no support for setting timeouts
	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(),
		},
	}

	// 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 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 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()},
	}

	// SampleCodeG202 - SQL query string building via string concatenation
	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{`
// 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()},
	}

	// 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-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()},
	}

	// 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"
	"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()}}

	// 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 (
"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()},
	}

	// 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 - Poor permissions for os.Create
	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"}},
	}

	// 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{`
// 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://golang.org/")
	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://golang.org/")
	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://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()},
		{[]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()},
	}

	// 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()},
		{[]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 (
	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()},
	}

	// 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()},
		{[]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()}}

	// 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()}}

	// SampleCodeG602 - Slice access out of bounds
	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 {
    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()},
	}
)
0707010000008D00008180000000000000000000000001652CE6670000028C000000000000000000000000000000000000002200000000gosec-2.18.1/testutils/visitor.gopackage testutils

import (
	"go/ast"

	"github.com/securego/gosec/v2"
)

// MockVisitor is useful for stubbing out ast.Visitor with callback
// and looking for specific conditions to exist.
type MockVisitor struct {
	Context  *gosec.Context
	Callback func(n ast.Node, ctx *gosec.Context) bool
}

// NewMockVisitor creates a new empty struct, the Context and
// Callback must be set manually. See call_list_test.go for an example.
func NewMockVisitor() *MockVisitor {
	return &MockVisitor{}
}

// Visit satisfies the ast.Visitor interface
func (v *MockVisitor) Visit(n ast.Node) ast.Visitor {
	if v.Callback(n, v.Context) {
		return v
	}
	return nil
}
0707010000008E000041C0000000000000000000000002652CE66700000000000000000000000000000000000000000000001300000000gosec-2.18.1/tools0707010000008F000081A4000000000000000000000001652CE667000000AF000000000000000000000000000000000000001C00000000gosec-2.18.1/tools/tools.go//go:build tools
// +build tools

package tools

// nolint
import (
	_ "github.com/lib/pq"
	_ "golang.org/x/crypto/ssh"
	_ "golang.org/x/lint/golint"
	_ "golang.org/x/text"
)
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1225 blocks
openSUSE Build Service is sponsored by