The SQL fragment to scan (a WHERE predicate, ORDER BY clause, etc.)
Candidate identifier names, matched case-insensitively
The subset of identifiers referenced, in their original casing, deduplicated.
Empty when expression is blank or nothing matches.
Finds which of the supplied identifier names are referenced anywhere in a SQL fragment.
Built for field-level security, where an
ExtraFilterorOrderBynaming a field the user cannot read must be rejected: output stripping alone is security theater, becauseExtraFilter: "Salary > 200000"reconstructs the values from the returned row set without the column ever appearing in a result.Deliberately conservative
String literals are NOT stripped,
[bracketed]/"quoted"/`backticked`identifiers match the same as bare ones, and a match inside a comment still counts. A false positive (a string literal that happens to contain a restricted field's name) rejects a legitimate query and is recoverable — the caller sees an error and rewrites. A false negative silently leaks the data the whole feature exists to protect. For a security gate that trade is not close.Direction matters, and getting it backwards is a silent leak
We search for each supplied NAME inside the fragment; we do NOT tokenize the fragment and look tokens up. Tokenizing forces an identifier character class (
[A-Z_][A-Z0-9_]*), and every field name outside that class then becomes unmatchable and silently permitted —Base Salary,Salary%, andSalárioall sailed through an earlier tokenizing version. Column names come from the database, so none of those shapes can be assumed away.Why there is no
dialectparameterNot an oversight, and not a barrier to adding Oracle or any other dialect. Each way dialects differ here is either already handled or provably safe:
[x], PostgreSQL/Oracle"x", MySQL`x`. Handled for all of them without knowing which: delimiters are not word characters, so the lookarounds below treat a quoted reference exactly like a bare one.A
dialectenum parameter would in fact be the thing that obstructs a new dialect: it forces every caller to thread a value through and forces this file to be revisited per dialect, in exchange for behavior that is already correct. If dialect-specific identifier handling is ever genuinely needed, the seam belongs on the SQLDialect driver (which already exposesPlatformKey/QuoteIdentifier) and the caller —ProviderBasealready knows its own platform — passes the normalized form in. That evolution needs no change here.KNOWN EDGE, recorded rather than hidden: a field name containing a quoting delimiter (a column literally named
Sal]aryorSal"ary) is escaped by doubling inside a quoted reference ([Sal]]ary],"Sal""ary"), which this literal search would not match. That IS dialect- specific and would need the driver seam above. It requires a column name containing a bracket or double quote, which CodeGen-generated MJ entities do not produce.