mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: address PR review on declarative toolbox sample
Two security fixes for PR #5933: 1. Add safe_mode flag to WorkflowFactory (default True) mirroring AgentFactory. Gates =Env.* exposure inside DeclarativeWorkflowState PowerFx symbols via _safe_mode_context, so workflow YAML loaded from untrusted sources no longer leaks the host's full os.environ snapshot into PowerFx evaluation. The flag is also forwarded to the internally-constructed AgentFactory so inline agent definitions follow the same policy. 2. Pin the invoke_foundry_toolbox_mcp sample's _client_provider to the resolved toolbox endpoint. The bearer-authenticated httpx client is now only returned when MCPToolInvocation.server_url matches the toolbox URL case-insensitively; any other URL gets None (the default unauthenticated path), preventing the Foundry AAD bearer token from being attached to a mis-configured or injected server URL. Mirrors the .NET sample's httpClientProvider guard. The sample is updated to opt in to safe_mode=False because its YAML intentionally uses =Env.FOUNDRY_TOOLBOX_* to keep configuration in env vars under the developer's control. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -93,6 +93,7 @@ import asyncio
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -445,7 +446,23 @@ async def main() -> None:
|
||||
follow_redirects=True,
|
||||
)
|
||||
|
||||
async def _client_provider(_inv: MCPToolInvocation) -> httpx.AsyncClient | None:
|
||||
async def _client_provider(invocation: MCPToolInvocation) -> httpx.AsyncClient | None:
|
||||
# Pin the bearer-authenticated client to the resolved toolbox URL.
|
||||
# The Foundry AAD bearer token is scoped to ``https://ai.azure.com``
|
||||
# but we still refuse to attach it to any URL we did not provision —
|
||||
# if the YAML resolves a different ``serverUrl`` (e.g. via a tampered
|
||||
# ``Env.*`` value or a config injection), returning ``None`` causes
|
||||
# ``DefaultMCPToolHandler`` to fall back to an unauthenticated client,
|
||||
# which will fail to authenticate to the proxy instead of forwarding
|
||||
# the token outbound. Mirrors the .NET sample's
|
||||
# ``httpClientProvider`` URL guard.
|
||||
if invocation.server_url.casefold() != toolbox_endpoint.casefold():
|
||||
print(
|
||||
f"[security] Refusing to attach Foundry bearer token to unexpected MCP URL: "
|
||||
f"{invocation.server_url}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return None
|
||||
return http_client
|
||||
|
||||
async with (
|
||||
@@ -456,6 +473,14 @@ async def main() -> None:
|
||||
factory = WorkflowFactory(
|
||||
agents={AGENT_NAME: summary_agent},
|
||||
mcp_tool_handler=mcp_handler,
|
||||
# The workflow YAML references ``=Env.FOUNDRY_TOOLBOX_*`` to keep
|
||||
# the sample's toolbox URL / tool names configurable without
|
||||
# editing the YAML. ``WorkflowFactory`` defaults to ``safe_mode=True``
|
||||
# which would block those expressions; this sample opts in to the
|
||||
# less-safe mode because we control both the YAML and the env
|
||||
# vars. Do NOT copy this flag into a workflow that loads YAML
|
||||
# from untrusted sources.
|
||||
safe_mode=False,
|
||||
)
|
||||
|
||||
workflow_path = Path(__file__).parent / "workflow.yaml"
|
||||
|
||||
Reference in New Issue
Block a user