Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package azure-cli-core for openSUSE:Factory 
checked in at 2026-07-08 17:38:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-core.new.1982 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-core"

Wed Jul  8 17:38:07 2026 rev:97 rq:1364354 version:2.88.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes    
2026-06-08 14:24:05.754011336 +0200
+++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.1982/azure-cli-core.changes  
2026-07-08 17:41:09.555134241 +0200
@@ -1,0 +2,9 @@
+Tue Jul  7 12:47:24 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- New upstream release
+  + Version 2.88.0
+  + For detailed information about changes see the
+    HISTORY.rst file provided with this package
+- Update Requires from setup.py
+
+-------------------------------------------------------------------

Old:
----
  azure_cli_core-2.87.0.tar.gz

New:
----
  azure_cli_core-2.88.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-core.spec ++++++
--- /var/tmp/diff_new_pack.lCLyTB/_old  2026-07-08 17:41:10.839178985 +0200
+++ /var/tmp/diff_new_pack.lCLyTB/_new  2026-07-08 17:41:10.839178985 +0200
@@ -24,7 +24,7 @@
 %global _sitelibdir %{%{pythons}_sitelib}
 
 Name:           azure-cli-core
-Version:        2.87.0
+Version:        2.88.0
 Release:        0
 Summary:        Microsoft Azure CLI Core Module
 License:        MIT
@@ -59,7 +59,7 @@
 Requires:       %{pythons}-msal-extensions >= 1.3.1
 Requires:       %{pythons}-packaging >= 20.9
 Requires:       %{pythons}-pip
-Requires:       %{pythons}-pkginfo >= 1.5.0.1
+Requires:       %{pythons}-pkginfo >= 1.12.0
 Requires:       %{pythons}-psutil >= 5.9
 Requires:       %{pythons}-py-deviceid
 Requires:       %{pythons}-pyOpenSSL >= 17.1.0

++++++ azure_cli_core-2.87.0.tar.gz -> azure_cli_core-2.88.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/HISTORY.rst 
new/azure_cli_core-2.88.0/HISTORY.rst
--- old/azure_cli_core-2.87.0/HISTORY.rst       2026-05-26 08:38:40.000000000 
+0200
+++ new/azure_cli_core-2.88.0/HISTORY.rst       2026-06-30 13:01:58.000000000 
+0200
@@ -3,6 +3,11 @@
 Release History
 ===============
 
