diff --git a/workspace/a2a_tools_delegation.py b/workspace/a2a_tools_delegation.py index 5a40891b..c6416122 100644 --- a/workspace/a2a_tools_delegation.py +++ b/workspace/a2a_tools_delegation.py @@ -167,12 +167,19 @@ async def _delegate_sync_via_polling( break if terminal: if (terminal.get("status") or "").lower() == "completed": - return terminal.get("response_preview") or "" - err = ( + # OFFSEC-003: sanitize response_preview before returning so + # boundary markers injected by a malicious peer cannot escape + # the trust boundary. + return sanitize_a2a_result(terminal.get("response_preview") or "") + # OFFSEC-003: sanitize error_detail / summary before wrapping with + # the _A2A_ERROR_PREFIX sentinel so injected markers cannot appear + # inside the trusted error block returned to the agent. + err_raw = ( terminal.get("error_detail") or terminal.get("summary") or "delegation failed" ) + err = sanitize_a2a_result(err_raw) return f"{_A2A_ERROR_PREFIX}{err}" await asyncio.sleep(_SYNC_POLL_INTERVAL_S) @@ -408,12 +415,11 @@ async def tool_check_task_status( # Filter by delegation_id matching = [d for d in delegations if d.get("delegation_id") == task_id] if matching: - entry = dict(matching[0]) - # OFFSEC-003: sanitize peer-generated text fields - for field in ("result", "response_preview"): - if field in entry and entry[field]: - entry[field] = sanitize_a2a_result(str(entry[field])) - return json.dumps(entry) + # OFFSEC-003: sanitize peer-supplied fields + d = matching[0] + d["summary"] = sanitize_a2a_result(d.get("summary", "")) + d["response_preview"] = sanitize_a2a_result(d.get("response_preview", "")) + return json.dumps(d) return json.dumps({"status": "not_found", "delegation_id": task_id}) # Return all recent delegations summary = [] @@ -425,7 +431,7 @@ async def tool_check_task_status( "delegation_id": d.get("delegation_id", ""), "target_id": d.get("target_id", ""), "status": d.get("status", ""), - "summary": d.get("summary", ""), + "summary": sanitize_a2a_result(d.get("summary", "")), "response_preview": preview, }) return json.dumps({"delegations": summary, "count": len(delegations)})