Skip to content

[azure-core] In-place set operators on HttpLoggingPolicy.allowed_header_names are silently ignored #49607

Open

Description

Library: azure-core 1.41.0 (also current main)

What happens

HttpLoggingPolicy.allowed_header_names and the allowed_query_params sets of HttpLoggingPolicy and DistributedTracingPolicy are now CaseInsensitiveSet instances. CaseInsensitiveSet subclasses the built-in set and keeps a separate lowercase cache that __contains__ reads. Only add, discard, remove, update, clear and pop maintain that cache. The inherited in-place operators (|=, -=, &=, ^=) and difference_update, intersection_update and symmetric_difference_update change the underlying set but not the cache, so membership checks no longer match the contents.

In practice:

  • policy.allowed_header_names |= {"x-custom-header"} has no effect: the header is still logged as REDACTED, even though len() and iteration show it.
  • policy.allowed_header_names -= {"x-ms-request-id"} has no effect: the header is still logged in clear text.

Both statements worked when these attributes were plain set objects.

What should happen

Every mutating set operation should keep the set consistent, so the allowlists behave like a normal set with case-insensitive lookups.

Minimal repro

from azure.core.pipeline.policies import HttpLoggingPolicy

policy = HttpLoggingPolicy()
policy.allowed_header_names |= {"x-custom-header"}
print("x-custom-header" in policy.allowed_header_names)        # False, expected True
print(policy._redact_header("x-custom-header", "value"))       # REDACTED, expected value

policy.allowed_header_names -= {"x-ms-request-id"}
print("x-ms-request-id" in policy.allowed_header_names)        # True, expected False
print(policy._redact_header("x-ms-request-id", "value"))       # value, expected REDACTED

Root cause

azure/core/utils/_utils.py: CaseInsensitiveSet(set) overrides __contains__ to look only at _lower_to_original, but does not override the in-place operators or the *_update methods, whose C implementations write to the base set directly. The equivalent class in corehttp derives from MutableSet and does not have this problem, because MutableSet implements those operations on top of add and discard.

Activity

  1. added
    questionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that
    ClientThis issue points to a problem in the data-plane of the library.
    customer-reportedIssues that are reported by GitHub users external to the Azure organization.
    needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK team
    on Oct 19, 2026
  2. github-actions commented on Oct 19, 2026

    @github-actions
    Contributor

    Thank you for your feedback. Tagging and routing to the team member(s) best able to assist.

    Generated by Agentic Triage for #49607 · copilot · sonnet50 · 25.1 AIC · ⌖ 9.53 AIC · ⊞ 13K · ◷

  3. github-actions commented on Oct 19, 2026

    @github-actions
    Contributor

    🎯 Agentic Issue Triage

    Summary: CaseInsensitiveSet in azure-core doesn't override the inherited in-place set operators (|=, -=, &=, ^=) or the *_update methods, so these mutate the underlying set without updating the internal lowercase cache, causing HttpLoggingPolicy.allowed_header_names/allowed_query_params and DistributedTracingPolicy.allowed_query_params membership checks to silently diverge from the set's actual contents.

    📋 Issue Details

    Package: azure-core 1.41.0 (also reproduces on main)
    Affected API: azure.core.utils._utils.CaseInsensitiveSet, used by HttpLoggingPolicy.allowed_header_names, HttpLoggingPolicy.allowed_query_params, and DistributedTracingPolicy.allowed_query_params
    Scenarios:

    • policy.allowed_header_names |= {"x-custom-header"} adds the item to the underlying set (visible via len()/iteration) but "x-custom-header" in policy.allowed_header_names still returns False, so the header remains redacted.
    • policy.allowed_header_names -= {"x-ms-request-id"} removes the item from the underlying set, but "x-ms-request-id" in policy.allowed_header_names still returns True, so the header is not redacted when it should be.

    Root ask: Make CaseInsensitiveSet keep its _lower_to_original cache consistent across all mutating operations (in-place operators and difference_update/intersection_update/symmetric_difference_update), matching the behavior of a plain set prior to this change, or match the corehttp equivalent which derives from MutableSet.

    🔎 Debugging / Reproduction Notes

    The reporter already did root-cause analysis: CaseInsensitiveSet subclasses the built-in set and overrides only add, discard, remove, update, clear, and pop to maintain a _lower_to_original lowercase cache used by __contains__. The C-implemented in-place operators (|=, -=, &=, ^=) and the *_update methods bypass these overrides and mutate the base set storage directly, leaving the cache stale. The reporter notes the corehttp equivalent avoids this bug because it derives from collections.abc.MutableSet, which builds all set operations on top of add/discard.

    A fix PR (#49608) has already been opened by the reporter: "[azure-core] Keep CaseInsensitiveSet consistent for in-place set operations".

    Suggested investigation steps:

    1. Review PR [azure-core] Keep CaseInsensitiveSet consistent for in-place set operations #49608 for correctness and test coverage of all affected operators/methods (|=, -=, &=, ^=, difference_update, intersection_update, symmetric_difference_update).
    2. Confirm HttpLoggingPolicy and DistributedTracingPolicy behavior is correctly restored for both header and query-param allowlists.
    3. Consider whether migrating CaseInsensitiveSet to a MutableSet-based implementation (like corehttp) would be a more robust long-term fix.
    🏷️ Label Confidence
    • Category: Client — azure-core is a shared data-plane library, not under azure-mgmt-*.
    • Service: Azure.Core — issue explicitly names azure-core and azure/core/utils/_utils.py.
    • Confidence: High — package and file path are unambiguous, no other service plausible.
    👥 Owner Routing
    • Matched CODEOWNERS entry: # ServiceLabel: %Azure.Core (line 19) — exact match for predicted service label, found scanning bottom-to-top.
    • AzureSdkOwners: @kashifkhan
    • ServiceOwners: none listed
    • Routing action: Assigned @kashifkhan; added needs-team-attention since issue is customer-reported.
    • Scan notes: The %Core.Http entry (also owned by @kashifkhan) was scanned first but does not match the predicted "Azure.Core" label, so it was skipped.

    Note: a fix PR (#49608) is already linked to this issue.

    Generated by Agentic Triage for #49607 · copilot · sonnet50 · 25.1 AIC · ⌖ 9.53 AIC · ⊞ 13K · ◷

  4. kashifkhan commented on Oct 19, 2026

    @kashifkhan
    Member

    Hi Sebastian Turner (@auraseb), thank you for the issue and PR. Can you point to an example when this happened while using an SDK scenario please?

  5. removed
    needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK team
    on Oct 19, 2026
  6. github-actions commented on Oct 19, 2026

    @github-actions
    Contributor

    Hi Sebastian Turner (@auraseb). Thank you for opening this issue and giving us the opportunity to assist. To help our team better understand your issue and the details of your scenario please provide a response to the question asked above or the information requested above. This will help us more accurately address your issue.

  7. auraseb commented on Oct 19, 2026

    @auraseb
    Author

    Thanks for looking. I found it reading the policy code after the allowlists became CaseInsensitiveSet, not from a production incident, so I do not have a customer trace to point to.

    The scenario is customizing logging on a client, for example policy.allowed_header_names |= {"x-ms-client-request-id"} to stop a header from being redacted, or policy.allowed_header_names -= {"x-ms-request-id"} to keep one out of logs. Both worked while these were plain sets, and code written that way now silently does nothing after upgrading. The second case is the one that matters more, since a header someone meant to hide keeps being logged in clear text.

    I also pushed a fix for the mypy errors in the PR.

  8. added
    needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK team
    and removed
    needs-author-feedbackWorkflow: More information is needed from author to address the issue.
    on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Azure.CoreClientThis issue points to a problem in the data-plane of the library.customer-reportedIssues that are reported by GitHub users external to the Azure organization.needs-team-attentionWorkflow: This issue needs attention from Azure service team or SDK teamquestionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions