Summary
The S3 worker pipeline evaluates the model's grounded-action string with the built-in eval(), with no restriction to the intended agent.<action>(...) dispatch. A model that emits arbitrary Python — via indirect prompt injection from on-screen/observation content, or a compromised model endpoint — gets it executed in the Agent-S host process.
Details
gui_agents/s3/utils/common_utils.py:
def create_pyautogui_code(agent, code: str, obs: Dict) -> str:
agent.assign_screenshot(obs) # Necessary for grounding
exec_code = eval(code) # <-- model-produced `code` run with bare eval()
return exec_code
code is model-derived: gui_agents/s3/agents/worker.py calls parse_code_from_string(plan), which returns the contents of the last fenced code block of the model response with no agent.X() filtering (unlike the legacy S1/S2/S2.5 path, which goes through extract_first_agent_function). The two format checkers (SINGLE_ACTION_FORMATTER, CODE_VALID_FORMATTER) only drive a re-prompt loop — call_llm_formatted returns the response after its retries regardless, and the "valid code" check is create_pyautogui_code, i.e. it runs the eval itself. single_action_check uses a greedy agent\.\w+\(\s*.*\) regex, so a payload that prepends arbitrary code before a single agent.click() passes the check.
This runs on the host orchestrator (cli_app.py: run_agent drives the local desktop via pyautogui), not in a sandbox, and on the default S3 path — no opt-in required.
Reproduction (deweaponized)
A model "plan" whose last python code block is:
__import__('os').system('touch /tmp/agent_s_poc') or agent.click(0, 0)
passes single_action_check and reaches eval(code) in create_pyautogui_code, executing os.system('touch /tmp/agent_s_poc') (sentinel only — no destructive action). Verified against current main (commit 73ea172) by reproducing the exact parse_code_from_string + single_action_check + create_pyautogui_code logic; the sentinel file was created.
Impact
Arbitrary code execution in the Agent-S host process from model-controlled output, on the default S3 path.
Suggested fix
A PR is attached: dispatch the grounded action via a safe ast-based parser — allow only a single agent.<method>(...) call where <method> is a real grounding-agent action (is_agent_action) and every argument is a plain literal (ast.literal_eval) — instead of eval. This preserves every valid grounded action while rejecting arbitrary expressions/builtins. A regression test is included.
Minor, related (not fixed in the attached PR): cli_app.py:215 runs exec(code[0]) under a comment "Ask for permission before executing", but there is no actual prompt — worth a separate look.
Summary
The S3 worker pipeline evaluates the model's grounded-action string with the built-in
eval(), with no restriction to the intendedagent.<action>(...)dispatch. A model that emits arbitrary Python — via indirect prompt injection from on-screen/observation content, or a compromised model endpoint — gets it executed in the Agent-S host process.Details
gui_agents/s3/utils/common_utils.py:codeis model-derived:gui_agents/s3/agents/worker.pycallsparse_code_from_string(plan), which returns the contents of the last fenced code block of the model response with noagent.X()filtering (unlike the legacy S1/S2/S2.5 path, which goes throughextract_first_agent_function). The two format checkers (SINGLE_ACTION_FORMATTER,CODE_VALID_FORMATTER) only drive a re-prompt loop —call_llm_formattedreturns the response after its retries regardless, and the "valid code" check iscreate_pyautogui_code, i.e. it runs theevalitself.single_action_checkuses a greedyagent\.\w+\(\s*.*\)regex, so a payload that prepends arbitrary code before a singleagent.click()passes the check.This runs on the host orchestrator (
cli_app.py: run_agentdrives the local desktop via pyautogui), not in a sandbox, and on the default S3 path — no opt-in required.Reproduction (deweaponized)
A model "plan" whose last python code block is:
passes
single_action_checkand reacheseval(code)increate_pyautogui_code, executingos.system('touch /tmp/agent_s_poc')(sentinel only — no destructive action). Verified against currentmain(commit73ea172) by reproducing the exactparse_code_from_string+single_action_check+create_pyautogui_codelogic; the sentinel file was created.Impact
Arbitrary code execution in the Agent-S host process from model-controlled output, on the default S3 path.
Suggested fix
A PR is attached: dispatch the grounded action via a safe
ast-based parser — allow only a singleagent.<method>(...)call where<method>is a real grounding-agent action (is_agent_action) and every argument is a plain literal (ast.literal_eval) — instead ofeval. This preserves every valid grounded action while rejecting arbitrary expressions/builtins. A regression test is included.