VYPR
Medium severity6.5GHSA Advisory· Published May 5, 2026· Updated May 12, 2026

CVE-2026-30246

CVE-2026-30246

Description

Fiber is a web framework for Go. In github.com/gofiber/fiber/v3 versions through 3.1.0, the default key generator in the cache middleware uses only the request path and does not include the query string. As a result, requests for the same path with different query parameters can share a cache key and receive the wrong cached response. This can cause response mix-up for query-dependent endpoints and may expose data intended for a different request. This issue is fixed after version 3.1.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/gofiber/fiber/v3Go
< 3.2.03.2.0

Affected products

1

Patches

2
050ff1ff1851

refactor: improve cache key generation by escaping key delimiters and normalizing method names

https://github.com/gofiber/fiberRenéApr 23, 2026via ghsa
3 files changed · +12 9
  • middleware/cache/cache.go+6 6 modified
    @@ -1332,12 +1332,12 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     	// Pre-scan query string to detect excessive parameters before expensive parsing.
     	// This prevents DoS via url.ParseQuery allocating large maps/slices.
     	if len(query) > maxQueryBufferSize {
    -		return boundKeySegment(query)
    +		return boundKeySegment(escapeKeyDelimiters(query))
     	}
     
     	// Fast path: single key=value pair needs no parsing or sorting
     	if strings.IndexByte(query, '&') < 0 {
    -		return boundKeySegment(query)
    +		return boundKeySegment(escapeKeyDelimiters(query))
     	}
     
     	// Quick count of potential parameters (ampersands + 1)
    @@ -1347,22 +1347,22 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     			paramCount++
     			if paramCount > maxQueryParams {
     				// Too many parameters detected, hash without parsing
    -				return boundKeySegment(query)
    +				return boundKeySegment(escapeKeyDelimiters(query))
     			}
     		}
     	}
     
     	parsed, err := url.ParseQuery(query)
     	if err != nil {
    -		return boundKeySegment(query)
    +		return boundKeySegment(escapeKeyDelimiters(query))
     	}
     
     	// Double-check actual parameter count after parsing
     	actualCount := 0
     	for _, values := range parsed {
     		actualCount += len(values)
     		if actualCount > maxQueryParams {
    -			return boundKeySegment(query)
    +			return boundKeySegment(escapeKeyDelimiters(query))
     		}
     	}
     
    @@ -1399,7 +1399,7 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     					*bufPtr = buf
     					keyBufferPool.Put(bufPtr)
     				}
    -				return boundKeySegment(query)
    +				return boundKeySegment(escapeKeyDelimiters(query))
     			}
     
     			buf = append(buf, escapedKey...)
    
  • middleware/cache/cache_test.go+1 1 modified
    @@ -5451,7 +5451,7 @@ func Test_hasDirective(t *testing.T) {
     		{"at start", "no-cache, max-age=0", "no-cache", true},
     		{"at end", "public, no-cache", "no-cache", true},
     		{"not present", "public, max-age=0", "no-cache", false},
    -		{"partial match (truncated)", "no-cach", "no-cache", false}, // cspell:disable-line -- intentionally truncated directive
    +		{"shorter token does not match", "no-catch", "no-cache", false},
     		{"substring of longer token", "no-cache-extended", "no-cache", false},
     
     		// Trailing whitespace (#4143)
    
  • middleware/cache/config.go+5 2 modified
    @@ -169,10 +169,13 @@ func configDefault(config ...Config) Config {
     		cfg.Methods = ConfigDefault.Methods
     	} else {
     		// Normalize method names to uppercase (HTTP methods are case-sensitive
    -		// and c.Method() returns uppercase, e.g. "GET" not "get")
    +		// and c.Method() returns uppercase, e.g. "GET" not "get").
    +		// Copy first to avoid mutating the caller's slice.
    +		normalized := make([]string, len(cfg.Methods))
     		for i, m := range cfg.Methods {
    -			cfg.Methods[i] = utilsstrings.ToUpper(m)
    +			normalized[i] = utilsstrings.ToUpper(m)
     		}
    +		cfg.Methods = normalized
     	}
     	if cfg.KeyGenerator == nil {
     		cfg.KeyGenerator = func(c fiber.Ctx) string {
    
9a0d12c07ed8

bug: harden cache middleware key generation and restore Methods config

https://github.com/gofiber/fiberRenéApr 23, 2026via ghsa
6 files changed · +240 24
  • docs/middleware/cache.md+3 1 modified
    @@ -120,7 +120,7 @@ This prevents common collisions from path-only keys (for example, `/?id=1` vs `/
     
     The middleware **does not include request body/form values in the default cache key**.
     
    -Cache lookup/storage is only applied for `GET` and `HEAD` requests. Other HTTP methods always bypass the cache middleware.
    +Cache lookup/storage is applied only for `GET` and `HEAD` requests by default. Other HTTP methods bypass the cache middleware. You can change this via the `Methods` config field.
     
     If a response sets `Vary`, request lookup/storage is also partitioned by those header values unless `DisableVaryHeaders` is `true`. Responses with `Vary: *` remain uncacheable.
     
    @@ -138,6 +138,7 @@ If a response sets `Vary`, request lookup/storage is also partitioned by those h
     | DisableQueryKeys     | `bool`                                         | Disables canonicalized query params in keys. | `false` |
     | KeyHeaders           | `[]string`                                     | Header allow-list used for key partitioning. Names are normalized case-insensitively and sorted. Use `[]string{}` to disable header-based partitioning. | `[]string{"accept","accept-encoding","accept-language"}` |
     | KeyCookies           | `[]string`                                     | Optional cookie allow-list for key partitioning. Explicit opt-in only; names remain case-sensitive. | `nil` |
    +| Methods              | `[]string`                                     | HTTP methods eligible for caching. Requests whose method is not in this list bypass the cache. | `[]string{fiber.MethodGet, fiber.MethodHead}` |
     | DisableVaryHeaders   | `bool`                                         | Disables response `Vary` dimensions in cache lookup/storage partitioning. | `false` |
     | ExpirationGenerator  | `func(fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request.                                                                                                                                                                                                                        | `nil`                                                            |
     | Storage              | `fiber.Storage`                                | Storage is used to store the state of the middleware.                                                                                                                                                                                                                                                            | In-memory store                                                  |
    @@ -162,6 +163,7 @@ var ConfigDefault = Config{
             fiber.HeaderAcceptLanguage,
         },
         KeyCookies: nil,
    +    Methods: []string{fiber.MethodGet, fiber.MethodHead},
         DisableVaryHeaders: false,
         ExpirationGenerator:  nil,
         StoreResponseHeaders: false,
    
  • docs/whats_new.md+2 1 modified
    @@ -1369,7 +1369,7 @@ Cache keys are now redacted in logs and error messages by default, and a `Disabl
     
     The default cache key strategy was also hardened. Instead of path-only behavior, keys now use structured request dimensions: method partitioning, path, canonical query string, and selected representation headers (`Accept`, `Accept-Encoding`, `Accept-Language`). This avoids collisions such as `/items?id=1` vs `/items?id=2` while keeping key generation deterministic. New config fields were added for explicit control: `DisableQueryKeys`, `KeyHeaders`, `KeyCookies`, and `DisableVaryHeaders`.
     
    -As a security/performance default, request body/form values are not part of the default cache key. Cache handling is limited to `GET` and `HEAD` requests.
    +As a security/performance default, request body/form values are not part of the default cache key. Cache handling is limited to `GET` and `HEAD` requests by default, configurable via the `Methods` field.
     
     :::note
     The deprecated `Store` and `Key` options have been removed in v3. Use `Storage` and `KeyGenerator` instead.
    @@ -2890,6 +2890,7 @@ To restore v2 behavior:
     
     Additional v3 cache key options:
     
    +- `Methods`: HTTP methods eligible for caching (default `GET`, `HEAD`)
     - `DisableQueryKeys`: disable canonicalized query args in keys (default `false`)
     - `KeyHeaders`: request header allow-list for key partitioning
     - `KeyCookies`: explicit cookie allow-list for key partitioning
    
  • middleware/cache/cache.go+47 22 modified
    @@ -10,6 +10,7 @@ import (
     	"fmt"
     	"math"
     	"net/url"
    +	"slices"
     	"sort"
     	"strings"
     	"sync"
    @@ -253,8 +254,8 @@ func New(config ...Config) fiber.Handler {
     
     		requestMethod := c.Method()
     
    -		// Cache only GET and HEAD requests.
    -		if requestMethod != fiber.MethodGet && requestMethod != fiber.MethodHead {
    +		// Only cache methods listed in cfg.Methods (default: GET, HEAD).
    +		if !slices.Contains(cfg.Methods, requestMethod) {
     			c.Set(cfg.CacheHeader, cacheUnreachable)
     			return c.Next()
     		}
    @@ -1284,7 +1285,8 @@ func defaultKeyGenerator(c fiber.Ctx, cfg *Config) string {
     	}
     
     	buf := (*bufPtr)[:0]
    -	buf = append(buf, boundKeySegment(c.Path())...)
    +	// Escape delimiters in path to prevent crafted paths from injecting key structure
    +	buf = append(buf, boundKeySegment(escapeKeyDelimiters(c.Path()))...)
     
     	if !cfg.DisableQueryKeys {
     		buf = append(buf, '|', 'q', '=')
    @@ -1301,7 +1303,7 @@ func defaultKeyGenerator(c fiber.Ctx, cfg *Config) string {
     		buf = append(buf, canonicalCookieSubset(c, cfg.KeyCookies)...)
     	}
     
    -	result := utils.CopyString(utils.UnsafeString(buf))
    +	result := string(buf)
     
     	// Reset buffer and return to pool, but discard if it grew too large
     	// to prevent pool from retaining oversized buffers
    @@ -1314,17 +1316,24 @@ func defaultKeyGenerator(c fiber.Ctx, cfg *Config) string {
     }
     
     func canonicalQueryString(uri *fasthttp.URI) string {
    -	query := utils.CopyString(utils.UnsafeString(uri.QueryString()))
    -	if query == "" {
    +	raw := uri.QueryString()
    +	if len(raw) == 0 {
     		return ""
     	}
     
    -	// Pre-scan query string to detect excessive parameters before expensive parsing
    -	// This prevents DoS via url.ParseQuery allocating large maps/slices
    +	query := utils.CopyString(utils.UnsafeString(raw))
    +
    +	// Pre-scan query string to detect excessive parameters before expensive parsing.
    +	// This prevents DoS via url.ParseQuery allocating large maps/slices.
     	if len(query) > maxQueryBufferSize {
     		return boundKeySegment(query)
     	}
     
    +	// Fast path: single key=value pair needs no parsing or sorting
    +	if strings.IndexByte(query, '&') < 0 {
    +		return boundKeySegment(query)
    +	}
    +
     	// Quick count of potential parameters (ampersands + 1)
     	paramCount := 1
     	for i := 0; i < len(query); i++ {
    @@ -1357,10 +1366,15 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     	}
     	sort.Strings(keys)
     
    -	// Use a bounded buffer to prevent excessive memory allocation during URL escaping
    -	// URL escaping can expand strings up to 3x (each byte -> %XX)
    -	initialCap := min(len(query)*2, maxQueryBufferSize/2)
    -	buf := make([]byte, 0, initialCap)
    +	// Use pooled buffer to prevent excessive memory allocation during URL escaping.
    +	// URL escaping can expand strings up to 3x (each byte -> %XX).
    +	v := keyBufferPool.Get()
    +	bufPtr, ok := v.(*[]byte)
    +	if !ok || bufPtr == nil {
    +		b := make([]byte, 0, defaultKeyBufferCap)
    +		bufPtr = &b
    +	}
    +	buf := (*bufPtr)[:0]
     
     	for _, key := range keys {
     		values := parsed[key]
    @@ -1370,12 +1384,15 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     				buf = append(buf, '&')
     			}
     
    -			// Check buffer size before appending to prevent unbounded growth
     			escapedKey := url.QueryEscape(key)
     			escapedValue := url.QueryEscape(value)
     
    -			// If buffer would exceed safe limits, hash the entire query
    +			// Check buffer size before appending to prevent unbounded growth
     			if len(buf)+len(escapedKey)+len(escapedValue)+2 > maxQueryBufferSize {
    +				if cap(buf) <= defaultKeyBufferCap*4 {
    +					*bufPtr = buf
    +					keyBufferPool.Put(bufPtr)
    +				}
     				return boundKeySegment(query)
     			}
     
    @@ -1385,7 +1402,15 @@ func canonicalQueryString(uri *fasthttp.URI) string {
     		}
     	}
     
    -	return boundKeySegment(utils.CopyString(utils.UnsafeString(buf)))
    +	result := boundKeySegment(string(buf))
    +
    +	// Return buffer to pool if not oversized
    +	if cap(buf) <= defaultKeyBufferCap*4 {
    +		*bufPtr = buf
    +		keyBufferPool.Put(bufPtr)
    +	}
    +
    +	return result
     }
     
     func canonicalHeaderSubset(header *fasthttp.RequestHeader, names []string) string {
    @@ -1404,10 +1429,10 @@ func canonicalHeaderSubset(header *fasthttp.RequestHeader, names []string) strin
     		headerValue := header.Peek(name)
     		// Escape value to prevent delimiter injection
     		escapedValue := escapeKeyDelimiters(utils.UnsafeString(headerValue))
    -		buf = append(buf, utils.UnsafeBytes(boundKeySegment(escapedValue))...)
    +		buf = append(buf, boundKeySegment(escapedValue)...)
     	}
     
    -	return utils.CopyString(utils.UnsafeString(buf))
    +	return string(buf)
     }
     
     func canonicalCookieSubset(c fiber.Ctx, names []string) string {
    @@ -1426,17 +1451,17 @@ func canonicalCookieSubset(c fiber.Ctx, names []string) string {
     		cookieValue := c.Cookies(name)
     		// Escape value to prevent delimiter injection
     		escapedValue := escapeKeyDelimiters(cookieValue)
    -		buf = append(buf, utils.UnsafeBytes(boundKeySegment(escapedValue))...)
    +		buf = append(buf, boundKeySegment(escapedValue)...)
     	}
     
    -	return utils.CopyString(utils.UnsafeString(buf))
    +	return string(buf)
     }
     
    -// escapeKeyDelimiters escapes pipe and colon characters used as delimiters in cache keys
    +// escapeKeyDelimiters escapes pipe, colon, and backslash characters used as delimiters in cache keys
     // to prevent injection attacks where crafted values could collide with different inputs
     func escapeKeyDelimiters(s string) string {
    -	// Fast path: no delimiters to escape
    -	if !strings.ContainsAny(s, "|:") {
    +	// Fast path: no characters to escape
    +	if !strings.ContainsAny(s, "|:\\") {
     		return s
     	}
     
    
  • middleware/cache/cache_security_test.go+56 0 modified
    @@ -539,3 +539,59 @@ func Test_Cache_Security_DelimiterCollisionPrevention(t *testing.T) {
     		seen[resp] = true
     	}
     }
    +
    +// Test_Cache_Security_EscapeKeyDelimiters_Unit is a direct regression test for the
    +// escapeKeyDelimiters function, ensuring backslashes are escaped to prevent collisions
    +// between e.g. a literal "a\pb" and the escaped form of "a|b" → "a\pb".
    +func Test_Cache_Security_EscapeKeyDelimiters_Unit(t *testing.T) {
    +	t.Parallel()
    +
    +	tests := []struct {
    +		input    string
    +		expected string
    +	}{
    +		// Fast path: no special characters
    +		{"hello", "hello"},
    +		{"", ""},
    +		{"foo/bar?baz=1", "foo/bar?baz=1"},
    +		// Pipe escaping
    +		{"a|b", "a\\pb"},
    +		// Colon escaping
    +		{"a:b", "a\\cb"},
    +		// Backslash escaping (regression: fast path must also check for \)
    +		{"a\\b", "a\\\\b"},
    +		// Backslash-pipe sequence must not collide with escaped pipe
    +		{"a\\pb", "a\\\\pb"}, // literal \p → \\p (differs from escaped | → \p)
    +		{"a\\cb", "a\\\\cb"}, // literal \c → \\c (differs from escaped : → \c)
    +		// Mixed delimiters
    +		{"k|v:w\\x", "k\\pv\\cw\\\\x"},
    +		// Multiple consecutive
    +		{"||", "\\p\\p"},
    +		{"::", "\\c\\c"},
    +		{"\\\\", "\\\\\\\\"},
    +	}
    +
    +	for _, tt := range tests {
    +		t.Run(fmt.Sprintf("escape_%q", tt.input), func(t *testing.T) {
    +			t.Parallel()
    +			result := escapeKeyDelimiters(tt.input)
    +			require.Equal(t, tt.expected, result, "escapeKeyDelimiters(%q)", tt.input)
    +		})
    +	}
    +
    +	// Verify no collisions between pairs that would collide without backslash escaping
    +	collisionPairs := [][2]string{
    +		{"a\\pb", "a|b"}, // literal \p vs escaped |
    +		{"a\\cb", "a:b"}, // literal \c vs escaped :
    +		{"\\\\", "\\"},   // double backslash vs single
    +		{"x\\py", "x|y"},
    +	}
    +	for _, pair := range collisionPairs {
    +		t.Run(fmt.Sprintf("no_collision_%q_vs_%q", pair[0], pair[1]), func(t *testing.T) {
    +			t.Parallel()
    +			a := escapeKeyDelimiters(pair[0])
    +			b := escapeKeyDelimiters(pair[1])
    +			require.NotEqual(t, a, b, "escapeKeyDelimiters(%q) must differ from escapeKeyDelimiters(%q)", pair[0], pair[1])
    +		})
    +	}
    +}
    
  • middleware/cache/cache_test.go+112 0 modified
    @@ -964,6 +964,118 @@ func Test_Cache_Post(t *testing.T) {
     	require.Equal(t, "3:12345", string(body))
     }
     
    +func Test_Cache_CustomMethods(t *testing.T) {
    +	t.Parallel()
    +
    +	t.Run("POST cached when in Methods", func(t *testing.T) {
    +		t.Parallel()
    +		app := fiber.New()
    +		app.Use(New(Config{
    +			Methods: []string{fiber.MethodGet, fiber.MethodHead, fiber.MethodPost},
    +		}))
    +
    +		var count atomic.Int32
    +		app.Post("/", func(c fiber.Ctx) error {
    +			current := count.Add(1)
    +			return c.SendString(strconv.Itoa(int(current)))
    +		})
    +
    +		// First POST — cache miss
    +		resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/", http.NoBody))
    +		require.NoError(t, err)
    +		body, err := io.ReadAll(resp.Body)
    +		require.NoError(t, err)
    +		require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
    +		require.Equal(t, "1", string(body))
    +
    +		// Second POST — cache hit
    +		resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/", http.NoBody))
    +		require.NoError(t, err)
    +		body, err = io.ReadAll(resp.Body)
    +		require.NoError(t, err)
    +		require.Equal(t, cacheHit, resp.Header.Get("X-Cache"))
    +		require.Equal(t, "1", string(body))
    +	})
    +
    +	t.Run("unconfigured method bypasses cache", func(t *testing.T) {
    +		t.Parallel()
    +		app := fiber.New()
    +		app.Use(New(Config{
    +			Methods: []string{fiber.MethodGet},
    +		}))
    +
    +		var count atomic.Int32
    +		app.Put("/", func(c fiber.Ctx) error {
    +			current := count.Add(1)
    +			return c.SendString(strconv.Itoa(int(current)))
    +		})
    +
    +		// PUT not in Methods — always bypasses cache
    +		resp, err := app.Test(httptest.NewRequest(fiber.MethodPut, "/", http.NoBody))
    +		require.NoError(t, err)
    +		require.Equal(t, cacheUnreachable, resp.Header.Get("X-Cache"))
    +		require.Equal(t, int32(1), count.Load())
    +
    +		resp, err = app.Test(httptest.NewRequest(fiber.MethodPut, "/", http.NoBody))
    +		require.NoError(t, err)
    +		require.Equal(t, cacheUnreachable, resp.Header.Get("X-Cache"))
    +		require.Equal(t, int32(2), count.Load(), "handler must be called on every bypass")
    +	})
    +
    +	t.Run("empty Methods slice disables caching", func(t *testing.T) {
    +		t.Parallel()
    +		app := fiber.New()
    +		app.Use(New(Config{
    +			Methods: []string{},
    +		}))
    +
    +		var count atomic.Int32
    +		app.Get("/", func(c fiber.Ctx) error {
    +			current := count.Add(1)
    +			return c.SendString(strconv.Itoa(int(current)))
    +		})
    +
    +		// Even GET bypasses cache when Methods is explicitly empty
    +		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", http.NoBody))
    +		require.NoError(t, err)
    +		require.Equal(t, cacheUnreachable, resp.Header.Get("X-Cache"))
    +
    +		resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", http.NoBody))
    +		require.NoError(t, err)
    +		require.Equal(t, cacheUnreachable, resp.Header.Get("X-Cache"))
    +		require.Equal(t, int32(2), count.Load(), "handler must be called each time with empty Methods")
    +	})
    +
    +	t.Run("lowercase method names are normalized", func(t *testing.T) {
    +		t.Parallel()
    +		app := fiber.New()
    +		app.Use(New(Config{
    +			Methods: []string{"get", "post"},
    +		}))
    +
    +		var count atomic.Int32
    +		app.Get("/", func(c fiber.Ctx) error {
    +			current := count.Add(1)
    +			return c.SendString(strconv.Itoa(int(current)))
    +		})
    +
    +		// "get" should be normalized to "GET" and match
    +		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", http.NoBody))
    +		require.NoError(t, err)
    +		body, err := io.ReadAll(resp.Body)
    +		require.NoError(t, err)
    +		require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
    +		require.Equal(t, "1", string(body))
    +
    +		resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", http.NoBody))
    +		require.NoError(t, err)
    +		body, err = io.ReadAll(resp.Body)
    +		require.NoError(t, err)
    +		require.Equal(t, cacheHit, resp.Header.Get("X-Cache"))
    +		require.Equal(t, "1", string(body))
    +	})
    +}
    +
     func Test_Cache_DefaultKeyDimensions(t *testing.T) {
     	t.Parallel()
     
    
  • middleware/cache/config.go+20 0 modified
    @@ -2,6 +2,7 @@ package cache
     
     import (
     	"sort"
    +	"strings"
     	"time"
     
     	"github.com/gofiber/fiber/v3"
    @@ -67,6 +68,14 @@ type Config struct {
     	// Optional. Default: nil
     	KeyCookies []string
     
    +	// Methods specifies which HTTP methods are eligible for caching.
    +	// Requests with methods not in this list bypass the cache entirely.
    +	// Method names are normalized to uppercase automatically.
    +	// Set to nil to use the default; set to an empty slice to disable caching for all methods.
    +	//
    +	// Default: []string{fiber.MethodGet, fiber.MethodHead}
    +	Methods []string
    +
     	// Expiration is the time that a cached response will live
     	//
     	// Optional. Default: 5 * time.Minute
    @@ -118,6 +127,7 @@ var ConfigDefault = Config{
     	DisableQueryKeys:      false,
     	KeyHeaders:            []string{fiber.HeaderAccept, fiber.HeaderAcceptEncoding, fiber.HeaderAcceptLanguage},
     	KeyCookies:            nil,
    +	Methods:               []string{fiber.MethodGet, fiber.MethodHead},
     	DisableVaryHeaders:    false,
     	ExpirationGenerator:   nil,
     	StoreResponseHeaders:  false,
    @@ -155,6 +165,16 @@ func configDefault(config ...Config) Config {
     	}
     	cfg.KeyHeaders = normalizeHeaderDimensions(cfg.KeyHeaders, ConfigDefault.KeyHeaders)
     	cfg.KeyCookies = normalizeCookieDimensions(cfg.KeyCookies, nil)
    +	// nil = use default methods; explicit empty slice = cache no methods
    +	if cfg.Methods == nil {
    +		cfg.Methods = ConfigDefault.Methods
    +	} else {
    +		// Normalize method names to uppercase (HTTP methods are case-sensitive
    +		// and c.Method() returns uppercase, e.g. "GET" not "get")
    +		for i, m := range cfg.Methods {
    +			cfg.Methods[i] = strings.ToUpper(m)
    +		}
    +	}
     	if cfg.KeyGenerator == nil {
     		cfg.KeyGenerator = func(c fiber.Ctx) string {
     			return defaultKeyGenerator(c, &cfg)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.