Skip to content

[BREAKING] Python: Refine SecretString handling - #8127

Open
Eduard van Valkenburg (eavanvalkenburg) wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:secretstring-masking
Open

[BREAKING] Python: Refine SecretString handling#8127
Eduard van Valkenburg (eavanvalkenburg) wants to merge 2 commits into
microsoft:mainfrom
eavanvalkenburg:secretstring-masking

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation & Context

Make settings-value behavior more consistent across Python formatting and provider integrations.

Description & Review Guide

  • What are the major changes? Refine SecretString behavior and align provider credential handling and type annotations.
  • What is the impact of these changes? SecretString is now a value wrapper rather than a str subclass. Callers needing a plain string should use get_secret_value(). Plain-string settings inputs remain supported.
  • What do you want reviewers to focus on? Consistent settings behavior and compatibility at provider boundaries.

Related Issue

N/A.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI balanced review requested due to automatic review settings September 7, 2026 18:20
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible labels Sep 7, 2026

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.

🟡 Changes recommended

The hashable wrapper remains mutable, and public OpenAI constructor annotations still reject supported SecretString values.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Refines Python secret handling by making SecretString a masked value wrapper and explicitly unwrapping credentials at provider SDK boundaries.

Changes:

  • Adds masked formatting, concatenation, equality, and validation semantics.
  • Updates provider credential annotations and SDK integrations.
  • Expands tests across supported providers.
File summaries
File Description
python/packages/core/agent_framework/_settings.py Implements the new wrapper and coercion.
python/packages/core/tests/core/test_settings.py Tests masking and value semantics.
python/packages/core/AGENTS.md Documents settings conventions.
python/packages/openai/tests/openai/test_openai_shared.py Tests SDK-boundary unwrapping.
python/packages/mistral/tests/mistral/test_mistral_embedding_client.py Tests embedding credentials.
python/packages/mistral/tests/mistral/test_mistral_chat_client.py Tests chat credentials.
python/packages/gemini/agent_framework_gemini/_chat_client.py Accepts wrapped API keys.
python/packages/gemini/tests/test_gemini_client.py Tests Gemini key resolution.
python/packages/bedrock/agent_framework_bedrock/_chat_client.py Updates credential annotations.
python/packages/bedrock/agent_framework_bedrock/_embedding_client.py Updates credential annotations.
python/packages/bedrock/tests/test_bedrock_client.py Tests session credential unwrapping.
python/packages/azure-cosmos/agent_framework_azure_cosmos/_history_provider.py Unwraps Cosmos credentials.
python/packages/azure-cosmos/agent_framework_azure_cosmos/_checkpoint_storage.py Unwraps checkpoint credentials.
python/packages/azure-cosmos/tests/test_cosmos_history_provider.py Tests history credentials.
python/packages/azure-cosmos/tests/test_cosmos_checkpoint_storage.py Tests checkpoint credentials.
python/packages/azure-ai-search/agent_framework_azure_ai_search/_context_provider.py Supports wrapped search keys.
python/packages/azure-ai-search/tests/test_aisearch_context_provider.py Tests search key boundaries.
python/packages/anthropic/agent_framework_anthropic/_chat_client.py Accepts wrapped API keys.
python/packages/anthropic/agent_framework_anthropic/_foundry_client.py Accepts wrapped Foundry keys.
python/packages/anthropic/agent_framework_anthropic/_bedrock_client.py Accepts wrapped AWS credentials.
python/packages/anthropic/tests/test_anthropic_client.py Tests Anthropic key handling.
python/packages/anthropic/tests/test_anthropic_provider_clients.py Tests provider credential boundaries.
Review details
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Balanced

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

Comment thread python/packages/core/agent_framework/_settings.py
Comment thread python/packages/core/agent_framework/_settings.py Outdated

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 4ceefd0b6ff1
Model: gpt-5.6-sol-fast

Overview

The PR replaces the str subclass with a masking value wrapper and adds explicit unwrapping at most provider SDK boundaries, backed by broad settings and provider tests. The masking, coercion, and direct SDK-boundary guards are strong, but the provider migration is incomplete: Foundry embeddings reject wrapped credentials at runtime, while the public OpenAI client signatures reject them during static checking despite runtime support.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (2 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_settings.py

Comment thread python/packages/core/agent_framework/_settings.py
Comment thread python/packages/core/agent_framework/_settings.py
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment on lines +90 to +92
def __setattr__(self, name: str, value: object) -> None:
"""Reject mutation after construction."""
raise AttributeError("SecretString is immutable.")

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.

Could the immutable wrapper implement the copy protocol? Both copy.copy(SecretString(...)) and copy.deepcopy(...) now raise this AttributeError, so State.set() and SessionStore.set() also crash when their otherwise arbitrary state contains a secret. Since the value is immutable, could __copy__ and __deepcopy__ return self?


class SecretString(str):
"""A string subclass that masks its value in repr() to prevent accidental exposure.
class SecretString:

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.

Before dropping str inheritance, should we migrate the remaining credential boundaries? Mem0ContextProvider(api_key=SecretString(...)) now crashes when the SDK calls encode(), while AnthropicVertexClient(access_token=...) and CopilotStudioAgent(token=...) pass the wrapper through and format the mask into authorization headers. Could those annotations accept SecretString and unwrap with get_secret_value() at the SDK boundary, as the migrated providers do?

Comment on lines +178 to +179
assert type(client.anthropic_client.api_key) is str
assert client.anthropic_client.api_key == anthropic_unit_test_env["ANTHROPIC_API_KEY"]

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.

Could we narrow client.anthropic_client before accessing api_key here? Its declared union includes AsyncAnthropicVertex, which has no api_key, so Zuban, mypy, and pyright all reject these new assertions and the PR's typing check currently fails.

Comment on lines +176 to +177
assert settings["api_key"] is secret
assert settings["api_key"].get_secret_value() == "my-secret"

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.

Would it make sense to bind and narrow settings["api_key"] before calling get_secret_value()? SecretSettings declares this entry as optional, and the identity assertion does not narrow a second indexed access, so pyright reports reportOptionalMemberAccess and keeps the typing gate red.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants