Skip to content

Commit 15dcb8a

Browse files
fix(spanner): escape embedded backticks in dbapi escape_name
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.
1 parent b7fa7df commit 15dcb8a

2 files changed

Lines changed: 21 additions & 4 deletions

File tree

packages/google-cloud-spanner/google/cloud/spanner_dbapi/parse_utils.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,15 +384,25 @@ def ensure_where_clause(sql):
384384

385385
def escape_name(name):
386386
"""
387-
Apply backticks to the name that either contain '-' or
388-
' ', or is a Cloud Spanner's reserved keyword.
387+
Apply backticks to the name if it is not a valid regular ASCII identifier,
388+
or if it is a Cloud Spanner's reserved keyword.
389389
390390
:type name: str
391391
:param name: Name to escape.
392392
393393
:rtype: str
394394
:returns: Name escaped if it has to be escaped.
395395
"""
396-
if "-" in name or " " in name or name.upper() in SPANNER_RESERVED_KEYWORDS:
397-
return "`" + name + "`"
396+
if not name:
397+
return name
398+
399+
is_valid_regular_identifier = (
400+
(name[0].isalpha() or name[0] == "_")
401+
and all(c.isalnum() or c == "_" for c in name)
402+
and name.isascii()
403+
)
404+
405+
if not is_valid_regular_identifier or name.upper() in SPANNER_RESERVED_KEYWORDS:
406+
return "`" + name.replace("`", "``") + "`"
407+
398408
return name

packages/google-cloud-spanner/tests/unit/spanner_dbapi/test_parse_utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,13 @@ def test_escape_name(self):
403403
("with space", "`with space`"),
404404
("name", "name"),
405405
("", ""),
406+
("col`; DROP TABLE t; -- x", "`col``; DROP TABLE t; -- x`"),
407+
("table`name", "`table``name`"),
408+
("`", "````"),
409+
("col/*comment*/name", "`col/*comment*/name`"),
410+
("123column", "`123column`"),
411+
("col;select", "`col;select`"),
412+
("col\nname", "`col\nname`"),
406413
)
407414
for name, want in cases:
408415
with self.subTest(name=name):

0 commit comments

Comments
 (0)