# Regex blocklist bypass to privileged sink

Safety enforced only by regex denylist over SQL, GraphQL, or Python, with no real privilege separation

_Area: AI and LLM app security. Seen in 4 disclosed reports; latest 2026-10-07. Updated 2026-10-24._

## How it works
A security control decides "is this input dangerous?" by matching a regex denylist against a language (SQL, GraphQL, Python). The assumption that breaks: that the denylist enumerates every dangerous form. Languages have insignificant tokens (GraphQL commas, SQL comments, whitespace), dynamic invocation primitives (EXEC @var, sp_executesql, importlib, chr()), and pre-imported library surfaces (pandas read_*). Any of these slips past the pattern and reaches a sink that runs with the caller's real privileges.

## Where it shows up
- A validator function named like `validate*Code`, `*AccessChecker`, or a "read-only mode" flag implemented as a list of regexes.
- Anchors on `^`, `;`, or `}` in the pattern, with a separate comment/string stripper that leaves other tokens intact.
- Pre-imported modules (pd, np) or dynamic dispatch (EXEC, sp_executesql, importlib, getattr) reachable from the sandbox.
- Secondary tools that skip the classifier entirely (e.g. backup/restore helpers).
- Agent executes with the connected user's privileges; no separate low-privileged connection.

## How to review
The invariant: the sink must be unreachable regardless of input text. Check that (1) the operation runs under a genuinely restricted principal (read-only DB role, no network egress, no filesystem), not just a text filter; (2) every tool in the agent's toolset passes through the same enforcement, including secondary tools; (3) the denylist, if it exists, is defense-in-depth only. For regexes over a language, test every insignificant separator as a prefix and every dynamic-invocation primitive the runtime exposes.

## Variants seen
- SQL: dynamic EXEC plus AGENTS.md prompt injection escalated db_owner to sysadmin; read-only enforced only by regex classifier [E1](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2026-from-select-to-sysadmin-sql-copilot-bluehat-asia.md).
- Python: 38-pattern blocklist missed pandas read_json/read_csv/read_html/read_fwf, np.ctypeslib, chr(); `\bimport\b` missed importlib, giving SSRF and data exfiltration [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7x8-q2gp-5cgg.md).
- GraphQL: write detector anchored on `^`/`;`/`}`, but commas are insignificant, so `,mutation{...}` passed as read-only; allow-list also unenforced [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-5648-rgj9-v224.md).

## Why fixes fail
Adding patterns to the blocklist treats symptoms; each new primitive (a new pandas reader, a new separator) reopens the gap. E2's blocklist already had 38 patterns and still missed the pre-imported API surface [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7x8-q2gp-5cgg.md).

## Impact and severity
Privilege escalation to sysadmin [E1](https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2026-from-select-to-sysadmin-sql-copilot-bluehat-asia.md), unauthenticated RCE/SSRF and dataset exfiltration [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-w7x8-q2gp-5cgg.md), arbitrary writes via prompt-injected agents [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-5648-rgj9-v224.md). Severity is high because the control is the only barrier and the agent holds the user's privileges.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Aembracethered-com-blog-posts-2026-from-select-to-sysadmin-sql-copilot-bluehat-asia.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-w7x8-q2gp-5cgg.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-5648-rgj9-v224.md
