You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-08 04:42:07 +03:00
Refactor repository layout and convert build system to Mage.
This commit implements a massive refactor of the repository, and moves the build system over to use Mage (magefile.org) which should allow seamless building across multiple platforms.
This commit is contained in:
13
tools/vendor/github.com/GoASTScanner/gas/rules/big.go
generated
vendored
13
tools/vendor/github.com/GoASTScanner/gas/rules/big.go
generated
vendored
@@ -15,24 +15,27 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type UsingBigExp struct {
|
||||
type usingBigExp struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *UsingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
func (r *usingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matched := gas.MatchCallByType(n, c, r.pkg, r.calls...); matched {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func NewUsingBigExp(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &UsingBigExp{
|
||||
|
||||
// NewUsingBigExp detects issues with modulus == 0 for Bignum
|
||||
func NewUsingBigExp(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &usingBigExp{
|
||||
pkg: "*math/big.Int",
|
||||
calls: []string{"Exp"},
|
||||
MetaData: gas.MetaData{
|
||||
|
33
tools/vendor/github.com/GoASTScanner/gas/rules/bind.go
generated
vendored
33
tools/vendor/github.com/GoASTScanner/gas/rules/bind.go
generated
vendored
@@ -18,30 +18,37 @@ import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
|
||||
type BindsToAllNetworkInterfaces struct {
|
||||
type bindsToAllNetworkInterfaces struct {
|
||||
gas.MetaData
|
||||
call *regexp.Regexp
|
||||
calls gas.CallList
|
||||
pattern *regexp.Regexp
|
||||
}
|
||||
|
||||
func (r *BindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := gas.MatchCall(n, r.call); node != nil {
|
||||
if arg, err := gas.GetString(node.Args[1]); err == nil {
|
||||
if r.pattern.MatchString(arg) {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
callExpr := r.calls.ContainsCallExpr(n, c)
|
||||
if callExpr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if arg, err := gas.GetString(callExpr.Args[1]); err == nil {
|
||||
if r.pattern.MatchString(arg) {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewBindsToAllNetworkInterfaces(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BindsToAllNetworkInterfaces{
|
||||
call: regexp.MustCompile(`^(net|tls)\.Listen$`),
|
||||
// NewBindsToAllNetworkInterfaces detects socket connections that are setup to
|
||||
// listen on all network interfaces.
|
||||
func NewBindsToAllNetworkInterfaces(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("net", "Listen")
|
||||
calls.Add("crypto/tls", "Listen")
|
||||
return &bindsToAllNetworkInterfaces{
|
||||
calls: calls,
|
||||
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
|
79
tools/vendor/github.com/GoASTScanner/gas/rules/blacklist.go
generated
vendored
79
tools/vendor/github.com/GoASTScanner/gas/rules/blacklist.go
generated
vendored
@@ -16,64 +16,67 @@ package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type BlacklistImport struct {
|
||||
type blacklistedImport struct {
|
||||
gas.MetaData
|
||||
Path string
|
||||
Blacklisted map[string]string
|
||||
}
|
||||
|
||||
func (r *BlacklistImport) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
func unquote(original string) string {
|
||||
copy := strings.TrimSpace(original)
|
||||
copy = strings.TrimLeft(copy, `"`)
|
||||
return strings.TrimRight(copy, `"`)
|
||||
}
|
||||
|
||||
func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node, ok := n.(*ast.ImportSpec); ok {
|
||||
if r.Path == node.Path.Value && node.Name.String() != "_" {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
if description, ok := r.Blacklisted[unquote(node.Path.Value)]; ok {
|
||||
return gas.NewIssue(c, node, description, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewBlacklist_crypto_md5(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BlacklistImport{
|
||||
// NewBlacklistedImports reports when a blacklisted import is being used.
|
||||
// Typically when a deprecated technology is being used.
|
||||
func NewBlacklistedImports(conf gas.Config, blacklist map[string]string) (gas.Rule, []ast.Node) {
|
||||
return &blacklistedImport{
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.High,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "Use of weak cryptographic primitive",
|
||||
},
|
||||
Path: `"crypto/md5"`,
|
||||
Blacklisted: blacklist,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
}
|
||||
|
||||
func NewBlacklist_crypto_des(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BlacklistImport{
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.High,
|
||||
Confidence: gas.High,
|
||||
What: "Use of weak cryptographic primitive",
|
||||
},
|
||||
Path: `"crypto/des"`,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
// NewBlacklistedImportMD5 fails if MD5 is imported
|
||||
func NewBlacklistedImportMD5(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(conf, map[string]string{
|
||||
"crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
func NewBlacklist_crypto_rc4(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BlacklistImport{
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.High,
|
||||
Confidence: gas.High,
|
||||
What: "Use of weak cryptographic primitive",
|
||||
},
|
||||
Path: `"crypto/rc4"`,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
// NewBlacklistedImportDES fails if DES is imported
|
||||
func NewBlacklistedImportDES(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(conf, map[string]string{
|
||||
"crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
func NewBlacklist_net_http_cgi(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BlacklistImport{
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.High,
|
||||
Confidence: gas.High,
|
||||
What: "Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
|
||||
},
|
||||
Path: `"net/http/cgi"`,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
// NewBlacklistedImportRC4 fails if DES is imported
|
||||
func NewBlacklistedImportRC4(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(conf, map[string]string{
|
||||
"crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportCGI fails if CGI is imported
|
||||
func NewBlacklistedImportCGI(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(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)",
|
||||
})
|
||||
}
|
||||
|
20
tools/vendor/github.com/GoASTScanner/gas/rules/errors.go
generated
vendored
20
tools/vendor/github.com/GoASTScanner/gas/rules/errors.go
generated
vendored
@@ -15,12 +15,13 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type NoErrorCheck struct {
|
||||
type noErrorCheck struct {
|
||||
gas.MetaData
|
||||
whitelist gas.CallList
|
||||
}
|
||||
@@ -29,7 +30,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
|
||||
if tv := ctx.Info.TypeOf(callExpr); tv != nil {
|
||||
switch t := tv.(type) {
|
||||
case *types.Tuple:
|
||||
for pos := 0; pos < t.Len(); pos += 1 {
|
||||
for pos := 0; pos < t.Len(); pos++ {
|
||||
variable := t.At(pos)
|
||||
if variable != nil && variable.Type().String() == "error" {
|
||||
return pos
|
||||
@@ -44,11 +45,11 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
switch stmt := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range stmt.Rhs {
|
||||
if callExpr, ok := expr.(*ast.CallExpr); ok && !r.whitelist.ContainsCallExpr(callExpr, ctx) {
|
||||
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
|
||||
@@ -59,7 +60,7 @@ func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
}
|
||||
}
|
||||
case *ast.ExprStmt:
|
||||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && !r.whitelist.ContainsCallExpr(callExpr, ctx) {
|
||||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
|
||||
pos := returnsError(callExpr, ctx)
|
||||
if pos >= 0 {
|
||||
return gas.NewIssue(ctx, n, r.What, r.Severity, r.Confidence), nil
|
||||
@@ -69,13 +70,14 @@ func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewNoErrorCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// NewNoErrorCheck detects if the returned error is unchecked
|
||||
func NewNoErrorCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
|
||||
// TODO(gm) Come up with sensible defaults here. Or flip it to use a
|
||||
// black list instead.
|
||||
whitelist := gas.NewCallList()
|
||||
whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString")
|
||||
whitelist.AddAll("fmt", "Print", "Printf", "Println")
|
||||
whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln")
|
||||
whitelist.Add("io.PipeWriter", "CloseWithError")
|
||||
|
||||
if configured, ok := conf["G104"]; ok {
|
||||
@@ -85,7 +87,7 @@ func NewNoErrorCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return &NoErrorCheck{
|
||||
return &noErrorCheck{
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
|
22
tools/vendor/github.com/GoASTScanner/gas/rules/fileperms.go
generated
vendored
22
tools/vendor/github.com/GoASTScanner/gas/rules/fileperms.go
generated
vendored
@@ -19,10 +19,10 @@ import (
|
||||
"go/ast"
|
||||
"strconv"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type FilePermissions struct {
|
||||
type filePermissions struct {
|
||||
gas.MetaData
|
||||
mode int64
|
||||
pkg string
|
||||
@@ -30,7 +30,7 @@ type FilePermissions struct {
|
||||
}
|
||||
|
||||
func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMode int64) int64 {
|
||||
var mode int64 = defaultMode
|
||||
var mode = defaultMode
|
||||
if value, ok := conf[configKey]; ok {
|
||||
switch value.(type) {
|
||||
case int64:
|
||||
@@ -46,7 +46,7 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod
|
||||
return mode
|
||||
}
|
||||
|
||||
func (r *FilePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if callexpr, matched := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
|
||||
modeArg := callexpr.Args[len(callexpr.Args)-1]
|
||||
if mode, err := gas.GetInt(modeArg); err == nil && mode > r.mode {
|
||||
@@ -56,9 +56,11 @@ func (r *FilePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewFilePerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// NewFilePerms creates a rule to detect file creation with a more permissive than configured
|
||||
// permission mask.
|
||||
func NewFilePerms(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G302", 0600)
|
||||
return &FilePermissions{
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"OpenFile", "Chmod"},
|
||||
@@ -70,9 +72,11 @@ func NewFilePerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
||||
func NewMkdirPerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G301", 0700)
|
||||
return &FilePermissions{
|
||||
// NewMkdirPerms creates a rule to detect directory creation with more permissive than
|
||||
// configured permission mask.
|
||||
func NewMkdirPerms(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G301", 0750)
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"Mkdir", "MkdirAll"},
|
||||
|
24
tools/vendor/github.com/GoASTScanner/gas/rules/hardcoded_credentials.go
generated
vendored
24
tools/vendor/github.com/GoASTScanner/gas/rules/hardcoded_credentials.go
generated
vendored
@@ -15,16 +15,16 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"regexp"
|
||||
|
||||
"github.com/nbutton23/zxcvbn-go"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/nbutton23/zxcvbn-go"
|
||||
)
|
||||
|
||||
type Credentials struct {
|
||||
type credentials struct {
|
||||
gas.MetaData
|
||||
pattern *regexp.Regexp
|
||||
entropyThreshold float64
|
||||
@@ -40,7 +40,7 @@ func truncate(s string, n int) string {
|
||||
return s[:n]
|
||||
}
|
||||
|
||||
func (r *Credentials) isHighEntropyString(str string) bool {
|
||||
func (r *credentials) isHighEntropyString(str string) bool {
|
||||
s := truncate(str, r.truncate)
|
||||
info := zxcvbn.PasswordStrength(s, []string{})
|
||||
entropyPerChar := info.Entropy / float64(len(s))
|
||||
@@ -49,7 +49,7 @@ func (r *Credentials) isHighEntropyString(str string) bool {
|
||||
entropyPerChar >= r.perCharThreshold))
|
||||
}
|
||||
|
||||
func (r *Credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
return r.matchAssign(node, ctx)
|
||||
@@ -59,7 +59,7 @@ func (r *Credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*gas.Issue, error) {
|
||||
for _, i := range assign.Lhs {
|
||||
if ident, ok := i.(*ast.Ident); ok {
|
||||
if r.pattern.MatchString(ident.Name) {
|
||||
@@ -76,7 +76,7 @@ func (r *Credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*ga
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *Credentials) matchGenDecl(decl *ast.GenDecl, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) matchGenDecl(decl *ast.GenDecl, ctx *gas.Context) (*gas.Issue, error) {
|
||||
if decl.Tok != token.CONST && decl.Tok != token.VAR {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -100,12 +100,14 @@ func (r *Credentials) matchGenDecl(decl *ast.GenDecl, ctx *gas.Context) (*gas.Is
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewHardcodedCredentials(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// NewHardcodedCredentials attempts to find high entropy string constants being
|
||||
// assigned to variables that appear to be related to credentials.
|
||||
func NewHardcodedCredentials(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
pattern := `(?i)passwd|pass|password|pwd|secret|token`
|
||||
entropyThreshold := 80.0
|
||||
perCharThreshold := 3.0
|
||||
ignoreEntropy := false
|
||||
var truncateString int = 16
|
||||
var truncateString = 16
|
||||
if val, ok := conf["G101"]; ok {
|
||||
conf := val.(map[string]string)
|
||||
if configPattern, ok := conf["pattern"]; ok {
|
||||
@@ -133,7 +135,7 @@ func NewHardcodedCredentials(conf map[string]interface{}) (gas.Rule, []ast.Node)
|
||||
}
|
||||
}
|
||||
|
||||
return &Credentials{
|
||||
return &credentials{
|
||||
pattern: regexp.MustCompile(pattern),
|
||||
entropyThreshold: entropyThreshold,
|
||||
perCharThreshold: perCharThreshold,
|
||||
|
11
tools/vendor/github.com/GoASTScanner/gas/rules/rand.go
generated
vendored
11
tools/vendor/github.com/GoASTScanner/gas/rules/rand.go
generated
vendored
@@ -17,16 +17,16 @@ package rules
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type WeakRand struct {
|
||||
type weakRand struct {
|
||||
gas.MetaData
|
||||
funcNames []string
|
||||
packagePath string
|
||||
}
|
||||
|
||||
func (w *WeakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
for _, funcName := range w.funcNames {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
|
||||
return gas.NewIssue(c, n, w.What, w.Severity, w.Confidence), nil
|
||||
@@ -36,8 +36,9 @@ func (w *WeakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewWeakRandCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &WeakRand{
|
||||
// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
|
||||
func NewWeakRandCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &weakRand{
|
||||
funcNames: []string{"Read", "Int"},
|
||||
packagePath: "math/rand",
|
||||
MetaData: gas.MetaData{
|
||||
|
26
tools/vendor/github.com/GoASTScanner/gas/rules/rsa.go
generated
vendored
26
tools/vendor/github.com/GoASTScanner/gas/rules/rsa.go
generated
vendored
@@ -17,31 +17,33 @@ package rules
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type WeakKeyStrength struct {
|
||||
type weakKeyStrength struct {
|
||||
gas.MetaData
|
||||
pattern *regexp.Regexp
|
||||
bits int
|
||||
calls gas.CallList
|
||||
bits int
|
||||
}
|
||||
|
||||
func (w *WeakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := gas.MatchCall(n, w.pattern); node != nil {
|
||||
if bits, err := gas.GetInt(node.Args[1]); err == nil && bits < (int64)(w.bits) {
|
||||
func (w *weakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if callExpr := w.calls.ContainsCallExpr(n, c); callExpr != nil {
|
||||
if bits, err := gas.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
|
||||
return gas.NewIssue(c, n, w.What, w.Severity, w.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewWeakKeyStrength(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits
|
||||
func NewWeakKeyStrength(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("crypto/rsa", "GenerateKey")
|
||||
bits := 2048
|
||||
return &WeakKeyStrength{
|
||||
pattern: regexp.MustCompile(`^rsa\.GenerateKey$`),
|
||||
bits: bits,
|
||||
return &weakKeyStrength{
|
||||
calls: calls,
|
||||
bits: bits,
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
|
102
tools/vendor/github.com/GoASTScanner/gas/rules/rulelist.go
generated
vendored
Normal file
102
tools/vendor/github.com/GoASTScanner/gas/rules/rulelist.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
// (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/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// RuleDefinition contains the description of a rule and a mechanism to
|
||||
// create it.
|
||||
type RuleDefinition struct {
|
||||
Description string
|
||||
Create gas.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() []gas.RuleBuilder {
|
||||
builders := make([]gas.RuleBuilder, 0, len(rl))
|
||||
for _, def := range rl {
|
||||
builders = append(builders, 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 := map[string]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},
|
||||
"G105": {"Audit the use of big.Exp function", NewUsingBigExp},
|
||||
"G106": {"Audit the use of ssh.InsecureIgnoreHostKey function", NewSSHHostKey},
|
||||
|
||||
// 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 permisions used when creation file or using chmod", NewFilePerms},
|
||||
"G303": {"Creating tempfile using a predictable path", NewBadTempFile},
|
||||
|
||||
// crypto
|
||||
"G401": {"Detect the usage of DES, RC4, or MD5", 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},
|
||||
}
|
||||
|
||||
for rule := range rules {
|
||||
for _, filter := range filters {
|
||||
if filter(rule) {
|
||||
delete(rules, rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
74
tools/vendor/github.com/GoASTScanner/gas/rules/sql.go
generated
vendored
74
tools/vendor/github.com/GoASTScanner/gas/rules/sql.go
generated
vendored
@@ -18,20 +18,32 @@ import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type SqlStatement struct {
|
||||
type sqlStatement struct {
|
||||
gas.MetaData
|
||||
pattern *regexp.Regexp
|
||||
|
||||
// Contains a list of patterns which must all match for the rule to match.
|
||||
patterns []*regexp.Regexp
|
||||
}
|
||||
|
||||
type SqlStrConcat struct {
|
||||
SqlStatement
|
||||
// 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
|
||||
}
|
||||
|
||||
// see if we can figure out what it is
|
||||
func (s *SqlStrConcat) checkObject(n *ast.Ident) bool {
|
||||
func (s *sqlStrConcat) checkObject(n *ast.Ident) bool {
|
||||
if n.Obj != nil {
|
||||
return n.Obj.Kind != ast.Var && n.Obj.Kind != ast.Fun
|
||||
}
|
||||
@@ -39,10 +51,13 @@ func (s *SqlStrConcat) checkObject(n *ast.Ident) bool {
|
||||
}
|
||||
|
||||
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1"
|
||||
func (s *SqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||
if start, ok := node.X.(*ast.BasicLit); ok {
|
||||
if str, e := gas.GetString(start); s.pattern.MatchString(str) && e == nil {
|
||||
if str, e := gas.GetString(start); e == nil {
|
||||
if !s.MatchPatterns(str) {
|
||||
return nil, nil
|
||||
}
|
||||
if _, ok := node.Y.(*ast.BasicLit); ok {
|
||||
return nil, nil // string cat OK
|
||||
}
|
||||
@@ -56,10 +71,13 @@ func (s *SqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewSqlStrConcat(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &SqlStrConcat{
|
||||
SqlStatement: SqlStatement{
|
||||
pattern: regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
||||
// NewSQLStrConcat looks for cases where we are building SQL strings via concatenation
|
||||
func NewSQLStrConcat(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &sqlStrConcat{
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
@@ -69,31 +87,39 @@ func NewSqlStrConcat(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
}, []ast.Node{(*ast.BinaryExpr)(nil)}
|
||||
}
|
||||
|
||||
type SqlStrFormat struct {
|
||||
SqlStatement
|
||||
call *regexp.Regexp
|
||||
type sqlStrFormat struct {
|
||||
sqlStatement
|
||||
calls gas.CallList
|
||||
}
|
||||
|
||||
// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)"
|
||||
func (s *SqlStrFormat) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := gas.MatchCall(n, s.call); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); s.pattern.MatchString(arg) && e == nil {
|
||||
func (s *sqlStrFormat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
|
||||
// TODO(gm) improve confidence if database/sql is being used
|
||||
if node := s.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewSqlStrFormat(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &SqlStrFormat{
|
||||
call: regexp.MustCompile(`^fmt\.Sprintf$`),
|
||||
SqlStatement: SqlStatement{
|
||||
pattern: regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
|
||||
// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
|
||||
func NewSQLStrFormat(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &sqlStrFormat{
|
||||
calls: gas.NewCallList(),
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
|
||||
regexp.MustCompile("%[^bdoxXfFp]"),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
What: "SQL string formatting",
|
||||
},
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
rule.calls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
33
tools/vendor/github.com/GoASTScanner/gas/rules/ssh.go
generated
vendored
Normal file
33
tools/vendor/github.com/GoASTScanner/gas/rules/ssh.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type sshHostKey struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *sshHostKey) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback.
|
||||
func NewSSHHostKey(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &sshHostKey{
|
||||
pkg: "golang.org/x/crypto/ssh",
|
||||
calls: []string{"InsecureIgnoreHostKey"},
|
||||
MetaData: gas.MetaData{
|
||||
What: "Use of ssh InsecureIgnoreHostKey should be audited",
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
52
tools/vendor/github.com/GoASTScanner/gas/rules/subproc.go
generated
vendored
52
tools/vendor/github.com/GoASTScanner/gas/rules/subproc.go
generated
vendored
@@ -16,41 +16,43 @@ package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
"strings"
|
||||
"go/types"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type Subprocess struct {
|
||||
pattern *regexp.Regexp
|
||||
type subprocess struct {
|
||||
gas.CallList
|
||||
}
|
||||
|
||||
func (r *Subprocess) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := gas.MatchCall(n, r.pattern); node != nil {
|
||||
// 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 *gas.Context) (*gas.Issue, error) {
|
||||
if node := r.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if !gas.TryResolve(arg, c) {
|
||||
what := "Subprocess launching with variable."
|
||||
return gas.NewIssue(c, n, what, gas.High, gas.High), nil
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) {
|
||||
return gas.NewIssue(c, n, "Subprocess launched with variable", gas.Medium, gas.High), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call with partially qualified command
|
||||
if str, err := gas.GetString(node.Args[0]); err == nil {
|
||||
if !strings.HasPrefix(str, "/") {
|
||||
what := "Subprocess launching with partial path."
|
||||
return gas.NewIssue(c, n, what, gas.Medium, gas.High), nil
|
||||
}
|
||||
}
|
||||
|
||||
what := "Subprocess launching should be audited."
|
||||
return gas.NewIssue(c, n, what, gas.Low, gas.High), nil
|
||||
return gas.NewIssue(c, n, "Subprocess launching should be audited", gas.Low, gas.High), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewSubproc(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &Subprocess{
|
||||
pattern: regexp.MustCompile(`^exec\.Command|syscall\.Exec$`),
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
// NewSubproc detects cases where we are forking out to an external process
|
||||
func NewSubproc(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &subprocess{gas.NewCallList()}
|
||||
rule.Add("os/exec", "Command")
|
||||
rule.Add("syscall", "Exec")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
24
tools/vendor/github.com/GoASTScanner/gas/rules/tempfiles.go
generated
vendored
24
tools/vendor/github.com/GoASTScanner/gas/rules/tempfiles.go
generated
vendored
@@ -18,17 +18,17 @@ import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type BadTempFile struct {
|
||||
type badTempFile struct {
|
||||
gas.MetaData
|
||||
args *regexp.Regexp
|
||||
call *regexp.Regexp
|
||||
calls gas.CallList
|
||||
args *regexp.Regexp
|
||||
}
|
||||
|
||||
func (t *BadTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := gas.MatchCall(n, t.call); node != nil {
|
||||
func (t *badTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
@@ -36,10 +36,14 @@ func (t *BadTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err erro
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewBadTempFile(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &BadTempFile{
|
||||
call: regexp.MustCompile(`ioutil\.WriteFile|os\.Create`),
|
||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||
// NewBadTempFile detects direct writes to predictable path in temporary directory
|
||||
func NewBadTempFile(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
calls.Add("io/ioutil", "WriteFile")
|
||||
calls.Add("os", "Create")
|
||||
return &badTempFile{
|
||||
calls: calls,
|
||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
|
25
tools/vendor/github.com/GoASTScanner/gas/rules/templates.go
generated
vendored
25
tools/vendor/github.com/GoASTScanner/gas/rules/templates.go
generated
vendored
@@ -16,18 +16,17 @@ package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type TemplateCheck struct {
|
||||
type templateCheck struct {
|
||||
gas.MetaData
|
||||
call *regexp.Regexp
|
||||
calls gas.CallList
|
||||
}
|
||||
|
||||
func (t *TemplateCheck) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := gas.MatchCall(n, t.call); node != nil {
|
||||
func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe
|
||||
return gas.NewIssue(c, n, t.What, t.Severity, t.Confidence), nil
|
||||
@@ -37,9 +36,17 @@ func (t *TemplateCheck) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err er
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewTemplateCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &TemplateCheck{
|
||||
call: regexp.MustCompile(`^template\.(HTML|JS|URL)$`),
|
||||
// NewTemplateCheck constructs the template check rule. This rule is used to
|
||||
// find use of tempaltes where HTML/JS escaping is not being used
|
||||
func NewTemplateCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
|
||||
calls := gas.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: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.Low,
|
||||
|
124
tools/vendor/github.com/GoASTScanner/gas/rules/tls.go
generated
vendored
124
tools/vendor/github.com/GoASTScanner/gas/rules/tls.go
generated
vendored
@@ -12,22 +12,22 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:generate tlsconfig
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"reflect"
|
||||
"regexp"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type InsecureConfigTLS struct {
|
||||
MinVersion int16
|
||||
MaxVersion int16
|
||||
pattern *regexp.Regexp
|
||||
goodCiphers []string
|
||||
type insecureConfigTLS struct {
|
||||
MinVersion int16
|
||||
MaxVersion int16
|
||||
requiredType string
|
||||
goodCiphers []string
|
||||
}
|
||||
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
@@ -39,15 +39,14 @@ func stringInSlice(a string, list []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *InsecureConfigTLS) processTlsCipherSuites(n ast.Node, c *gas.Context) *gas.Issue {
|
||||
a := reflect.TypeOf(&ast.KeyValueExpr{})
|
||||
b := reflect.TypeOf(&ast.CompositeLit{})
|
||||
if node, ok := gas.SimpleSelect(n, a, b).(*ast.CompositeLit); ok {
|
||||
for _, elt := range node.Elts {
|
||||
if ident, ok := elt.(*ast.SelectorExpr); ok {
|
||||
func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) *gas.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) {
|
||||
str := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name)
|
||||
return gas.NewIssue(c, n, str, gas.High, gas.High)
|
||||
err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name)
|
||||
return gas.NewIssue(c, ident, err, gas.High, gas.High)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,9 +54,10 @@ func (t *InsecureConfigTLS) processTlsCipherSuites(n ast.Node, c *gas.Context) *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *InsecureConfigTLS) processTlsConfVal(n *ast.KeyValueExpr, c *gas.Context) *gas.Issue {
|
||||
func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Context) *gas.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" {
|
||||
@@ -97,7 +97,7 @@ func (t *InsecureConfigTLS) processTlsConfVal(n *ast.KeyValueExpr, c *gas.Contex
|
||||
}
|
||||
|
||||
case "CipherSuites":
|
||||
if ret := t.processTlsCipherSuites(n, c); ret != nil {
|
||||
if ret := t.processTLSCipherSuites(n.Value, c); ret != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -107,85 +107,19 @@ func (t *InsecureConfigTLS) processTlsConfVal(n *ast.KeyValueExpr, c *gas.Contex
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *InsecureConfigTLS) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if node := gas.MatchCompLit(n, t.pattern); node != nil {
|
||||
for _, elt := range node.Elts {
|
||||
if kve, ok := elt.(*ast.KeyValueExpr); ok {
|
||||
gi = t.processTlsConfVal(kve, c)
|
||||
if gi != nil {
|
||||
break
|
||||
func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.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
|
||||
}
|
||||
|
||||
func NewModernTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
||||
return &InsecureConfigTLS{
|
||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||
MinVersion: 0x0303, // TLS 1.2 only
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
||||
|
||||
func NewIntermediateTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
||||
return &InsecureConfigTLS{
|
||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
||||
|
||||
func NewCompatTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Old_compatibility_.28default.29
|
||||
return &InsecureConfigTLS{
|
||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||
MinVersion: 0x0301, // TLS 1.2, 1.1, 1.0
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
return nil, nil
|
||||
}
|
||||
|
132
tools/vendor/github.com/GoASTScanner/gas/rules/tls_config.go
generated
vendored
Normal file
132
tools/vendor/github.com/GoASTScanner/gas/rules/tls_config.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
// NewModernTLSCheck creates a check for Modern TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewModernTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0303,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
||||
|
||||
// NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewIntermediateTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0301,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"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_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_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_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_128_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"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)}
|
||||
}
|
||||
|
||||
// NewOldTLSCheck creates a check for Old TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewOldTLSCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0300,
|
||||
MaxVersion: 0x0303,
|
||||
goodCiphers: []string{
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"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_DHE_DSS_WITH_AES_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384",
|
||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",
|
||||
"TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",
|
||||
"TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",
|
||||
"TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",
|
||||
"TLS_DHE_RSA_WITH_SEED_CBC_SHA",
|
||||
"TLS_DHE_DSS_WITH_SEED_CBC_SHA",
|
||||
"TLS_RSA_WITH_SEED_CBC_SHA",
|
||||
},
|
||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||
}
|
13
tools/vendor/github.com/GoASTScanner/gas/rules/unsafe.go
generated
vendored
13
tools/vendor/github.com/GoASTScanner/gas/rules/unsafe.go
generated
vendored
@@ -15,25 +15,28 @@
|
||||
package rules
|
||||
|
||||
import (
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type UsingUnsafe struct {
|
||||
type usingUnsafe struct {
|
||||
gas.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
||||
func (r *UsingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
func (r *usingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewUsingUnsafe(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
return &UsingUnsafe{
|
||||
// NewUsingUnsafe rule detects the use of the unsafe package. This is only
|
||||
// really useful for auditing purposes.
|
||||
func NewUsingUnsafe(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
return &usingUnsafe{
|
||||
pkg: "unsafe",
|
||||
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
|
||||
MetaData: gas.MetaData{
|
||||
|
12
tools/vendor/github.com/GoASTScanner/gas/rules/weakcrypto.go
generated
vendored
12
tools/vendor/github.com/GoASTScanner/gas/rules/weakcrypto.go
generated
vendored
@@ -17,15 +17,15 @@ package rules
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
gas "github.com/GoASTScanner/gas/core"
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
|
||||
type UsesWeakCryptography struct {
|
||||
type usesWeakCryptography struct {
|
||||
gas.MetaData
|
||||
blacklist map[string][]string
|
||||
}
|
||||
|
||||
func (r *UsesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *usesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
|
||||
for pkg, funcs := range r.blacklist {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched {
|
||||
@@ -35,13 +35,13 @@ func (r *UsesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, er
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Uses des.* md5.* or rc4.*
|
||||
func NewUsesWeakCryptography(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||
// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.*
|
||||
func NewUsesWeakCryptography(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := make(map[string][]string)
|
||||
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
||||
calls["crypto/md5"] = []string{"New", "Sum"}
|
||||
calls["crypto/rc4"] = []string{"NewCipher"}
|
||||
rule := &UsesWeakCryptography{
|
||||
rule := &usesWeakCryptography{
|
||||
blacklist: calls,
|
||||
MetaData: gas.MetaData{
|
||||
Severity: gas.Medium,
|
||||
|
Reference in New Issue
Block a user