Skip to content

[azure-core] Warn when an LRO poll target host differs from the client's endpoint - #48804

Open
Anna Tchijova (annatchijova) wants to merge 1 commit into
Azure:mainfrom
annatchijova:fix/azure-core-lro-poll-host-cleanup
Open

[azure-core] Warn when an LRO poll target host differs from the client's endpoint#48804
Anna Tchijova (annatchijova) wants to merge 1 commit into
Azure:mainfrom
annatchijova:fix/azure-core-lro-poll-host-cleanup

Conversation

@annatchijova

Copy link
Copy Markdown

Summary

Long-running-operation (LRO) polling reads the next poll URL from a service response header (Operation-Location / Azure-AsyncOperation / Location) and sends that GET through the client's credentialed pipeline (LROBasePolling.request_status, sync and async). There is no check that the poll target host matches the host the client was configured for, so when the response names a different host, the poll request carries the client's credentials (API key or bearer token) to that host.

SensitiveHeaderCleanupPolicy already strips sensitive headers on cross-domain 3xx redirects, but the LRO poll target reaches a new host without a 3xx redirect, so that policy does not cover this path.

Change

Add a defense-in-depth warning (it does not block the request) in both the sync and async request_status paths when the poll target host differs from the initial request host, reusing the existing get_domain helper for the comparison.

  • Same-host poll targets and relative (no-host) targets are quiet.
  • The warning names both hosts so operators can see when credentials would go to an unconfigured host.
_LOGGER.warning(
    "Long-running-operation poll target host '%s' differs from the client's configured "
    "endpoint host '%s'. The poll request carries the client's credentials to that host. ...",
    poll_domain, initial_domain,
)

Why a warning, not a block (question for maintainers)

I deliberately left this as a warning rather than a hard block or a credential strip. A raise-on-mismatch broke 16 existing test_base_polling tests, because several fixtures configure the client on one host (http://example.org) while the Operation-Location/Location header points to another (http://dummyurl...). That could be test-only sloppiness, or it could reflect services that legitimately poll a different host — I can't tell from outside.

Question: Is credentialed cross-host LRO polling ever legitimate for shipping Azure services (regional/failover endpoints, sovereign clouds, private endpoints, Traffic-Manager-fronted services)? If not, this could be escalated to strip the credential (mirroring SensitiveHeaderCleanupPolicy) or reject the poll. I kept it non-breaking so the decision — which needs the service inventory only Microsoft has — stays with you.

Testing

  • New test_warn_on_cross_host_poll_target_warns_only_when_host_differs: warns on cross-host; quiet on same-host, same-host-with-port, and relative targets.
  • Existing test_base_polling suite unchanged (no fixtures needed rewriting, since this does not block). pylint 10.00/10 on the changed modules.

Context

Reported privately to MSRC (assessed None severity / defense-in-depth; host-validation gap acknowledged). Filing this as a public hardening improvement per that assessment.

…t's endpoint

Long-running-operation polling reads the next poll URL from a service response
header (Operation-Location / Azure-AsyncOperation / Location) and sends that GET
through the client's credentialed pipeline. When the response names a host the
client was not configured for, the poll request carries the client's credentials
(API key or bearer token) to that host. SensitiveHeaderCleanupPolicy strips
sensitive headers on cross-domain 3xx redirects, but the LRO poll target reaches a
new host without a 3xx redirect, so that policy does not cover it.

This adds a defense-in-depth warning (it does not block the request) in both the
sync and async request_status paths when the poll target host differs from the
initial request host, reusing get_domain for the comparison. Same-host and
relative (no-host) poll targets are quiet, so services that legitimately poll the
same host are unaffected.

Left as a warning rather than a hard block deliberately: some services may poll a
different host legitimately, and blocking by default would break them. A question
for maintainers is on the PR about whether to escalate to a strip/reject.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWmkpCnuhCPKFPD2fexY1a
Copilot AI balanced review requested due to automatic review settings August 30, 2026 15:41
@github-actions github-actions Bot added the Community Contribution Community members are working on the issue label Aug 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution Anna Tchijova (@annatchijova)! We will review the pull request and get back to you soon.

@github-actions github-actions Bot added the customer-reported Issues that are reported by GitHub users external to the Azure organization. label Aug 30, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
5 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds defense-in-depth warnings for credentialed cross-host LRO polling.

Changes:

  • Warns in synchronous and asynchronous polling paths.
  • Documents the behavior and adds host-comparison tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
base_polling.py Implements warning logic and sync integration.
async_base_polling.py Adds async integration.
test_base_polling.py Tests warning behavior.
CHANGELOG.md Documents the hardening change.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +249 to +251
initial_domain = get_domain(initial_request_url)
poll_domain = get_domain(status_link)
if initial_domain and poll_domain and poll_domain != initial_domain:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution Community members are working on the issue customer-reported Issues that are reported by GitHub users external to the Azure organization.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants