Skip to content

Commit 3f193ba

Browse files
fix: Resolve CodeQL issues to avoid unsafe use of DefaultAzureCredential in python application.
2 parents cdf62bd + cf8dfc7 commit 3f193ba

9 files changed

Lines changed: 55 additions & 34 deletions

File tree

src/ContentProcessor/src/libs/utils/azure_credential_utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ def get_azure_credential():
130130
logging.info(
131131
"[AUTH] All CLI credentials failed - falling back to DefaultAzureCredential"
132132
)
133-
return DefaultAzureCredential()
133+
raise RuntimeError(
134+
"No Azure authentication available. "
135+
"Use Managed Identity in Azure or run "
136+
"'az login' / 'azd auth login' locally."
137+
)
134138

135139

136140
def get_async_azure_credential():

src/ContentProcessor/src/libs/utils/credential_util.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,11 @@ def get_azure_credential():
130130
logging.info(
131131
"[AUTH] All CLI credentials failed - falling back to DefaultAzureCredential"
132132
)
133-
return DefaultAzureCredential()
133+
raise RuntimeError(
134+
"No Azure authentication available. "
135+
"Use Managed Identity in Azure or run "
136+
"'az login' / 'azd auth login' locally."
137+
)
134138

135139

136140
def get_async_azure_credential():

src/ContentProcessorAPI/app/libs/base/application_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import os
1616
from abc import ABC, abstractmethod
1717

18-
from azure.identity import DefaultAzureCredential
18+
from app.utils.azure_credential_utils import get_azure_credential
1919
from dotenv import load_dotenv
2020

