Skip to content

Commit 6abf112

Browse files
SNOW-2057503 allow only whitelisted schemes for OAuth url parameters
1 parent ce85800 commit 6abf112

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

src/snowflake/connector/connection.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
ER_INVALID_WIF_SETTINGS,
9595
ER_NO_ACCOUNT_NAME,
9696
ER_NO_CLIENT_ID,
97+
ER_NO_CLIENT_SECRET,
9798
ER_NO_NUMPY,
9899
ER_NO_PASSWORD,
99100
ER_NO_USER,
@@ -1211,7 +1212,7 @@ def __open_connection(self):
12111212
)
12121213
elif self._authenticator == OAUTH_AUTHORIZATION_CODE:
12131214
self._check_experimental_authentication_flag()
1214-
self._check_oauth_required_parameters()
1215+
self._check_oauth_parameters()
12151216
features = self.oauth_security_features
12161217
if self._role and (self._oauth_scope == ""):
12171218
# if role is known then let's inject it into scope
@@ -1239,7 +1240,7 @@ def __open_connection(self):
12391240
)
12401241
elif self._authenticator == OAUTH_CLIENT_CREDENTIALS:
12411242
self._check_experimental_authentication_flag()
1242-
self._check_oauth_required_parameters()
1243+
self._check_oauth_parameters()
12431244
features = self.oauth_security_features
12441245
if self._role and (self._oauth_scope == ""):
12451246
# if role is known then let's inject it into scope
@@ -2245,7 +2246,7 @@ def _check_experimental_authentication_flag(self) -> None:
22452246
},
22462247
)
22472248

2248-
def _check_oauth_required_parameters(self) -> None:
2249+
def _check_oauth_parameters(self) -> None:
22492250
if self._oauth_client_id is None:
22502251
Error.errorhandler_wrapper(
22512252
self,
@@ -2263,6 +2264,32 @@ def _check_oauth_required_parameters(self) -> None:
22632264
ProgrammingError,
22642265
{
22652266
"msg": "Oauth code flow requirement 'client_secret' is empty",
2266-
"errno": ER_NO_CLIENT_ID,
2267+
"errno": ER_NO_CLIENT_SECRET,
2268+
},
2269+
)
2270+
if (
2271+
self._oauth_authorization_url
2272+
and not self._oauth_authorization_url.startswith("https://")
2273+
):
2274+
Error.errorhandler_wrapper(
2275+
self,
2276+
None,
2277+
ProgrammingError,
2278+
{
2279+
"msg": "OAuth supports only authorization urls that use 'https' scheme",
2280+
"errno": ER_INVALID_VALUE,
2281+
},
2282+
)
2283+
if self._oauth_redirect_uri and not (
2284+
self._oauth_redirect_uri.startswith("http://")
2285+
or self._oauth_redirect_uri.startswith("https://")
2286+
):
2287+
Error.errorhandler_wrapper(
2288+
self,
2289+
None,
2290+
ProgrammingError,
2291+
{
2292+
"msg": "OAuth supports only authorization urls that use 'http(s)' scheme",
2293+
"errno": ER_INVALID_VALUE,
22672294
},
22682295
)

src/snowflake/connector/errorcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ER_INVALID_WIF_SETTINGS = 251017
3535
ER_WIF_CREDENTIALS_NOT_FOUND = 251018
3636
ER_EXPERIMENTAL_AUTHENTICATION_NOT_SUPPORTED = 251019
37+
ER_NO_CLIENT_SECRET = 251020
3738

3839
# cursor
3940
ER_FAILED_TO_REWRITE_MULTI_ROW_INSERT = 252001

test/unit/test_oauth_token.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ def remove(self, key: TokenKey) -> None:
119119
yield tmp_cache
120120

121121

122+
@pytest.fixture()
123+
def omit_oauth_urls_check():
124+
with mock.patch(
125+
"snowflake.connector.SnowflakeConnection._check_oauth_parameters",
126+
return_value=None,
127+
):
128+
yield
129+
130+
122131
@pytest.mark.skipolddriver
123132
@patch("snowflake.connector.auth._http_server.AuthHttpServer.DEFAULT_TIMEOUT", 30)
124133
def test_oauth_code_successful_flow(
@@ -127,6 +136,7 @@ def test_oauth_code_successful_flow(
127136
wiremock_generic_mappings_dir,
128137
webbrowser_mock,
129138
monkeypatch,
139+
omit_oauth_urls_check,
130140
) -> None:
131141
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
132142
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -169,6 +179,7 @@ def test_oauth_code_invalid_state(
169179
wiremock_oauth_authorization_code_dir,
170180
webbrowser_mock,
171181
monkeypatch,
182+
omit_oauth_urls_check,
172183
) -> None:
173184
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
174185
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -241,6 +252,7 @@ def test_oauth_code_token_request_error(
241252
wiremock_oauth_authorization_code_dir,
242253
webbrowser_mock,
243254
monkeypatch,
255+
omit_oauth_urls_check,
244256
) -> None:
245257
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
246258
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -279,6 +291,7 @@ def test_oauth_code_browser_timeout(
279291
wiremock_oauth_authorization_code_dir,
280292
webbrowser_mock,
281293
monkeypatch,
294+
omit_oauth_urls_check,
282295
) -> None:
283296
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
284297
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -320,6 +333,7 @@ def test_oauth_code_custom_urls(
320333
wiremock_generic_mappings_dir,
321334
webbrowser_mock,
322335
monkeypatch,
336+
omit_oauth_urls_check,
323337
) -> None:
324338
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
325339
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -363,6 +377,7 @@ def test_oauth_code_successful_refresh_token_flow(
363377
wiremock_generic_mappings_dir,
364378
monkeypatch,
365379
temp_cache,
380+
omit_oauth_urls_check,
366381
) -> None:
367382
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
368383
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -423,6 +438,7 @@ def test_oauth_code_expired_refresh_token_flow(
423438
webbrowser_mock,
424439
monkeypatch,
425440
temp_cache,
441+
omit_oauth_urls_check,
426442
) -> None:
427443
monkeypatch.setenv("SF_ENABLE_EXPERIMENTAL_AUTHENTICATION", "true")
428444
monkeypatch.setenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "true")
@@ -526,7 +542,6 @@ def test_client_creds_successful_flow(
526542
protocol="http",
527543
role="ANALYST",
528544
oauth_token_request_url=f"http://{wiremock_client.wiremock_host}:{wiremock_client.wiremock_http_port}/oauth/token-request",
529-
oauth_authorization_url=f"http://{wiremock_client.wiremock_host}:{wiremock_client.wiremock_http_port}/oauth/authorize",
530545
host=wiremock_client.wiremock_host,
531546
port=wiremock_client.wiremock_http_port,
532547
)

0 commit comments

Comments
 (0)