Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
uses: actions/setup-go@v6
with:
# NOTE: Keep this in sync with the version from go.mod
go-version: "1.23.x"
go-version: "1.25.x"

- name: Run Benchmark
run: set -o pipefail; go test ./... -benchmem -run=^$ -bench . | tee output.txt
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- uses: actions/setup-go@v6
with:
# NOTE: Keep this in sync with the version from go.mod
go-version: "1.23.x"
go-version: "1.25.x"
cache: false

- name: golangci-lint
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
Build:
strategy:
matrix:
go-version: [1.23.x, 1.24.x]
go-version: [1.24.x, 1.25.x]
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
Expand All @@ -31,7 +31,7 @@ jobs:
go-version: ${{ matrix.go-version }}

- name: Install gotestsum
run: go install gotest.tools/gotestsum@v1.12.0
run: go install gotest.tools/gotestsum@v1.13.0

- name: Test
run: gotestsum -f testname -- ./... -race -count=1 -shuffle=on
Expand All @@ -40,10 +40,10 @@ jobs:
run: gotestsum -f testname -- ./... -race -count=1 -coverprofile=coverage.txt -covermode=atomic -shuffle=on

- name: Upload coverage reports to Codecov
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.24.x' }}
uses: codecov/codecov-action@v5.5.2
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.25.x' }}
uses: codecov/codecov-action@v5.5.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.txt
files: ./coverage.txt
flags: unittests
slug: gofiber/utils
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ benchmark:
## coverage: ☂️ Generate coverage report
.PHONY: coverage
coverage:
go run gotest.tools/gotestsum@v1.12.0 -f testname -- ./... -race -count=1 -coverprofile=coverage.out -covermode=atomic
go run gotest.tools/gotestsum@v1.13.0 -f testname -- ./... -race -count=1 -coverprofile=coverage.out -covermode=atomic
go tool cover -html=coverage.out -o coverage.html
open coverage.html &

Expand All @@ -46,7 +46,7 @@ modernize:
## test: 🚦 Execute all tests
.PHONY: test
test:
go run gotest.tools/gotestsum@v1.12.0 -f testname -- ./... -race -count=1 -shuffle=on
go run gotest.tools/gotestsum@v1.13.0 -f testname -- ./... -race -count=1 -shuffle=on