+2.88.0
+++++++
+* Resolve CVE-2026-48526 (#33562)
+* Update global policy argument `--acquire-policy-token` to pick up new 
api-version and propagate correlation-id (#33661)
+
 2.87.0
 ++++++
 * Resolve CVE-2026-44431 (#33351)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/PKG-INFO 
new/azure_cli_core-2.88.0/PKG-INFO
--- old/azure_cli_core-2.87.0/PKG-INFO  2026-05-26 08:39:29.832620900 +0200
+++ new/azure_cli_core-2.88.0/PKG-INFO  2026-06-30 13:02:37.228244300 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-cli-core
-Version: 2.87.0
+Version: 2.88.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,7 @@
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: License :: OSI Approved :: MIT License
 Requires-Python: >=3.10.0
 License-File: LICENSE.txt
@@ -32,7 +33,7 @@
 Requires-Dist: msal[broker]==1.36.0; sys_platform == "win32"
 Requires-Dist: msal==1.36.0; sys_platform != "win32"
 Requires-Dist: packaging>=20.9
-Requires-Dist: pkginfo>=1.5.0.1
+Requires-Dist: pkginfo>=1.12.0
 Requires-Dist: psutil>=5.9; sys_platform != "cygwin"
 Requires-Dist: PyJWT>=2.1.0
 Requires-Dist: pyopenssl>=17.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/__init__.py 
new/azure_cli_core-2.88.0/azure/cli/core/__init__.py
--- old/azure_cli_core-2.87.0/azure/cli/core/__init__.py        2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/__init__.py        2026-06-30 
13:01:58.000000000 +0200
@@ -4,7 +4,7 @@
 # 
--------------------------------------------------------------------------------------------
 # pylint: disable=line-too-long
 
-__version__ = "2.87.0"
+__version__ = "2.88.0"
 
 import os
 import sys
@@ -37,10 +37,6 @@
 ALWAYS_LOADED_MODULES = []
 # Extensions that will always be loaded if installed. They don't expose 
commands but hook into CLI core.
 ALWAYS_LOADED_EXTENSIONS = ['azext_ai_examples', 'azext_next']
-# Timeout (in seconds) for loading a single module. Acts as a safety valve to 
prevent indefinite hangs
-MODULE_LOAD_TIMEOUT_SECONDS = 60
-# Maximum number of worker threads for parallel module loading.
-MAX_WORKER_THREAD_COUNT = 4
 
 
 def _get_top_level_command(args):
@@ -322,6 +318,7 @@
 
             start_time = time.perf_counter()
             logger.debug("Loading command modules...")
+            logger.debug(self.header_mod)
             results = self._load_modules(args, command_modules)
 
             count, cumulative_group_count, cumulative_command_count = \
@@ -664,42 +661,14 @@
                 loader._update_command_definitions()  # pylint: 
disable=protected-access
 
     def _load_modules(self, args, command_modules):
-        """Load command modules using ThreadPoolExecutor with timeout 
protection."""
-        import concurrent.futures
-        from concurrent.futures import ThreadPoolExecutor
+        """Load command modules sequentially."""
         from azure.cli.core.commands import BLOCKED_MODS
 
         results = []
-        with ThreadPoolExecutor(max_workers=MAX_WORKER_THREAD_COUNT) as 
executor:
-            future_to_module = {executor.submit(self._load_single_module, mod, 
args): mod
-                                for mod in command_modules if mod not in 
BLOCKED_MODS}
-
-            try:
-                for future in 
concurrent.futures.as_completed(future_to_module, 
timeout=MODULE_LOAD_TIMEOUT_SECONDS):
-                    try:
-                        result = future.result()
-                        results.append(result)
-                    except (ImportError, AttributeError, TypeError, 
ValueError) as ex:
-                        mod = future_to_module[future]
-                        logger.warning("Module '%s' load failed: %s", mod, ex)
-                        results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
-                    except Exception as ex:  # pylint: 
disable=broad-exception-caught
-                        mod = future_to_module[future]
-                        logger.warning("Module '%s' load failed with 
unexpected exception: %s", mod, ex)
-                        results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
-            except concurrent.futures.TimeoutError:
-                for future, mod in future_to_module.items():
-                    if future.done():
-                        try:
-                            result = future.result()
-                            results.append(result)
-                        except Exception as ex:  # pylint: 
disable=broad-exception-caught
-                            logger.warning("Module '%s' load failed: %s", mod, 
ex)
-                            results.append(ModuleLoadResult(mod, {}, {}, 0, 
ex))
-                    else:
-                        logger.warning("Module '%s' load timeout after %s 
seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS)
-                        results.append(ModuleLoadResult(mod, {}, {}, 0,
-                                                        Exception(f"Module 
'{mod}' load timeout")))
+        for mod in command_modules:
+            if mod in BLOCKED_MODS:
+                continue
+            results.append(self._load_single_module(mod, args))
 
         return results
 
@@ -712,6 +681,8 @@
             module_command_table, module_group_table, command_loader = 
_load_module_command_loader(self, args, mod)
             import_module_breaking_changes(mod)
             elapsed_time = time.perf_counter() - start_time
+            logger.debug(self.item_format_string, mod, elapsed_time,
+                         len(module_group_table), len(module_command_table))
             return ModuleLoadResult(mod, module_command_table, 
module_group_table, elapsed_time, command_loader=command_loader)
         except Exception as ex:  # pylint: disable=broad-except
             tb_str = traceback.format_exc()
@@ -744,13 +715,9 @@
         self.command_table.update(result.command_table)
         self.command_group_table.update(result.group_table)
 
-        logger.debug(self.item_format_string, result.module_name, 
result.elapsed_time,
-                     len(result.group_table), len(result.command_table))
-
     def _process_results_with_timing(self, results):
         """Process pre-loaded module results with timing and progress 
reporting."""
-        logger.debug("Loaded command modules in parallel:")
-        logger.debug(self.header_mod)
+        logger.debug("Loaded command modules:")
 
         count = 0
         cumulative_group_count = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/_profile.py 
new/azure_cli_core-2.88.0/azure/cli/core/_profile.py
--- old/azure_cli_core-2.87.0/azure/cli/core/_profile.py        2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/_profile.py        2026-06-30 
13:01:58.000000000 +0200
@@ -441,6 +441,11 @@
     def get_msal_token(self, scopes, data):
         """Get VM SSH certificate. DO NOT use it for other purposes. To get an 
access token, use get_raw_token instead.
         """
+        account = self.get_subscription()
+        managed_identity_type, _ = 
Profile._parse_managed_identity_account(account)
+        if managed_identity_type or (in_cloud_console() and 
account[_USER_ENTITY].get(_CLOUD_SHELL_ID)):
+            raise AuthenticationError("VM SSH currently doesn't support 
managed identity or Cloud Shell.")
+
         credential, _, _ = self.get_login_credentials(sdk_credential=False)
         from .auth.constants import ACCESS_TOKEN
         certificate_string = credential.acquire_token(scopes, 
data=data)[ACCESS_TOKEN]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/aaz/_arg_fmt.py 
new/azure_cli_core-2.88.0/azure/cli/core/aaz/_arg_fmt.py
--- old/azure_cli_core-2.87.0/azure/cli/core/aaz/_arg_fmt.py    2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/aaz/_arg_fmt.py    2026-06-30 
13:01:58.000000000 +0200
@@ -760,10 +760,19 @@
                 raise AAZInvalidArgValueError("Decoded object is not a 
dictionary.")
 
             try:
-                _, _ = obj["next_link"], obj["offset"]
+                next_link, _ = obj["next_link"], obj["offset"]
             except KeyError:
                 raise AAZInvalidArgValueError("`next_link` or `offset` doesn't 
exist.")
 
+            # `next_link` is a URL that the next page request is sent to with 
an Azure access token.
+            # Validate it shares the same origin as a trusted endpoint of the 
active cloud to prevent
+            # the token from being sent to an attacker-controlled host such as
+            # `https://management.azure.com.attacker`.
+            if next_link is not None:
+                from azure.cli.core.util import is_trusted_cloud_endpoint
+                if not is_trusted_cloud_endpoint(next_link, ctx.cli_ctx):
+                    raise AAZInvalidArgValueError("`next_link` '{}' is not a 
valid endpoint.".format(next_link))
+
         assert isinstance(value, AAZSimpleValue)
         data = value._data
         if data == AAZUndefined or data is None or value._is_patch:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.87.0/azure/cli/core/commandIndex.latest.json 
new/azure_cli_core-2.88.0/azure/cli/core/commandIndex.latest.json
--- old/azure_cli_core-2.87.0/azure/cli/core/commandIndex.latest.json   
2026-05-26 08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/commandIndex.latest.json   
2026-06-30 13:01:58.000000000 +0200
@@ -1,5 +1,5 @@
 {
-  "version": "2.87.0",
+  "version": "2.88.0",
   "cloudProfile": "latest",
   "commandIndex": {
     "account": [
@@ -15,9 +15,6 @@
     "advisor": [
       "azure.cli.command_modules.advisor"
     ],
-    "afd": [
-      "azure.cli.command_modules.cdn"
-    ],
     "aks": [
       "azure.cli.command_modules.acs",
       "azure.cli.command_modules.serviceconnector"
@@ -61,9 +58,6 @@
     "capacity": [
       "azure.cli.command_modules.vm"
     ],
-    "cdn": [
-      "azure.cli.command_modules.cdn"
-    ],
     "cloud": [
       "azure.cli.command_modules.cloud"
     ],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/commands/arm.py 
new/azure_cli_core-2.88.0/azure/cli/core/commands/arm.py
--- old/azure_cli_core-2.87.0/azure/cli/core/commands/arm.py    2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/commands/arm.py    2026-06-30 
13:01:58.000000000 +0200
@@ -774,6 +774,11 @@
     import time
     from azure.core.exceptions import HttpResponseError
 
+    # Coerce AAZSimpleValue/model values to plain strings for SDK 5.0.0b2 
compatibility
+    principal_id = str(principal_id) if principal_id else principal_id
+    identity_role = str(identity_role) if identity_role else identity_role
+    identity_scope = str(identity_scope) if identity_scope else identity_scope
+
     identity_role_id = resolve_role_id(cli_ctx, identity_role, identity_scope)
     assignments_client = get_mgmt_service_client(cli_ctx, 
ResourceType.MGMT_AUTHORIZATION).role_assignments
     RoleAssignmentCreateParameters = get_sdk(cli_ctx, 
ResourceType.MGMT_AUTHORIZATION,
@@ -818,7 +823,7 @@
         except ValueError:
             pass
         if not role_id:  # retrieve role id
-            role_defs = list(client.list(scope, "roleName eq 
'{}'".format(role)))
+            role_defs = list(client.list(scope, filter="roleName eq 
'{}'".format(role)))
             if not role_defs:
                 raise CLIError("Role '{}' doesn't exist.".format(role))
             if len(role_defs) > 1:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.87.0/azure/cli/core/helpIndex.latest.json 
new/azure_cli_core-2.88.0/azure/cli/core/helpIndex.latest.json
--- old/azure_cli_core-2.87.0/azure/cli/core/helpIndex.latest.json      
2026-05-26 08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/helpIndex.latest.json      
2026-06-30 13:01:58.000000000 +0200
@@ -1,5 +1,5 @@
 {
-  "version": "2.87.0",
+  "version": "2.88.0",
   "cloudProfile": "latest",
   "helpIndex": {
     "groups": {
@@ -19,10 +19,6 @@
         "summary": "Manage Azure Advisor.",
         "tags": ""
       },
-      "afd": {
-        "summary": "Manage Azure Front Door Standard/Premium.",
-        "tags": ""
-      },
       "aks": {
         "summary": "Azure Kubernetes Service.",
         "tags": ""
@@ -75,10 +71,6 @@
         "summary": "Manage capacity.",
         "tags": ""
       },
-      "cdn": {
-        "summary": "Manage Azure Content Delivery Networks (CDNs).",
-        "tags": ""
-      },
       "cloud": {
         "summary": "Manage registered Azure clouds.",
         "tags": ""
@@ -89,7 +81,7 @@
       },
       "compute-fleet": {
         "summary": "Manage for Azure Compute Fleet.",
-        "tags": "[Preview]"
+        "tags": ""
       },
       "compute-recommender": {
         "summary": "Manage sku/zone/region recommender info for compute 
resources.",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.87.0/azure/cli/core/profiles/_shared.py 
new/azure_cli_core-2.88.0/azure/cli/core/profiles/_shared.py
--- old/azure_cli_core-2.87.0/azure/cli/core/profiles/_shared.py        
2026-05-26 08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/profiles/_shared.py        
2026-06-30 13:01:58.000000000 +0200
@@ -97,7 +97,6 @@
     MGMT_BATCHAI = ('azure.mgmt.batchai', None)
     MGMT_BILLING = ('azure.mgmt.billing', None)
     MGMT_BOTSERVICE = ('azure.mgmt.botservice', None)
-    MGMT_CDN = ('azure.mgmt.cdn', None)
     MGMT_COGNITIVESERVICES = ('azure.mgmt.cognitiveservices', None)
     MGMT_CONTAINERINSTANCE = ('azure.mgmt.containerinstance', None)
     MGMT_COSMOSDB = ('azure.mgmt.cosmosdb', None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/sdk/policies.py 
new/azure_cli_core-2.88.0/azure/cli/core/sdk/policies.py
--- old/azure_cli_core-2.87.0/azure/cli/core/sdk/policies.py    2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/sdk/policies.py    2026-06-30 
13:01:58.000000000 +0200
@@ -6,6 +6,7 @@
 import logging
 import re
 import types
+import uuid
 
 from azure.core.pipeline.policies import SansIOHTTPPolicy, UserAgentPolicy
 from knack.log import get_logger
@@ -111,7 +112,7 @@
         http_request = request.http_request
         if getattr(http_request, 'method', '') == 'GET':
             return
-        ACQUIRE_POLICY_TOKEN_URL = 
'/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/acquirePolicyToken?api-version=2025-03-01'
+        ACQUIRE_POLICY_TOKEN_URL = 
'/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/acquirePolicyToken?api-version=2025-11-01'
         policy_token = None
 
         from azure.cli.core.azclierror import ServiceError
@@ -135,10 +136,18 @@
                 },
                 "changeReference": cli_ctx.data.get('_change_reference', None)
             }
+
+            # Reuse the original request's x-ms-correlation-request-id so the 
acquirePolicyToken call shares the same correlation ID as the operation it is 
gating.
+            headers = ['Content-Type=application/json', 'x-ms-force-sync=true']
+            correlation_id = 
http_request.headers.get('x-ms-correlation-request-id')
+            if not correlation_id:
+                correlation_id = str(uuid.uuid4())
+                http_request.headers['x-ms-correlation-request-id'] = 
correlation_id
+            
headers.append('x-ms-correlation-request-id={}'.format(correlation_id))
+
             acquire_policy_token_response = send_raw_request(cli_ctx, 'POST',
                                                              
ACQUIRE_POLICY_TOKEN_URL,
-                                                             
headers=['Content-Type=application/json',
-                                                                      
'x-ms-force-sync=true'],
+                                                             headers=headers,
                                                              
body=json.dumps(acquire_policy_token_body))
             if acquire_policy_token_response.status_code == 200 and 
acquire_policy_token_response.content:
                 response_content = 
json.loads(acquire_policy_token_response.content)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/azure/cli/core/util.py 
new/azure_cli_core-2.88.0/azure/cli/core/util.py
--- old/azure_cli_core-2.87.0/azure/cli/core/util.py    2026-05-26 
08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure/cli/core/util.py    2026-06-30 
13:01:58.000000000 +0200
@@ -1041,21 +1041,13 @@
     if not skip_authorization_header and url.lower().startswith('https://'):
         # Prepare `resource` for `get_raw_token`
         if not resource:
-            # If url starts with ARM endpoint, like 
`https://management.azure.com/`,
+            # If url's origin matches the ARM endpoint, like 
`https://management.azure.com/`,
             # use `active_directory_resource_id` for resource, like 
`https://management.core.windows.net/`.
             # This follows the same behavior as 
`azure.cli.core.commands.client_factory._get_mgmt_service_client`
-            if url.lower().startswith(endpoints.resource_manager.rstrip('/')):
+            if is_same_origin(url, endpoints.resource_manager):
                 resource = endpoints.active_directory_resource_id
             else:
-                from azure.cli.core.cloud import CloudEndpointNotSetException
-                for p in [x for x in dir(endpoints) if not x.startswith('_')]:
-                    try:
-                        value = getattr(endpoints, p)
-                    except CloudEndpointNotSetException:
-                        continue
-                    if isinstance(value, str) and 
url.lower().startswith(value.lower()):
-                        resource = value
-                        break
+                resource = match_cloud_endpoint(url, cli_ctx)
         if resource:
             # Prepare `subscription` for `get_raw_token`
             # If this is an ARM request, try to extract subscription ID from 
the URL.
@@ -1063,7 +1055,7 @@
             # TODO: In the future when multi-tenant subscription is supported, 
we won't be able to uniquely identify
             #   the token from subscription anymore.
             token_subscription = None
-            if url.lower().startswith(endpoints.resource_manager.rstrip('/')):
+            if is_same_origin(url, endpoints.resource_manager):
                 token_subscription = _extract_subscription_id(url)
             if token_subscription:
                 logger.debug('Retrieving token for resource %s, subscription 
%s', resource, token_subscription)
@@ -1210,6 +1202,92 @@
     return req.read()
 
 
+def is_same_origin(url, endpoint):
+    """Check whether ``url`` and ``endpoint`` share the same origin (scheme + 
host + port).
+
+    This performs an exact origin comparison rather than substring or prefix 
matching, so a
+    malicious host such as ``https://management.azure.com.attacker`` is not 
mistaken for
+    the trusted endpoint ``https://management.azure.com``. It is intended for 
validating that a
+    URL points to a trusted endpoint before sensitive data (e.g., an Azure 
access token) is sent
+    to it.
+
+    :param url: The URL to validate, e.g., 
``https://management.azure.com/subscriptions/...``.
+    :param endpoint: The trusted endpoint to validate against, e.g., 
``https://management.azure.com/``.
+    :return: ``True`` if both share the same origin, otherwise ``False``.
+    :rtype: bool
+    """
+    from urllib.parse import urlparse
+
+    if not isinstance(url, str) or not isinstance(endpoint, str):
+        return False
+
+    try:
+        url_parts = urlparse(url)
+        endpoint_parts = urlparse(endpoint)
+
+    except (TypeError, ValueError):
+        return False
+
+    # Require a real host on both sides to avoid false positives against 
non-URL endpoint values.
+    # `hostname` (rather than `netloc`) is used so that userinfo tricks like
+    # `https://management.azure.com@attacker` resolve to the actual host 
`attacker`.
+    if not url_parts.hostname or not endpoint_parts.hostname:
+        return False
+
+    def _effective_port(parts):
+        # Treat a missing port as the scheme's default port so that, e.g.,
+        # `https://management.azure.com:443/` and 
`https://management.azure.com/` are equivalent.
+        if parts.port is not None:
+            return parts.port
+
+        return {'http': 80, 'https': 443}.get(parts.scheme.lower())
+
+    try:
+        if _effective_port(url_parts) != _effective_port(endpoint_parts):
+            return False
+
+    except ValueError:
+        return False
+
+    return (url_parts.scheme.lower() == endpoint_parts.scheme.lower() and
+            url_parts.hostname.lower() == endpoint_parts.hostname.lower())
+
+
+def match_cloud_endpoint(url, cli_ctx):
+    """Return the active cloud endpoint that shares the same origin as ``url``.
+
+    :param url: The URL to match, e.g., 
``https://management.azure.com/subscriptions/...``.
+    :param cli_ctx: The CLI context whose active cloud's endpoints are treated 
as trusted.
+    :return: The matching endpoint value, or ``None`` if no endpoint shares 
``url``'s origin.
+    :rtype: str or None
+    """
+    from azure.cli.core.cloud import CloudEndpointNotSetException
+
+    endpoints = cli_ctx.cloud.endpoints
+    for p in [x for x in dir(endpoints) if not x.startswith('_')]:
+        try:
+            value = getattr(endpoints, p)
+
+        except CloudEndpointNotSetException:
+            continue
+
+        if isinstance(value, str) and is_same_origin(url, value):
+            return value
+
+    return None
+
+
+def is_trusted_cloud_endpoint(url, cli_ctx):
+    """Check whether ``url`` shares the same origin as any endpoint of the 
active cloud.
+
+    :param url: The URL to validate, e.g., 
``https://management.azure.com/subscriptions/...``.
+    :param cli_ctx: The CLI context whose active cloud's endpoints are treated 
as trusted.
+    :return: ``True`` if ``url`` shares the same origin as any cloud endpoint, 
otherwise ``False``.
+    :rtype: bool
+    """
+    return match_cloud_endpoint(url, cli_ctx) is not None
+
+
 def parse_proxy_resource_id(rid):
     """Parses a resource_id into its various parts.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.87.0/azure_cli_core.egg-info/PKG-INFO 
new/azure_cli_core-2.88.0/azure_cli_core.egg-info/PKG-INFO
--- old/azure_cli_core-2.87.0/azure_cli_core.egg-info/PKG-INFO  2026-05-26 
08:39:29.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure_cli_core.egg-info/PKG-INFO  2026-06-30 
13:02:37.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-cli-core
-Version: 2.87.0
+Version: 2.88.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,7 @@
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: License :: OSI Approved :: MIT License
 Requires-Python: >=3.10.0
 License-File: LICENSE.txt
@@ -32,7 +33,7 @@
 Requires-Dist: msal[broker]==1.36.0; sys_platform == "win32"
 Requires-Dist: msal==1.36.0; sys_platform != "win32"
 Requires-Dist: packaging>=20.9
-Requires-Dist: pkginfo>=1.5.0.1
+Requires-Dist: pkginfo>=1.12.0
 Requires-Dist: psutil>=5.9; sys_platform != "cygwin"
 Requires-Dist: PyJWT>=2.1.0
 Requires-Dist: pyopenssl>=17.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_cli_core-2.87.0/azure_cli_core.egg-info/requires.txt 
new/azure_cli_core-2.88.0/azure_cli_core.egg-info/requires.txt
--- old/azure_cli_core-2.87.0/azure_cli_core.egg-info/requires.txt      
2026-05-26 08:39:29.000000000 +0200
+++ new/azure_cli_core-2.88.0/azure_cli_core.egg-info/requires.txt      
2026-06-30 13:02:37.000000000 +0200
@@ -9,7 +9,7 @@
 microsoft-security-utilities-secret-masker~=1.0.0b4
 msal-extensions==1.3.1
 packaging>=20.9
-pkginfo>=1.5.0.1
+pkginfo>=1.12.0
 PyJWT>=2.1.0
 pyopenssl>=17.1.0
 py-deviceid
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_cli_core-2.87.0/setup.py 
new/azure_cli_core-2.88.0/setup.py
--- old/azure_cli_core-2.87.0/setup.py  2026-05-26 08:38:40.000000000 +0200
+++ new/azure_cli_core-2.88.0/setup.py  2026-06-30 13:01:58.000000000 +0200
@@ -8,7 +8,7 @@
 from codecs import open
 from setuptools import setup, find_packages
 
-VERSION = "2.87.0"
+VERSION = "2.88.0"
 
 # If we have source, validate that our version numbers match
 # This should prevent uploading releases with mismatched versions.
@@ -39,6 +39,7 @@
     'Programming Language :: Python :: 3.11',
     'Programming Language :: Python :: 3.12',
     'Programming Language :: Python :: 3.13',
+    'Programming Language :: Python :: 3.14',
     'License :: OSI Approved :: MIT License',
 ]
 
@@ -58,7 +59,10 @@
     'msal[broker]==1.36.0; sys_platform == "win32"',
     'msal==1.36.0; sys_platform != "win32"',
     'packaging>=20.9',
-    'pkginfo>=1.5.0.1',
+    # pkginfo>=1.12.0 reads the spec-defined wheel METADATA / unpacked 
.dist-info
+    # layout produced by modern wheel/setuptools (no metadata.json). Required 
so
+    # WheelExtension.get_metadata works without the legacy wheel==0.30.0 
artifact.
+    'pkginfo>=1.12.0',
     # psutil can't install on cygwin: 
https://github.com/Azure/azure-cli/issues/9399
     'psutil>=5.9; sys_platform != "cygwin"',
     'PyJWT>=2.1.0',

Reply via email to