Skip to content

Commit 0082672

Browse files
SNOW-1902886: Validate account input
1 parent b3126c0 commit 0082672

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

src/snowflake/connector/connection.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,12 @@
134134
from .telemetry import TelemetryClient, TelemetryData, TelemetryField
135135
from .time_util import HeartBeatTimer, get_time_millis
136136
from .url_util import extract_top_level_domain_from_hostname
137-
from .util_text import construct_hostname, parse_account, split_statements
137+
from .util_text import (
138+
construct_hostname,
139+
is_valid_account_identifier,
140+
parse_account,
141+
split_statements,
142+
)
138143
from .wif_util import AttestationProvider
139144

140145
if sys.version_info >= (3, 13) or typing.TYPE_CHECKING:
@@ -1743,8 +1748,20 @@ def __config(self, **kwargs):
17431748
ProgrammingError,
17441749
{"msg": "Account must be specified", "errno": ER_NO_ACCOUNT_NAME},
17451750
)
1746-
if self._account and "." in self._account:
1747-
self._account = parse_account(self._account)
1751+
if self._account:
1752+
# Allow legacy formats like "acc.region" to continue parsing into simple account id
1753+
if "." in self._account:
1754+
self._account = parse_account(self._account)
1755+
if not is_valid_account_identifier(self._account):
1756+
Error.errorhandler_wrapper(
1757+
self,
1758+
None,
1759+
ProgrammingError,
1760+
{
1761+
"msg": "Invalid account identifier: only letters, digits, '_' and '-' allowed; no dots or slashes",
1762+
"errno": ER_INVALID_VALUE,
1763+
},
1764+
)
17481765

17491766
if not isinstance(self._backoff_policy, Callable) or not isinstance(
17501767
self._backoff_policy(), Iterator

src/snowflake/connector/util_text.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,22 @@ def _is_china_region(r: str) -> bool:
254254
return host
255255

256256

257+
ACCOUNT_ID_VALIDATOR_RE = re.compile(r"^[A-Za-z0-9_-]+$")
258+
259+
260+
def is_valid_account_identifier(account: str) -> bool:
261+
"""Validate the Snowflake account identifier format.
262+
263+
The account identifier must be a single label (no dots or slashes) composed
264+
only of ASCII letters, digits, underscores, or hyphens.
265+
"""
266+
if not isinstance(account, str) or not account:
267+
return False
268+
if "." in account or "/" in account or "\\" in account:
269+
return False
270+
return bool(ACCOUNT_ID_VALIDATOR_RE.fullmatch(account))
271+
272+
257273
def parse_account(account):
258274
url_parts = account.split(".")
259275
# if this condition is true, then we have some extra

test/unit/test_parse_account.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#!/usr/bin/env python
22
from __future__ import annotations
33

4-
from snowflake.connector.util_text import parse_account
4+
import pytest
5+
6+
from snowflake.connector.util_text import is_valid_account_identifier, parse_account
57

68

79
def test_parse_account_basic():
@@ -12,3 +14,20 @@ def test_parse_account_basic():
1214
assert (
1315
parse_account("account1-jkabfvdjisoa778wqfgeruishafeuw89q.global") == "account1"
1416
)
17+
18+
19+
@pytest.mark.parametrize(
20+
"value,expected",
21+
[
22+
("abc", True),
23+
("ABC", True),
24+
("a_b-c1", True),
25+
("a.b", False),
26+
("a/b", False),
27+
("a\\b", False),
28+
("", False),
29+
("snowflakecomputing.com", False),
30+
],
31+
)
32+
def test_is_valid_account_identifier(value, expected):
33+
assert is_valid_account_identifier(value) is expected

0 commit comments

Comments
 (0)