CVE-2021-4238 (Go, published 2022-12-27T22:15Z)
Description: value: Randomly-generated alphanumeric strings contain significantly less entropy than expected. The RandomAlphaNumeric and CryptoRandomAlphaNumeric functions always return strings containing at least one digit from 0 to 9. This significantly reduces the amount of entropy in short strings generated by these functions.
Severity: nan (CVSS v3 base score: 9.1)
Weakness: CWE-331 \u2014 Insufficient Entropy
The product uses an algorithm or scheme that produces insufficient entropy, leaving patterns or clusters of values that are more likely to occur than others.
Repository: https://github.com/Masterminds/goutils
Files changed:
cryptorandomstringutils.go
cryptorandomstringutils_test.go
randomstringutils.go
randomstringutils_test.go
Commit message: Remove unnecessary checks on a value that is already definitely an alphanum

Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>


Vulnerable code:
	"regexp"
	RandomString, err := CryptoRandom(count, 0, 0, true, true)
	if err != nil {
		return "", fmt.Errorf("Error: %s", err)
	}
	match, err := regexp.MatchString("([0-9]+)", RandomString)
	if err != nil {
		panic(err)
	}

	if !match {
		//Get the position between 0 and the length of the string-1  to insert a random number
		position := getCryptoRandomInt(count)
		//Insert a random number between [0-9] in the position
		RandomString = RandomString[:position] + string('0' + getCryptoRandomInt(10)) + RandomString[position + 1:]
		return RandomString, err
	}
	return RandomString, err
			ch = chars[getCryptoRandomInt(gap) + int64(start)]
	"regexp"
Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
	letters - if true, generated string may include alphabetic characters
	numbers - if true, generated string may include numeric characters
	match, err := regexp.MatchString("([0-9]+)", RandomString)
	if err != nil {
		panic(err)
	}
	if !match {
		//Get the position between 0 and the length of the string-1  to insert a random number
		position := rand.Intn(count)
		//Insert a random number between [0-9] in the position
		RandomString = RandomString[:position] + string('0'+rand.Intn(10)) + RandomString[position+1:]
		return RandomString, err
	}
	return RandomString, err

Fixed code:
	return CryptoRandom(count, 0, 0, true, true)
			ch = chars[getCryptoRandomInt(gap)+int64(start)]
	"regexp"
	"strconv"

func TestCryptoRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {

	// Testing for a reported regression in which some versions produced
	// a predictably small set of chars.
	iters := 1000
	charlen := 0
	for i := 0; i < 16; i++ {
		numOnly := 0
		charlen++
		for i := 0; i < iters; i++ {
			out, err := CryptoRandomAlphaNumeric(charlen)
			println(out)
			if err != nil {
				t.Fatal("func failed to produce a random thinger")
			}
			if _, err := strconv.Atoi(out); err == nil {
				numOnly++
			}

			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
			if err != nil {
				t.Fatal(err)
			}
			if !m {
				t.Fatal("Character is not alphanum")
			}
		}

		if numOnly == iters {
			t.Fatalf("Got %d numeric-only random sequences", numOnly)
		}
	}

}
Characters will be chosen from the set of alphabetic characters.
	return RandomString[:count], nil
	"regexp"
	"strconv"

func TestRandAlphaNumeric_FuzzOnlyNumeric(t *testing.T) {

	// Testing for a reported regression in which some versions produced
	// a predictably small set of chars.
	iters := 1000
	charlen := 0
	for i := 0; i < 16; i++ {
		numOnly := 0
		charlen++
		for i := 0; i < iters; i++ {
			out, err := RandomAlphaNumeric(charlen)
			println(out)
			if err != nil {
				t.Fatal("func failed to produce a random thinger")
			}
			if _, err := strconv.Atoi(out); err == nil {
				numOnly++
			}

			m, err := regexp.MatchString("^[0-9a-zA-Z]+$", out)
			if err != nil {
				t.Fatal(err)
			}
			if !m {
				t.Fatal("Character is not alphanum")
			}
		}

		if numOnly == iters {
			t.Fatalf("Got %d numeric-only random sequences", numOnly)
		}
	}

}