Add support for math/rand/v2 added in Go 1.22

This commit is contained in:
Hiroki Yorimitsu 2024-03-06 09:29:57 +09:00 committed by Cosmin Cojocar
parent 36878a9423
commit be378e682f
3 changed files with 109 additions and 10 deletions

View file

@ -15,9 +15,14 @@ package gosec
import ( import (
"go/ast" "go/ast"
"go/types" "go/types"
"regexp"
"strings" "strings"
) )
var (
versioningPackagePattern = regexp.MustCompile(`v[0-9]+$`)
)
// ImportTracker is used to normalize the packages that have been imported // ImportTracker is used to normalize the packages that have been imported
// by a source file. It is able to differentiate between plain imports, aliased // by a source file. It is able to differentiate between plain imports, aliased
// imports and init only imports. // imports and init only imports.
@ -66,5 +71,10 @@ func importName(importPath string) string {
if len(parts) > 0 { if len(parts) > 0 {
name = parts[len(parts)-1] name = parts[len(parts)-1]
} }
// If the last segment of the path is version information, consider the second to last segment as the package name.
// (e.g., `math/rand/v2` would be `rand`)
if len(parts) > 1 && versioningPackagePattern.MatchString(name) {
name = parts[len(parts)-2]
}
return name return name
} }

View file

@ -23,8 +23,7 @@ import (
type weakRand struct { type weakRand struct {
issue.MetaData issue.MetaData
funcNames []string blocklist map[string][]string
packagePath string
} }
func (w *weakRand) ID() string { func (w *weakRand) ID() string {
@ -32,8 +31,8 @@ func (w *weakRand) ID() string {
} }
func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
for _, funcName := range w.funcNames { for pkg, funcs := range w.blocklist {
if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched { if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil
} }
} }
@ -43,17 +42,22 @@ func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) {
// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure // NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) {
calls := make(map[string][]string)
calls["math/rand"] = []string{
"New", "Read", "Float32", "Float64", "Int", "Int31", "Int31n",
"Int63", "Int63n", "Intn", "NormFloat64", "Uint32", "Uint64",
}
calls["math/rand/v2"] = []string{
"New", "Float32", "Float64", "Int", "Int32", "Int32N",
"Int64", "Int64N", "IntN", "N", "NormFloat64", "Uint32", "Uint32N", "Uint64", "Uint64N", "UintN",
}
return &weakRand{ return &weakRand{
funcNames: []string{ blocklist: calls,
"New", "Read", "Float32", "Float64", "Int", "Int31",
"Int31n", "Int63", "Int63n", "Intn", "NormalFloat64", "Uint32", "Uint64",
},
packagePath: "math/rand",
MetaData: issue.MetaData{ MetaData: issue.MetaData{
ID: id, ID: id,
Severity: issue.High, Severity: issue.High,
Confidence: issue.Medium, Confidence: issue.Medium,
What: "Use of weak random number generator (math/rand instead of crypto/rand)", What: "Use of weak random number generator (math/rand or math/rand/v2 instead of crypto/rand)",
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}
} }

View file

@ -27,6 +27,16 @@ func main() {
{[]string{` {[]string{`
package main package main
import "math/rand/v2"
func main() {
bad := rand.Int()
println(bad)
}
`}, 1, gosec.NewConfig()},
{[]string{`
package main
import ( import (
"crypto/rand" "crypto/rand"
mrand "math/rand" mrand "math/rand"
@ -42,6 +52,21 @@ func main() {
{[]string{` {[]string{`
package main package main
import (
"crypto/rand"
mrand "math/rand/v2"
)
func main() {
good, _ := rand.Read(nil)
println(good)
bad := mrand.Int32()
println(bad)
}
`}, 1, gosec.NewConfig()},
{[]string{`
package main
import ( import (
"math/rand" "math/rand"
) )
@ -55,6 +80,19 @@ func main() {
{[]string{` {[]string{`
package main package main
import (
"math/rand/v2"
)
func main() {
gen := rand.New(rand.NewPCG(1, 2))
bad := gen.Int()
println(bad)
}
`}, 1, gosec.NewConfig()},
{[]string{`
package main
import ( import (
"math/rand" "math/rand"
) )
@ -67,6 +105,18 @@ func main() {
{[]string{` {[]string{`
package main package main
import (
"math/rand/v2"
)
func main() {
bad := rand.IntN(10)
println(bad)
}
`}, 1, gosec.NewConfig()},
{[]string{`
package main
import ( import (
"crypto/rand" "crypto/rand"
"math/big" "math/big"
@ -83,6 +133,22 @@ func main() {
{[]string{` {[]string{`
package main package main
import (
"crypto/rand"
"math/big"
rnd "math/rand/v2"
)
func main() {
good, _ := rand.Int(rand.Reader, big.NewInt(int64(2)))
println(good)
bad := rnd.IntN(2)
println(bad)
}
`}, 1, gosec.NewConfig()},
{[]string{`
package main
import ( import (
crand "crypto/rand" crand "crypto/rand"
"math/big" "math/big"
@ -98,5 +164,24 @@ func main() {
_ = rand2.Intn(2) // bad _ = rand2.Intn(2) // bad
_ = rand3.Intn(2) // bad _ = rand3.Intn(2) // bad
} }
`}, 3, gosec.NewConfig()},
{[]string{`
package main
import (
crand "crypto/rand"
"math/big"
"math/rand/v2"
rand2 "math/rand/v2"
rand3 "math/rand/v2"
)
func main() {
_, _ = crand.Int(crand.Reader, big.NewInt(int64(2))) // good
_ = rand.IntN(2) // bad
_ = rand2.IntN(2) // bad
_ = rand3.IntN(2) // bad
}
`}, 3, gosec.NewConfig()}, `}, 3, gosec.NewConfig()},
} }