Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-azure-keyvault-secrets for 
openSUSE:Factory checked in at 2026-08-19 17:57:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-azure-keyvault-secrets (Old)
 and      /work/SRC/openSUSE:Factory/.python-azure-keyvault-secrets.new.1258 
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-azure-keyvault-secrets"

Wed Aug 19 17:57:47 2026 rev:16 rq:1371812 version:4.11.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-azure-keyvault-secrets/python-azure-keyvault-secrets.changes
      2026-04-22 16:55:59.815062449 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-azure-keyvault-secrets.new.1258/python-azure-keyvault-secrets.changes
    2026-08-19 17:58:04.547471066 +0200
@@ -1,0 +2,8 @@
+Tue Aug 18 08:56:30 UTC 2026 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- New upstream release
+  + Version 4.11.1
+  + For detailed information about changes see the
+    CHANGELOG.md file provided with this package
+
+-------------------------------------------------------------------

Old:
----
  azure_keyvault_secrets-4.11.0.tar.gz

New:
----
  azure_keyvault_secrets-4.11.1.tar.gz

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

Other differences:
------------------
++++++ python-azure-keyvault-secrets.spec ++++++
--- /var/tmp/diff_new_pack.yxEp8A/_old  2026-08-19 17:58:05.233495811 +0200
+++ /var/tmp/diff_new_pack.yxEp8A/_new  2026-08-19 17:58:05.235495883 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-azure-keyvault-secrets
-Version:        4.11.0
+Version:        4.11.1
 Release:        0
 Summary:        Microsoft Azure Key Vault Secrets Client Library for Python
 License:        MIT

++++++ azure_keyvault_secrets-4.11.0.tar.gz -> 
azure_keyvault_secrets-4.11.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_keyvault_secrets-4.11.0/CHANGELOG.md 
new/azure_keyvault_secrets-4.11.1/CHANGELOG.md
--- old/azure_keyvault_secrets-4.11.0/CHANGELOG.md      2026-04-17 
01:49:40.000000000 +0200
+++ new/azure_keyvault_secrets-4.11.1/CHANGELOG.md      2026-08-12 
03:17:58.000000000 +0200
@@ -1,5 +1,16 @@
 # Release History
 
