fix(sandbox): improve sandbox security and preserve multimodal content (#2114)
* fix: improve sandbox security and preserve multimodal content * Add unit test modifications for test_injects_uploaded_files_tag_into_list_content * format updated_content * Add regression tests for multimodal upload content and host bash default safety
This commit is contained in:
parent
024ac0e464
commit
02569136df
|
|
@ -262,21 +262,25 @@ class UploadsMiddleware(AgentMiddleware[UploadsMiddlewareState]):
|
||||||
files_message = self._create_files_message(new_files, historical_files)
|
files_message = self._create_files_message(new_files, historical_files)
|
||||||
|
|
||||||
# Extract original content - handle both string and list formats
|
# Extract original content - handle both string and list formats
|
||||||
original_content = ""
|
original_content = last_message.content
|
||||||
if isinstance(last_message.content, str):
|
if isinstance(original_content, str):
|
||||||
original_content = last_message.content
|
# Simple case: string content, just prepend files message
|
||||||
elif isinstance(last_message.content, list):
|
updated_content = f"{files_message}\n\n{original_content}"
|
||||||
text_parts = []
|
elif isinstance(original_content, list):
|
||||||
for block in last_message.content:
|
# Complex case: list content (multimodal), preserve all blocks
|
||||||
if isinstance(block, dict) and block.get("type") == "text":
|
# Prepend files message as the first text block
|
||||||
text_parts.append(block.get("text", ""))
|
files_block = {"type": "text", "text": f"{files_message}\n\n"}
|
||||||
original_content = "\n".join(text_parts)
|
# Keep all original blocks (including images)
|
||||||
|
updated_content = [files_block, *original_content]
|
||||||
|
else:
|
||||||
|
# Other types, preserve as-is
|
||||||
|
updated_content = original_content
|
||||||
|
|
||||||
# Create new message with combined content.
|
# Create new message with combined content.
|
||||||
# Preserve additional_kwargs (including files metadata) so the frontend
|
# Preserve additional_kwargs (including files metadata) so the frontend
|
||||||
# can read structured file info from the streamed message.
|
# can read structured file info from the streamed message.
|
||||||
updated_message = HumanMessage(
|
updated_message = HumanMessage(
|
||||||
content=f"{files_message}\n\n{original_content}",
|
content=updated_content,
|
||||||
id=last_message.id,
|
id=last_message.id,
|
||||||
additional_kwargs=last_message.additional_kwargs,
|
additional_kwargs=last_message.additional_kwargs,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ def is_host_bash_allowed(config=None) -> bool:
|
||||||
|
|
||||||
sandbox_cfg = getattr(config, "sandbox", None)
|
sandbox_cfg = getattr(config, "sandbox", None)
|
||||||
if sandbox_cfg is None:
|
if sandbox_cfg is None:
|
||||||
return True
|
return False
|
||||||
if not uses_local_sandbox_provider(config):
|
if not uses_local_sandbox_provider(config):
|
||||||
return True
|
return True
|
||||||
return bool(getattr(sandbox_cfg, "allow_host_bash", False))
|
return bool(getattr(sandbox_cfg, "allow_host_bash", False))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from deerflow.sandbox.security import is_host_bash_allowed
|
||||||
from deerflow.tools.tools import get_available_tools
|
from deerflow.tools.tools import get_available_tools
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -79,3 +80,8 @@ def test_get_available_tools_keeps_bash_for_aio_sandbox(monkeypatch):
|
||||||
|
|
||||||
assert "bash" in names
|
assert "bash" in names
|
||||||
assert "ls" in names
|
assert "ls" in names
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_host_bash_allowed_defaults_false_when_sandbox_missing():
|
||||||
|
assert is_host_bash_allowed(SimpleNamespace()) is False
|
||||||
|
assert is_host_bash_allowed(SimpleNamespace(sandbox=None)) is False
|
||||||
|
|
|
||||||
|
|
@ -256,8 +256,10 @@ class TestBeforeAgent:
|
||||||
|
|
||||||
assert result is not None
|
assert result is not None
|
||||||
updated_msg = result["messages"][-1]
|
updated_msg = result["messages"][-1]
|
||||||
assert "<uploaded_files>" in updated_msg.content
|
assert isinstance(updated_msg.content, list)
|
||||||
assert "analyse this" in updated_msg.content
|
combined_text = "\n".join(block.get("text", "") for block in updated_msg.content if isinstance(block, dict))
|
||||||
|
assert "<uploaded_files>" in combined_text
|
||||||
|
assert "analyse this" in combined_text
|
||||||
|
|
||||||
def test_preserves_additional_kwargs_on_updated_message(self, tmp_path):
|
def test_preserves_additional_kwargs_on_updated_message(self, tmp_path):
|
||||||
mw = _middleware(tmp_path)
|
mw = _middleware(tmp_path)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue