Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion python/packages/core/agent_framework/_compaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__(
Expand All @@ -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
Expand Down
7 changes: 7 additions & 0 deletions python/packages/core/agent_framework/_harness/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__(
Expand All @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions python/packages/core/agent_framework/_harness/_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions python/packages/core/agent_framework/_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
11 changes: 11 additions & 0 deletions python/packages/core/agent_framework/observability.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions python/samples/02-agents/compaction/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
24 changes: 24 additions & 0 deletions python/samples/02-agents/harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
9 changes: 9 additions & 0 deletions python/samples/02-agents/middleware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
23 changes: 18 additions & 5 deletions python/samples/02-agents/observability/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 <http://localhost:18888> 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 <http://localhost:18888> 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.
Loading
Loading