2121
from app.libs.application.application_configuration import (
@@ -72,7 +72,7 @@ def __init__(self, env_file_path: str | None = None, **data):
7272
self._load_env(env_file_path=env_file_path)
7373

7474
self.application_context = AppContext()
75-
self.application_context.set_credential(DefaultAzureCredential())
75+
self.application_context.set_credential(get_azure_credential())
7676

7777
app_config_endpoint: str | None = EnvConfiguration().app_config_endpoint
7878
if app_config_endpoint != "" and app_config_endpoint is not None:

src/ContentProcessorWorkflow/src/libs/azure/app_configuration.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,13 @@ def __init__(
9191
ValueError: If *app_configuration_url* is ``None`` or the
9292
credential is missing after defaulting.
9393
"""
94-
self.credential = credential or DefaultAzureCredential()
94+
if credential is None:
95+
raise ValueError(
96+
"Azure credential is required. "
97+
"Use Managed Identity, AzureCliCredential, or AzureDeveloperCliCredential."
98+
)
99+
100+
self.credential = credential
95101
self.app_config_endpoint = app_configuration_url
96102
self._initialize_client()
97103

src/ContentProcessorWorkflow/src/libs/base/application_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def run(self):
3535
import os
3636
from abc import ABC, abstractmethod
3737

38-
from azure.identity import DefaultAzureCredential
38+
from utils.credential_util import get_azure_credential
3939
from dotenv import load_dotenv
4040

4141
from libs.agent_framework.agent_framework_settings import AgentFrameworkSettings
@@ -117,7 +117,7 @@ def __init__(self, env_file_path: str | None = None, **data):
117117
self._load_env(env_file_path=env_file_path)
118118

119119
self.application_context = AppContext()
120-
self.application_context.set_credential(DefaultAzureCredential())
120+
self.application_context.set_credential(get_azure_credential())
121121

122122
app_config_url: str | None = _envConfiguration().app_config_endpoint
123123
if app_config_url != "" and app_config_url is not None:

src/ContentProcessorWorkflow/src/utils/credential_util.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,12 @@ def get_azure_credential():
126126
logging.info(
127127
"[AUTH] All CLI credentials failed - falling back to DefaultAzureCredential"
128128
)
129-
return DefaultAzureCredential()
129+
130+
raise RuntimeError(
131+
"No Azure authentication available. "
132+
"Use Managed Identity in Azure or run "
133+
"'az login' / 'azd auth login' locally."
134+
)
130135

131136

132137
def get_async_azure_credential():

src/tests/ContentProcessor/utils/test_azure_credential_utils.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
from unittest.mock import MagicMock, patch
99

10+
import pytest
11+
1012
import libs.utils.azure_credential_utils as azure_credential_utils
1113

1214
MODULE = "libs.utils.azure_credential_utils"
@@ -45,16 +47,16 @@ def test_returns_user_assigned_with_client_id(self, mock_managed):
4547
mock_managed.assert_called_once_with(client_id="test-client-id")
4648
assert credential == mock_instance
4749

48-
@patch(f"{MODULE}.DefaultAzureCredential")
4950
@patch(f"{MODULE}.AzureDeveloperCliCredential", side_effect=Exception("no azd"))
5051
@patch(f"{MODULE}.AzureCliCredential", side_effect=Exception("no az"))
5152
@patch.dict("os.environ", {}, clear=True)
52-
def test_falls_back_to_default(self, mock_cli, mock_dev_cli, mock_default):
53-
mock_instance = MagicMock()
54-
mock_default.return_value = mock_instance
55-
credential = azure_credential_utils.get_azure_credential()
56-
mock_default.assert_called_once()
57-
assert credential == mock_instance
53+
def test_raises_when_no_credentials_available(
54+
self, mock_cli, mock_dev_cli
55+
):
56+
with pytest.raises(RuntimeError) as exc:
57+
azure_credential_utils.get_azure_credential()
58+
59+
assert "No Azure authentication available" in str(exc.value)
5860

5961

6062
# ── TestGetAsyncAzureCredential ─────────────────────────────────────────

src/tests/ContentProcessor/utils/test_azure_credential_utils_extended.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,22 @@ def test_get_azure_credential_with_website_site_name(self, monkeypatch):
4242
assert credential == mock_instance
4343

4444
def test_get_azure_credential_cli_failure_fallback(self, monkeypatch):
45-
"""Test fallback to DefaultAzureCredential when CLI credentials fail"""
45+
"""Test RuntimeError when all credential options fail"""
4646
# Clear all Azure environment indicators
4747
for key in ["WEBSITE_SITE_NAME", "AZURE_CLIENT_ID", "MSI_ENDPOINT",
4848
"IDENTITY_ENDPOINT", "KUBERNETES_SERVICE_HOST", "CONTAINER_REGISTRY_LOGIN"]:
4949
monkeypatch.delenv(key, raising=False)
5050

5151
with patch('libs.utils.azure_credential_utils.AzureCliCredential') as mock_cli_cred, \
52-
patch('libs.utils.azure_credential_utils.AzureDeveloperCliCredential') as mock_azd_cred, \
53-
patch('libs.utils.azure_credential_utils.DefaultAzureCredential') as mock_default:
52+
patch('libs.utils.azure_credential_utils.AzureDeveloperCliCredential') as mock_azd_cred:
5453

55-
# Make both CLI credentials raise exceptions
5654
mock_cli_cred.side_effect = Exception("CLI credential failed")
5755
mock_azd_cred.side_effect = Exception("AZD credential failed")
58-
mock_default_instance = Mock()
59-
mock_default.return_value = mock_default_instance
6056

61-
credential = get_azure_credential()
57+
with pytest.raises(RuntimeError) as exc:
58+
get_azure_credential()
6259

63-
assert credential == mock_default_instance
64-
mock_default.assert_called_once()
60+
assert "No Azure authentication available" in str(exc.value)
6561

6662
def test_get_azure_credential_azd_success(self, monkeypatch):
6763
"""Test successful Azure Developer CLI credential"""

src/tests/ContentProcessorWorkflow/utils/test_credential_util_extended.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Extended tests for credential_util.py to improve coverage"""
22
from unittest.mock import Mock, patch
3+
import pytest
34
from utils.credential_util import (
45
get_azure_credential,
56
get_async_azure_credential,
@@ -40,24 +41,27 @@ def test_get_azure_credential_app_service_environment(self, monkeypatch):
4041
assert credential == mock_instance
4142

4243
def test_get_azure_credential_all_cli_fail(self, monkeypatch):
43-
"""Test fallback when all CLI credentials fail"""
44-
for key in ["WEBSITE_SITE_NAME", "AZURE_CLIENT_ID", "MSI_ENDPOINT",
45-
"IDENTITY_ENDPOINT", "KUBERNETES_SERVICE_HOST", "CONTAINER_REGISTRY_LOGIN"]:
44+
"""Test RuntimeError when all credential options fail"""
45+
for key in [
46+
"WEBSITE_SITE_NAME",
47+
"AZURE_CLIENT_ID",
48+
"MSI_ENDPOINT",
49+
"IDENTITY_ENDPOINT",
50+
"KUBERNETES_SERVICE_HOST",
51+
"CONTAINER_REGISTRY_LOGIN",
52+
]:
4653
monkeypatch.delenv(key, raising=False)
4754

4855
with patch('utils.credential_util.AzureCliCredential') as mock_cli, \
49-
patch('utils.credential_util.AzureDeveloperCliCredential') as mock_azd, \
50-
patch('utils.credential_util.DefaultAzureCredential') as mock_default:
56+
patch('utils.credential_util.AzureDeveloperCliCredential') as mock_azd:
5157

5258
mock_cli.side_effect = Exception("AzureCLI not available")
5359
mock_azd.side_effect = Exception("AzureDeveloperCLI not available")
54-
mock_default_instance = Mock()
55-
mock_default.return_value = mock_default_instance
5660

57-
credential = get_azure_credential()
61+
with pytest.raises(RuntimeError) as exc:
62+
get_azure_credential()
5863

59-
assert credential == mock_default_instance
60-
mock_default.assert_called_once()
64+
assert "No Azure authentication available" in str(exc.value)
6165

6266
def test_get_azure_credential_cli_success(self, monkeypatch):
6367
"""Test successful Azure CLI credential"""

0 commit comments

Comments
 (0)