From c7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 4 Jul 2020 11:12:55 +0200 Subject: go.mod: vendor golangci-lint --- vendor/github.com/securego/gosec/v2/.gitignore | 35 ++ .../github.com/securego/gosec/v2/.goreleaser.yml | 20 + vendor/github.com/securego/gosec/v2/Dockerfile | 14 + vendor/github.com/securego/gosec/v2/LICENSE.txt | 154 ++++++++ vendor/github.com/securego/gosec/v2/Makefile | 71 ++++ vendor/github.com/securego/gosec/v2/README.md | 326 +++++++++++++++ vendor/github.com/securego/gosec/v2/action.yml | 19 + vendor/github.com/securego/gosec/v2/analyzer.go | 378 ++++++++++++++++++ vendor/github.com/securego/gosec/v2/call_list.go | 109 ++++++ vendor/github.com/securego/gosec/v2/config.go | 125 ++++++ vendor/github.com/securego/gosec/v2/errors.go | 33 ++ vendor/github.com/securego/gosec/v2/go.mod | 19 + vendor/github.com/securego/gosec/v2/go.sum | 82 ++++ vendor/github.com/securego/gosec/v2/helpers.go | 435 +++++++++++++++++++++ .../github.com/securego/gosec/v2/import_tracker.go | 75 ++++ vendor/github.com/securego/gosec/v2/install.sh | 372 ++++++++++++++++++ vendor/github.com/securego/gosec/v2/issue.go | 177 +++++++++ vendor/github.com/securego/gosec/v2/renovate.json | 7 + vendor/github.com/securego/gosec/v2/resolve.go | 95 +++++ vendor/github.com/securego/gosec/v2/rule.go | 59 +++ .../github.com/securego/gosec/v2/rules/archive.go | 60 +++ .../securego/gosec/v2/rules/bad_defer.go | 69 ++++ vendor/github.com/securego/gosec/v2/rules/bind.go | 83 ++++ .../securego/gosec/v2/rules/blacklist.go | 94 +++++ .../securego/gosec/v2/rules/decompression-bomb.go | 109 ++++++ .../github.com/securego/gosec/v2/rules/errors.go | 119 ++++++ .../securego/gosec/v2/rules/fileperms.go | 111 ++++++ .../gosec/v2/rules/hardcoded_credentials.go | 173 ++++++++ .../securego/gosec/v2/rules/implicit_aliasing.go | 116 ++++++ .../securego/gosec/v2/rules/integer_overflow.go | 89 +++++ vendor/github.com/securego/gosec/v2/rules/pprof.go | 42 ++ vendor/github.com/securego/gosec/v2/rules/rand.go | 55 +++ .../github.com/securego/gosec/v2/rules/readfile.go | 106 +++++ vendor/github.com/securego/gosec/v2/rules/rsa.go | 58 +++ .../github.com/securego/gosec/v2/rules/rulelist.go | 116 ++++++ vendor/github.com/securego/gosec/v2/rules/sql.go | 219 +++++++++++ vendor/github.com/securego/gosec/v2/rules/ssh.go | 38 ++ vendor/github.com/securego/gosec/v2/rules/ssrf.go | 66 ++++ .../github.com/securego/gosec/v2/rules/subproc.go | 85 ++++ .../securego/gosec/v2/rules/tempfiles.go | 58 +++ .../securego/gosec/v2/rules/templates.go | 61 +++ vendor/github.com/securego/gosec/v2/rules/tls.go | 130 ++++++ .../securego/gosec/v2/rules/tls_config.go | 88 +++++ .../github.com/securego/gosec/v2/rules/unsafe.go | 53 +++ .../securego/gosec/v2/rules/weakcrypto.go | 58 +++ 45 files changed, 4861 insertions(+) create mode 100644 vendor/github.com/securego/gosec/v2/.gitignore create mode 100644 vendor/github.com/securego/gosec/v2/.goreleaser.yml create mode 100644 vendor/github.com/securego/gosec/v2/Dockerfile create mode 100644 vendor/github.com/securego/gosec/v2/LICENSE.txt create mode 100644 vendor/github.com/securego/gosec/v2/Makefile create mode 100644 vendor/github.com/securego/gosec/v2/README.md create mode 100644 vendor/github.com/securego/gosec/v2/action.yml create mode 100644 vendor/github.com/securego/gosec/v2/analyzer.go create mode 100644 vendor/github.com/securego/gosec/v2/call_list.go create mode 100644 vendor/github.com/securego/gosec/v2/config.go create mode 100644 vendor/github.com/securego/gosec/v2/errors.go create mode 100644 vendor/github.com/securego/gosec/v2/go.mod create mode 100644 vendor/github.com/securego/gosec/v2/go.sum create mode 100644 vendor/github.com/securego/gosec/v2/helpers.go create mode 100644 vendor/github.com/securego/gosec/v2/import_tracker.go create mode 100644 vendor/github.com/securego/gosec/v2/install.sh create mode 100644 vendor/github.com/securego/gosec/v2/issue.go create mode 100644 vendor/github.com/securego/gosec/v2/renovate.json create mode 100644 vendor/github.com/securego/gosec/v2/resolve.go create mode 100644 vendor/github.com/securego/gosec/v2/rule.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/archive.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/bad_defer.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/bind.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/blacklist.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/errors.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/fileperms.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/integer_overflow.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/pprof.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/rand.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/readfile.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/rsa.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/rulelist.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/sql.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/ssh.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/ssrf.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/subproc.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/tempfiles.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/templates.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/tls.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/tls_config.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/unsafe.go create mode 100644 vendor/github.com/securego/gosec/v2/rules/weakcrypto.go (limited to 'vendor/github.com/securego/gosec') diff --git a/vendor/github.com/securego/gosec/v2/.gitignore b/vendor/github.com/securego/gosec/v2/.gitignore new file mode 100644 index 000000000..f282cda24 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/.gitignore @@ -0,0 +1,35 @@ +# 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 diff --git a/vendor/github.com/securego/gosec/v2/.goreleaser.yml b/vendor/github.com/securego/gosec/v2/.goreleaser.yml new file mode 100644 index 000000000..4f8fc4128 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/.goreleaser.yml @@ -0,0 +1,20 @@ +--- +project_name: gosec + +release: + github: + owner: securego + name: gosec + +builds: + - main : ./cmd/gosec/ + binary: gosec + goos: + - darwin + - linux + - windows + goarch: + - amd64 + ldflags: -X main.Version={{.Version}} -X main.GitTag={{.Tag}} -X main.BuildDate={{.Date}} + env: + - CGO_ENABLED=0 diff --git a/vendor/github.com/securego/gosec/v2/Dockerfile b/vendor/github.com/securego/gosec/v2/Dockerfile new file mode 100644 index 000000000..a874697e9 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/Dockerfile @@ -0,0 +1,14 @@ +ARG GO_VERSION +FROM golang:${GO_VERSION}-alpine AS builder +RUN apk add --update --no-cache ca-certificates make git curl gcc libc-dev +RUN mkdir -p /build +WORKDIR /build +COPY . /build/ +RUN go mod download +RUN make build-linux + +FROM golang:${GO_VERSION}-alpine +RUN apk add --update --no-cache ca-certificates git gcc libc-dev +ENV GO111MODULE on +COPY --from=builder /build/gosec /bin/gosec +ENTRYPOINT ["/bin/gosec"] diff --git a/vendor/github.com/securego/gosec/v2/LICENSE.txt b/vendor/github.com/securego/gosec/v2/LICENSE.txt new file mode 100644 index 000000000..1756c7821 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/LICENSE.txt @@ -0,0 +1,154 @@ +Apache 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 diff --git a/vendor/github.com/securego/gosec/v2/Makefile b/vendor/github.com/securego/gosec/v2/Makefile new file mode 100644 index 000000000..217651c43 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/Makefile @@ -0,0 +1,71 @@ +GIT_TAG?= $(shell git describe --always --tags) +BIN = gosec +FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr) +IMAGE_REPO = securego +BUILDFLAGS := '-w -s' +CGO_ENABLED = 0 +GO := GO111MODULE=on go +GO_NOMOD :=GO111MODULE=off go +GOPATH ?= $(shell $(GO) env GOPATH) +GOBIN ?= $(GOPATH)/bin +GOLINT ?= $(GOBIN)/golint +GOSEC ?= $(GOBIN)/gosec +GINKGO ?= $(GOBIN)/ginkgo +GO_VERSION = 1.14 + +default: + $(MAKE) build + +install-test-deps: + $(GO_NOMOD) get -u github.com/onsi/ginkgo/ginkgo + $(GO_NOMOD) get -u golang.org/x/crypto/ssh + $(GO_NOMOD) get -u github.com/lib/pq + +test: install-test-deps build fmt lint sec + $(GINKGO) -r -v + +fmt: + @echo "FORMATTING" + @FORMATTED=`$(GO) fmt ./...` + @([[ ! -z "$(FORMATTED)" ]] && printf "Fixed unformatted files:\n$(FORMATTED)") || true + +lint: + @echo "LINTING" + $(GO_NOMOD) get -u golang.org/x/lint/golint + $(GOLINT) -set_exit_status ./... + @echo "VETTING" + $(GO) vet ./... + +sec: + @echo "SECURITY SCANNING" + ./$(BIN) ./... + +test-coverage: install-test-deps + go test -race -coverprofile=coverage.txt -covermode=atomic + +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 + +.PHONY: test build clean release image image-push diff --git a/vendor/github.com/securego/gosec/v2/README.md b/vendor/github.com/securego/gosec/v2/README.md new file mode 100644 index 000000000..52be73423 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/README.md @@ -0,0 +1,326 @@ + +# gosec - Golang Security Checker + +Inspects source code for security problems by scanning the Go AST. + + + +## 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/badge/github.com/securego/gosec) +[![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec) +[![Docs](https://readthedocs.org/projects/docs/badge/?version=latest)](https://securego.io/) +[![Downloads](https://img.shields.io/github/downloads/securego/gosec/total.svg)](https://github.com/securego/gosec/releases) +[![Docker Pulls](https://img.shields.io/docker/pulls/securego/gosec.svg)](https://hub.docker.com/r/securego/gosec/tags) +[![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com) + +## Install + +### CI Installation + +```bash +# binary will be $GOPATH/bin/gosec +curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $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 " 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@v2 + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + args: ./... +``` + +### Local Installation + +```bash +go get github.com/securego/gosec/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 +- 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 archive +- G306: Poor file permissions used when writing to a new file +- G307: Deferring a method which returns an error +- G401: Detect the usage of DES, RC4, MD5 or SHA1 +- G402: Look for bad TLS connection settings +- G403: Ensure minimum RSA key length of 2048 bits +- G404: Insecure random number source (rand) +- G501: Import blacklist: crypto/md5 +- G502: Import blacklist: crypto/des +- G503: Import blacklist: crypto/rc4 +- G504: Import blacklist: net/http/cgi +- G505: Import blacklist: crypto/sha1 +- G601: Implicit memory aliasing of items from a range statement + +### Retired rules + +- G105: Audit the use of math/big.Int.Exp - [CVE is fixed](https://github.com/golang/go/issues/15184) + +### 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/53be8dd8644ee48802114178cff6eb7e29757414/issue.go#L49). + +### 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": { + "io/ioutil": ["WriteFile"] + } +} +``` + +You can also configure the hard-coded credentials rule `G101` with additional patters, or adjust the entropy threshold: + +```JSON +{ + "G101": { + "pattern": "(?i)passwd|pass|password|pwd|secret|private_key|token", + "ingnore_entropy": false, + "entropy_threshold": "80.0", + "per_char_threshold": "3.0", + "trucate": "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 ./... +``` + +### 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 `#nosec` comment. + +The annotation causes gosec to stop processing any further nodes within the +AST so can apply to a whole block or more granularly to a single expression. + +```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` + +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 ./... +``` + +### 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 -tag 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 +``` + +## Development + +### Build + +You can build the binary with: +```bash +make +``` + +### 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. + +### 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 -it -v /:/ securego/gosec //... +``` + +### 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/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. diff --git a/vendor/github.com/securego/gosec/v2/action.yml b/vendor/github.com/securego/gosec/v2/action.yml new file mode 100644 index 000000000..aab6c8039 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/action.yml @@ -0,0 +1,19 @@ +name: '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' + args: + - ${{ inputs.args }} + +branding: + icon: 'shield' + color: 'blue' diff --git a/vendor/github.com/securego/gosec/v2/analyzer.go b/vendor/github.com/securego/gosec/v2/analyzer.go new file mode 100644 index 000000000..ca4440c20 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzer.go @@ -0,0 +1,378 @@ +// (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" + + "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 + +// The Context is populated with data parsed from the source code as it is scanned. +// It is passed through to all rule functions as they are called. Rules may use +// this data in conjunction withe the encountered AST node. +type Context struct { + FileSet *token.FileSet + Comments ast.CommentMap + Info *types.Info + Pkg *types.Package + PkgFiles []*ast.File + Root *ast.File + Config Config + Imports *ImportTracker + Ignores []map[string]bool + PassedValues map[string]interface{} +} + +// 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 + stats *Metrics + errors map[string][]Error // keys are file paths; values are the golang errors in those files + tests bool +} + +// NewAnalyzer builds a new analyzer. +func NewAnalyzer(conf Config, tests bool, logger *log.Logger) *Analyzer { + ignoreNoSec := false + if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil { + ignoreNoSec = enabled + } + if logger == nil { + logger = log.New(os.Stderr, "[gosec]", log.LstdFlags) + } + return &Analyzer{ + ignoreNosec: ignoreNoSec, + ruleset: make(RuleSet), + context: &Context{}, + config: conf, + logger: logger, + issues: make([]*Issue, 0, 16), + stats: &Metrics{}, + errors: make(map[string][]Error), + tests: tests, + } +} + +// SetConfig upates 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) { + for id, def := range ruleDefinitions { + r, nodes := def(id, gosec.config) + gosec.ruleset.Register(r, nodes...) + } +} + +// Process kicks off the analysis process for a given package +func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error { + config := gosec.pkgConfig(buildTags) + for _, pkgPath := range packagePaths { + pkgs, err := gosec.load(pkgPath, config) + if err != nil { + gosec.AppendError(pkgPath, err) + } + for _, pkg := range pkgs { + if pkg.Name != "" { + err := gosec.ParseErrors(pkg) + if err != nil { + return fmt.Errorf("parsing errors in pkg %q: %v", pkg.Name, err) + } + gosec.Check(pkg) + } + } + } + sortErrors(gosec.errors) + return nil +} + +func (gosec *Analyzer) pkgConfig(buildTags []string) *packages.Config { + flags := []string{} + if len(buildTags) > 0 { + tagsFlag := "-tags=" + strings.Join(buildTags, " ") + flags = append(flags, tagsFlag) + } + return &packages.Config{ + Mode: LoadMode, + BuildFlags: flags, + Tests: gosec.tests, + } +} + +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) + basePackage, err := build.Default.ImportDir(pkgPath, build.ImportComment) + if err != nil { + return []*packages.Package{}, fmt.Errorf("importing dir %q: %v", 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 := []string{} + testsFiles = append(testsFiles, basePackage.TestGoFiles...) + testsFiles = append(testsFiles, basePackage.XTestGoFiles...) + for _, filename := range testsFiles { + packageFiles = append(packageFiles, path.Join(pkgPath, filename)) + } + } + + pkgs, err := packages.Load(conf, packageFiles...) + if err != nil { + return []*packages.Package{}, fmt.Errorf("loading files from package %q: %v", pkgPath, err) + } + return pkgs, nil +} + +// Check runs analysis on the given package +func (gosec *Analyzer) Check(pkg *packages.Package) { + gosec.logger.Println("Checking package:", pkg.Name) + for _, file := range pkg.Syntax { + checkedFile := pkg.Fset.File(file.Pos()).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 + } + 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.Imports.TrackFile(file) + gosec.context.PassedValues = make(map[string]interface{}) + ast.Walk(gosec, file) + gosec.stats.NumFiles++ + gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount() + } +} + +// 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: %v", err) + } + } + var column int + if len(parts) > 2 { + if column, err = strconv.Atoi(parts[2]); err != nil { + return fmt.Errorf("parsing column: %v", 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 := []Error{} + 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) ([]string, bool) { + 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 := "#nosec" + noSecAlternativeTag, err := gosec.config.GetGlobal(NoSecAlternative) + if err != nil { + noSecAlternativeTag = noSecDefaultTag + } + + for _, group := range groups { + + foundDefaultTag := strings.Contains(group.Text(), noSecDefaultTag) + foundAlternativeTag := strings.Contains(group.Text(), noSecAlternativeTag) + + if foundDefaultTag || foundAlternativeTag { + gosec.stats.NumNosec++ + + // Pull out the specific rules that are listed to be ignored. + re := regexp.MustCompile(`(G\d{3})`) + matches := re.FindAllStringSubmatch(group.Text(), -1) + + // If no specific rules were given, ignore everything. + if len(matches) == 0 { + return nil, true + } + + // Find the rule IDs to ignore. + var ignores []string + for _, v := range matches { + ignores = append(ignores, v[1]) + } + return ignores, false + } + } + } + return nil, false +} + +// Visit runs the gosec visitor logic over an AST created by parsing go code. +// Rule methods added with AddRule will be invoked as necessary. +func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor { + // If we've reached the end of this branch, pop off the ignores stack. + if n == nil { + if len(gosec.context.Ignores) > 0 { + gosec.context.Ignores = gosec.context.Ignores[1:] + } + return gosec + } + + // Get any new rule exclusions. + ignoredRules, ignoreAll := gosec.ignore(n) + if ignoreAll { + return nil + } + + // Now create the union of exclusions. + ignores := map[string]bool{} + if len(gosec.context.Ignores) > 0 { + for k, v := range gosec.context.Ignores[0] { + ignores[k] = v + } + } + + for _, v := range ignoredRules { + ignores[v] = true + } + + // Push the new set onto the stack. + gosec.context.Ignores = append([]map[string]bool{ignores}, gosec.context.Ignores...) + + // Track aliased and initialization imports + gosec.context.Imports.TrackImport(n) + + for _, rule := range gosec.ruleset.RegisteredFor(n) { + if _, ok := ignores[rule.ID()]; ok { + continue + } + issue, err := rule.Match(n, gosec.context) + if err != nil { + file, line := GetLocation(n, gosec.context) + file = path.Base(file) + gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line) + } + if issue != nil { + gosec.issues = append(gosec.issues, issue) + gosec.stats.NumFound++ + } + } + return gosec +} + +// Report returns the current issues discovered and the metrics about the scan +func (gosec *Analyzer) Report() ([]*Issue, *Metrics, map[string][]Error) { + return gosec.issues, gosec.stats, gosec.errors +} + +// Reset clears state such as context, issues and metrics from the configured analyzer +func (gosec *Analyzer) Reset() { + gosec.context = &Context{} + gosec.issues = make([]*Issue, 0, 16) + gosec.stats = &Metrics{} + gosec.ruleset = NewRuleSet() +} diff --git a/vendor/github.com/securego/gosec/v2/call_list.go b/vendor/github.com/securego/gosec/v2/call_list.go new file mode 100644 index 000000000..115c6c88e --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/call_list.go @@ -0,0 +1,109 @@ +// +// 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 + } + + // Use only explicit path (optionally strip vendor path prefix) to reduce conflicts + path, ok := GetImportPath(selector, ctx) + if !ok { + return nil + } + if stripVendor { + if vendorIdx := strings.Index(path, vendorPath); vendorIdx >= 0 { + path = path[vendorIdx+len(vendorPath):] + } + } + if !c.Contains(path, 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) +} diff --git a/vendor/github.com/securego/gosec/v2/config.go b/vendor/github.com/securego/gosec/v2/config.go new file mode 100644 index 000000000..5b7f73936 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/config.go @@ -0,0 +1,125 @@ +package gosec + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" +) + +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" + // Audit global option which indicates that gosec runs in audit mode + Audit GlobalOption = "audit" + // NoSecAlternative global option alternative for #nosec directive + NoSecAlternative GlobalOption = "#nosec" +) + +// 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 := ioutil.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 +} diff --git a/vendor/github.com/securego/gosec/v2/errors.go b/vendor/github.com/securego/gosec/v2/errors.go new file mode 100644 index 000000000..a27aa5821 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/errors.go @@ -0,0 +1,33 @@ +package 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, + } +} + +// sortErros sorts the golang erros 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 + }) + } +} diff --git a/vendor/github.com/securego/gosec/v2/go.mod b/vendor/github.com/securego/gosec/v2/go.mod new file mode 100644 index 000000000..edfa3434c --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/go.mod @@ -0,0 +1,19 @@ +module github.com/securego/gosec/v2 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.3.2 // indirect + github.com/gookit/color v1.2.4 + github.com/kr/pretty v0.1.0 // indirect + github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee + github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d + github.com/onsi/ginkgo v1.12.0 + github.com/onsi/gomega v1.9.0 + github.com/stretchr/testify v1.4.0 // indirect + golang.org/x/text v0.3.2 // indirect + golang.org/x/tools v0.0.0-20200331202046-9d5940d49312 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + gopkg.in/yaml.v2 v2.2.8 +) + +go 1.14 diff --git a/vendor/github.com/securego/gosec/v2/go.sum b/vendor/github.com/securego/gosec/v2/go.sum new file mode 100644 index 000000000..fff56a309 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/go.sum @@ -0,0 +1,82 @@ +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/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gookit/color v1.2.4 h1:xOYBan3Fwlrqj1M1UN2TlHOCRiek3bGzWf/vPnJ1roE= +github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee h1:1xJ+Xi9lYWLaaP4yB67ah0+548CD3110mCPWhVVjFkI= +github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200331202046-9d5940d49312 h1:2PHG+Ia3gK1K2kjxZnSylizb//eyaMG8gDFbOG7wLV8= +golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/securego/gosec/v2/helpers.go b/vendor/github.com/securego/gosec/v2/helpers.go new file mode 100644 index 000000000..40dc8e9c3 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/helpers.go @@ -0,0 +1,435 @@ +// (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) { + + importedName, found := GetImportedName(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 + } + if packageName == importedName { + 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) +} + +// 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" { + 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, ctx *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 +} + +// GetIdentStringValues return the string values of an Ident if they can be resolved +func GetIdentStringValues(ident *ast.Ident) []string { + values := []string{} + obj := ident.Obj + if obj != nil { + switch decl := obj.Decl.(type) { + case *ast.ValueSpec: + for _, v := range decl.Values { + value, err := GetString(v) + if err == nil { + values = append(values, value) + } + } + case *ast.AssignStmt: + for _, v := range decl.Rhs { + value, err := GetString(v) + if err == nil { + values = append(values, value) + } + } + } + + } + return values +} + +// GetImportedName returns the name used for the package within the +// code. It will resolve aliases and ignores initialization only imports. +func GetImportedName(path string, ctx *Context) (string, bool) { + importName, imported := ctx.Imports.Imported[path] + if !imported { + return "", false + } + + if _, initonly := ctx.Imports.InitOnly[path]; initonly { + return "", false + } + + if alias, ok := ctx.Imports.Aliased[path]; ok { + importName = alias + } + return importName, true +} + +// GetImportPath resolves the full import path of an identifier based on +// the imports in the current context. +func GetImportPath(name string, ctx *Context) (string, bool) { + for path := range ctx.Imports.Imported { + if imported, ok := GetImportedName(path, ctx); ok && imported == name { + return path, true + } + } + 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 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(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([\\/])?`, 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) { + if strings.HasSuffix(root, "...") { + root = root[0 : len(root)-3] + } + return filepath.Abs(root) +} diff --git a/vendor/github.com/securego/gosec/v2/import_tracker.go b/vendor/github.com/securego/gosec/v2/import_tracker.go new file mode 100644 index 000000000..cbb8c5518 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/import_tracker.go @@ -0,0 +1,75 @@ +// 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 map[string]string + Aliased map[string]string + InitOnly map[string]bool +} + +// NewImportTracker creates an empty Import tracker instance +func NewImportTracker() *ImportTracker { + return &ImportTracker{ + make(map[string]string), + make(map[string]string), + make(map[string]bool), + } +} + +// TrackFile track all the imports used by the supplied file +func (t *ImportTracker) TrackFile(file *ast.File) { + for _, imp := range file.Imports { + path := strings.Trim(imp.Path.Value, `"`) + parts := strings.Split(path, "/") + if len(parts) > 0 { + name := parts[len(parts)-1] + t.Imported[path] = name + } + } +} + +// TrackPackages tracks all the imports used by the supplied packages +func (t *ImportTracker) TrackPackages(pkgs ...*types.Package) { + for _, pkg := range pkgs { + t.Imported[pkg.Path()] = pkg.Name() + } +} + +// TrackImport tracks imports and handles the 'unsafe' import +func (t *ImportTracker) TrackImport(n ast.Node) { + if imported, ok := n.(*ast.ImportSpec); ok { + path := strings.Trim(imported.Path.Value, `"`) + if imported.Name != nil { + if imported.Name.Name == "_" { + // Initialization only import + t.InitOnly[path] = true + } else { + // Aliased import + t.Aliased[path] = imported.Name.Name + } + } + if path == "unsafe" { + t.Imported[path] = path + } + } +} diff --git a/vendor/github.com/securego/gosec/v2/install.sh b/vendor/github.com/securego/gosec/v2/install.sh new file mode 100644 index 000000000..37bed0a2e --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/install.sh @@ -0,0 +1,372 @@ +#!/bin/sh +set -e +# Code generated by godownloader on 2020-03-02T13:35:13Z. DO NOT EDIT. +# + +usage() { + this=$1 + cat </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 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + 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 < 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 gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } else if ident, ok := arg.(*ast.Ident); ok { + values := gosec.GetIdentStringValues(ident) + for _, value := range values { + if r.pattern.MatchString(value) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + } else if len(callExpr.Args) > 0 { + values := gosec.GetCallStringArgsValues(callExpr.Args[0], c) + for _, value := range values { + if r.pattern.MatchString(value) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + return nil, nil +} + +// NewBindsToAllNetworkInterfaces detects socket connections that are setup to +// listen on all network interfaces. +func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() + calls.Add("net", "Listen") + calls.Add("crypto/tls", "Listen") + return &bindsToAllNetworkInterfaces{ + calls: calls, + pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`), + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: "Binds to all network interfaces", + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/blacklist.go b/vendor/github.com/securego/gosec/v2/rules/blacklist.go new file mode 100644 index 000000000..9bb73381f --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/blacklist.go @@ -0,0 +1,94 @@ +// (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" +) + +type blacklistedImport struct { + gosec.MetaData + Blacklisted map[string]string +} + +func unquote(original string) string { + copy := strings.TrimSpace(original) + copy = strings.TrimLeft(copy, `"`) + return strings.TrimRight(copy, `"`) +} + +func (r *blacklistedImport) ID() string { + return r.MetaData.ID +} + +func (r *blacklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if node, ok := n.(*ast.ImportSpec); ok { + if description, ok := r.Blacklisted[unquote(node.Path.Value)]; ok { + return gosec.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil + } + } + return nil, nil +} + +// NewBlacklistedImports reports when a blacklisted import is being used. +// Typically when a deprecated technology is being used. +func NewBlacklistedImports(id string, conf gosec.Config, blacklist map[string]string) (gosec.Rule, []ast.Node) { + return &blacklistedImport{ + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + }, + Blacklisted: blacklist, + }, []ast.Node{(*ast.ImportSpec)(nil)} +} + +// NewBlacklistedImportMD5 fails if MD5 is imported +func NewBlacklistedImportMD5(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlacklistedImports(id, conf, map[string]string{ + "crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive", + }) +} + +// NewBlacklistedImportDES fails if DES is imported +func NewBlacklistedImportDES(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlacklistedImports(id, conf, map[string]string{ + "crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive", + }) +} + +// NewBlacklistedImportRC4 fails if DES is imported +func NewBlacklistedImportRC4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlacklistedImports(id, conf, map[string]string{ + "crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive", + }) +} + +// NewBlacklistedImportCGI fails if CGI is imported +func NewBlacklistedImportCGI(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlacklistedImports(id, conf, map[string]string{ + "net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)", + }) +} + +// NewBlacklistedImportSHA1 fails if SHA1 is imported +func NewBlacklistedImportSHA1(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlacklistedImports(id, conf, map[string]string{ + "crypto/sha1": "Blacklisted import crypto/sha1: weak cryptographic primitive", + }) +} diff --git a/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go b/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go new file mode 100644 index 000000000..bfc589763 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go @@ -0,0 +1,109 @@ +// (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" +) + +type decompressionBombCheck struct { + gosec.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) (*gosec.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 gosec.NewIssue(ctx, 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, conf 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") + + return &decompressionBombCheck{ + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.Medium, + What: "Potential DoS vulnerability via decompression bomb", + }, + readerCalls: readerCalls, + copyCalls: copyCalls, + }, []ast.Node{(*ast.FuncDecl)(nil), (*ast.AssignStmt)(nil), (*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/errors.go b/vendor/github.com/securego/gosec/v2/rules/errors.go new file mode 100644 index 000000000..f16f91d04 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/errors.go @@ -0,0 +1,119 @@ +// (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" +) + +type noErrorCheck struct { + gosec.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) (*gosec.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 gosec.NewIssue(ctx, 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 gosec.NewIssue(ctx, 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") + + if configured, ok := conf["G104"]; 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: gosec.MetaData{ + ID: id, + Severity: gosec.Low, + Confidence: gosec.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 +} diff --git a/vendor/github.com/securego/gosec/v2/rules/fileperms.go b/vendor/github.com/securego/gosec/v2/rules/fileperms.go new file mode 100644 index 000000000..ffe7b97d5 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/fileperms.go @@ -0,0 +1,111 @@ +// (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" +) + +type filePermissions struct { + gosec.MetaData + mode int64 + pkg string + calls []string +} + +func (r *filePermissions) ID() string { + return r.MetaData.ID +} + +func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMode int64) int64 { + var 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 (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if callexpr, matched := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matched { + modeArg := callexpr.Args[len(callexpr.Args)-1] + if mode, err := gosec.GetInt(modeArg); err == nil && mode > r.mode { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + 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, "G306", 0600) + return &filePermissions{ + mode: mode, + pkg: "io/ioutil", + calls: []string{"WriteFile"}, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.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, "G302", 0600) + return &filePermissions{ + mode: mode, + pkg: "os", + calls: []string{"OpenFile", "Chmod"}, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.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, "G301", 0750) + return &filePermissions{ + mode: mode, + pkg: "os", + calls: []string{"Mkdir", "MkdirAll"}, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode), + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go new file mode 100644 index 000000000..6b360c5b9 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go @@ -0,0 +1,173 @@ +// (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/token" + "regexp" + "strconv" + + zxcvbn "github.com/nbutton23/zxcvbn-go" + "github.com/securego/gosec/v2" +) + +type credentials struct { + gosec.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) Match(n ast.Node, ctx *gosec.Context) (*gosec.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) (*gosec.Issue, error) { + for _, i := range assign.Lhs { + if ident, ok := i.(*ast.Ident); ok { + if r.pattern.MatchString(ident.Name) { + for _, e := range assign.Rhs { + if val, err := gosec.GetString(e); err == nil { + if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { + return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + } + } + } + return nil, nil +} + +func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*gosec.Issue, error) { + 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 gosec.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + } + return nil, nil +} + +func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.Context) (*gosec.Issue, error) { + if binaryExpr.Op == token.EQL || binaryExpr.Op == token.NEQ { + if ident, ok := binaryExpr.X.(*ast.Ident); ok { + if r.pattern.MatchString(ident.Name) { + if val, err := gosec.GetString(binaryExpr.Y); err == nil { + if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { + return gosec.NewIssue(ctx, binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + } + } + 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` + entropyThreshold := 80.0 + perCharThreshold := 3.0 + ignoreEntropy := false + var truncateString = 16 + if val, ok := conf["G101"]; 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: gosec.MetaData{ + ID: id, + What: "Potential hardcoded credentials", + Confidence: gosec.Low, + Severity: gosec.High, + }, + }, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil), (*ast.BinaryExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go new file mode 100644 index 000000000..65c7ae36d --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go @@ -0,0 +1,116 @@ +package rules + +import ( + "fmt" + "github.com/securego/gosec/v2" + "go/ast" + "go/token" +) + +type implicitAliasing struct { + gosec.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 (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.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 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 ident, ok := node.X.(*ast.Ident); ok && node.Op.String() == "&" { + if _, contains := r.aliases[ident.Obj]; contains { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + 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, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &implicitAliasing{ + aliases: make(map[*ast.Object]struct{}), + rightBrace: token.NoPos, + acceptableAlias: make([]*ast.UnaryExpr, 0), + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.Medium, + What: fmt.Sprintf("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. +*/ diff --git a/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go b/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go new file mode 100644 index 000000000..dfcda94a8 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go @@ -0,0 +1,89 @@ +// (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" +) + +type integerOverflowCheck struct { + gosec.MetaData + calls gosec.CallList +} + +func (i *integerOverflowCheck) ID() string { + return i.MetaData.ID +} + +func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.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 n, ok := atoiVarObj[idt.Obj]; ok { + // Detect int32(v) and int16(v) + return gosec.NewIssue(ctx, n, i.ID(), i.What, i.Severity, i.Confidence), nil + } + } + } + } + } + + return nil, nil +} + +// NewIntegerOverflowCheck detects if there is potential Integer OverFlow +func NewIntegerOverflowCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() + calls.Add("strconv", "Atoi") + return &integerOverflowCheck{ + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.High, + Confidence: gosec.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)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/pprof.go b/vendor/github.com/securego/gosec/v2/rules/pprof.go new file mode 100644 index 000000000..4c99af752 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/pprof.go @@ -0,0 +1,42 @@ +package rules + +import ( + "go/ast" + + "github.com/securego/gosec/v2" +) + +type pprofCheck struct { + gosec.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) (*gosec.Issue, error) { + if node, ok := n.(*ast.ImportSpec); ok { + if p.importPath == unquote(node.Path.Value) && node.Name != nil && p.importName == node.Name.Name { + return gosec.NewIssue(c, node, p.ID(), p.What, p.Severity, p.Confidence), nil + } + } + return nil, nil +} + +// NewPprofCheck detects when the profiling endpoint is automatically exposed +func NewPprofCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &pprofCheck{ + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.High, + Confidence: gosec.High, + What: "Profiling endpoint is automatically exposed on /debug/pprof", + }, + importPath: "net/http/pprof", + importName: "_", + }, []ast.Node{(*ast.ImportSpec)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/rand.go b/vendor/github.com/securego/gosec/v2/rules/rand.go new file mode 100644 index 000000000..08c28fcad --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/rand.go @@ -0,0 +1,55 @@ +// (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" +) + +type weakRand struct { + gosec.MetaData + funcNames []string + packagePath string +} + +func (w *weakRand) ID() string { + return w.MetaData.ID +} + +func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + for _, funcName := range w.funcNames { + if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched { + return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + } + } + + return nil, nil +} + +// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure +func NewWeakRandCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &weakRand{ + funcNames: []string{"Read", "Int"}, + packagePath: "math/rand", + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.High, + Confidence: gosec.Medium, + What: "Use of weak random number generator (math/rand instead of crypto/rand)", + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/readfile.go b/vendor/github.com/securego/gosec/v2/rules/readfile.go new file mode 100644 index 000000000..a52f7425f --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/readfile.go @@ -0,0 +1,106 @@ +// (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" +) + +type readfile struct { + gosec.MetaData + gosec.CallList + pathJoin gosec.CallList +} + +// 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 +} + +// Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile` +func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if node := r.ContainsPkgCallExpr(n, c, false); node != nil { + for _, arg := range node.Args { + // handles path joining functions in Arg + // eg. os.Open(filepath.Join("/tmp/", file)) + if callExpr, ok := arg.(*ast.CallExpr); ok { + if r.isJoinFunc(callExpr, c) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + // handles binary string concatenation eg. ioutil.Readfile("/tmp/" + file + "/blob") + if binExp, ok := arg.(*ast.BinaryExpr); ok { + // resolve all found identities from the BinaryExpr + if _, ok := gosec.FindVarIdentities(binExp, c); ok { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + + if ident, ok := arg.(*ast.Ident); ok { + obj := c.Info.ObjectOf(ident) + if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + } + return nil, nil +} + +// NewReadFile detects cases where we read files +func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + rule := &readfile{ + pathJoin: gosec.NewCallList(), + CallList: gosec.NewCallList(), + MetaData: gosec.MetaData{ + ID: id, + What: "Potential file inclusion via variable", + Severity: gosec.Medium, + Confidence: gosec.High, + }, + } + rule.pathJoin.Add("path/filepath", "Join") + rule.pathJoin.Add("path", "Join") + rule.Add("io/ioutil", "ReadFile") + rule.Add("os", "Open") + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/rsa.go b/vendor/github.com/securego/gosec/v2/rules/rsa.go new file mode 100644 index 000000000..f2ed5db53 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/rsa.go @@ -0,0 +1,58 @@ +// (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" +) + +type weakKeyStrength struct { + gosec.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) (*gosec.Issue, error) { + if callExpr := w.calls.ContainsPkgCallExpr(n, c, false); callExpr != nil { + if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) { + return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + } + } + return nil, nil +} + +// NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits +func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() + calls.Add("crypto/rsa", "GenerateKey") + bits := 2048 + return &weakKeyStrength{ + calls: calls, + bits: bits, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: fmt.Sprintf("RSA keys should be at least %d bits", bits), + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/rulelist.go b/vendor/github.com/securego/gosec/v2/rules/rulelist.go new file mode 100644 index 000000000..06e1dfb97 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/rulelist.go @@ -0,0 +1,116 @@ +// (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 is a mapping of rule ID's to rule definitions +type RuleList map[string]RuleDefinition + +// Builders returns all the create methods for a given rule list +func (rl RuleList) Builders() map[string]gosec.RuleBuilder { + builders := make(map[string]gosec.RuleBuilder) + for _, def := range rl { + builders[def.ID] = def.Create + } + return builders +} + +// 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(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}, + + // 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", "Unsafe defer call of a method returning an error", NewDeferredClosing}, + + // 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}, + + // blacklist + {"G501", "Import blacklist: crypto/md5", NewBlacklistedImportMD5}, + {"G502", "Import blacklist: crypto/des", NewBlacklistedImportDES}, + {"G503", "Import blacklist: crypto/rc4", NewBlacklistedImportRC4}, + {"G504", "Import blacklist: net/http/cgi", NewBlacklistedImportCGI}, + {"G505", "Import blacklist: crypto/sha1", NewBlacklistedImportSHA1}, + + // memory safety + {"G601", "Implicit memory aliasing in RangeStmt", NewImplicitAliasing}, + } + + ruleMap := make(map[string]RuleDefinition) + +RULES: + for _, rule := range rules { + for _, filter := range filters { + if filter(rule.ID) { + continue RULES + } + } + ruleMap[rule.ID] = rule + } + return ruleMap +} diff --git a/vendor/github.com/securego/gosec/v2/rules/sql.go b/vendor/github.com/securego/gosec/v2/rules/sql.go new file mode 100644 index 000000000..3279a3400 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/sql.go @@ -0,0 +1,219 @@ +// (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" +) + +type sqlStatement struct { + gosec.MetaData + + // Contains a list of patterns which must all match for the rule to match. + patterns []*regexp.Regexp +} + +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 +} + +// 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 +} + +// Look for "SELECT * FROM table WHERE " + " ' OR 1=1" +func (s *sqlStrConcat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if node, ok := n.(*ast.BinaryExpr); ok { + if start, ok := node.X.(*ast.BasicLit); ok { + if str, e := gosec.GetString(start); e == nil { + if !s.MatchPatterns(str) { + return nil, nil + } + if _, ok := node.Y.(*ast.BasicLit); ok { + return nil, nil // string cat OK + } + if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second, c) { + return nil, nil + } + return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil + } + } + } + return nil, nil +} + +// NewSQLStrConcat looks for cases where we are building SQL strings via concatenation +func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &sqlStrConcat{ + sqlStatement: sqlStatement{ + patterns: []*regexp.Regexp{ + regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `), + }, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: "SQL string concatenation", + }, + }, + }, []ast.Node{(*ast.BinaryExpr)(nil)} +} + +type sqlStrFormat struct { + sqlStatement + calls 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 +} + +// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)" +func (s *sqlStrFormat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + + // argIndex changes the function argument which gets matched to the regex + argIndex := 0 + + // TODO(gm) improve confidence if database/sql is being used + if node := s.calls.ContainsPkgCallExpr(n, c, 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, nil + } + } + } + // the function is Fprintf so set argIndex = 1 + argIndex = 1 + } + } + + // no formatter + if len(node.Args) == 0 { + return nil, 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, 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, c, true); n == nil && !s.constObject(arg, c) { + allSafe = false + break + } + } + if allSafe { + return nil, nil + } + } + if s.MatchPatterns(formatter) { + return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil + } + } + return nil, nil +} + +// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings +func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + rule := &sqlStrFormat{ + calls: gosec.NewCallList(), + noIssue: gosec.NewCallList(), + noIssueQuoted: gosec.NewCallList(), + sqlStatement: sqlStatement{ + patterns: []*regexp.Regexp{ + regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "), + regexp.MustCompile("%[^bdoxXfFp]"), + }, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: "SQL string formatting", + }, + }, + } + rule.calls.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.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/ssh.go b/vendor/github.com/securego/gosec/v2/rules/ssh.go new file mode 100644 index 000000000..01f37da51 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/ssh.go @@ -0,0 +1,38 @@ +package rules + +import ( + "go/ast" + + "github.com/securego/gosec/v2" +) + +type sshHostKey struct { + gosec.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 *gosec.Issue, err error) { + if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + return nil, nil +} + +// NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback. +func NewSSHHostKey(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &sshHostKey{ + pkg: "golang.org/x/crypto/ssh", + calls: []string{"InsecureIgnoreHostKey"}, + MetaData: gosec.MetaData{ + ID: id, + What: "Use of ssh InsecureIgnoreHostKey should be audited", + Severity: gosec.Medium, + Confidence: gosec.High, + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/ssrf.go b/vendor/github.com/securego/gosec/v2/rules/ssrf.go new file mode 100644 index 000000000..86bb8278d --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/ssrf.go @@ -0,0 +1,66 @@ +package rules + +import ( + "go/ast" + "go/types" + + "github.com/securego/gosec/v2" +) + +type ssrf struct { + gosec.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) (*gosec.Issue, error) { + // Call expression is using http package directly + if node := r.ContainsPkgCallExpr(n, c, false); node != nil { + if r.ResolveVar(node, c) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + return nil, nil +} + +// NewSSRFCheck detects cases where HTTP requests are sent +func NewSSRFCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + rule := &ssrf{ + CallList: gosec.NewCallList(), + MetaData: gosec.MetaData{ + ID: id, + What: "Potential HTTP request made with variable url", + Severity: gosec.Medium, + Confidence: gosec.Medium, + }, + } + rule.AddAll("net/http", "Do", "Get", "Head", "Post", "PostForm", "RoundTrip") + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/subproc.go b/vendor/github.com/securego/gosec/v2/rules/subproc.go new file mode 100644 index 000000000..30c32cc03 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/subproc.go @@ -0,0 +1,85 @@ +// (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" +) + +type subprocess struct { + gosec.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) (*gosec.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) + if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) { + return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil + } + } else if !gosec.TryResolve(arg, c) { + // the arg is not a constant or a variable but instead a function call or os.Args[i] + return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with function call as argument or cmd arguments", gosec.Medium, gosec.High), nil + } + } + } + return nil, nil +} + +// isContext checks whether or not the node is a CommandContext call or not +// Thi is requried in order to skip the first argument from the check. +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, conf gosec.Config) (gosec.Rule, []ast.Node) { + rule := &subprocess{gosec.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") + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/tempfiles.go b/vendor/github.com/securego/gosec/v2/rules/tempfiles.go new file mode 100644 index 000000000..36f0f979b --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/tempfiles.go @@ -0,0 +1,58 @@ +// (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" +) + +type badTempFile struct { + gosec.MetaData + calls gosec.CallList + args *regexp.Regexp +} + +func (t *badTempFile) ID() string { + return t.MetaData.ID +} + +func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { + if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil { + if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil { + return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil + } + } + return nil, nil +} + +// NewBadTempFile detects direct writes to predictable path in temporary directory +func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() + calls.Add("io/ioutil", "WriteFile") + calls.Add("os", "Create") + return &badTempFile{ + calls: calls, + args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`), + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: "File creation in shared tmp directory without using ioutil.Tempfile", + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/templates.go b/vendor/github.com/securego/gosec/v2/rules/templates.go new file mode 100644 index 000000000..819240905 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/templates.go @@ -0,0 +1,61 @@ +// (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" +) + +type templateCheck struct { + gosec.MetaData + calls gosec.CallList +} + +func (t *templateCheck) ID() string { + return t.MetaData.ID +} + +func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil { + for _, arg := range node.Args { + if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe + return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil + } + } + } + return 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, conf 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: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.Low, + What: "this method will not auto-escape HTML. Verify data is well formed.", + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/tls.go b/vendor/github.com/securego/gosec/v2/rules/tls.go new file mode 100644 index 000000000..fab9ee164 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/tls.go @@ -0,0 +1,130 @@ +// (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 ( + "fmt" + "go/ast" + + "github.com/securego/gosec/v2" +) + +type insecureConfigTLS struct { + gosec.MetaData + MinVersion int16 + MaxVersion int16 + requiredType string + goodCiphers []string +} + +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) *gosec.Issue { + + if ciphers, ok := n.(*ast.CompositeLit); ok { + for _, cipher := range ciphers.Elts { + if ident, ok := cipher.(*ast.SelectorExpr); ok { + if !stringInSlice(ident.Sel.Name, t.goodCiphers) { + err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name) + return gosec.NewIssue(c, ident, t.ID(), err, gosec.High, gosec.High) + } + } + } + } + return nil +} + +func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Context) *gosec.Issue { + if ident, ok := n.Key.(*ast.Ident); ok { + switch ident.Name { + + case "InsecureSkipVerify": + if node, ok := n.Value.(*ast.Ident); ok { + if node.Name != "false" { + return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gosec.High, gosec.High) + } + } else { + // TODO(tk): symbol tab look up to get the actual value + return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gosec.High, gosec.Low) + } + + case "PreferServerCipherSuites": + if node, ok := n.Value.(*ast.Ident); ok { + if node.Name == "false" { + return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gosec.Medium, gosec.High) + } + } else { + // TODO(tk): symbol tab look up to get the actual value + return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gosec.Medium, gosec.Low) + } + + case "MinVersion": + if ival, ierr := gosec.GetInt(n.Value); ierr == nil { + if (int16)(ival) < t.MinVersion { + return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gosec.High, gosec.High) + } + // TODO(tk): symbol tab look up to get the actual value + return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gosec.High, gosec.Low) + } + + case "MaxVersion": + if ival, ierr := gosec.GetInt(n.Value); ierr == nil { + if (int16)(ival) < t.MaxVersion { + return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gosec.High, gosec.High) + } + // TODO(tk): symbol tab look up to get the actual value + return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gosec.High, gosec.Low) + } + + case "CipherSuites": + if ret := t.processTLSCipherSuites(n.Value, c); ret != nil { + return ret + } + + } + + } + return nil +} + +func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil { + actualType := c.Info.TypeOf(complit.Type) + if actualType != nil && actualType.String() == t.requiredType { + for _, elt := range complit.Elts { + if kve, ok := elt.(*ast.KeyValueExpr); ok { + issue := t.processTLSConfVal(kve, c) + if issue != nil { + return issue, nil + } + } + } + } + } + return nil, nil +} diff --git a/vendor/github.com/securego/gosec/v2/rules/tls_config.go b/vendor/github.com/securego/gosec/v2/rules/tls_config.go new file mode 100644 index 000000000..ff4f3fe2e --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/tls_config.go @@ -0,0 +1,88 @@ +package rules + +import ( + "go/ast" + + "github.com/securego/gosec/v2" +) + +// NewModernTLSCheck creates a check for Modern TLS ciphers +// DO NOT EDIT - generated by tlsconfig tool +func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &insecureConfigTLS{ + MetaData: gosec.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)} +} + +// NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers +// DO NOT EDIT - generated by tlsconfig tool +func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &insecureConfigTLS{ + MetaData: gosec.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_RSA_WITH_CHACHA20_POLY1305", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + }, + }, []ast.Node{(*ast.CompositeLit)(nil)} +} + +// NewOldTLSCheck creates a check for Old TLS ciphers +// DO NOT EDIT - generated by tlsconfig tool +func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &insecureConfigTLS{ + MetaData: gosec.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_RSA_WITH_CHACHA20_POLY1305", + "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)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/unsafe.go b/vendor/github.com/securego/gosec/v2/rules/unsafe.go new file mode 100644 index 000000000..88a298fb5 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/unsafe.go @@ -0,0 +1,53 @@ +// (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" +) + +type usingUnsafe struct { + gosec.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 *gosec.Issue, err error) { + if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + return nil, nil +} + +// NewUsingUnsafe rule detects the use of the unsafe package. This is only +// really useful for auditing purposes. +func NewUsingUnsafe(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return &usingUnsafe{ + pkg: "unsafe", + calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"}, + MetaData: gosec.MetaData{ + ID: id, + What: "Use of unsafe calls should be audited", + Severity: gosec.Low, + Confidence: gosec.High, + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go b/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go new file mode 100644 index 000000000..0e45393db --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go @@ -0,0 +1,58 @@ +// (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" +) + +type usesWeakCryptography struct { + gosec.MetaData + blacklist map[string][]string +} + +func (r *usesWeakCryptography) ID() string { + return r.MetaData.ID +} + +func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + for pkg, funcs := range r.blacklist { + if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + return nil, nil +} + +// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.* +func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + 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{ + blacklist: calls, + MetaData: gosec.MetaData{ + ID: id, + Severity: gosec.Medium, + Confidence: gosec.High, + What: "Use of weak cryptographic primitive", + }, + } + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} -- cgit mrf-deployment