+## 4.11.1 (2026-08-12)
+
+### Bugs Fixed
+
+- Fixed a replay bug in the challenge authentication policy where a request 
copy stashed on the shared policy
+  instance was never cleared, allowing one request's method, URL, and body to 
leak into a later, unrelated request
+  made by the same client (for example, after an Entra ID Continuous Access 
Evaluation challenge). The original
+  request is now stored per-request instead of on the policy instance. This 
mirrors the fix already applied to
+  `azure-keyvault-keys` and `azure-keyvault-administration` in
+  [#47742](https://github.com/Azure/azure-sdk-for-python/pull/47742).
+
 ## 4.11.0 (2026-03-26)
 
 ### Features Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_keyvault_secrets-4.11.0/PKG-INFO 
new/azure_keyvault_secrets-4.11.1/PKG-INFO
--- old/azure_keyvault_secrets-4.11.0/PKG-INFO  2026-04-17 01:50:27.042300700 
+0200
+++ new/azure_keyvault_secrets-4.11.1/PKG-INFO  2026-08-12 03:19:09.812467000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-keyvault-secrets
-Version: 4.11.0
+Version: 4.11.1
 Summary: Microsoft Corporation Key Vault Secrets Client Library for Python
 Author-email: Microsoft Corporation <[email protected]>
 License: MIT License
@@ -406,6 +406,17 @@
 
 # Release History
 
+## 4.11.1 (2026-08-12)
+
+### Bugs Fixed
+
+- Fixed a replay bug in the challenge authentication policy where a request 
copy stashed on the shared policy
+  instance was never cleared, allowing one request's method, URL, and body to 
leak into a later, unrelated request
+  made by the same client (for example, after an Entra ID Continuous Access 
Evaluation challenge). The original
+  request is now stored per-request instead of on the policy instance. This 
mirrors the fix already applied to
+  `azure-keyvault-keys` and `azure-keyvault-administration` in
+  [#47742](https://github.com/Azure/azure-sdk-for-python/pull/47742).
+
 ## 4.11.0 (2026-03-26)
 
 ### Features Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure_keyvault_secrets-4.11.0/api.md 
new/azure_keyvault_secrets-4.11.1/api.md
--- old/azure_keyvault_secrets-4.11.0/api.md    1970-01-01 01:00:00.000000000 
+0100
+++ new/azure_keyvault_secrets-4.11.1/api.md    2026-08-12 03:17:58.000000000 
+0200
@@ -0,0 +1,329 @@
+```py
+namespace azure.keyvault.secrets
+
+    class azure.keyvault.secrets.ApiVersion(str, Enum, 
metaclass=CaseInsensitiveEnumMeta):
+        V2016_10_01 = "2016-10-01"
+        V2025_07_01 = "2025-07-01"
+        V7_0 = "7.0"
+        V7_1 = "7.1"
+        V7_2 = "7.2"
+        V7_3 = "7.3"
+        V7_4 = "7.4"
+        V7_5 = "7.5"
+        V7_6 = "7.6"
+
+
+    class azure.keyvault.secrets.ContentType(str, Enum, 
metaclass=CaseInsensitiveEnumMeta):
+        PEM = "application/x-pem-file"
+        PFX = "application/x-pkcs12"
+
+
+    class azure.keyvault.secrets.DeletedSecret:
+        property deleted_date: Optional[datetime]    # Read-only
+        property id: Optional[str]    # Read-only
+        property name: Optional[str]    # Read-only
+        property properties: SecretProperties    # Read-only
+        property recovery_id: Optional[str]    # Read-only
+        property scheduled_purge_date: Optional[datetime]    # Read-only
+
+        def __init__(
+                self, 
+                properties: SecretProperties, 
+                deleted_date: Optional[datetime] = None, 
+                recovery_id: Optional[str] = None, 
+                scheduled_purge_date: Optional[datetime] = None
+            ) -> None: ...
+
+        def __repr__(self) -> str: ...
+
+
+    class azure.keyvault.secrets.KeyVaultSecret:
+        property id: Optional[str]    # Read-only
+        property name: Optional[str]    # Read-only
+        property properties: SecretProperties    # Read-only
+        property value: Optional[str]    # Read-only
+
+        def __init__(
+                self, 
+                properties: SecretProperties, 
+                value: Optional[str]
+            ) -> None: ...
+
+        def __repr__(self) -> str: ...
+
+
+    class azure.keyvault.secrets.KeyVaultSecretIdentifier:
+        property name: str    # Read-only
+        property source_id: str    # Read-only
+        property vault_url: str    # Read-only
+        property version: Optional[str]    # Read-only
+
+        def __init__(self, source_id: str) -> None: ...
+
+
+    class azure.keyvault.secrets.SecretClient(KeyVaultClientBase): implements 
ContextManager 
+        property vault_url: str    # Read-only
+
+        def __init__(
+                self, 
+                vault_url: str, 
+                credential: TokenCredential, 
+                *, 
+                api_version: Union[ApiVersion, str] = ..., 
+                verify_challenge_resource: Optional[bool] = ..., 
+                **kwargs: Any
+            ) -> None: ...
+
+        @distributed_trace
+        def backup_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> bytes: ...
+
+        @distributed_trace
+        def begin_delete_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> LROPoller[DeletedSecret]: ...
+
+        @distributed_trace
+        def begin_recover_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> LROPoller[SecretProperties]: ...
+
+        def close(self) -> None: ...
+
+        @distributed_trace
+        def get_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> DeletedSecret: ...
+
+        @distributed_trace
+        def get_secret(
+                self, 
+                name: str, 
+                version: Optional[str] = None, 
+                *, 
+                out_content_type: Optional[Union[str, ContentType]] = ..., 
+                **kwargs: Any
+            ) -> KeyVaultSecret: ...
+
+        @distributed_trace
+        def list_deleted_secrets(self, **kwargs: Any) -> 
ItemPaged[DeletedSecret]: ...
+
+        @distributed_trace
+        def list_properties_of_secret_versions(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> ItemPaged[SecretProperties]: ...
+
+        @distributed_trace
+        def list_properties_of_secrets(self, **kwargs: Any) -> 
ItemPaged[SecretProperties]: ...
+
+        @distributed_trace
+        def purge_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> None: ...
+
+        @distributed_trace
+        def restore_secret_backup(
+                self, 
+                backup: bytes, 
+                **kwargs: Any
+            ) -> SecretProperties: ...
+
+        @distributed_trace
+        def send_request(
+                self, 
+                request: HttpRequest, 
+                *, 
+                stream: bool = False, 
+                **kwargs: Any
+            ) -> HttpResponse: ...
+
+        @distributed_trace
+        def set_secret(
+                self, 
+                name: str, 
+                value: str, 
+                *, 
+                content_type: Optional[str] = ..., 
+                enabled: Optional[bool] = ..., 
+                expires_on: Optional[datetime] = ..., 
+                not_before: Optional[datetime] = ..., 
+                tags: Optional[Dict[str, str]] = ..., 
+                **kwargs: Any
+            ) -> KeyVaultSecret: ...
+
+        @distributed_trace
+        def update_secret_properties(
+                self, 
+                name: str, 
+                version: Optional[str] = None, 
+                *, 
+                content_type: Optional[str] = ..., 
+                enabled: Optional[bool] = ..., 
+                expires_on: Optional[datetime] = ..., 
+                not_before: Optional[datetime] = ..., 
+                tags: Optional[Dict[str, str]] = ..., 
+                **kwargs: Any
+            ) -> SecretProperties: ...
+
+
+    class azure.keyvault.secrets.SecretProperties:
+        property content_type: Optional[str]    # Read-only
+        property created_on: Optional[datetime]    # Read-only
+        property enabled: Optional[bool]    # Read-only
+        property expires_on: Optional[datetime]    # Read-only
+        property id: Optional[str]    # Read-only
+        property key_id: Optional[str]    # Read-only
+        property managed: Optional[bool]    # Read-only
+        property name: Optional[str]    # Read-only
+        property not_before: Optional[datetime]    # Read-only
+        property previous_version: Optional[str]    # Read-only
+        property recoverable_days: Optional[int]    # Read-only
+        property recovery_level: Optional[str]    # Read-only
+        property tags: Optional[Dict[str, str]]    # Read-only
+        property updated_on: Optional[datetime]    # Read-only
+        property vault_url: Optional[str]    # Read-only
+        property version: Optional[str]    # Read-only
+
+        def __init__(
+                self, 
+                *args: Any, 
+                **kwargs: Any
+            ) -> None: ...
+
+        def __repr__(self) -> str: ...
+
+
+namespace azure.keyvault.secrets.aio
+
+    class azure.keyvault.secrets.aio.SecretClient(AsyncKeyVaultClientBase): 
implements AsyncContextManager 
+        property vault_url: str    # Read-only
+
+        def __init__(
+                self, 
+                vault_url: str, 
+                credential: AsyncTokenCredential, 
+                *, 
+                api_version: Union[ApiVersion, str] = ..., 
+                verify_challenge_resource: Optional[bool] = ..., 
+                **kwargs: Any
+            ) -> None: ...
+
+        @distributed_trace_async
+        async def backup_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> bytes: ...
+
+        async def close(self) -> None: ...
+
+        @distributed_trace_async
+        async def delete_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> DeletedSecret: ...
+
+        @distributed_trace_async
+        async def get_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> DeletedSecret: ...
+
+        @distributed_trace_async
+        async def get_secret(
+                self, 
+                name: str, 
+                version: Optional[str] = None, 
+                *, 
+                out_content_type: Optional[Union[str, ContentType]] = ..., 
+                **kwargs: Any
+            ) -> KeyVaultSecret: ...
+
+        @distributed_trace
+        def list_deleted_secrets(self, **kwargs: Any) -> 
AsyncItemPaged[DeletedSecret]: ...
+
+        @distributed_trace
+        def list_properties_of_secret_versions(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> AsyncItemPaged[SecretProperties]: ...
+
+        @distributed_trace
+        def list_properties_of_secrets(self, **kwargs: Any) -> 
AsyncItemPaged[SecretProperties]: ...
+
+        @distributed_trace_async
+        async def purge_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> None: ...
+
+        @distributed_trace_async
+        async def recover_deleted_secret(
+                self, 
+                name: str, 
+                **kwargs: Any
+            ) -> SecretProperties: ...
+
+        @distributed_trace_async
+        async def restore_secret_backup(
+                self, 
+                backup: bytes, 
+                **kwargs: Any
+            ) -> SecretProperties: ...
+
+        @distributed_trace_async
+        def send_request(
+                self, 
+                request: HttpRequest, 
+                *, 
+                stream: bool = False, 
+                **kwargs: Any
+            ) -> Awaitable[AsyncHttpResponse]: ...
+
+        @distributed_trace_async
+        async def set_secret(
+                self, 
+                name: str, 
+                value: str, 
+                *, 
+                content_type: Optional[str] = ..., 
+                enabled: Optional[bool] = ..., 
+                expires_on: Optional[datetime] = ..., 
+                not_before: Optional[datetime] = ..., 
+                tags: Optional[Dict[str, str]] = ..., 
+                **kwargs: Any
+            ) -> KeyVaultSecret: ...
+
+        @distributed_trace_async
+        async def update_secret_properties(
+                self, 
+                name: str, 
+                version: Optional[str] = None, 
+                *, 
+                content_type: Optional[str] = ..., 
+                enabled: Optional[bool] = ..., 
+                expires_on: Optional[datetime] = ..., 
+                not_before: Optional[datetime] = ..., 
+                tags: Optional[Dict[str, str]] = ..., 
+                **kwargs: Any
+            ) -> SecretProperties: ...
+
+
+```
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_shared/async_challenge_auth_policy.py
 
new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_shared/async_challenge_auth_policy.py
--- 
old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_shared/async_challenge_auth_policy.py
     2026-04-17 01:49:40.000000000 +0200
+++ 
new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_shared/async_challenge_auth_policy.py
     2026-08-12 03:17:58.000000000 +0200
@@ -30,7 +30,7 @@
 
 from .http_challenge import HttpChallenge
 from . import http_challenge_cache as ChallengeCache
-from .challenge_auth_policy import _enforce_tls, _has_claims, _update_challenge
+from .challenge_auth_policy import _enforce_tls, _has_claims, 
_update_challenge, _REQUEST_COPY_KEY
 
 if sys.version_info < (3, 9):
     from typing import Awaitable
@@ -81,7 +81,6 @@
         self._credential: AsyncTokenProvider = credential
         self._token: Optional[Union["AccessToken", "AccessTokenInfo"]] = None
         self._verify_challenge_resource = 
kwargs.pop("verify_challenge_resource", True)
-        self._request_copy: Optional[HttpRequest] = None
 
     async def send(
         self, request: PipelineRequest[HttpRequest]
@@ -175,8 +174,10 @@
         # saving it for later. Key Vault will reject the request as 
unauthorized and respond with a challenge.
         # on_challenge will parse that challenge, use the original request 
including the body, authorize the
         # request, and tell super to send it again.
-        if request.http_request.content:
-            self._request_copy = request.http_request
+        # The original request is stashed on the request's context 
(per-request), so it cannot leak into a later
+        # request made by the same client. Don't overwrite a previously 
stashed copy if this is a retry.
+        if request.http_request.content and _REQUEST_COPY_KEY not in 
request.context:
+            request.context[_REQUEST_COPY_KEY] = request.http_request
             bodiless_request = HttpRequest(
                 method=request.http_request.method,
                 url=request.http_request.url,
@@ -218,9 +219,10 @@
                     "See https://aka.ms/azsdk/blog/vault-uri for more 
information."
                 )
 
-        # If we had created a request copy in on_request, use it now to send 
along the original body content
-        if self._request_copy:
-            request.http_request = self._request_copy
+        # If we stashed the original request in on_request, use it now to send 
along the original body content
+        request_copy = request.context.get(_REQUEST_COPY_KEY)
+        if request_copy:
+            request.http_request = request_copy
 
         # The tenant parsed from AD FS challenges is "adfs"; we don't actually 
need a tenant for AD FS authentication
         # For AD FS we skip cross-tenant authentication per 
https://github.com/Azure/azure-sdk-for-python/issues/28648
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_shared/challenge_auth_policy.py
 
new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_shared/challenge_auth_policy.py
--- 
old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_shared/challenge_auth_policy.py
   2026-04-17 01:49:40.000000000 +0200
+++ 
new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_shared/challenge_auth_policy.py
   2026-08-12 03:17:58.000000000 +0200
@@ -36,6 +36,12 @@
 from . import http_challenge_cache as ChallengeCache
 
 
+# Key under which the original request is stashed on the per-request pipeline 
context during the challenge flow.
+# Storing this per-request (rather than on the policy instance) prevents the 
body of one request from leaking into a
+# subsequent request made by the same client.
+_REQUEST_COPY_KEY = "key_vault_request_copy"
+
+
 def _enforce_tls(request: PipelineRequest) -> None:
     if not request.http_request.url.lower().startswith("https"):
         raise ServiceRequestError(
@@ -93,7 +99,6 @@
         self._credential: TokenProvider = credential
         self._token: Optional[Union["AccessToken", "AccessTokenInfo"]] = None
         self._verify_challenge_resource = 
kwargs.pop("verify_challenge_resource", True)
-        self._request_copy: Optional[HttpRequest] = None
 
     def send(self, request: PipelineRequest[HttpRequest]) -> 
PipelineResponse[HttpRequest, HttpResponse]:
         """Authorize request with a bearer token and send it to the next 
policy.
@@ -184,8 +189,10 @@
         # saving it for later. Key Vault will reject the request as 
unauthorized and respond with a challenge.
         # on_challenge will parse that challenge, use the original request 
including the body, authorize the
         # request, and tell super to send it again.
-        if request.http_request.content:
-            self._request_copy = request.http_request
+        # The original request is stashed on the request's context 
(per-request), so it cannot leak into a later
+        # request made by the same client. Don't overwrite a previously 
stashed copy if this is a retry.
+        if request.http_request.content and _REQUEST_COPY_KEY not in 
request.context:
+            request.context[_REQUEST_COPY_KEY] = request.http_request
             bodiless_request = HttpRequest(
                 method=request.http_request.method,
                 url=request.http_request.url,
@@ -227,9 +234,10 @@
                     "See https://aka.ms/azsdk/blog/vault-uri for more 
information."
                 )
 
-        # If we had created a request copy in on_request, use it now to send 
along the original body content
-        if self._request_copy:
-            request.http_request = self._request_copy
+        # If we stashed the original request in on_request, use it now to send 
along the original body content
+        request_copy = request.context.get(_REQUEST_COPY_KEY)
+        if request_copy:
+            request.http_request = request_copy
 
         # The tenant parsed from AD FS challenges is "adfs"; we don't actually 
need a tenant for AD FS authentication
         # For AD FS we skip cross-tenant authentication per 
https://github.com/Azure/azure-sdk-for-python/issues/28648
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_version.py 
new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_version.py
--- old/azure_keyvault_secrets-4.11.0/azure/keyvault/secrets/_version.py        
2026-04-17 01:49:40.000000000 +0200
+++ new/azure_keyvault_secrets-4.11.1/azure/keyvault/secrets/_version.py        
2026-08-12 03:17:58.000000000 +0200
@@ -3,4 +3,4 @@
 # Licensed under the MIT License.
 # ------------------------------------
 
-VERSION = "4.11.0"
+VERSION = "4.11.1"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/azure_keyvault_secrets.egg-info/PKG-INFO 
new/azure_keyvault_secrets-4.11.1/azure_keyvault_secrets.egg-info/PKG-INFO
--- old/azure_keyvault_secrets-4.11.0/azure_keyvault_secrets.egg-info/PKG-INFO  
2026-04-17 01:50:26.000000000 +0200
+++ new/azure_keyvault_secrets-4.11.1/azure_keyvault_secrets.egg-info/PKG-INFO  
2026-08-12 03:19:09.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: azure-keyvault-secrets
-Version: 4.11.0
+Version: 4.11.1
 Summary: Microsoft Corporation Key Vault Secrets Client Library for Python
 Author-email: Microsoft Corporation <[email protected]>
 License: MIT License
@@ -406,6 +406,17 @@
 
 # Release History
 
+## 4.11.1 (2026-08-12)
+
+### Bugs Fixed
+
+- Fixed a replay bug in the challenge authentication policy where a request 
copy stashed on the shared policy
+  instance was never cleared, allowing one request's method, URL, and body to 
leak into a later, unrelated request
+  made by the same client (for example, after an Entra ID Continuous Access 
Evaluation challenge). The original
+  request is now stored per-request instead of on the policy instance. This 
mirrors the fix already applied to
+  `azure-keyvault-keys` and `azure-keyvault-administration` in
+  [#47742](https://github.com/Azure/azure-sdk-for-python/pull/47742).
+
 ## 4.11.0 (2026-03-26)
 
 ### Features Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/azure_keyvault_secrets.egg-info/SOURCES.txt 
new/azure_keyvault_secrets-4.11.1/azure_keyvault_secrets.egg-info/SOURCES.txt
--- 
old/azure_keyvault_secrets-4.11.0/azure_keyvault_secrets.egg-info/SOURCES.txt   
    2026-04-17 01:50:27.000000000 +0200
+++ 
new/azure_keyvault_secrets-4.11.1/azure_keyvault_secrets.egg-info/SOURCES.txt   
    2026-08-12 03:19:09.000000000 +0200
@@ -3,6 +3,7 @@
 MANIFEST.in
 README.md
 TROUBLESHOOTING.md
+api.md
 migration_guide.md
 pyproject.toml
 azure/__init__.py
@@ -66,6 +67,8 @@
 tests/_async_test_case.py
 tests/_test_case.py
 tests/conftest.py
+tests/test_challenge_auth.py
+tests/test_challenge_auth_async.py
 tests/test_context_manager.py
 tests/test_context_manager_async.py
 tests/test_multi_api.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/tests/test_challenge_auth.py 
new/azure_keyvault_secrets-4.11.1/tests/test_challenge_auth.py
--- old/azure_keyvault_secrets-4.11.0/tests/test_challenge_auth.py      
1970-01-01 01:00:00.000000000 +0100
+++ new/azure_keyvault_secrets-4.11.1/tests/test_challenge_auth.py      
2026-08-12 03:17:58.000000000 +0200
@@ -0,0 +1,106 @@
+# ------------------------------------
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+# ------------------------------------
+"""
+Tests for the HTTP challenge authentication implementation. These tests aren't 
parallelizable, because
+the challenge cache is global to the process.
+"""
+
+import functools
+import time
+from unittest.mock import Mock
+from uuid import uuid4
+
+import pytest
+from azure.core.credentials import AccessToken, AccessTokenInfo
+from azure.core.pipeline import Pipeline
+from azure.core.rest import HttpRequest
+from azure.keyvault.secrets._shared import ChallengeAuthPolicy, 
HttpChallengeCache
+
+TOKEN_TYPES = [AccessToken, AccessTokenInfo]
+
+
+def empty_challenge_cache(fn):
+    @functools.wraps(fn)
+    def wrapper(**kwargs):
+        HttpChallengeCache.clear()
+        assert len(HttpChallengeCache._cache) == 0
+        return fn(**kwargs)
+
+    return wrapper
+
+
+def get_random_url():
+    """The challenge cache is keyed on URLs. Random URLs defend against tests 
interfering with each other."""
+
+    return f"https://{uuid4()}.vault.azure.net/{uuid4()}".replace("-", "")
+
+
+@empty_challenge_cache
[email protected]("token_type", TOKEN_TYPES)
+def test_request_body_not_reused_across_requests(token_type):
+    """A request's body must not leak into a later request made by the same 
client.
+
+    Regression test for the replay bug: the original request used to be 
stashed on the policy instance and was
+    never cleared, so a subsequent bodiless request (e.g. a polling GET) that 
triggered its own challenge would
+    have the earlier request's body (and method/URL) replayed onto it. The 
copy is now stored per-request on the
+    pipeline context, so it cannot leak across requests. See
+    https://github.com/Azure/azure-sdk-for-python/pull/47742.
+    """
+
+    expected_token = "expected_token"
+    first_content = b"a duck"
+    first_url = get_random_url()
+    second_url = get_random_url()
+    challenge = Mock(
+        status_code=401,
+        headers={"WWW-Authenticate": 'Bearer 
authorization="https://authority.net/tenant";, 
resource=https://vault.azure.net'},
+    )
+
+    class Requests:
+        count = 0
+
+    def send(request):
+        Requests.count += 1
+        if Requests.count == 1:
+            # first request (POST with body): the body is stripped to elicit a 
challenge
+            assert not request.body
+            assert request.headers["Content-Length"] == "0"
+            return challenge
+        elif Requests.count == 2:
+            # first request is retried with its original body and authorization
+            assert request.body == first_content
+            assert expected_token in request.headers["Authorization"]
+            return Mock(status_code=200)
+        elif Requests.count == 3:
+            # second request (bodiless GET): elicits its own challenge and 
must have no body
+            assert not request.body
+            return challenge
+        elif Requests.count == 4:
+            # second request is retried: it must remain a bodiless GET, i.e. 
the first request's body and
+            # method/URL must NOT be replayed onto it
+            assert not request.body
+            assert request.method == "GET"
+            assert request.url == second_url
+            assert expected_token in request.headers["Authorization"]
+            return Mock(status_code=200)
+        raise ValueError("unexpected request")
+
+    def get_token(*_, **__):
+        return token_type(expected_token, time.time() + 3600)
+
+    if token_type == AccessToken:
+        credential = Mock(spec_set=["get_token"], 
get_token=Mock(wraps=get_token))
+    else:
+        credential = Mock(spec_set=["get_token_info"], 
get_token_info=Mock(wraps=get_token))
+
+    # a single policy instance handles both requests; the fix prevents state 
from one request leaking into the next
+    policy = ChallengeAuthPolicy(credential=credential)
+    pipeline = Pipeline(policies=[policy], transport=Mock(send=send))
+
+    first_request = HttpRequest("POST", first_url)
+    first_request.set_bytes_body(first_content)
+    pipeline.run(first_request)
+
+    pipeline.run(HttpRequest("GET", second_url))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure_keyvault_secrets-4.11.0/tests/test_challenge_auth_async.py 
new/azure_keyvault_secrets-4.11.1/tests/test_challenge_auth_async.py
--- old/azure_keyvault_secrets-4.11.0/tests/test_challenge_auth_async.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/azure_keyvault_secrets-4.11.1/tests/test_challenge_auth_async.py        
2026-08-12 03:17:58.000000000 +0200
@@ -0,0 +1,100 @@
+# ------------------------------------
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+# ------------------------------------
+"""
+Tests for the HTTP challenge authentication implementation. These tests aren't 
parallelizable, because
+the challenge cache is global to the process.
+"""
+
+import functools
+import time
+from unittest.mock import Mock
+
+import pytest
+from azure.core.credentials import AccessToken, AccessTokenInfo
+from azure.core.pipeline import AsyncPipeline
+from azure.core.rest import HttpRequest
+from azure.keyvault.secrets._shared import AsyncChallengeAuthPolicy, 
HttpChallengeCache
+
+from test_challenge_auth import get_random_url, TOKEN_TYPES
+
+
+def empty_challenge_cache(fn):
+    @functools.wraps(fn)
+    async def wrapper(**kwargs):
+        HttpChallengeCache.clear()
+        assert len(HttpChallengeCache._cache) == 0
+        return await fn(**kwargs)
+
+    return wrapper
+
+
[email protected]
+@empty_challenge_cache
[email protected]("token_type", TOKEN_TYPES)
+async def test_request_body_not_reused_across_requests(token_type):
+    """A request's body must not leak into a later request made by the same 
client.
+
+    Regression test for the replay bug: the original request used to be 
stashed on the policy instance and was
+    never cleared, so a subsequent bodiless request (e.g. a polling GET) that 
triggered its own challenge would
+    have the earlier request's body (and method/URL) replayed onto it. The 
copy is now stored per-request on the
+    pipeline context, so it cannot leak across requests. See
+    https://github.com/Azure/azure-sdk-for-python/pull/47742.
+    """
+
+    expected_token = "expected_token"
+    first_content = b"a duck"
+    first_url = get_random_url()
+    second_url = get_random_url()
+    challenge = Mock(
+        status_code=401,
+        headers={"WWW-Authenticate": 'Bearer 
authorization="https://authority.net/tenant";, 
resource=https://vault.azure.net'},
+    )
+
+    class Requests:
+        count = 0
+
+    async def send(request):
+        Requests.count += 1
+        if Requests.count == 1:
+            # first request (POST with body): the body is stripped to elicit a 
challenge
+            assert not request.body
+            assert request.headers["Content-Length"] == "0"
+            return challenge
+        elif Requests.count == 2:
+            # first request is retried with its original body and authorization
+            assert request.body == first_content
+            assert expected_token in request.headers["Authorization"]
+            return Mock(status_code=200)
+        elif Requests.count == 3:
+            # second request (bodiless GET): elicits its own challenge and 
must have no body
+            assert not request.body
+            return challenge
+        elif Requests.count == 4:
+            # second request is retried: it must remain a bodiless GET, i.e. 
the first request's body and
+            # method/URL must NOT be replayed onto it
+            assert not request.body
+            assert request.method == "GET"
+            assert request.url == second_url
+            assert expected_token in request.headers["Authorization"]
+            return Mock(status_code=200)
+        raise ValueError("unexpected request")
+
+    async def get_token(*_, **__):
+        return token_type(expected_token, time.time() + 3600)
+
+    if token_type == AccessToken:
+        credential = Mock(spec_set=["get_token"], 
get_token=Mock(wraps=get_token))
+    else:
+        credential = Mock(spec_set=["get_token_info"], 
get_token_info=Mock(wraps=get_token))
+
+    # a single policy instance handles both requests; the fix prevents state 
from one request leaking into the next
+    policy = AsyncChallengeAuthPolicy(credential=credential)
+    pipeline = AsyncPipeline(policies=[policy], transport=Mock(send=send))
+
+    first_request = HttpRequest("POST", first_url)
+    first_request.set_bytes_body(first_content)
+    await pipeline.run(first_request)
+
+    await pipeline.run(HttpRequest("GET", second_url))

Reply via email to