[EventGrid] Fix credential leak on cross-host redirect in legacy publisher (MSRC 126697) - #48239
Conversation
…her (MSRC 126697)
…her (MSRC 126697)
|
Azure Pipelines: Successfully started running 1 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Fixes cross-host redirect credential leakage in the legacy Event Grid publisher clients.
Changes:
- Adds sensitive-header cleanup to sync and async pipelines.
- Adds redirect regression tests for all credential types.
- Bumps the patch version and updates the changelog.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_eg_redirect.py |
Tests credential stripping after redirects. |
CHANGELOG.md |
Documents the security fix. |
_version.py |
Bumps version to 4.22.1. |
_legacy/_publisher_client.py |
Adds sync header cleanup. |
_legacy/aio/_publisher_client_async.py |
Adds async header cleanup. |
Comments suppressed due to low confidence (2)
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/_publisher_client.py:138
- This remains vulnerable when the redirected host returns a retryable response. The package still permits
azure-core>=1.30.0, butSensitiveHeaderCleanupPolicydid not preserve the cross-domain flag across retries until azure-core 1.38.3; on older supported versions the retry auth pass can re-add the credential after cleanup. Require azure-core 1.38.3+ (adjusting Python support metadata as needed), or implement equivalent retry-safe cleanup locally, and cover a 301 -> 500 -> 200 sequence.
SensitiveHeaderCleanupPolicy(
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/aio/_publisher_client_async.py:132
- The async pipeline has the same retry gap: with the declared
azure-core>=1.30.0, versions before 1.38.3 lose the cross-domain flag after the first redirected send, allowing the async auth policy to restore the credential on a retry to the new host. Require azure-core 1.38.3+ (and reconcile Python support), or implement retry-safe cleanup locally, with a 301 -> 500 -> 200 regression test.
SensitiveHeaderCleanupPolicy(
This comment has been minimized.
This comment has been minimized.
|
Thanks for the review — addressed all comments: 1. Inaccurate policy comment (sync + async): Reworded to note that 2. Retry gap before azure-core 1.38.3: Raised the minimum dependency to 3. Async AAD test used a sync credential: Added an 4. Python 3.8 vs azure-core 1.38.3: azure-core dropped 3.8 in 1.33.0, so bumped Note on the CI failures ( |
|
Addressed all three review comments: 1 & 2. Duplicate blocked_redirect_headers = [
"Authorization", "x-ms-authorization-auxiliary", "aeg-sas-key", "aeg-sas-token",
*kwargs.pop("blocked_redirect_headers", []),
]
...
SensitiveHeaderCleanupPolicy(blocked_redirect_headers=blocked_redirect_headers, **kwargs)(The pop is scoped to
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_model_base.py:780
- This added pragma pushes the line past the 120-character pylint limit configured at
pylintrc:25. Reflow it or include the resultingline-too-longdiagnostic in the targeted suppression.
next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore # pylint: disable=unidiomatic-typecheck
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_model_base.py:786
- The appended suppression takes this line beyond the repository's 120-character maximum (
pylintrc:25), which can make pylint fail withline-too-long. Please wrap the expression or suppress that diagnostic explicitly.
annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore # pylint: disable=unidiomatic-typecheck
|
/azp run python - pullrequest |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
Re: the
No change needed; resolving. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/_publisher_client.py:133
- An explicit
blocked_redirect_headers=Noneis valid forSensitiveHeaderCleanupPolicy, but unpacking it here raisesTypeErrorduring client construction. Normalize the optional value before merging so the policy's documented default form remains usable.
*kwargs.pop("blocked_redirect_headers", []),
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/aio/_publisher_client_async.py:127
- An explicit
blocked_redirect_headers=Noneis valid forSensitiveHeaderCleanupPolicy, but unpacking it here raisesTypeErrorduring async client construction. Normalize the optional value before merging so the policy's documented default form remains usable.
*kwargs.pop("blocked_redirect_headers", []),
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/_publisher_client.py:133
SensitiveHeaderCleanupPolicyacceptsblocked_redirect_headers=None, but explicitly forwarding that value through the client now raisesTypeError: Value after * must be an iterablebefore the pipeline is created. TreatNoneas “no additional headers” while retaining the mandatory Event Grid headers.
*kwargs.pop("blocked_redirect_headers", []),
sdk/eventgrid/azure-eventgrid/azure/eventgrid/_legacy/aio/_publisher_client_async.py:127
- The async client has the same regression: explicitly passing
blocked_redirect_headers=None, which is valid forSensitiveHeaderCleanupPolicy, attempts to unpackNoneand fails during construction. NormalizeNoneto an empty list before merging the mandatory headers.
*kwargs.pop("blocked_redirect_headers", []),
|
Thanks @kashifkhan — updated the baseline to Python 3.10 per your feedback:
(The bump was originally driven by |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
sdk/eventgrid/azure-eventgrid/setup.py:69
- The PR describes 4.22.1 as a backward-compatible security patch, but this change makes the patch release uninstallable on Python 3.8 and 3.9, which 4.22.0 supported. Please either retain the existing Python floor for this patch release, or use an appropriate non-patch release and remove the backward-compatible claim; the README, classifiers, changelog, and version must remain aligned with that choice.
python_requires=">=3.10",
Summary
Fixes a credential-leak vulnerability (MSRC 126697 / ICM 31000000661197) in the legacy
EventGridPublisherClient.When the server returns an HTTP 3xx redirect to a different host, the client re-attached the caller's credential header to the redirected request and sent it to the new host, disclosing the credential.
Root cause: the legacy client hand-rolls its pipeline (
_policies()) and omitted azure-core'sSensitiveHeaderCleanupPolicy. The stateless auth policies re-add the credential header on every redirect hop;RedirectPolicyflags the cross-domain hop (insecure_domain_change) but nothing consumed that flag to strip the header.Fix
Add
SensitiveHeaderCleanupPolicyafter the authentication policy in both the sync (_legacy/_publisher_client.py) and async (_legacy/aio/_publisher_client_async.py) pipelines. The policy's default only coversAuthorization, so the Event Grid SAS headers are added explicitly:This covers all three credential types:
TokenCredential) ->AuthorizationAzureKeyCredential) ->aeg-sas-keyAzureSasCredential) ->aeg-sas-tokenTests
tests/test_eg_redirect.pydrives the pipeline through a mock transport that issues a cross-host 301 redirect and asserts the credential header is stripped from the redirected request, for all three credential types, sync and async.Changelog / version
4.22.0->4.22.1(backward-compatible security patch).Bugs FixedCHANGELOG entry.Notes
aeg-sas-key/aeg-sas-tokenleak by default) and as latent hardening gaps in JS/Java/.NET; those are tracked separately.