When you call TemplateContext.Reset() in Scriban, you expect the context to return to a pristine state. It doesn't. The CachedTemplates dictionary survives the reset intact, silently accumulating compiled templates across what should be isolated execution contexts.

This matters because Scriban is frequently used in pooled or reused context scenarios—particularly in web frameworks—where multiple logical requests or users share a single context instance. When your code calls Reset() to enforce boundaries between requests or users, the cache violates that boundary without warning. A template compiled under User A's authorization context remains cached and serves User B's render request without re-evaluation.

The exploitability path requires a specific but common pattern: an ITemplateLoader implementation that varies its output based on runtime context—user identity, authorization tier, tenant isolation, or environment branching. The loader isn't triggered because the cache satisfies the lookup. The semantic gap is the vulnerability: Reset() promises isolation but the cache preserves state across boundaries.

The fix is straightforward—clear CachedTemplates on Reset(). But the deeper question is architectural. The cache is a shared global dictionary that Reset() has no authority over. This ownership mismatch means other cached state may also survive Reset() unexpectedly. Before deploying, audit what else your Reset() calls aren't clearing.