Skip to content

Commit a019ab2

Browse files
sfc-gh-gmerticariusfc-gh-mmishchenko
authored andcommitted
SNOW-2161716: Fix config file permissions check and skip warning using env variable (#2488)
Co-authored-by: Maxim Mishchenko <[email protected]>
1 parent 6cef5bb commit a019ab2

File tree

3 files changed

+109
-14
lines changed

3 files changed

+109
-14
lines changed

DESCRIPTION.md

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,82 @@ https://docs.snowflake.com/
77
Source code is also available at: https://github.com/snowflakedb/snowflake-connector-python
88

99
# Release Notes
10-
- v3.14.1(TBD)
10+
- v3.18(TBD)
11+
- Enhanced configuration file permission warning messages.
12+
- Improved warning messages for readable permission issues to include clear instructions on how to skip warnings using the `SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE` environment variable.
13+
14+
- v3.17.2(August 23,2025)
15+
- Fixed a bug where platform_detection was retrying failed requests with warnings to non-existent endpoints.
16+
- Added disabling endpoint-based platform detection by setting `platform_detection_timeout_seconds` to zero.
17+
18+
- v3.17.1(August 17,2025)
19+
- Added `infer_schema` parameter to `write_pandas` to perform schema inference on the passed data.
20+
- Namespace `snowlake` reverted back to non-module.
21+
22+
- v3.17.0(August 16,2025)
23+
- Added in-band HTTP exception telemetry.
24+
- Added an `unsafe_skip_file_permissions_check` flag to skip file permission checks on the cache and configuration.
25+
- Added `APPLICATION_PATH` within `CLIENT_ENVIRONMENT` to distinguish between multiple scripts using the Python Connector in the same environment.
26+
- Added basic JSON support for Interval types.
27+
- Added in-band OCSP exception telemetry.
28+
- Added support for new authentication methods with Workload Identity Federation (WIF).
29+
- Added the `WORKLOAD_IDENTITY` value for the authenticator type.
30+
- Added the `workload_identity_provider` and `workload_identity_entra_resource` parameters.
31+
- Added support for the `use_vectorized_scanner` parameter in the write_pandas function.
32+
- Added support of proxy setup using connection parameters without emitting environment variables.
33+
- Added populating of `type_code` in `ResultMetadata` for interval types.
34+
- Introduced the `snowflake_version` property to the connection.
35+
- Moved `OAUTH_TYPE` to `CLIENT_ENVIROMENT`.
36+
- Relaxed the `pyarrow` version constrain; versions >= 19 can now be used.
37+
- Disabled token caching for OAuth Client Credentials authentication.
38+
- Fixed OAuth authenticator values.
39+
- Fixed a bug where a PAT with an external session authenticator was used while `external_session_id` was not provided in `SnowflakeRestful.fetch`.
40+
- Fixed the case-sensitivity of `Oauth` and `programmatic_access_token` authenticator values.
41+
- Fixed unclear error messages for incorrect `authenticator` values.
42+
- Fixed GCS staging by ensuring the endpoint has a scheme.
43+
- Fixed a bug where time-zoned timestamps fetched as a `pandas.DataFrame` or `pyarrow.Table` would overflow due to unnecessary precision. A clear error will now be raised if an overflow cannot be prevented.
44+
45+
- v3.16.0(July 04,2025)
46+
- Bumped numpy dependency from <2.1.0 to <=2.2.4.
47+
- Added Windows support for Python 3.13.
48+
- Added `bulk_upload_chunks` parameter to `write_pandas` function. Setting this parameter to True changes the behaviour of write_pandas function to first write all the data chunks to the local disk and then perform the wildcard upload of the chunks folder to the stage. In default behaviour the chunks are being saved, uploaded and deleted one by one.
49+
- Added support for new authentication mechanism PAT with external session ID.
50+
- Added `client_fetch_use_mp` parameter that enables multiprocessed fetching of result batches.
51+
- Added basic arrow support for Interval types.
52+
- Fixed `write_pandas` special characters usage in the location name.
53+
- Fixed usage of `use_virtual_url` when building the location for gcs storage client.
54+
- Added support for Snowflake OAuth for local applications.
55+
56+
- v3.15.0(Apr 29,2025)
57+
- Bumped up min boto and botocore version to 1.24.
58+
- OCSP: terminate certificates chain traversal if a trusted certificate already reached.
59+
- Added new authentication methods support for programmatic access tokens (PATs), OAuth 2.0 Authorization Code Flow, OAuth 2.0 Client Credentials Flow, and OAuth Token caching.
60+
- For OAuth 2.0 Authorization Code Flow:
61+
- Added the `oauth_client_id`, `oauth_client_secret`, `oauth_authorization_url`, `oauth_token_request_url`, `oauth_redirect_uri`, `oauth_scope`, `oauth_disable_pkce`, `oauth_enable_refresh_tokens` and `oauth_enable_single_use_refresh_tokens` parameters.
62+
- Added the `OAUTH_AUTHORIZATION_CODE` value for the parameter authenticator.
63+
- For OAuth 2.0 Client Credentials Flow:
64+
- Added the `oauth_client_id`, `oauth_client_secret`, `oauth_token_request_url`, and `oauth_scope` parameters.
65+
- Added the `OAUTH_CLIENT_CREDENTIALS` value for the parameter authenticator.
66+
- For OAuth Token caching: Passing a username to driver configuration is required, and the `client_store_temporary_credential property` is to be set to `true`.
67+
68+
- v3.14.1(April 21, 2025)
1169
- Added support for Python 3.13.
1270
- NOTE: Windows 64 support is still experimental and should not yet be used for production environments.
1371
- Dropped support for Python 3.8.
14-
- Basic decimal floating-point type support.
15-
- Added handling of PAT provided in `password` field.
16-
- Added experimental support for OAuth authorization code and client credentials flows.
17-
- Improved error message for client-side query cancellations due to timeouts.
72+
- Added basic decimal floating-point type support.
73+
- Added experimental authentication methods.
1874
- Added support of GCS regional endpoints.
19-
- Added `gcs_use_virtual_endpoints` connection property that forces the usage of the virtual GCS usage. Thanks to this it should be possible to set up private DNS entry for the GCS endpoint. See more: https://cloud.google.com/storage/docs/request-endpoints#xml-api
20-
- Fixed a bug that caused driver to fail silently on `TO_DATE` arrow to python conversion when invalid date was followed by the correct one.
75+
- Added support of GCS virtual urls. See more: https://cloud.google.com/storage/docs/request-endpoints#xml-api
76+
- Added `client_fetch_threads` experimental parameter to better utilize threads for fetching query results.
2177
- Added `check_arrow_conversion_error_on_every_column` connection property that can be set to `False` to restore previous behaviour in which driver will ignore errors until it occurs in the last column. This flag's purpose is to unblock workflows that may be impacted by the bugfix and will be removed in later releases.
22-
- Lower log levels from info to debug for some of the messages to make the output easier to follow.
23-
- Allow the connector to inherit a UUID4 generated upstream, provided in statement parameters (field: `requestId`), rather than automatically generate a UUID4 to use for the HTTP Request ID.
78+
- Lowered log levels from info to debug for some of the messages to make the output easier to follow.
79+
- Allowed the connector to inherit a UUID4 generated upstream, provided in statement parameters (field: `requestId`), rather than automatically generate a UUID4 to use for the HTTP Request ID.
80+
- Improved logging in urllib3, boto3, botocore - assured data masking even after migration to the external owned library in the future.
81+
- Improved error message for client-side query cancellations due to timeouts.
82+
- Improved security and robustness for the temporary credentials cache storage.
83+
- Fixed a bug that caused driver to fail silently on `TO_DATE` arrow to python conversion when invalid date was followed by the correct one.
84+
- Fixed expired S3 credentials update and increment retry when expired credentials are found.
85+
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.
2486

2587
- v3.14.0(March 03, 2025)
2688
- Bumped pyOpenSSL dependency upper boundary from <25.0.0 to <26.0.0.
@@ -32,7 +94,6 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
3294
- Fixed a bug where file permission check happened on Windows.
3395
- Added support for File types.
3496
- Added `unsafe_file_write` connection parameter that restores the previous behaviour of saving files downloaded with GET with 644 permissions.
35-
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.
3697

3798
- v3.13.2(January 29, 2025)
3899
- Changed not to use scoped temporary objects.

src/snowflake/connector/config_manager.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
READABLE_BY_OTHERS = stat.S_IRGRP | stat.S_IROTH
3030

3131

32+
SKIP_WARNING_ENV_VAR = "SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE"
33+
34+
35+
def _should_skip_warning_for_read_permissions_on_config_file() -> bool:
36+
"""Check if the warning should be skipped based on environment variable."""
37+
return os.getenv(SKIP_WARNING_ENV_VAR, "false").lower() == "true"
38+
39+
3240
class ConfigSliceOptions(NamedTuple):
3341
"""Class that defines settings individual configuration files."""
3442

@@ -329,6 +337,7 @@ def read_config(
329337
)
330338
continue
331339

340+
# Check for readable by others or wrong ownership - this should warn
332341
if (
333342
not IS_WINDOWS # Skip checking on Windows
334343
and sliceoptions.check_permissions # Skip checking if this file couldn't hold sensitive information
@@ -342,9 +351,10 @@ def read_config(
342351
and filep.stat().st_uid != os.getuid()
343352
)
344353
):
345-
chmod_message = f'.\n * To change owner, run `chown $USER "{str(filep)}"`.\n * To restrict permissions, run `chmod 0600 "{str(filep)}"`.\n'
354+
chmod_message = f'.\n * To change owner, run `chown $USER "{str(filep)}"`.\n * To restrict permissions, run `chmod 0600 "{str(filep)}"`.\n * To skip this warning, set environment variable {SKIP_WARNING_ENV_VAR}=true.\n'
346355

347-
warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
356+
if not _should_skip_warning_for_read_permissions_on_config_file():
357+
warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
348358
LOGGER.debug(f"reading configuration file from {str(filep)}")
349359
try:
350360
read_config_piece = tomlkit.parse(filep.read_text())

test/unit/test_configmanager.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ def test_warn_config_file_owner(tmp_path, monkeypatch):
567567
assert (
568568
str(c[0].message)
569569
== f"Bad owner or permissions on {str(c_file)}"
570-
+ f'.\n * To change owner, run `chown $USER "{str(c_file)}"`.\n * To restrict permissions, run `chmod 0600 "{str(c_file)}"`.\n'
570+
+ f'.\n * To change owner, run `chown $USER "{str(c_file)}"`.\n * To restrict permissions, run `chmod 0600 "{str(c_file)}"`.\n * To skip this warning, set environment variable SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE=true.\n'
571571
)
572572

573573

@@ -587,7 +587,7 @@ def test_warn_config_file_permissions(tmp_path):
587587
with warnings.catch_warnings(record=True) as c:
588588
assert c1["b"] is True
589589
assert len(c) == 1
590-
chmod_message = f'.\n * To change owner, run `chown $USER "{str(c_file)}"`.\n * To restrict permissions, run `chmod 0600 "{str(c_file)}"`.\n'
590+
chmod_message = f'.\n * To change owner, run `chown $USER "{str(c_file)}"`.\n * To restrict permissions, run `chmod 0600 "{str(c_file)}"`.\n * To skip this warning, set environment variable SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE=true.\n'
591591
assert (
592592
str(c[0].message)
593593
== f"Bad owner or permissions on {str(c_file)}" + chmod_message
@@ -639,6 +639,30 @@ def test_log_debug_config_file_parent_dir_permissions(tmp_path, caplog):
639639
shutil.rmtree(tmp_dir)
640640

641641

642+
@pytest.mark.skipif(IS_WINDOWS, reason="chmod doesn't work on Windows")
643+
def test_skip_warning_config_file_permissions(tmp_path, monkeypatch):
644+
c_file = tmp_path / "config.toml"
645+
c1 = ConfigManager(file_path=c_file, name="root_parser")
646+
c1.add_option(name="b", parse_str=lambda e: e.lower() == "true")
647+
c_file.write_text(
648+
dedent(
649+
"""\
650+
b = true
651+
"""
652+
)
653+
)
654+
# Make file readable by others (would normally trigger warning)
655+
c_file.chmod(stat.S_IMODE(c_file.stat().st_mode) | stat.S_IROTH)
656+
657+
with monkeypatch.context() as m:
658+
# Set environment variable to skip warning
659+
m.setenv("SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE", "true")
660+
with warnings.catch_warnings(record=True) as c:
661+
assert c1["b"] is True
662+
# Should have no warnings when skip is enabled
663+
assert len(c) == 0
664+
665+
642666
def test_configoption_missing_root_manager():
643667
with pytest.raises(
644668
TypeError,

0 commit comments

Comments
 (0)