fix(spanner): escape embedded backticks in dbapi escape_name#17810
fix(spanner): escape embedded backticks in dbapi escape_name#17810sakthivelmanii wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the escape_name function in parse_utils.py to escape backticks by doubling them, and adds corresponding unit tests. The review feedback points out a critical security vulnerability (SQL injection) where identifiers containing other special characters (such as comments or semicolons) but no spaces, hyphens, or backticks could bypass escaping. A code suggestion is provided to resolve this by strictly validating identifiers and escaping any that are not valid regular ASCII identifiers.
| if "-" in name or " " in name or "`" in name or name.upper() in SPANNER_RESERVED_KEYWORDS: | ||
| return "`" + name.replace("`", "``") + "`" | ||
| return name |
There was a problem hiding this comment.
While escaping backticks prevents direct breakout from backtick-quoted identifiers, the current check still allows other special characters (such as newlines \n, semicolons ;, or comments /**/) to pass through unescaped if the identifier does not happen to contain spaces, hyphens, or backticks. This can lead to SQL injection (CWE-89) or syntax errors.
For example, an identifier like col/**/union/**/select/**/password/**/from/**/users contains no spaces, hyphens, or backticks, so it would not be escaped, allowing an attacker to inject arbitrary SQL commands.
To fully secure escape_name, any identifier that is not a valid regular identifier (i.e., contains characters other than ASCII letters, numbers, and underscores, or starts with a digit) should be enclosed in backticks.
| if "-" in name or " " in name or "`" in name or name.upper() in SPANNER_RESERVED_KEYWORDS: | |
| return "`" + name.replace("`", "``") + "`" | |
| return name | |
| if not name: | |
| return name | |
| is_valid = ( | |
| (name[0].isalpha() or name[0] == "_") | |
| and all(c.isalnum() or c == "_" for c in name) | |
| and name.isascii() | |
| ) | |
| if not is_valid or name.upper() in SPANNER_RESERVED_KEYWORDS: | |
| backtick = chr(96) | |
| return backtick + name.replace(backtick, backtick + backtick) + backtick | |
| return name |
48a958a to
044851b
Compare
In google-cloud-spanner DB-API, escape_name() did not check for or double embedded backtick characters (`) when wrapping identifiers. This allowed identifiers containing backticks to break out of backtick-quoted identifier scopes (CWE-89 identifier injection).
This commit updates escape_name() to:
- Detect embedded backtick characters in identifier names.
- Escape internal backticks by doubling them (replace("`", "``")) when enclosing identifiers in backticks.
- Add unit test cases for embedded backticks in test_escape_name.
044851b to
15dcb8a
Compare
In google-cloud-spanner DB-API, escape_name() did not check for or double embedded backtick characters (`) when wrapping identifiers. This allowed identifiers containing backticks to break out of backtick-quoted identifier scopes (CWE-89 identifier injection).
This commit updates escape_name() to:
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Fixes #<issue_number_goes_here> 🦕