## tidy: 📌 Clean and tidy dependencies
.PHONY: tidy
Expand Down
33 changes: 12 additions & 21 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,39 +99,30 @@ func UUIDv4() string {
// GenerateSecureToken generates a cryptographically secure random token encoded in base64.
// It uses crypto/rand for randomness and base64.RawURLEncoding for URL-safe output.
// If length is less than or equal to 0, it defaults to 32 bytes (256 bits of entropy).
// Returns an error if the random source fails.
func GenerateSecureToken(length int) (string, error) {
// Panics if the random source fails.
func GenerateSecureToken(length int) string {
if length <= 0 {
length = 32
}
Comment thread
ReneWerner87 marked this conversation as resolved.
bytes := make([]byte, length)
if _, err := randRead(bytes); err != nil {
return "", fmt.Errorf("utils: failed to read random bytes for token: %w", err)
// On Go 1.24+, crypto/rand.Read panics internally and never returns an error.
// On Go 1.23 and earlier, we panic for the same reasons: RNG failures indicate
// a broken system state (uninitialized entropy pool, misconfigured VM, etc.)
// that is almost certainly permanent rather than transient.
// See: https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/crypto/rand/rand.go
// https://go.dev/issue/66821
panic(fmt.Errorf("utils: failed to read random bytes for token: %w", err))
}
return base64.RawURLEncoding.EncodeToString(bytes), nil
}

// GenerateSecureTokenMust is a convenience wrapper that panics on failure.
func GenerateSecureTokenMust(length int) string {
s, err := GenerateSecureToken(length)
if err != nil {
panic(err)
}
return s
return base64.RawURLEncoding.EncodeToString(bytes)
}

// SecureToken generates a secure token with 32 bytes of entropy.
// Returns an error if the random source fails.
func SecureToken() (string, error) {
// Panics if the random source fails. See GenerateSecureToken for details.
func SecureToken() string {
return GenerateSecureToken(32)
}

// SecureTokenMust generates a secure token with 32 bytes of entropy.
// It panics if the random source fails.
func SecureTokenMust() string {
return GenerateSecureTokenMust(32)
}

// FunctionName returns function name
func FunctionName(fn any) string {
if fn == nil {
Expand Down
41 changes: 14 additions & 27 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,32 +103,26 @@ func Test_UUIDv4_Concurrency(t *testing.T) {
func Test_GenerateSecureToken(t *testing.T) {
t.Parallel()
// Test with 32 bytes
token, err := GenerateSecureToken(32)
require.NoError(t, err)
token := GenerateSecureToken(32)
require.Len(t, token, 43) // base64 encoding of 32 bytes
require.NotEmpty(t, token)

// Test custom length
token8, err := GenerateSecureToken(8)
require.NoError(t, err)
token8 := GenerateSecureToken(8)
require.Len(t, token8, 11) // base64 of 8 bytes ~11 chars

token16, err := GenerateSecureToken(16)
require.NoError(t, err)
token16 := GenerateSecureToken(16)
require.Len(t, token16, 22) // base64 of 16 bytes ~22 chars

// Test uniqueness
token2, err := GenerateSecureToken(32)
require.NoError(t, err)
token2 := GenerateSecureToken(32)
require.NotEqual(t, token, token2)

// Test invalid length defaults to 32
tokenZero, err := GenerateSecureToken(0)
require.NoError(t, err)
tokenZero := GenerateSecureToken(0)
require.Len(t, tokenZero, 43)

tokenNegative, err := GenerateSecureToken(-1)
require.NoError(t, err)
tokenNegative := GenerateSecureToken(-1)
require.Len(t, tokenNegative, 43)
}

Expand All @@ -139,7 +133,7 @@ func Test_GenerateSecureToken_Concurrency(t *testing.T) {
results := make(map[string]string)
for i := 0; i < iterations; i++ {
go func() {
ch <- GenerateSecureTokenMust(32)
ch <- GenerateSecureToken(32)
}()
}
for i := 0; i < iterations; i++ {
Expand All @@ -159,25 +153,18 @@ func Test_GenerateSecureToken_ErrorOnRandFail(t *testing.T) {
return 0, errors.New("simulated failure")
}

s, err := GenerateSecureToken(16)
require.Error(t, err)
require.Empty(t, s)
require.Contains(t, err.Error(), "simulated failure")

// Must variant should panic on failure
require.Panics(t, func() { GenerateSecureTokenMust(16) })
// Should panic on failure
require.Panics(t, func() { GenerateSecureToken(16) })
}

func Test_SecureToken(t *testing.T) {
t.Parallel()
token, err := SecureToken()
require.NoError(t, err)
token := SecureToken()
require.Len(t, token, 43)
require.NotEmpty(t, token)

// Test uniqueness
token2, err := SecureToken()
require.NoError(t, err)
token2 := SecureToken()
require.NotEqual(t, token, token2)
}

Expand Down Expand Up @@ -381,13 +368,13 @@ func Benchmark_GenerateSecureToken(b *testing.B) {
var res string
b.Run("16_bytes", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = GenerateSecureTokenMust(16)
res = GenerateSecureToken(16)
}
require.Len(b, res, 22)
})
b.Run("32_bytes", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = GenerateSecureTokenMust(32)
res = GenerateSecureToken(32)
}
require.Len(b, res, 43)
})
Expand All @@ -404,7 +391,7 @@ func Benchmark_TokenGenerators(b *testing.B) {
})
b.Run("SecureToken", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = SecureTokenMust()
res = SecureToken()
}
require.Len(b, res, 43)
})
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/gofiber/utils/v2

go 1.23.0
go 1.24.0

require (
github.com/fxamacker/cbor/v2 v2.9.0
Expand Down
Loading