GHSA-q8r4-6gpp-5fx2
QueryGuard ignores MySQL executable comments, bypassing table allow-lists and PII column protection
Impact
QueryGuard in qwed-finance parses SQL without a dialect, and the parser treats MySQL executable comments (/*! ... */ and /*!NNNNN ... */) as ordinary comments. MySQL and MariaDB execute the contents of these comments. Table references and columns placed inside an executable comment are therefore invisible to the table allow-list and to PII column enforcement, while the database still reads them.
Reproduced on v3.0.0:
| Call | Result |
|---|---|
QueryGuard().verify_table_access("SELECT a FROM t1 /*!50000 , t2 */", {"t1"}) |
safe=True, tables ['t1'], no violations (MySQL reads t2) |
CrossGuard().verify_query_with_pii_protection("SELECT a /*!50000 , ssn */ FROM t1", ["t1"], ["ssn"]) |
passed=True, no violations (MySQL projects ssn) |
QueryGuard().sanitize_query("SELECT a FROM t1 /*!50000 , t2 */") |
safe=True, sanitized_query returned unchanged, including the executable comment |
Who is affected: applications that execute LLM-generated SQL against MySQL or MariaDB and rely on QueryGuard / CrossGuard for table allow-listing or PII column protection. Other database engines do not execute these comments.
Root cause
query_guard.py calls sqlglot.parse_one(sql_query) with no dialect and drops comments, and sanitize_query returns the original input string instead of a re-serialized form of what was analysed. The analysed AST and the executed statement differ.
Patches
Fixed in qwed-finance 3.0.1 (release). Upgrade with pip install -U qwed-finance==3.0.1.
Any query containing a MySQL /*! ... */ or MariaDB /*M! ... */ executable comment is now unsafe in verify_readonly_safety, and therefore in verify_table_access, verify_column_access, sanitize_query and CrossGuard.verify_query_with_pii_protection. sanitize_query no longer rewrites such queries. Ordinary comments and optimizer hints (/*+ ... */) are unaffected.
Workarounds
- Reject any query containing
/*!before verification when the target is MySQL or MariaDB. - Execute the re-serialized AST rather than the original string, and use database-level permissions (a read-only role with access only to permitted tables and columns) as the actual enforcement boundary.
References
Reproduced on qwed-finance 3.0.0 (bb0cdf3).