Skip to content

Commit e758f29

Browse files
committed
Redact CodeArtifact index from pip command logs
1 parent a3a20c7 commit e758f29

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

sagemaker-core/src/sagemaker/core/utils/install_requirements.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import re
4141
import subprocess
4242
import sys
43+
import tempfile
4344

4445
logger = logging.getLogger(__name__)
4546

@@ -111,6 +112,20 @@ def _login_awscli(region, account, domain, repo):
111112
)
112113

113114

115+
def _write_pip_config(index):
116+
"""Write the authenticated package index to a temporary pip config file."""
117+
config = tempfile.NamedTemporaryFile(
118+
mode="w", prefix="sagemaker-pip-", suffix=".conf", delete=False
119+
)
120+
try:
121+
config.write("[global]\n")
122+
config.write(f"index-url = {index}\n")
123+
finally:
124+
config.close()
125+
os.chmod(config.name, 0o600)
126+
return config.name
127+
128+
114129
def configure_pip(auth_method=CodeArtifactAuthMethod.AUTO):
115130
"""Configure pip for CodeArtifact if ``CA_REPOSITORY_ARN`` is set.
116131
@@ -183,10 +198,26 @@ def install_requirements(
183198
python_executable = python_executable or sys.executable
184199
pip_cmd = [python_executable, "-m", "pip", "install", "-r", requirements_file]
185200
index = configure_pip(auth_method=auth_method)
201+
env = None
202+
pip_config = None
186203
if index:
187-
pip_cmd.extend(["-i", index])
204+
pip_config = _write_pip_config(index)
205+
env = os.environ.copy()
206+
env["PIP_CONFIG_FILE"] = pip_config
188207
logger.info("Running: %s", " ".join(pip_cmd))
189-
subprocess.check_call(pip_cmd)
208+
try:
209+
if env is not None:
210+
subprocess.check_call(pip_cmd, env=env)
211+
else:
212+
subprocess.check_call(pip_cmd)
213+
finally:
214+
if pip_config:
215+
try:
216+
os.remove(pip_config)
217+
except FileNotFoundError:
218+
pass
219+
except OSError as e:
220+
logger.warning("Failed to remove temporary pip config file %s: %s", pip_config, e)
190221

191222

192223
def main():

sagemaker-core/tests/unit/test_install_requirements.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# language governing permissions and limitations under the License.
1313
from __future__ import absolute_import
1414

15+
import logging
16+
import os
1517
import subprocess
1618
import sys
1719
from unittest import mock
@@ -187,10 +189,34 @@ def test_without_codeartifact(self):
187189
mock_call.assert_called_once_with(_pip_cmd())
188190

189191
def test_with_codeartifact_index(self):
192+
captured = {}
193+
194+
def fake_check_call(cmd, env=None):
195+
captured["cmd"] = cmd
196+
captured["env"] = env
197+
with open(env["PIP_CONFIG_FILE"], "r") as config:
198+
captured["config"] = config.read()
199+
200+
with mock.patch(f"{_MODULE}.configure_pip", return_value=EXPECTED_INDEX):
201+
with mock.patch("subprocess.check_call", side_effect=fake_check_call):
202+
install_requirements("reqs.txt")
203+
204+
assert captured["cmd"] == _pip_cmd()
205+
assert captured["env"]["PIP_CONFIG_FILE"]
206+
assert EXPECTED_INDEX in captured["config"]
207+
assert not os.path.exists(captured["env"]["PIP_CONFIG_FILE"])
208+
209+
def test_codeartifact_index_not_logged_or_passed_in_argv(self, caplog):
210+
caplog.set_level(logging.INFO, logger=_MODULE)
190211
with mock.patch(f"{_MODULE}.configure_pip", return_value=EXPECTED_INDEX):
191212
with mock.patch("subprocess.check_call") as mock_call:
192213
install_requirements("reqs.txt")
193-
mock_call.assert_called_once_with(_pip_cmd("-i", EXPECTED_INDEX))
214+
215+
assert FAKE_TOKEN not in caplog.text
216+
assert EXPECTED_INDEX not in caplog.text
217+
argv = mock_call.call_args[0][0]
218+
assert FAKE_TOKEN not in " ".join(argv)
219+
assert EXPECTED_INDEX not in " ".join(argv)
194220

195221
def test_with_cli_fallback_no_index_flag(self):
196222
with mock.patch(f"{_MODULE}.configure_pip", return_value=None):

0 commit comments

Comments
 (0)