aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/securego/gosec/v2/rules
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-07-04 11:12:55 +0200
committerDmitry Vyukov <dvyukov@google.com>2020-07-04 15:05:30 +0200
commitc7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 (patch)
tree0dff0ee1f98dbfa3ad8776112053a450d176592b /vendor/github.com/securego/gosec/v2/rules
parent9573094ce235bd9afe88f5da27a47dd6bcc1e13b (diff)
go.mod: vendor golangci-lint
Diffstat (limited to 'vendor/github.com/securego/gosec/v2/rules')
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/archive.go60
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/bad_defer.go69
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/bind.go83
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/blacklist.go94
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go109
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/errors.go119
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/fileperms.go111
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go173
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go116
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/integer_overflow.go89
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/pprof.go42
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/rand.go55
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/readfile.go106
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/rsa.go58
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/rulelist.go116
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/sql.go219
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/ssh.go38
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/ssrf.go66
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/subproc.go85
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/tempfiles.go58
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/templates.go61
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/tls.go130
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/tls_config.go88
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/unsafe.go53
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/weakcrypto.go58
25 files changed, 2256 insertions, 0 deletions
diff --git a/vendor/github.com/securego/gosec/v2/rules/archive.go b/vendor/github.com/securego/gosec/v2/rules/archive.go
new file mode 100644
index 000000000..ca7a46e0b
--- /dev/null
+++ b/vendor/github.com/securego/gosec/v2/rules/archive.go
@@ -0,0 +1,60 @@
+package rules
+
+import (
+ "go/ast"
+ "go/types"
+
+ "github.com/securego/gosec/v2"
+)
+
+type archive struct {
+ gosec.MetaData
+ calls gosec.CallList
+ argType string
+}
+
+func (a *archive) ID() string {
+ return a.MetaData.ID
+}
+
+// Match inspects AST nodes to determine if the filepath.Joins uses any argument derived from type zip.File
+func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+ if node := a.calls.ContainsPkgCallExpr(n, c, false); node != nil {
+ for _, arg := range node.Args {
+ var argType types.Type
+ if selector, ok := arg.(*ast.SelectorExpr); ok {
+ argType = c.Info.TypeOf(selector.X)
+ } else if ident, ok := arg.(*ast.Ident); ok {
+ if ident.Obj != nil && ident.Obj.Kind == ast.Var {
+ decl := ident.Obj.Decl
+ if assign, ok := decl.(*ast.AssignStmt); ok {
+ if selector, ok := assign.Rhs[0].(*ast.SelectorExpr); ok {
+ argType = c.Info.TypeOf(selector.X)
+ }
+ }
+ }
+ }
+
+ if argType != nil && argType.String() == a.argType {
+ return gosec.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil
+ }
+ }
+ }
+ return nil, nil
+}
+
+// NewArchive creates a new rule which detects the file traversal when extracting zip archives
+func NewArchive(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+ calls := gosec.NewCallList()
+ calls.Add("path/filepath", "Join")
+ return &archive{
+ calls: calls,
+ argType: "*archive/zip.File",
+ MetaData: gosec.MetaData{
+ ID: id,
+ Severity: gosec.Medium,
+ Confidence: gosec.High,
+ What: "File traversal when extracting zip archive",
+ },
+ }, []ast.Node{(*ast.CallExpr)(nil)}
+}
diff --git a/vendor/github.com/securego/gosec/v2/rules/bad_defer.go b/vendor/github.com/securego/gosec/v2/rules/bad_defer.go
new file mode 100644
index 000000000..3c358806f
--- /dev/null
+++ b/vendor/github.com/securego/gosec/v2/rules/bad_defer.go
@@ -0,0 +1,69 @@
+package rules
+
+import (
+ "fmt"
+ "go/ast"
+ "strings"
+
+ "github.com/securego/gosec/v2"
+)
+
+type deferType struct {
+ typ string
+ methods []string
+}
+
+type badDefer struct {
+ gosec.MetaData
+ types []deferType
+}
+
+func (r *badDefer) ID() string {
+ return r.MetaData.ID
+}
+
+func normalize(typ string) string {
+ return strings.TrimPrefix(typ, "*")
+}
+
+func contains(methods []string, method string) bool {
+ for _, m := range methods {
+ if m == method {
+ return true
+ }
+ }
+ return false
+}
+
+func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+ if deferStmt, ok := n.(*ast.DeferStmt); ok {
+ for _, deferTyp := range r.types {
+ if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil {
+ if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) {
+ return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, typ, method), r.Severity, r.Confidence), nil
+ }
+ }
+ }
+
+ }
+
+ return nil, nil
+}
+
+// NewDeferredClosing detects unsafe defer of error returning methods
+func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
+ return &badDefer{
+ types: []deferType{
+ {
+ typ: "os.File",
+ methods: []string{"Close"},
+ },
+ },
+ MetaData: gosec.MetaData{
+ ID: id,
+ Severity: gosec.Medium,
+ Confidence: gosec.High,
+ What: "Deferring unsafe method %q on type %q",
+ },
+ }, []ast.Node{(*ast.DeferStmt)(nil)}
+}
diff --git a/vendor/github.com/securego/gosec/v2/rules/bind.go b/vendor/github.com/securego/gosec/v2/rules/bind.go
new file mode 100644
index 000000000..8f6af067a
--- /dev/null
+++ b/vendor/github.com/securego/gosec/v2/rules/bind.go
@@ -0,0 +1,83 @@
+// (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"
+)
+
+// Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
+type bindsToAllNetworkInterfaces struct {
+ gosec.MetaData
+ calls gosec.CallList
+ pattern *regexp.Regexp
+}
+
+func (r *bindsToAllNetworkInterfaces) ID() string {
+ return r.MetaData.ID
+}
+
+func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
+ callExpr := r.calls.ContainsPkgCallExpr(n, c, false)
+ if callExpr == nil {
+ return nil, nil
+ }
+ if len(callExpr.Args) > 1 {
+ arg := callExpr.Args[1]
+ if bl, ok := arg.(*ast.BasicLit); ok {
+ if arg, err := gosec.GetString(bl); err == nil {
+ if r.pattern.MatchString(arg) {
+ return 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)}
+}