Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
- Upcoming Release
- Fixed default `crl_download_max_size` to be 20MB instead of 200MB, as the previous value was set too high and could cause out-of-memory issues.
- Fixed a bug where Azure GET commands would incorrectly set the file status to UPLOADED instead of preserving the DOWNLOADED status during metadata retrieval.
- Renamed the environment variable for skipping config file permission warnings from `SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE` to `SF_SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION`. The old variable is still supported but emits a deprecation warning.

- v4.3.0(February 12,2026)
- Ensured proper list conversion - the converter runs to_snowflake on all list elements.
Expand Down
13 changes: 11 additions & 2 deletions src/snowflake/connector/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,21 @@
READABLE_BY_OTHERS = stat.S_IRGRP | stat.S_IROTH
WRITABLE_BY_OTHERS = stat.S_IWGRP | stat.S_IWOTH

SKIP_WARNING_ENV_VAR = "SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE"
DEPRECATED_SKIP_WARNING_ENV_VAR = "SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE"
SKIP_WARNING_ENV_VAR = "SF_SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION"


def _should_skip_warning_for_read_permissions_on_config_file() -> bool:
"""Check if the warning should be skipped based on environment variable."""
return os.getenv(SKIP_WARNING_ENV_VAR, "false").lower() == "true"
if SKIP_WARNING_ENV_VAR in os.environ:
return os.getenv(SKIP_WARNING_ENV_VAR, "false").lower() == "true"
# Else fallback to old value
if DEPRECATED_SKIP_WARNING_ENV_VAR in os.environ:
warn(
f"{DEPRECATED_SKIP_WARNING_ENV_VAR} is deprecated. Please use {SKIP_WARNING_ENV_VAR} instead."
)
return os.getenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "false").lower() == "true"
return False


