Refuse public-link writes on the owner's account unless allowlisted

Someone who reaches an agent only through its public link was offered the
approval card for writes on the owner's connected accounts, so a stranger
could approve for the owner. Those writes are now refused with a tool
result, like an API-key caller's, unless the owner allowed the action in
the agent's Access details. Team members keep the card, and a tool on the
caller's own account (member mode) is unaffected. The flag survives a
resume, and workflow nodes now follow the run's caller rules (scheduled,
API-key and public-link) instead of starting from none.
This commit is contained in:
arc53-machine committed 2026-09-29 15:12:46 +01:00
1 parent 27495f0bc2
commit 3dc5080058
7 files changed
+290 -9

No files matched your search

+22 -3
View File
@@ -446,9 +446,9 @@ class WorkflowEngine:
# the tool_call_attempts primary key and drop the later journal rows.
node_executor = getattr(node_agent, "tool_executor", None)
if node_executor is not None:
node_executor.message_id = getattr(
getattr(self.agent, "tool_executor", None), "message_id", None
)
run_executor = getattr(self.agent, "tool_executor", None)
node_executor.message_id = getattr(run_executor, "message_id", None)
self._inherit_caller_policy(node_executor, run_executor)
# Run-scope the node agent's tools so artifact_generator / code_executor
# address artifacts by this workflow run: a short ref (A1) created by one
# node resolves for edit_artifact in a later node within the same run. Only
@@ -1323,6 +1323,25 @@ class WorkflowEngine:
docs_together = "\n\n".join(docs_together_parts) if docs_together_parts else None
return docs, docs_together
@staticmethod
def _inherit_caller_policy(node_executor: Any, run_executor: Any) -> None:
"""Give a node's executor the caller rules of the run that started it.
A node's tools run for the same caller as the workflow agent: a
scheduled run still can't pause, and an API-key or public-link caller
still can't write on the owner's account unless it is allowlisted.
Args:
node_executor: The node agent's ``ToolExecutor``.
run_executor: The workflow agent's ``ToolExecutor``, if any.
"""
if run_executor is None:
return
for attr in ("headless", "external_caller", "public_link_caller"):
setattr(node_executor, attr, bool(getattr(run_executor, attr, False)))
for attr in ("tool_allowlist", "api_write_allowlist"):
setattr(node_executor, attr, set(getattr(run_executor, attr, None) or ()))
def _workflow_owner_id(self) -> Optional[str]:
"""The workflow's owner, whom node tools and sources run as.