# Stale cache across context reset

Per-object permission or template results cached per session and reused after the parameter or tenant changes

_Area: Web access control and IDOR. Seen in 3 disclosed reports; one confirmed bounty: $900; latest 2026-07-21. Updated 2026-11-01._

## How it works
A check or compiled artifact is computed once with a per-object input (branch name, tenant, template path) but stored in a cache scoped to the session, request, or pooled context. Later uses reuse the cached result even though the input has changed or the context has been reset. The assumption that breaks: that a cache keyed by lifetime is equivalent to a cache keyed by the parameter. Reset/Clear routines that do not cover every cache field leave prior-tenant or prior-branch state live.

## Where it shows up
- A permission function taking a per-object argument whose result is stored in a boolean/field on the session or request object.
- Batch endpoints (multi-ref push, bulk APIs, loops over refs) that mutate the parameter between calls to the cached check.
- Reset()/Clear()/Dispose() that clears output, globals, cultures but not template or compiled-object caches.
- Pooled or reused context objects (template engines, request contexts) initialized once.
- Preview/screenshot URLs or tokens issued by a secondary renderer or CDN path not covered by the revocation logic.

## How to review
For each cache, identify its key and its lifetime. The invariant: the cache key must include every input that affects the result, or the cache must be cleared whenever any such input or the tenant/context changes. Trace Reset()/Clear() field by field against the struct's cache fields. For batch loops, confirm the check is re-evaluated per item or keyed by item. For revocation, enumerate every place a token or URL is emitted (HTML, JSON, CDN hosts) and confirm each is invalidated.

## Variants seen
- Branch-specific write check cached in a session boolean, reused across refs in one push [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-649p-mmhf-85c7.md).
- Template include cache surviving TemplateContext.Reset(), serving a prior tenant's template [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-x6m9-38vm-2xhf.md).
- A second preview URL from a different CDN host not expired by the fix for the first [E3](https://bugbountylibrary.com/r/h1%3A1489077.md).

## Why fixes fail
E3 shows a fix that expired only the token surfaced in the admin UI, leaving a second token in the same page response live; the bypass targeted the uncovered path.

## Impact and severity
Full repository write to protected branches [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-649p-mmhf-85c7.md); cross-tenant/admin template disclosure [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-x6m9-38vm-2xhf.md); permanent access to a password-protected store after password change and user removal [E3](https://bugbountylibrary.com/r/h1%3A1489077.md). Severity rises with pooling/reuse breadth and with tokens that never expire.

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-649p-mmhf-85c7.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-x6m9-38vm-2xhf.md
- E3: https://bugbountylibrary.com/r/h1%3A1489077.md
