Skip to content

Commit 393aad1

Browse files
Aryamanz29claude
andcommitted
feat: allow extending sensitive path blocklist via env vars
Users can now extend the upload path blocklist without modifying the SDK: PYATLAN_SENSITIVE_SYSTEM_PREFIXES – additional path prefixes (e.g. /vault/,/secrets/) PYATLAN_SENSITIVE_DIR_NAMES – additional hidden dir names (e.g. .vault,.myconfig) PYATLAN_SENSITIVE_FILE_PREFIXES – additional file name prefixes (e.g. .credentials) Each env var accepts a comma-separated list merged with the built-in defaults at call time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bf487f1 commit 393aad1

2 files changed

Lines changed: 45 additions & 6 deletions

File tree

pyatlan/client/common/file.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# SPDX-License-Identifier: Apache-2.0
22
# Copyright 2025 Atlan Pte. Ltd.
3+
import os
34
from pathlib import Path
45
from typing import Any
56

6-
# System directories that must never be read from
7+
# System directories that must never be read from.
8+
# Extend via PYATLAN_SENSITIVE_SYSTEM_PREFIXES (comma-separated paths, e.g. "/vault/,/secrets/")
79
_SENSITIVE_SYSTEM_PREFIXES = (
810
"/etc/",
911
"/proc/",
@@ -14,12 +16,19 @@
1416
"/private/var/", # macOS
1517
)
1618

17-
# Hidden credential/config directories that must never be read from
19+
# Hidden credential/config directories that must never be read from.
20+
# Extend via PYATLAN_SENSITIVE_DIR_NAMES (comma-separated names, e.g. ".vault,.myconfig")
1821
_SENSITIVE_DIR_NAMES = frozenset({".aws", ".ssh", ".gnupg"})
1922

20-
# File name prefixes for environment/secret files
23+
# File name prefixes for environment/secret files.
24+
# Extend via PYATLAN_SENSITIVE_FILE_PREFIXES (comma-separated prefixes, e.g. ".secrets,.credentials")
2125
_SENSITIVE_FILE_PREFIXES = (".env",)
2226

27+
28+
def _parse_env_list(env_var: str) -> list:
29+
val = os.environ.get(env_var, "")
30+
return [p.strip() for p in val.split(",") if p.strip()] if val else []
31+
2332
from pyatlan.client.constants import (
2433
API,
2534
PRESIGNED_URL,
@@ -87,20 +96,30 @@ def validate_file_path(file_path: str) -> Any:
8796
resolved = path.resolve()
8897
resolved_str = str(resolved)
8998

99+
system_prefixes = _SENSITIVE_SYSTEM_PREFIXES + tuple(
100+
_parse_env_list("PYATLAN_SENSITIVE_SYSTEM_PREFIXES")
101+
)
102+
dir_names = _SENSITIVE_DIR_NAMES | frozenset(
103+
_parse_env_list("PYATLAN_SENSITIVE_DIR_NAMES")
104+
)
105+
file_prefixes = _SENSITIVE_FILE_PREFIXES + tuple(
106+
_parse_env_list("PYATLAN_SENSITIVE_FILE_PREFIXES")
107+
)
108+
90109
# Block sensitive system directories (e.g. /etc/, /proc/, /dev/)
91-
if resolved_str.startswith(_SENSITIVE_SYSTEM_PREFIXES):
110+
if resolved_str.startswith(system_prefixes):
92111
raise ErrorCode.INVALID_UPLOAD_FILE_PATH_SENSITIVE.exception_with_parameters(
93112
file_path
94113
)
95114

96115
# Block credential/config hidden directories (e.g. .aws, .ssh, .gnupg)
97-
if any(part in _SENSITIVE_DIR_NAMES for part in resolved.parts):
116+
if any(part in dir_names for part in resolved.parts):
98117
raise ErrorCode.INVALID_UPLOAD_FILE_PATH_SENSITIVE.exception_with_parameters(
99118
file_path
100119
)
101120

102121
# Block environment/secret files (e.g. .env, .env.local, .env.production)
103-
if resolved.name.startswith(_SENSITIVE_FILE_PREFIXES):
122+
if resolved.name.startswith(file_prefixes):
104123
raise ErrorCode.INVALID_UPLOAD_FILE_PATH_SENSITIVE.exception_with_parameters(
105124
file_path
106125
)

tests/unit/test_file_client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,26 @@ def test_file_client_upload_file_raises_invalid_request_error(
210210
)
211211

212212

213+
@pytest.mark.parametrize(
214+
"env_var, env_value, file_path",
215+
[
216+
("PYATLAN_SENSITIVE_SYSTEM_PREFIXES", "/custom/secrets/", "/custom/secrets/key"),
217+
("PYATLAN_SENSITIVE_DIR_NAMES", ".vault", "/home/user/.vault/token"),
218+
("PYATLAN_SENSITIVE_FILE_PREFIXES", ".credentials", "/app/.credentials.prod"),
219+
],
220+
)
221+
def test_file_client_upload_file_user_defined_sensitive_paths(
222+
monkeypatch, mock_api_caller, env_var, env_value, file_path
223+
):
224+
monkeypatch.setenv(env_var, env_value)
225+
client = FileClient(client=mock_api_caller)
226+
with pytest.raises(
227+
InvalidRequestError,
228+
match="ATLAN-PYTHON-400-078 Access to sensitive file path is not allowed",
229+
):
230+
client.upload_file(presigned_url="test-url", file_path=file_path)
231+
232+
213233
def test_file_client_download_file_invalid_format_raises_invalid_request_error(
214234
client, s3_presigned_url, mock_session_invalid
215235
):

0 commit comments

Comments
 (0)