{"repro_id":"REPRO-2026-00157","version":8,"title":"Fiber v3: cache middleware key collision leaks responses across different query strings","repro_type":"security","status":"published","severity":"medium","cvss_score":6.5,"description":"The default `KeyGenerator` of Fiber v3's `cache` middleware returns only\n`c.Path()`. The cache key therefore ignores the query string and any other\nrequest component that distinguishes one user's request from another's.\n\nTwo requests to the same path with different query strings collide on the\ncache key: the second request returns the **first user's cached response**,\nleaking that response across users (cache confusion / information disclosure).","root_cause":"# RCA Report: CVE-2026-30246 — Fiber v3 Cache Middleware Key Collision\n\n## Summary\n\nIn `github.com/gofiber/fiber/v3` versions `<= 3.1.0`, the default `KeyGenerator` of the `cache` middleware returns only `c.Path()`, ignoring the query string and all other request dimensions. This causes cache key collisions for requests to the same path with different query parameters (e.g., `/?id=1` vs `/?id=2`). The second request receives the first request's cached response, leading to cross-user information disclosure and cache confusion (CWE-200 / CWE-524).\n\n## Impact\n\n- **Package**: `github.com/gofiber/fiber/v3` (Go module)\n- **Affected versions**: `<= 3.1.0`\n- **Fixed version**: `3.2.0`\n- **Severity**: Medium (CVSS 3.1 base 6.5)\n- **Consequences**: Any application using the default cache middleware without a custom `KeyGenerator` is vulnerable to cache confusion. Responses tailored to one user's query parameters may be leaked to another user requesting the same path with different parameters.\n\n## Root Cause\n\nThe vulnerable code in `middleware/cache/config.go` at v3.1.0 defines the default `KeyGenerator` as:\n\n```go\nKeyGenerator: func(c fiber.Ctx) string {\n    return utils.CopyString(c.Path())\n},\n```\n\nIn `middleware/cache/cache.go`, the cache lookup key is built as:\n\n```go\nbaseKey := cfg.KeyGenerator(c) + \"_\" + requestMethod\n```\n\nBecause `c.Path()` returns only the URL path without the query string, requests to `/?id=1` and `/?id=2` both resolve to the same cache key (`/_GET`). The second request therefore hits the cached entry from the first request and receives the wrong response body.\n\nThe fix in v3.2.0 (commits `9a0d12c07ed895b84c72987f9288b04137afe5de` and `050ff1ff18511c1475b8ec627460216aaec627460216aaecddd4e`) completely rewrites the default key generator (`defaultKeyGenerator`) to include:\n- HTTP method\n- Escaped path (preventing delimiter injection)\n- Canonical query string (sorted, with bounds to prevent DoS)\n- Selected representation headers (`Accept`, `Accept-Encoding`, `Accept-Language`)\n- Optional cookie dimensions\n\nThis ensures requests with different query parameters produce distinct cache keys, eliminating the collision.\n\n## Reproduction Steps\n\n1. Run `repro/reproduction_steps.sh`.\n2. The script creates a scratch Go module, installs Fiber v3.1.0 (vulnerable) and v3.2.0 (fixed) separately, and for each version:\n   - Builds a small Fiber server with the default `cache.New()` middleware.\n   - Registers a handler on `/` that returns the `id` query parameter.\n   - Issues `GET /?id=1` followed by `GET /?id=2`.\n   - Captures the two response bodies to `logs/vulnerable_output.txt` and `logs/fixed_output.txt`.\n3. **Expected evidence**:\n   - **Vulnerable (v3.1.0)**: both responses are `1` (the second request hits the cache entry from the first because the query string is ignored in the key).\n   - **Fixed (v3.2.0)**: responses are `1` then `2` (each request gets its own cache key because the query string is included).\n\n## Evidence\n\n- `logs/vulnerable_output.txt`:\n  ```\n  1\n  1\n  ```\n- `logs/fixed_output.txt`:\n  ```\n  1\n  2\n  ```\n- `logs/summary.txt`: contains the full run output with confirmation messages.\n- `repro/runtime_manifest.json`: structured evidence with request/response pairs and verdict.\n\n## Recommendations / Next Steps\n\n1. **Upgrade immediately** to `github.com/gofiber/fiber/v3@v3.2.0` or later.\n2. **If a custom `KeyGenerator` is in use**, audit it to ensure query strings, headers, or other user-specific dimensions are incorporated into the key.\n3. **Regression test**: add an integration test that issues two requests to the same path with different query parameters and asserts distinct response bodies when caching is enabled.\n\n## Additional Notes\n\n- **Idempotency**: `repro/reproduction_steps.sh` was executed twice consecutively with identical results, confirming idempotency.\n- **Edge cases**: The vulnerability is present regardless of the number or names of query parameters, as long as the path component remains identical. The default cache middleware only caches `GET` and `HEAD` requests, so other HTTP methods are not affected by this specific key collision.\n","ghsa_id":"GHSA-35hp-hqmv-8qg8","cve_id":"CVE-2026-30246","cwe_id":"CWE-436","package":{"name":"github.com/gofiber/fiber/v3","ecosystem":"go","affected_versions":"<= 3.1.0","fixed_version":"3.2.0","tested_patched":"v3.2.0"},"reproduced_at":"2026-05-23T06:41:54.495511+00:00","duration_secs":1631.1860671043396,"tool_calls":116,"turns":106,"handoffs":2,"total_cost_usd":0.8754828599999998,"agent_costs":{"repro":0.2257719599999999,"support":0.029792019999999995,"vuln_variant":0.61991888},"cost_breakdown":{"repro":{"accounts/fireworks/models/kimi-k2p6":0.2257719599999999},"support":{"accounts/fireworks/models/kimi-k2p6":0.029792019999999995},"vuln_variant":{"accounts/fireworks/models/kimi-k2p6":0.61991888}},"vulnerable_version_variant_outcome":"unknown","fix_bypass_outcome":"unknown","variant_disclosure_state":"unknown","quality":{"idempotent_verified":false,"community_verifications":0},"published_at":"2026-05-23T06:41:57.774642+00:00","retracted":false,"artifacts":[{"path":"repro/rca_report.md","filename":"rca_report.md","size":4144,"category":"analysis"},{"path":"repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":5043,"category":"reproduction_script"},{"path":"bundle/context.json","filename":"context.json","size":3039,"category":"other"},{"path":"bundle/metadata.json","filename":"metadata.json","size":741,"category":"other"},{"path":"bundle/ticket.md","filename":"ticket.md","size":3111,"category":"ticket"},{"path":"repro/runtime_manifest.json","filename":"runtime_manifest.json","size":456,"category":"other"},{"path":"repro/validation_verdict.json","filename":"validation_verdict.json","size":1001,"category":"other"},{"path":"logs/fixed_build.log","filename":"fixed_build.log","size":6228,"category":"log"},{"path":"logs/vulnerable_output.txt","filename":"vulnerable_output.txt","size":4,"category":"other"},{"path":"logs/fixed_output.txt","filename":"fixed_output.txt","size":4,"category":"other"},{"path":"logs/vulnerable_build.log","filename":"vulnerable_build.log","size":7044,"category":"log"},{"path":"logs/summary.txt","filename":"summary.txt","size":337,"category":"other"}]}