# Allowlist and denylist gaps in access control

Auth matchers, SQL denylists, or sandbox allowlists that miss routes, functions, or getters

_Area: Web access control and IDOR. Seen in 3 disclosed reports; latest 2026-06-08. Updated 2026-10-24._

## How it works
Access control here is enforced by enumerating what is dangerous and blocking it, or enumerating what is safe and permitting it. The assumption that breaks is completeness: the list is written once against the routes, functions, or methods that existed then, and silently stops covering the surface as features add new entries. Anything not named falls to the permissive side — a new route is public, an unnamed SQL function is callable, an unlisted getter is reachable.

## Where it shows up
- Framework middleware with an explicit route matcher (e.g. Next.js `matcher` in `src/proxy.js`) that lists a fixed set of paths [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md)
- Route trees that grew well beyond the matcher after a feature release (40+ routes vs 8 guarded) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md)
- Regex or name denylists over a SQL engine that exposes a family of I/O functions [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md)
- RBAC extraction that only parses `FROM`/`JOIN`, ignoring table functions in the `SELECT` list [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md)
- Sandbox allowlists (Twig `security.yaml`) permitting getter methods like `toArray()`/`all()` on injected config or container objects [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md)
- Full config or service objects injected into a sandboxed render scope [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md)

## How to review
The invariant: the guarded set must equal the reachable set, and the permitted set must contain no capability that reads or writes outside the caller's authority.
- Diff the auth matcher against the actual route tree; every route must be covered or explicitly public by design [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md)
- For SQL, enumerate the whole I/O function family rather than naming a few; confirm RBAC sees table references anywhere in the query, not just `FROM`/`JOIN` [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md)
- For sandboxes, list every permitted method on injected objects and ask what each returns; a getter that returns the whole config is equivalent to granting config access [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md)
- Prefer default-deny: new routes/functions/methods should be blocked until reviewed.

## Variants seen
- Route allowlist in middleware missing newly added API routes [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md)
- Function-name denylist missing sibling I/O functions in the same engine family [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md)
- Sandbox allowlist permitting a single getter that dumps all secrets [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md)

## Impact and severity
Severity ranged from unauthenticated RCE (plugin registration reaching `spawn()`) [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md), to authenticated local-file read and cloud metadata access [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md), to editor-role exfiltration of all plugin secrets [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md). Worse when the gap is unauthenticated [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md) or when one permitted method exposes the entire config [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md).

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-fhh6-4qxv-rpqj.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-p2j4-c4g6-rpf5.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-j274-39qw-32c9.md