class ConfigSliceOptions(NamedTuple):
Expand Down
110 changes: 105 additions & 5 deletions test/unit/test_configmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
try:
from snowflake.connector.config_manager import (
CONFIG_MANAGER,
DEPRECATED_SKIP_WARNING_ENV_VAR,
SKIP_WARNING_ENV_VAR,
ConfigManager,
ConfigOption,
ConfigSlice,
ConfigSliceOptions,
_should_skip_warning_for_read_permissions_on_config_file,
)
from snowflake.connector.errors import (
ConfigManagerError,
Expand Down Expand Up @@ -567,7 +570,7 @@ def test_warn_config_file_owner(tmp_path, monkeypatch):
assert (
str(c[0].message)
== f"Bad owner or permissions on {str(c_file)}"
+ 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'
+ 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_TOKEN_FILE_PERMISSIONS_VERIFICATION=true.\n'
)


Expand All @@ -587,7 +590,7 @@ def test_warn_config_file_permissions(tmp_path):
with warnings.catch_warnings(record=True) as c:
assert c1["b"] is True
assert len(c) == 1
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'
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_TOKEN_FILE_PERMISSIONS_VERIFICATION=true.\n'
assert (
str(c[0].message)
== f"Bad owner or permissions on {str(c_file)}" + chmod_message
Expand Down Expand Up @@ -709,11 +712,9 @@ def test_skip_warning_config_file_permissions(tmp_path, monkeypatch):
c_file.chmod(stat.S_IMODE(c_file.stat().st_mode) | stat.S_IROTH)

with monkeypatch.context() as m:
# Set environment variable to skip warning
m.setenv("SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE", "true")
m.setenv("SF_SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION", "true")
with warnings.catch_warnings(record=True) as c:
assert c1["b"] is True
# Should have no warnings when skip is enabled
assert len(c) == 0


Expand Down Expand Up @@ -837,3 +838,102 @@ def test_defaultconnectionname(tmp_path, monkeypatch):
finally:
CONFIG_MANAGER.file_path = old_path
CONFIG_MANAGER.conf_file_cache = None


class TestShouldSkipWarningForReadPermissions:
"""Tests for _should_skip_warning_for_read_permissions_on_config_file
with the new/deprecated env var logic."""

def test_returns_false_when_no_env_vars_set(self, monkeypatch):
monkeypatch.delenv(SKIP_WARNING_ENV_VAR, raising=False)
monkeypatch.delenv(DEPRECATED_SKIP_WARNING_ENV_VAR, raising=False)
assert _should_skip_warning_for_read_permissions_on_config_file() is False

def test_new_env_var_true(self, monkeypatch):
monkeypatch.setenv(SKIP_WARNING_ENV_VAR, "true")
monkeypatch.delenv(DEPRECATED_SKIP_WARNING_ENV_VAR, raising=False)
assert _should_skip_warning_for_read_permissions_on_config_file() is True

def test_new_env_var_false(self, monkeypatch):
monkeypatch.setenv(SKIP_WARNING_ENV_VAR, "false")
monkeypatch.delenv(DEPRECATED_SKIP_WARNING_ENV_VAR, raising=False)
assert _should_skip_warning_for_read_permissions_on_config_file() is False

def test_new_env_var_case_insensitive(self, monkeypatch):
monkeypatch.delenv(DEPRECATED_SKIP_WARNING_ENV_VAR, raising=False)
for value in ("True", "TRUE", "TrUe"):
monkeypatch.setenv(SKIP_WARNING_ENV_VAR, value)
assert _should_skip_warning_for_read_permissions_on_config_file() is True

def test_deprecated_env_var_true_emits_warning(self, monkeypatch):
monkeypatch.delenv(SKIP_WARNING_ENV_VAR, raising=False)
monkeypatch.setenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "true")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
result = _should_skip_warning_for_read_permissions_on_config_file()
assert result is True
assert len(w) == 1
assert (
f"{DEPRECATED_SKIP_WARNING_ENV_VAR} is deprecated. "
f"Please use {SKIP_WARNING_ENV_VAR} instead." in str(w[0].message)
)

def test_deprecated_env_var_false_emits_warning(self, monkeypatch):
monkeypatch.delenv(SKIP_WARNING_ENV_VAR, raising=False)
monkeypatch.setenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "false")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
result = _should_skip_warning_for_read_permissions_on_config_file()
assert result is False
assert len(w) == 1
assert DEPRECATED_SKIP_WARNING_ENV_VAR in str(w[0].message)

def test_new_env_var_takes_precedence_over_deprecated(self, monkeypatch):
monkeypatch.setenv(SKIP_WARNING_ENV_VAR, "true")
monkeypatch.setenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "false")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
result = _should_skip_warning_for_read_permissions_on_config_file()
assert result is True
assert len(w) == 0

def test_new_env_var_false_takes_precedence_over_deprecated_true(self, monkeypatch):
monkeypatch.setenv(SKIP_WARNING_ENV_VAR, "false")
monkeypatch.setenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "true")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
result = _should_skip_warning_for_read_permissions_on_config_file()
assert result is False
assert len(w) == 0


@pytest.mark.skipif(IS_WINDOWS, reason="chmod doesn't work on Windows")
def test_skip_warning_with_deprecated_env_var_emits_deprecation(tmp_path, monkeypatch):
"""Using the deprecated env var still suppresses the permissions warning
but also emits a deprecation warning."""
c_file = tmp_path / "config.toml"
c1 = ConfigManager(file_path=c_file, name="root_parser")
c1.add_option(name="b", parse_str=lambda e: e.lower() == "true")
c_file.write_text(
dedent(
"""\
b = true
"""
)
)
c_file.chmod(stat.S_IMODE(c_file.stat().st_mode) | stat.S_IROTH)

with monkeypatch.context() as m:
m.delenv(SKIP_WARNING_ENV_VAR, raising=False)
m.setenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "true")
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
assert c1["b"] is True
deprecation_warnings = [
x for x in w if DEPRECATED_SKIP_WARNING_ENV_VAR in str(x.message)
]
assert len(deprecation_warnings) == 1
permission_warnings = [
x for x in w if "Bad owner or permissions" in str(x.message)
]
assert len(permission_warnings) == 0
Loading