CVE-2021-4238 (published 2022-12-27T22:15Z)

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.

CVSS v3 base score: 9.1, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H)

Weakness information:
cwe_id: CWE-331
cwe_name: Insufficient Entropy
description: 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.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/Masterminds/goutils
commit_details: repo_url: https://github.com/Masterminds/goutils
author: Matt Butcher
author_date: 2021-01-28 14:00:48-07:00
msg: Remove unnecessary checks on a value that is already definitely an alphanum

Signed-off-by: Matt Butcher <matt.butcher@microsoft.com>
repository: repo_url: https://github.com/Masterminds/goutils
repo_name: Masterminds/goutils
description: GoUtils is a Go implementation of some string manipulation libraries of Apache Commons. This is an open source project aimed at providing Go users with utility functions to manipulate strings in various ways.
repo_language: Go
owner: Masterminds
file_changes: filename: cryptorandomstringutils.go
change_type: ModificationType.MODIFY
diff: @@ -21,7 +21,6 @@ import (
 	"fmt"
 	"math"
 	"math/big"
-	"regexp"
 	"unicode"
 )
 
@@ -102,24 +101,8 @@ func CryptoRandomAlphaNumeric(count int) (string, error) {
 	if count == 0 {
 		return "", nil
 	}
-	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
 
+	return CryptoRandom(count, 0, 0, true, true)
 }
 
 /*
@@ -204,7 +187,7 @@ func CryptoRandom(count int, start int, end int, letters bool, numbers bool, cha
 		if chars == nil {
 			ch = rune(getCryptoRandomInt(gap) + int64(start))
 		} else {
-			ch = chars[getCryptoRandomInt(gap) + int64(start)]
+			ch = chars[getCryptoRandomInt(gap)+int64(start)]
 		}
 
 		if letters && unicode.IsLetter(ch) || numbers && unicode.IsDigit(ch) || !letters && !numbers {

programming_language: Go
filename: cryptorandomstringutils_test.go
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,8 @@
 package goutils
 
 import (
+	"regexp"
+	"strconv"
 	"testing"
 	"unicode/utf8"
 )
@@ -74,3 +76,38 @@ func TestCryptoRandomAlphaNumeric(t *testing.T) {
 		}
 	}
 }
+
+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)
+		}
+	}
+
+}

programming_language: Go
filename: randomstringutils.go
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,6 @@ import (
 	"fmt"
 	"math"
 	"math/rand"
-	"regexp"
 	"time"
 	"unicode"
 )
@@ -75,12 +74,10 @@ func RandomNumeric(count int) (string, error) {
 
 /*
 RandomAlphabetic creates a random string whose length is the number of characters specified.
-Characters will be chosen from the set of alpha-numeric characters as indicated by the arguments.
+Characters will be chosen from the set of alphabetic characters.
 
 Parameters:
 	count - the length of random string to create
-	letters - if true, generated string may include alphabetic characters
-	numbers - if true, generated string may include numeric characters
 
 Returns:
 	string - the random string
@@ -106,19 +103,8 @@ func RandomAlphaNumeric(count int) (string, error) {
 	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 := 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
+	return RandomString[:count], nil
 
 }
 

programming_language: Go
filename: randomstringutils_test.go
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,8 @@ package goutils
 import (
 	"fmt"
 	"math/rand"
+	"regexp"
+	"strconv"
 	"testing"
 )
 
@@ -76,3 +78,38 @@ func ExampleRandomSeed() {
 	// H_I;E
 	// 2b2ca
 }
+
+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)
+		}
+	}
+
+}

programming_language: Go