From 7030ca85eaa36b4b93c8da125560c74634b4418f Mon Sep 17 00:00:00 2001 From: Ayushh Garg Date: Tue, 5 May 2026 12:55:36 +0530 Subject: [PATCH] Fix path traversal vulnerability in artifact tool zip extraction --- .../azure/ai/ml/_utils/_artifact_utils.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sdk/ml/azure-ai-ml/azure/ai/ml/_utils/_artifact_utils.py b/sdk/ml/azure-ai-ml/azure/ai/ml/_utils/_artifact_utils.py index 9b00040ef19c..249fefaae112 100644 --- a/sdk/ml/azure-ai-ml/azure/ai/ml/_utils/_artifact_utils.py +++ b/sdk/ml/azure-ai-ml/azure/ai/ml/_utils/_artifact_utils.py @@ -200,13 +200,23 @@ def _redirect_artifacts_tool_path(self, organization: Optional[str]): url, headers=header ) if response.status_code == 200: - artifacts_tool_path = tempfile.mkdtemp() # nosec B306 + artifacts_tool_path = Path(tempfile.mkdtemp()) # nosec B306 artifacts_tool_uri = response.json()["uri"] response = requests_pipeline.get(artifacts_tool_uri) # pylint: disable=too-many-function-args + resolved_dest = artifacts_tool_path.resolve() with zipfile.ZipFile(BytesIO(response.content)) as zip_file: - zip_file.extractall(artifacts_tool_path) - os.environ["AZURE_DEVOPS_EXT_ARTIFACTTOOL_OVERRIDE_PATH"] = str(artifacts_tool_path.resolve()) - self._artifacts_tool_path = artifacts_tool_path + for member in zip_file.namelist(): + member_path = (resolved_dest / member).resolve() + if member_path != resolved_dest and not str(member_path).startswith( + str(resolved_dest) + os.sep + ): + raise RuntimeError( + f"Artifact tool archive contains a path traversal entry and cannot be " + f"extracted safely: {member}" + ) + zip_file.extractall(resolved_dest) + os.environ["AZURE_DEVOPS_EXT_ARTIFACTTOOL_OVERRIDE_PATH"] = str(resolved_dest) + self._artifacts_tool_path = resolved_dest else: _logger.warning("Download artifact tool failed: %s", response.text)