diff --git a/python/packages/core/agent_framework/_compaction.py b/python/packages/core/agent_framework/_compaction.py index 3644bf4a9cc..00c610a0a98 100644 --- a/python/packages/core/agent_framework/_compaction.py +++ b/python/packages/core/agent_framework/_compaction.py @@ -955,6 +955,17 @@ class SummarizationStrategy: ``target_count`` (subject to atomic group boundaries). It writes trace metadata in both directions: summary -> original message/group IDs and original -> summary ID. + + Security considerations: + Unlike strategies that only remove or reorder existing messages (which carry no additional + risk), this strategy calls out to an LLM to produce replacement summary content that + permanently becomes part of chat history and is trusted the same as any other assistant + message going forward. Using it is an explicit opt-in — it must be constructed with a + summarization ``client``. A compromised or malicious summarization service could therefore + return a summary containing unsafe instructions, which become a persistent part of the + conversation — a form of indirect prompt injection that survives beyond the turn in which it + was introduced. Only point ``client`` at a summarization service you trust as much as the + primary model. """ def __init__( @@ -969,7 +980,10 @@ def __init__( Keyword Args: client: A chat client compatible with ``SupportsChatGetResponse`` - used to generate summary text. + used to generate summary text. **Security:** its output permanently replaces the + original messages in chat history, so only use a summarization service you trust as + much as the primary model — see the class-level security considerations for the + indirect-prompt-injection risk of an untrusted summarizer. target_count: Target number of included non-system messages to retain after summarization. Must be greater than 0. threshold: Extra included non-system messages allowed above diff --git a/python/packages/core/agent_framework/_harness/_agent.py b/python/packages/core/agent_framework/_harness/_agent.py index 107f2708037..53b32296aeb 100644 --- a/python/packages/core/agent_framework/_harness/_agent.py +++ b/python/packages/core/agent_framework/_harness/_agent.py @@ -401,6 +401,9 @@ def create_harness_agent( (default), they require approval. Ignored when disable_file_access is True. skills_provider: Custom SkillsProvider instance for code-defined skills. Can be combined with ``skills_paths`` to aggregate file and code-based skills. + **Security:** if the provider is configured with an external skill source (e.g. + :class:`~agent_framework.MCPSkillsSource`), the skill content it loads is untrusted input + — only enable sources you trust; see :class:`~agent_framework.SkillsSource`. skills_paths: Paths for file-based skill discovery (looks for SKILL.md files). Accepts a single ``str`` or :class:`~pathlib.Path`, or a sequence of ``str | Path``. Can be combined with ``skills_provider``. When neither @@ -410,6 +413,10 @@ def create_harness_agent( When provided, a ``BackgroundAgentsProvider`` is automatically included, enabling the agent to start, monitor, and retrieve results from background tasks. Each agent must have a non-empty, unique name (case-insensitive). + **Security:** supplied agents receive text input from this agent and their output is fed + back into its context, so only supply agents you have vetted and trust — see + :class:`~agent_framework.BackgroundAgentsProvider` for the exfiltration and + prompt-injection risks of untrusted agents. background_agents_instructions: Optional instruction override for the ``BackgroundAgentsProvider``. May include ``{background_agents}`` placeholder which will be replaced with the agent listing. diff --git a/python/packages/core/agent_framework/_harness/_background_agents.py b/python/packages/core/agent_framework/_harness/_background_agents.py index 4bd7d65975a..22fac3bbd44 100644 --- a/python/packages/core/agent_framework/_harness/_background_agents.py +++ b/python/packages/core/agent_framework/_harness/_background_agents.py @@ -253,6 +253,15 @@ class BackgroundAgentsProvider(ContextProvider): - ``background_agents_get_all_tasks`` — List all background tasks with their IDs, statuses, and descriptions. - ``background_agents_continue_task`` — Send follow-up input to a completed task's session to resume work. - ``background_agents_clear_completed_task`` — Remove a completed task and release its session. + + Security considerations: + The agents passed to the constructor are delegated arbitrary work by the parent agent — the + parent sends them text input (which may include content derived from the parent's own + untrusted context) and receives back whatever text they produce. A compromised or malicious + supplied agent (for example, one with a compromised system prompt, tools, or upstream model) + could exfiltrate that input to an external system, or return adversarial output designed to + influence the parent agent via indirect prompt injection once its result is retrieved. Only + supply background agents you have vetted and trust with the data the parent may pass to them. """ def __init__( @@ -267,6 +276,10 @@ def __init__( Args: agents: Collection of background agents available for delegation. Each agent must have a non-empty, unique name (case-insensitive). + **Security:** each supplied agent should be vetted and trusted, since it will receive + text input from the parent agent and its output is fed back into the parent's + context — see the class-level security considerations for the exfiltration and + prompt-injection risks of untrusted agents. Keyword Args: source_id: Unique source ID for serializable task state in session. diff --git a/python/packages/core/agent_framework/_harness/_loop.py b/python/packages/core/agent_framework/_harness/_loop.py index 8f9a5e7be9e..7640d624cfd 100644 --- a/python/packages/core/agent_framework/_harness/_loop.py +++ b/python/packages/core/agent_framework/_harness/_loop.py @@ -364,8 +364,22 @@ def with_judge( why its previous answer was judged incomplete. See :meth:`__init__` for the full meaning of each argument. + Security considerations: + Using a judge is an explicit opt-in — the caller must supply a ``judge_client`` — and + introduces a second external LLM boundary in addition to the agent's own model. On every + iteration the judge is sent the original request and the agent's latest response, both of + which may contain sensitive or untrusted content. A compromised or malicious judge + endpoint could exfiltrate that data, or return a manipulated :class:`JudgeVerdict` / gap + analysis that is fed back into the loop as feedback, potentially steering the agent via + indirect prompt injection. Only configure a ``judge_client`` that points at a service you + trust as much as the primary model. + Args: judge_client: Chat client used to judge whether the original request was answered. + **Security:** this client is sent the original request and the agent's latest + response on every iteration, so only point it at a service you trust as much as the + primary model — see the security considerations above for the exfiltration and + prompt-injection risks of an untrusted judge. Keyword Args: criteria: Optional list of criteria the response must satisfy. When provided, they are diff --git a/python/packages/core/agent_framework/_skills.py b/python/packages/core/agent_framework/_skills.py index 006d9476243..9954f4e73a0 100644 --- a/python/packages/core/agent_framework/_skills.py +++ b/python/packages/core/agent_framework/_skills.py @@ -1850,6 +1850,17 @@ class SkillsProvider(ContextProvider): and file-based resource reads are guarded against path traversal and symlink escape. Only use skills from trusted sources. + **Security considerations (external skill sources):** which skills are + available, and how much trust to place in them, is entirely determined by + the :class:`SkillsSource` instances this provider is configured with — see + :class:`SkillsSource` for source-level trust-boundary guidance (this + includes external sources such as skills discovered over MCP via + :class:`MCPSkillsSource`). Skill content (names, descriptions, and full + bodies loaded via ``load_skill``) is injected into the agent's context + as-is, so a compromised or adversarial source can attempt indirect prompt + injection, and the ``run_skill_script`` tool executes scripts supplied by + the source. Only enable script-capable or external sources you trust. + **Tool approval:** by default every tool exposed by this provider (``load_skill``, ``read_skill_resource``, and ``run_skill_script``) is registered with ``approval_mode="always_require"``, so each skill operation @@ -2647,6 +2658,20 @@ class SkillsSource(ABC): are discovered (filesystem, memory, network, etc.). Subclass this to create custom skill sources. + + Security considerations: + A skill source is a trust boundary. The skills it returns — their + names, descriptions, instructions, and any scripts or resources — are + injected into the agent's context and tool surface, and may be + executed (for sources that support script execution). Skills only + reach the agent when a source is explicitly registered, so this is + opt-in. Sources that read from a remote or third-party origin (e.g. a + remote MCP server via :class:`MCPSkillsSource`, a shared filesystem, or + a database) can be compromised or adversarial, and may return skill + content designed to manipulate the agent (indirect prompt injection) or + to exfiltrate data through instructions or scripts the agent is induced + to run. Only register skill sources for origins you trust, and evaluate + the content they can return before enabling them in production. """ @abstractmethod @@ -3986,6 +4011,19 @@ class MCPSkillsSource(SkillsSource): If ``skill://index.json`` is absent, unreadable, empty, or fails to parse, this source returns an empty list. + Security considerations: + Discovering skills over MCP means an *external* MCP server controls + what skill content (including instructions and, for script-capable + skills, the scripts the agent may run) reaches the agent. This source + is never enabled by default; it is only used when the caller connects + it to a server explicitly. A compromised, malicious, or simply + untrustworthy server can return adversarial skill content designed to + manipulate the agent through indirect prompt injection, or + instructions/scripts designed to exfiltrate data once loaded and, for + script-capable skills, executed. Only connect this source to MCP + servers you have vetted and trust, and treat their responses as + untrusted input. + Examples: .. code-block:: python diff --git a/python/packages/core/agent_framework/observability.py b/python/packages/core/agent_framework/observability.py index 8bbde33e7da..347cd1940ac 100644 --- a/python/packages/core/agent_framework/observability.py +++ b/python/packages/core/agent_framework/observability.py @@ -692,6 +692,17 @@ class ObservabilitySettings: Warning: Sensitive events should only be enabled on test and development environments. + Security considerations: + Agent Framework emits telemetry via the standard OpenTelemetry APIs — it does not itself + contact any external system. Where that telemetry is sent (a local collector, a hosted + observability backend, the VS Code extension port, etc.) is entirely determined by the + exporters and pipeline the developer configures. By default, emitted telemetry is limited to + metadata (e.g. token counts, operation names, durations) and does not include message + content. Enabling ``enable_sensitive_data`` (env var ``ENABLE_SENSITIVE_DATA``) is an + explicit, separate opt-in that additionally emits raw chat message content, function-call + arguments, and function-call results — treat that data as sensitive and ensure it is not sent + to, or retained by, a telemetry backend you have not secured appropriately. + Keyword Args: enable_instrumentation: Enable OpenTelemetry diagnostics. Default is True. Can be disabled by setting environment variable ENABLE_INSTRUMENTATION=false. diff --git a/python/samples/02-agents/compaction/README.md b/python/samples/02-agents/compaction/README.md index 42806bd6dec..779893eefeb 100644 --- a/python/samples/02-agents/compaction/README.md +++ b/python/samples/02-agents/compaction/README.md @@ -23,3 +23,14 @@ uv run samples/02-agents/compaction/custom.py uv run samples/02-agents/compaction/tiktoken_tokenizer.py uv run samples/02-agents/compaction/compaction_provider.py # requires OPENAI_API_KEY ``` + +## Security Considerations + +Most compaction strategies in this folder (`TruncationStrategy`, `SlidingWindowStrategy`, +`SelectiveToolCallCompactionStrategy`, `ToolResultCompactionStrategy`) only remove or reorder +existing messages and carry no additional risk. `SummarizationStrategy` is the exception: it +calls out to an LLM to produce replacement summary content that permanently becomes part of +chat history. A compromised or malicious summarization service could return a summary +containing unsafe instructions, creating a persistent indirect-prompt-injection vector. Using +`SummarizationStrategy` is optional and requires explicit configuration — only point its +chat client at a summarization service you trust as much as the primary model. diff --git a/python/samples/02-agents/harness/README.md b/python/samples/02-agents/harness/README.md index 62f30f27536..4bda12e4032 100644 --- a/python/samples/02-agents/harness/README.md +++ b/python/samples/02-agents/harness/README.md @@ -138,3 +138,27 @@ async with LocalShellTool(acknowledge_unsafe=True) as shell: ) ``` + +## Security Considerations + +Several harness capabilities extend the agent's trust boundary to external systems the developer +configures. Each is opt-in and requires explicit configuration by the developer, who is responsible +for vetting the external service, agent, skill source, or provider before enabling it: + +- **`background_agents`** (`BackgroundAgentsProvider`) — delegates work to developer-supplied agents, + which receive input from the parent and whose output is fed back into its context. A compromised + agent could exfiltrate data or inject adversarial content via indirect prompt injection. Vet all + supplied agents. +- **External skill sources** (`skills_provider` with e.g. `MCPSkillsSource`) — load skill content, + and potentially scripts, from a remote source. A compromised source could return adversarial skills + (indirect prompt injection) or exfiltrate data. Only enable sources you trust. +- **`AgentLoopMiddleware.with_judge`** — sends the request and the agent's latest response to a second, + external judge chat client on every iteration. A compromised judge could exfiltrate that data or + return manipulated feedback. Trust the judge as much as the primary model. +- **`SummarizationStrategy`** (via `before_compaction_strategy` / `after_compaction_strategy`) — calls + out to an LLM whose output permanently becomes chat history. A compromised summarization service + could inject unsafe, persistent instructions. Only use a service you trust as much as the primary + model. +- **Telemetry** — when observability is enabled, telemetry destinations are developer-configured. + Default telemetry is metadata only; enabling sensitive data additionally emits raw message content, + tool arguments, and tool results. See the [observability samples](../observability/README.md). diff --git a/python/samples/02-agents/middleware/README.md b/python/samples/02-agents/middleware/README.md index cf9eb4ebe50..3a272bd23af 100644 --- a/python/samples/02-agents/middleware/README.md +++ b/python/samples/02-agents/middleware/README.md @@ -39,3 +39,12 @@ uv run samples/02-agents/middleware/usage_tracking_middleware.py ``` The sample forces a tool call so you can see middleware output for each inner model call in both non-streaming and streaming modes. + +## Security Considerations + +`AgentLoopMiddleware.with_judge` (used by `agent_loop_middleware_judge.py` and +`agent_loop_middleware_report.py`) is an explicit opt-in to sending the original request and the +agent's latest response to a second, external judge chat client on every iteration. A compromised +or malicious judge endpoint could exfiltrate that data, or return a manipulated verdict/gap +analysis that gets fed back into the loop as feedback — a form of indirect prompt injection. Only +configure a judge client that points at a service you trust as much as the primary model. diff --git a/python/samples/02-agents/observability/README.md b/python/samples/02-agents/observability/README.md index d6a251eef59..a94a199a535 100644 --- a/python/samples/02-agents/observability/README.md +++ b/python/samples/02-agents/observability/README.md @@ -2,16 +2,16 @@ These samples show how to send Agent Framework observability data to the Application Performance Management (APM) backend of your choice, based on the OpenTelemetry standard. -The samples target [Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview), the [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview?tabs=bash), and the console, but any OTLP-compatible backend works. +The samples target [Application Insights](https://learn.microsoft.com/azure/azure-monitor/app/app-insights-overview), the [Aspire Dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/overview?tabs=bash), and the console, but any OTLP-compatible backend works. -> **Quick Start**: For local development without Azure setup, use the [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone) (runs locally via Docker), or the built-in tracing module of the [AI Toolkit for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-windows-ai-studio.windows-ai-studio). +> **Quick Start**: For local development without Azure setup, use the [Aspire Dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) (runs locally via Docker), or the built-in tracing module of the [AI Toolkit for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-windows-ai-studio.windows-ai-studio). > Other backends such as [Prometheus](https://prometheus.io/docs/introduction/overview/) are also supported. See the [OpenTelemetry Python exporters](https://opentelemetry.io/docs/languages/python/exporters/) page for the full list. For more information, please refer to the following resources: 1. [Azure Monitor OpenTelemetry Exporter](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry-exporter) -2. [Aspire Dashboard for Python Apps](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone-for-python?tabs=flask%2Cwindows) +2. [Aspire Dashboard for Python Apps](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone-for-python?tabs=flask%2Cwindows) 3. [AI Toolkit for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-windows-ai-studio.windows-ai-studio) 4. [Python Logging](https://docs.python.org/3/library/logging.html) 5. [Observability in Python](https://www.cncf.io/blog/2022/04/22/opentelemetry-and-python-a-complete-instrumentation-guide/) @@ -418,7 +418,7 @@ enable_sensitive_telemetry() ## Aspire Dashboard -The [Aspire Dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone) is a local telemetry viewing tool that provides an excellent experience for viewing OpenTelemetry data without requiring Azure setup. +The [Aspire Dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) is a local telemetry viewing tool that provides an excellent experience for viewing OpenTelemetry data without requiring Azure setup. ### Setting up Aspire Dashboard with Docker @@ -456,4 +456,17 @@ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 python configure_otel_provider > Make sure you have the dashboard running to receive telemetry data. -Once your sample finishes running, navigate to in a web browser to see the telemetry data. Follow the [Aspire Dashboard exploration guide](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/explore) to authenticate to the dashboard and start exploring your traces, logs, and metrics! +Once your sample finishes running, navigate to in a web browser to see the telemetry data. Follow the [Aspire Dashboard exploration guide](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/explore) to authenticate to the dashboard and start exploring your traces, logs, and metrics! + +## Security Considerations + +Agent Framework emits telemetry via the standard OpenTelemetry APIs — it does not itself +contact any external system. Where that telemetry is sent (a local collector, a hosted +observability backend, the VS Code extension port, etc.) is entirely determined by the +exporters and pipeline the developer configures. By default, emitted telemetry is limited to +metadata (e.g. token counts, operation names, durations) and does not include message content. +Enabling sensitive-data capture — via `enable_sensitive_telemetry()` or the +`ENABLE_SENSITIVE_DATA` environment variable — is an explicit, separate opt-in that +additionally emits raw chat message content, function-call arguments, and function-call +results — treat that data as sensitive and only send it to a telemetry backend +you have secured appropriately. diff --git a/python/samples/02-agents/skills/mcp_based_skill/README.md b/python/samples/02-agents/skills/mcp_based_skill/README.md index 994fa2d7fba..0f7f7ff2d66 100644 --- a/python/samples/02-agents/skills/mcp_based_skill/README.md +++ b/python/samples/02-agents/skills/mcp_based_skill/README.md @@ -49,3 +49,13 @@ resources (`skill://index.json` plus per-skill `SKILL.md`). - The Model Context Protocol working group maintains reference MCP-skills servers at [`modelcontextprotocol/experimental-ext-skills`](https://github.com/modelcontextprotocol/experimental-ext-skills). + +## Security Considerations + +Discovering skills over MCP means an *external* MCP server controls what skill content +(including instructions and, for script-capable skills, the scripts the agent may run) +reaches the agent. A compromised or untrustworthy server could return adversarial content +designed to manipulate the agent (indirect prompt injection) or to exfiltrate data through +skill instructions/scripts. This source is never enabled by default — connecting +`MCPSkillsSource` to a server is an explicit opt-in. Only connect to MCP servers you have +vetted and trust, and treat their responses as untrusted input.