Skip to content

Commit 26fa33f

Browse files
Python: Warn about unsupported FIDES CodeAct tools (#9175)
* Python: warn about unsupported FIDES CodeAct tools * Python: avoid duplicate CodeAct FIDES warnings
1 parent 1cfd2c5 commit 26fa33f

7 files changed

Lines changed: 349 additions & 2 deletions

File tree

‎python/packages/hyperlight/README.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
Hyperlight-backed CodeAct integrations for Microsoft Agent Framework.
44

5+
## FIDES compatibility
6+
7+
> [!WARNING]
8+
> **FIDES integration is not supported with CodeAct providers.** Host-tool calls
9+
> made inside generated code bypass the agent's function middleware, so FIDES
10+
> policy enforcement and label tracking do not apply to those nested calls or
11+
> intermediate code values. Checks on the outer `execute_code` invocation do not
12+
> enforce the policies of individual tools called inside it.
13+
>
14+
> Registering tools with recognized FIDES `additional_properties` logs a warning,
15+
> but does not block execution. This is a best-effort diagnostic: FIDES also uses
16+
> defaults for unannotated tools, so no warning does not imply support or safety.
17+
> Keep tools that require FIDES as direct agent tools, not Hyperlight-managed tools,
18+
> and enforce authorization and destination restrictions inside host functions.
19+
> Sandbox isolation does not supply FIDES enforcement.
20+
> See the [FIDES guide](../../samples/02-agents/security/FIDES_DEVELOPER_GUIDE.md).
21+
522
## Installation
623

724
```bash

‎python/packages/hyperlight/agent_framework_hyperlight/_execute_code_tool.py‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import asyncio
66
import contextvars
7+
import logging
78
import mimetypes
89
import os
910
import shutil
@@ -32,9 +33,21 @@
3233
from ._instructions import build_codeact_instructions, build_execute_code_description
3334
from ._types import AllowedDomain, AllowedDomainInput, FileMount, FileMountHostPath, FileMountInput
3435

36+
logger = logging.getLogger("agent_framework")
37+
3538
DEFAULT_HYPERLIGHT_BACKEND = "wasm"
3639
DEFAULT_HYPERLIGHT_MODULE = "python_guest.path"
3740
EXECUTE_CODE_TOOL_DESCRIPTION = "Execute Python in an isolated Hyperlight sandbox."
41+
_FIDES_TOOL_PROPERTY_KEYS = frozenset({
42+
"source_integrity",
43+
"confidentiality",
44+
"max_allowed_confidentiality",
45+
"accepts_untrusted",
46+
"standing_guidance",
47+
"agent_framework.security.principals",
48+
"_agent_framework_internal_security_tool",
49+
"_mcp_trust_server_ifc",
50+
})
3851
OUTPUT_FILE_RETRY_ATTEMPTS = 10
3952
OUTPUT_FILE_RETRY_DELAY_SECONDS = 0.1
4053
DEFAULT_MAX_OUTPUT_FILES = 20
@@ -1607,8 +1620,19 @@ def add_tools(
16071620
) -> None:
16081621
"""Add sandbox-managed tools to this execute_code surface."""
16091622
with self._state_lock:
1610-
combined_tools = _collect_tools(self._managed_tools, tools)
1623+
new_tools = _collect_tools(tools)
1624+
combined_tools = _collect_tools(self._managed_tools, new_tools)
16111625
self._managed_tools = combined_tools
1626+
for tool_obj in new_tools:
1627+
if tool_obj.additional_properties and not _FIDES_TOOL_PROPERTY_KEYS.isdisjoint(
1628+
tool_obj.additional_properties
1629+
):
1630+
logger.warning(
1631+
"FIDES is not supported with CodeAct providers. Metadata on tool '%s' will not be enforced "
1632+
"through function middleware for calls made inside generated code. "
1633+
"Keep FIDES-dependent tools as direct agent tools.",
1634+
tool_obj.name,
1635+
)
16121636
self._refresh_approval_mode()
16131637

16141638
def get_tools(self) -> list[FunctionTool]:

‎python/packages/hyperlight/tests/hyperlight/test_hyperlight_codeact.py‎

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,130 @@ def test_execute_code_tool_updates_approval_with_managed_tools() -> None:
523523
assert execute_code.approval_mode == "always_require"
524524

525525

526+
@pytest.mark.parametrize("entry_point", [HyperlightExecuteCodeTool, HyperlightCodeActProvider])
527+
@pytest.mark.parametrize("register_later", [False, True])
528+
@pytest.mark.parametrize(
529+
"properties",
530+
[
531+
{"source_integrity": "untrusted"},
532+
{"confidentiality": "private"},
533+
{"max_allowed_confidentiality": "public"},
534+
{"max_allowed_confidentiality": None},
535+
{"accepts_untrusted": False},
536+
{"standing_guidance": []},
537+
{"agent_framework.security.principals": []},
538+
{"_agent_framework_internal_security_tool": object()},
539+
{"_mcp_trust_server_ifc": False},
540+
],
541+
)
542+
def test_codeact_warns_for_fides_tool_metadata(
543+
entry_point: type[HyperlightExecuteCodeTool] | type[HyperlightCodeActProvider],
544+
register_later: bool,
545+
properties: dict[str, Any],
546+
caplog: pytest.LogCaptureFixture,
547+
) -> None:
548+
@tool(additional_properties=properties)
549+
def annotated_tool(value: int) -> int:
550+
return value
551+
552+
with caplog.at_level("WARNING", logger="agent_framework"):
553+
codeact = entry_point(tools=None if register_later else annotated_tool, _registry=_FakeRuntime())
554+
if register_later:
555+
codeact.add_tools(annotated_tool)
556+
557+
records = [
558+
record for record in caplog.records if "FIDES is not supported with CodeAct providers." in record.message
559+
]
560+
assert len(records) == 1
561+
assert records[0].levelname == "WARNING"
562+
assert records[0].args == ("annotated_tool",)
563+
assert codeact.get_tools()[0] is annotated_tool
564+
assert annotated_tool.additional_properties == properties
565+
assert annotated_tool.invocation_count == annotated_tool.invocation_exception_count == 0
566+
567+
568+
@pytest.mark.parametrize("entry_point", [HyperlightExecuteCodeTool, HyperlightCodeActProvider])
569+
@pytest.mark.parametrize("properties", [None, {}, {"custom_property": False}])
570+
def test_codeact_does_not_warn_for_unannotated_tools(
571+
entry_point: type[HyperlightExecuteCodeTool] | type[HyperlightCodeActProvider],
572+
properties: dict[str, Any] | None,
573+
caplog: pytest.LogCaptureFixture,
574+
) -> None:
575+
@tool(additional_properties=properties)
576+
def plain_tool(value: int) -> int:
577+
return value
578+
579+
with caplog.at_level("WARNING", logger="agent_framework"):
580+
codeact = entry_point(tools=plain_tool, _registry=_FakeRuntime())
581+
582+
assert "FIDES" not in caplog.text
583+
assert codeact.get_tools()[0] is plain_tool
584+
585+
586+
def test_run_snapshot_warns_for_fides_metadata_added_after_registration(caplog: pytest.LogCaptureFixture) -> None:
587+
@tool
588+
def annotated_tool(value: int) -> int:
589+
return value
590+
591+
codeact = HyperlightExecuteCodeTool(tools=annotated_tool, _registry=_FakeRuntime())
592+
assert annotated_tool.additional_properties is not None
593+
annotated_tool.additional_properties["accepts_untrusted"] = False
594+
595+
with caplog.at_level("WARNING", logger="agent_framework"):
596+
run_tool = codeact.create_run_tool()
597+
598+
assert "FIDES is not supported with CodeAct providers." in caplog.text
599+
assert run_tool.get_tools()[0] is annotated_tool
600+
assert annotated_tool.invocation_count == annotated_tool.invocation_exception_count == 0
601+
602+
603+
@pytest.mark.parametrize("entry_point", [HyperlightExecuteCodeTool, HyperlightCodeActProvider])
604+
def test_codeact_does_not_repeat_fides_warning_for_unrelated_registration(
605+
entry_point: type[HyperlightExecuteCodeTool] | type[HyperlightCodeActProvider],
606+
caplog: pytest.LogCaptureFixture,
607+
) -> None:
608+
@tool(additional_properties={"source_integrity": "untrusted"})
609+
def annotated_tool(value: int) -> int:
610+
return value
611+
612+
@tool
613+
def plain_tool(value: int) -> int:
614+
return value
615+
616+
with caplog.at_level("WARNING", logger="agent_framework"):
617+
codeact = entry_point(tools=annotated_tool, _registry=_FakeRuntime())
618+
caplog.clear()
619+
codeact.add_tools(plain_tool)
620+
621+
assert "FIDES" not in caplog.text
622+
assert codeact.get_tools() == [annotated_tool, plain_tool]
623+
624+
625+
@pytest.mark.parametrize("entry_point", [HyperlightExecuteCodeTool, HyperlightCodeActProvider])
626+
def test_codeact_warns_once_for_annotated_tool_replacement(
627+
entry_point: type[HyperlightExecuteCodeTool] | type[HyperlightCodeActProvider],
628+
caplog: pytest.LogCaptureFixture,
629+
) -> None:
630+
@tool(name="shared")
631+
def original_tool(value: int) -> int:
632+
return value
633+
634+
@tool(name="shared", additional_properties={"accepts_untrusted": False})
635+
def replacement_tool(value: int) -> int:
636+
return value
637+
638+
codeact = entry_point(tools=original_tool, _registry=_FakeRuntime())
639+
with caplog.at_level("WARNING", logger="agent_framework"):
640+
codeact.add_tools(replacement_tool)
641+
642+
records = [
643+
record for record in caplog.records if "FIDES is not supported with CodeAct providers." in record.message
644+
]
645+
assert len(records) == 1
646+
assert records[0].args == ("shared",)
647+
assert codeact.get_tools() == [replacement_tool]
648+
649+
526650
def test_execute_code_tool_replaces_tools_with_the_same_name() -> None:
527651
execute_code = HyperlightExecuteCodeTool(tools=[compute], _registry=_FakeRuntime())
528652

‎python/packages/monty/README.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ Monty-backed CodeAct integrations for Microsoft Agent Framework.
66
> This package is in **beta**. APIs may change before its stable release. It is
77
> included in `agent-framework[all]`.
88
9+
## FIDES compatibility
10+
11+
> [!WARNING]
12+
> **FIDES integration is not supported with CodeAct providers.** Host-tool calls
13+
> made inside generated code bypass the agent's function middleware, so FIDES
14+
> policy enforcement and label tracking do not apply to those nested calls or
15+
> intermediate code values. Checks on the outer `execute_code` invocation do not
16+
> enforce the policies of individual tools called inside it.
17+
>
18+
> Registering tools with recognized FIDES `additional_properties` logs a warning,
19+
> but does not block execution. This is a best-effort diagnostic: FIDES also uses
20+
> defaults for unannotated tools, so no warning does not imply support or safety.
21+
> Keep tools that require FIDES as direct agent tools, not Monty-managed tools, and
22+
> enforce authorization and destination restrictions inside host functions.
23+
> See the [FIDES guide](../../samples/02-agents/security/FIDES_DEVELOPER_GUIDE.md).
24+
925
## Installation
1026

1127
```bash

‎python/packages/monty/agent_framework_monty/_execute_code_tool.py‎

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from __future__ import annotations
1414

1515
import json
16+
import logging
1617
import mimetypes
1718
from collections.abc import Callable, Iterator, Sequence
1819
from functools import partial
@@ -32,9 +33,22 @@
3233
from ._monty_bridge import InlineCodeBridge, generate_type_stubs
3334
from ._types import FileMount, FileMountInput
3435

36+
logger = logging.getLogger("agent_framework")
37+
3538
EXECUTE_CODE_TOOL_NAME = "execute_code"
3639
EXECUTE_CODE_TOOL_DESCRIPTION = "Execute Python in a Monty interpreter."
3740

41+
_FIDES_TOOL_PROPERTY_KEYS = frozenset({
42+
"source_integrity",
43+
"confidentiality",
44+
"max_allowed_confidentiality",
45+
"accepts_untrusted",
46+
"standing_guidance",
47+
"agent_framework.security.principals",
48+
"_agent_framework_internal_security_tool",
49+
"_mcp_trust_server_ifc",
50+
})
51+
3852
#: Virtual path that the optional ``workspace_root`` directory is mounted at,
3953
#: matching the Hyperlight default. Use ``file_mounts`` for any other path.
4054
WORKSPACE_MOUNT_PATH = "/input"
@@ -258,7 +272,18 @@ def add_tools(
258272
tools: FunctionTool | Callable[..., Any] | Sequence[FunctionTool | Callable[..., Any]],
259273
) -> None:
260274
"""Add Monty-side tools to this execute_code surface."""
261-
self._managed_tools = _collect_tools(self._managed_tools, tools)
275+
new_tools = _collect_tools(tools)
276+
self._managed_tools = _collect_tools(self._managed_tools, new_tools)
277+
for tool_obj in new_tools:
278+
if tool_obj.additional_properties and not _FIDES_TOOL_PROPERTY_KEYS.isdisjoint(
279+
tool_obj.additional_properties
280+
):
281+
logger.warning(
282+
"FIDES is not supported with CodeAct providers. Metadata on tool '%s' will not be enforced "
283+
"through function middleware for calls made inside generated code. "
284+
"Keep FIDES-dependent tools as direct agent tools.",
285+
tool_obj.name,
286+
)
262287
self._refresh_approval_mode()
263288

264289
def get_tools(self) -> list[FunctionTool]:

0 commit comments

Comments
 (0)