# Defense-in-Depth Score: UFO (UFO² / UFO³ Galaxy)

**Repo:** https://github.com/microsoft/ufo · **Commit:** `a795552d976c4c019d7c2f778a0effb5cef7de6b` · **Reviewed:** 2026-10-04
**What it is:** Microsoft's Windows desktop GUI agent (UFO²) and multi-device orchestration framework (UFO³ Galaxy) that operate apps via UI Automation and MCP tools.
**Category:** AI Assistants
**Scored configuration:** UFO³ Galaxy (README-recommended) dispatching to UFO² Windows device agents with shipped config/ufo and config/galaxy defaults (SAFE_GUARD on, CLI and GUI executors enabled, RAG/memory off).
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory opt-in · untrusted input yes · third party extensions opt-in · sub agents yes · external communication yes

## Score: 2.6 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C2 | Approval gates | L1 | L0 | L2 | L0 | 0.17 | C2-POWERBYPASS | **0.17** | High |
| C3 | Tool & action scoping | L2 | L1 | L1 | L0 | 0.28 | — | **0.28** | High |
| C4 | Code-execution isolation | L1 | L0 | L1 | L0 | 0.12 | G2 | **0.12** | High |
| C5 | Untrusted input blast radius | L1 | L1 | L2 | L0 | 0.25 | C5-WORSTCASE | **0.25** | High |
| C6 | Memory, context & configuration integrity | L1 | L1 | L2 | L2 | 0.35 | — | **0.35** | High |
| C7 | Third-party extensions | L1 | L0 | L2 | L2 | 0.28 | — | **0.28** | Medium |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |
| C9 | Audit & traceability | L2 | L2 | L1 | L2 | 0.45 | — | **0.45** | High |
| C10 | Limits & kill switch | L2 | L2 | L2 | L1 | 0.45 | — | **0.45** | High |


UFO controls the Windows desktop as the logged-in user with no sandbox, and its keyboard and mouse tools can type anything into any window, including a PowerShell window. The command-line and Linux tools are tightly allowlisted, but the GUI path goes around them. The human confirmation prompt is well built yet only appears when the model decides to ask, and Galaxy's orchestrator auto-confirms, so prompt injection from on-screen content can leak data and send or delete things unattended.

## Critical gaps
- The agent acts with the desktop user's full session and every signed-in account; nothing narrows its authority. (ASI03, T3; C1) — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/client/mcp/local_servers/ui_mcp_server.py:201](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L201)
- Human confirmation only triggers when the model labels an action CONFIRM; HostAgent confirmation is a no-op and Galaxy auto-confirms, so the most powerful actions bypass the gate. (ASI09, ASI02, T10; C2) — [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1361-1363](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1361-L1363); [ufo/agents/agent/host_agent.py:365-369](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/agent/host_agent.py#L365-L369); [galaxy/agents/constellation_agent.py:524-526](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/galaxy/agents/constellation_agent.py#L524-L526)
- No sandbox; keyboard_input can type arbitrary commands into a shell window, bypassing the CLI allowlist, with host-equivalent reach. (ASI05, T11; C4) — [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488); [ufo/client/mcp/local_servers/ui_mcp_server.py:201](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L201); [ufo/client/mcp/local_servers/cli_mcp_server.py:104](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L104)
- Untrusted on-screen text reaches the model unsanitized in a session that can see private data and send/delete via GUI without enforced approval. (ASI01, LLM01, T6; C5) — [ufo/prompter/agent_prompter.py:268](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/agent_prompter.py#L268); [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1361-1363](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1361-L1363)

## Criterion details

### C1 Identity & least privilege — 0.00 (high)

UFO drives the Windows desktop through UI Automation as the logged-in user, so every app, file and signed-in account (browser sessions, Outlook, Office) is within reach. Nothing narrows that authority: there is no separate low-privilege identity, no per-action authorization check, and launched programs inherit the user's environment. The device server and Galaxy web UI do require an API token, which stops strangers from issuing tasks, but once a task runs it acts with the user's full session.

- **S L0:** The agent acts through the interactive user's desktop session with no dedicated or narrowed identity; launched apps inherit the full environment. — [ufo/client/mcp/local_servers/ui_mcp_server.py:201](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L201); [ufo/client/mcp/local_servers/cli_mcp_server.py:161](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L161); [config/config_loader.py:476](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/config_loader.py#L476) (verified)
  - *To reach the next level:* Run device agents under a dedicated low-privilege Windows account or scoped identity.
- **C L0:** No authorization layer sits between tool calls and the desktop; the only authentication is on who may connect to the device server. — [ufo/server/app.py:103](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/server/app.py#L103); [ufo/agents/processors/strategies/host_agent_processing_strategy.py:971-976](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/host_agent_processing_strategy.py#L971-L976) (verified)
  - *To reach the next level:* Add a per-action authorization check that every tool path passes before acting.
- **D L0:** Default install runs with the operator's full interactive privileges; least privilege would need manual setup outside UFO. — [config/ufo/mcp.yaml:41](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/mcp.yaml#L41); [config/ufo/mcp.yaml:28](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/mcp.yaml#L28) (verified)
  - *To reach the next level:* Ship a default restricted account or read-only mode with explicit elevation.
- **B L0:** A hijacked session can act in every application and signed-in account on the desktop (email, browser, files). — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488); [ufo/client/mcp/local_servers/ui_mcp_server.py:201](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L201) (verified)
  - *To reach the next level:* Confine the agent to a scoped account or VM so a hijack reaches one tenant or system.
- **Cap:** none

### C2 Approval gates — 0.17 (high)

A well-built confirmation prompt exists: it shows the exact function and arguments, defaults to 'no', and the approved batch is snapshotted so what runs is what was approved. But it only fires when the model itself labels an action 'CONFIRM', as instructed by the prompt. The HostAgent's confirmation is an empty TODO and Galaxy's orchestrator always auto-confirms, so typing, clicking 'Send' or launching apps proceeds without a human whenever the model doesn't ask.

- **S L1:** Per-call approval shows the exact call and executes the snapshotted batch, but whether a human is asked is decided by the model's own CONFIRM status. — [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1361-1363](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1361-L1363); [ufo/agents/agent/app_agent.py:410](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/agent/app_agent.py#L410); [ufo/module/interactor.py:190](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/interactor.py#L190); [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1364](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1364); [ufo/prompts/share/base/app_agent.yaml:36](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompts/share/base/app_agent.yaml#L36) (verified)
  - *To reach the next level:* Decide which actions need approval in code (risk tiers by tool/target), not by the model's status field.
- **C L0:** The most powerful paths (keyboard_input, clicks, HostAgent commands, Galaxy constellation actions) are ungated unless the model self-flags; HostAgent confirmation is a no-op. — [ufo/agents/agent/host_agent.py:365-369](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/agent/host_agent.py#L365-L369); [galaxy/agents/constellation_agent.py:524-526](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/galaxy/agents/constellation_agent.py#L524-L526); [ufo/agents/processors/strategies/host_agent_processing_strategy.py:971-976](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/host_agent_processing_strategy.py#L971-L976); [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472) (verified)
  - *To reach the next level:* Route every state-changing tool through the gate regardless of model status, including HostAgent and Galaxy paths.
- **D L2:** SAFE_GUARD is on by default but a single config boolean disables it silently. — [config/ufo/system.yaml:27](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L27); [ufo/agents/agent/app_agent.py:410](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/agent/app_agent.py#L410) (verified)
  - *To reach the next level:* Require an explicit, loudly named operator flag to disable confirmation.
- **B L0:** Wrongly executed actions include sending email/messages and deleting files through the GUI, with no undo or checkpointing. — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/prompts/share/base/app_agent.yaml:36](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompts/share/base/app_agent.yaml#L36) (verified)
  - *To reach the next level:* Add checkpoints/undo for file state and previews for external sends.
- **Cap:** C2-POWERBYPASS — The most powerful action path (arbitrary keyboard/mouse input via AppUIExecutor and all HostAgent commands) reaches execution without crossing the gate in the default configuration unless the model chooses to ask.

### C3 Tool & action scoping — 0.28 (high)

The non-GUI tools are carefully scoped: the CLI tool launches only a bare name from a fixed app allowlist with no arguments and no shell, the Linux tool uses an allowlist with per-command argument policies and pinned executables, and Office save paths are resolved and contained. But the main tools are general GUI primitives (click anywhere, type any key sequence into any window), which no validation can bound, and all of them are enabled by default.

- **S L2:** Strong allowlist validation on CLI/Linux/save-path tools, but the core GUI tools accept arbitrary key sequences and coordinates; the PDF reader uses a sensitive-directory denylist. — [ufo/client/mcp/local_servers/cli_mcp_server.py:104](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L104); [ufo/client/mcp/http_servers/linux_mcp_server.py:128](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/http_servers/linux_mcp_server.py#L128); [ufo/automator/path_validator.py:239-257](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/automator/path_validator.py#L239-L257); [ufo/client/mcp/local_servers/pdf_reader_mcp_server.py:58](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/pdf_reader_mcp_server.py#L58); [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488) (verified)
  - *To reach the next level:* Replace or bound the general GUI input tools with allowlisted targets/argument policies.
- **C L1:** Validation covers the CLI, Linux and Office/PDF tools, but not the GUI tools that perform most actions. — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/client/mcp/local_servers/cli_mcp_server.py:104](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L104); [ufo/client/mcp/http_servers/linux_mcp_server.py:397](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/http_servers/linux_mcp_server.py#L397) (verified)
  - *To reach the next level:* Validate inputs on the GUI tools (target windows, key sequences) through a shared policy layer.
- **D L1:** GUI write tools and the CLI launcher are enabled for HostAgent and AppAgent by default; they can be removed per agent in mcp.yaml. — [config/ufo/mcp.yaml:28](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/mcp.yaml#L28); [config/ufo/mcp.yaml:41](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/mcp.yaml#L41) (verified)
  - *To reach the next level:* Default to a read-only tool set and require explicit enabling of write/exec tools.
- **B L0:** A misused GUI tool reaches the whole desktop; the Linux 'cat' tool reads any file. — [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488); [ufo/client/mcp/http_servers/linux_mcp_server.py:397](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/http_servers/linux_mcp_server.py#L397) (verified)
  - *To reach the next level:* Scope tools to a workspace or app set with quantity bounds.
- **Cap:** none

### C4 Code-execution isolation — 0.12 (high)

There is no sandbox: tools run as the desktop user on the host. The command-line tool is limited to launching allowlisted apps without arguments, and the Linux tool runs allowlisted read-only commands with shell disabled, but these are filters, not isolation. The GUI keyboard tool can type arbitrary text into any open window, including a PowerShell or terminal window, so the model can run any command around the allowlist; the shipped device config even suggests launching PowerShell windows.

- **S L1:** Only command allowlists/denylists filter execution; there is no OS-level isolation primitive. — [ufo/client/mcp/local_servers/cli_mcp_server.py:104](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L104); [ufo/client/mcp/http_servers/linux_mcp_server.py:128](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/http_servers/linux_mcp_server.py#L128); searched `rg -n -i 'docker|sandbox|firejail|appcontainer|seccomp' --type py` in `ufo galaxy aip` → 1 hits (Only hit is a Chrome '--no-sandbox' argument string in the Linux MCP server's argument denylist; no isolation backend exists.) (verified)
  - *To reach the next level:* Run device agents in a container, Windows Sandbox, VM, or dedicated low-privilege account.
- **C L0:** The filters cover the CLI and Linux tools, but the main path to execution (typing into a shell window via keyboard_input) is unfiltered. — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488); [ufo/client/mcp/local_servers/ui_mcp_server.py:201](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L201); [config/galaxy/devices.yaml:25](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/galaxy/devices.yaml#L25) (verified)
  - *To reach the next level:* Apply the execution policy to the GUI input path or block shell/terminal windows as targets.
- **D L1:** Filters are on by default, but the model can sidestep them at will by driving a terminal through the GUI without human approval. — [ufo/client/mcp/local_servers/ui_mcp_server.py:488](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L488); [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1361-1363](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1361-L1363) (verified)
  - *To reach the next level:* Make escalation to arbitrary command execution require a per-call human approval decided in code.
- **B L0:** Execution lands on the host as the user with full filesystem, network and signed-in credentials. — [ufo/client/mcp/local_servers/cli_mcp_server.py:161](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/cli_mcp_server.py#L161); [config/config_loader.py:476](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/config_loader.py#L476) (verified)
  - *To reach the next level:* Isolate execution so only a workspace is mounted, without secrets, with egress allowlisted.
- **Cap:** G2 — The model can bypass the command allowlist at runtime by selecting a shell window and sending arbitrary keystrokes with keyboard_input, with no human approval required.

### C5 Untrusted input blast radius — 0.25 (high)

UFO reads whatever is on screen, including web pages, emails and documents, and passes UI text and screenshots to the model alongside the user's task. A regex sanitizer strips some injection phrases and wraps the user request and retrieved documents in tags, but the on-screen control text is passed through raw. Because the same session can read untrusted content, see private data and send email or type into a browser without approval, a successful injection can both leak data and take irreversible actions.

- **S L1:** Only detection-style regex filtering and delimiter wrapping exist; nothing structurally limits a hijacked session. — [ufo/prompter/prompt_sanitizer.py:39-41](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/prompt_sanitizer.py#L39-L41); [ufo/prompter/prompt_sanitizer.py:128](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/prompt_sanitizer.py#L128) (verified)
  - *To reach the next level:* Disable or force approval of egress and state-changing tools once untrusted screen content has been read.
- **C L1:** The sanitizer covers user_request, subtask and retrieved_docs, but not on-screen control text, screenshots or tool results. — [ufo/prompter/agent_prompter.py:271-275](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/agent_prompter.py#L271-L275); [ufo/prompter/agent_prompter.py:268](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/agent_prompter.py#L268); searched `rg -n -i 'untrusted|provenance|taint' --type py` in `ufo/agents ufo/prompter galaxy/agents` → 1 hits (Single hit is a docstring in prompt_sanitizer; no provenance or taint tracking on screen/UI content.) (verified)
  - *To reach the next level:* Treat UI control text, screenshots and tool outputs as untrusted data too.
- **D L2:** The sanitizer is hard-wired into prompt construction with no toggle, but it is only a filter. — [ufo/prompter/agent_prompter.py:271-275](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/agent_prompter.py#L271-L275) (verified)
  - *To reach the next level:* Pair the always-on filter with a structural control that content cannot route around.
- **B L0:** A hijacked session can read private desktop data and exfiltrate it or send/delete via the GUI unattended, since approval depends on the model. — [ufo/client/mcp/local_servers/ui_mcp_server.py:472](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/local_servers/ui_mcp_server.py#L472); [ufo/agents/processors/strategies/app_agent_processing_strategy.py:1361-1363](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/strategies/app_agent_processing_strategy.py#L1361-L1363); [ufo/prompter/agent_prompter.py:268](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/prompter/agent_prompter.py#L268) (verified)
  - *To reach the next level:* Make exfiltration and irreversible actions require human approval enforced in code.
- **Cap:** C5-WORSTCASE — Worst case (B L0): a hijacked agent can leak data and take irreversible actions unattended.

### C6 Memory, context & configuration integrity — 0.35 (high)

Long-term memory features exist (experience and demonstration retrieval stores, saved Q&A customization) but are all off by default, and configuration is loaded only from UFO's own install directory rather than from a workspace the agent works on. When experience saving is enabled in 'always' or 'auto' mode, summaries of past runs are written without review and later re-injected as examples. Nothing tags retrieved memories with provenance.

- **S L1:** When enabled, experience/demonstration summaries are written and re-injected as examples without validation (the 'ask' mode is the only gate). — [config/ufo/system.yaml:76](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L76); [ufo/rag/retriever.py:120-121](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/rag/retriever.py#L120-L121) (verified)
  - *To reach the next level:* Gate memory writes with validation or review and add expiry.
- **C L1:** Only the experience save path offers an ask option; the demonstration and customization stores have no write controls. — [config/ufo/system.yaml:85](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L85); [config/ufo/rag.yaml:14](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/rag.yaml#L14) (verified)
  - *To reach the next level:* Apply the same write gate to every persistent store.
- **D L2:** All memory stores are off by default and stored locally per install, with no auto-loaded workspace config or .env. — [config/ufo/rag.yaml:14](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/rag.yaml#L14); [config/ufo/rag.yaml:4](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/rag.yaml#L4); [config/ufo/system.yaml:76](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L76); searched `rg -n 'dotenv' --type py` in `.` → 0 hits (No .env auto-loading.) (verified)
  - *To reach the next level:* Enforce namespaces the model cannot change and default retention limits.
- **B L2:** By default nothing persists into later context; if enabled, poisoned experience would persist across the user's sessions. — [config/ufo/rag.yaml:14](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/rag.yaml#L14); [config/ufo/system.yaml:76](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L76) (verified)
  - *To reach the next level:* Make persisted memory session-scoped or easily inspected and purged by default.
- **Cap:** none

### C7 Third-party extensions — 0.28 (medium)

By default UFO only runs its own in-repo MCP servers; no third-party extension is loaded. Operators can add stdio MCP servers by command in mcp.yaml, with no pinning or integrity check, and the optional retrieval features load FAISS indexes with pickle deserialization explicitly allowed. Added stdio servers get only the environment configured for them (inferred from the MCP SDK's default environment handling).

- **S L1:** Operator-chosen stdio commands run unpinned; FAISS indexes load with allow_dangerous_deserialization=True when RAG is enabled. — [ufo/client/mcp/mcp_server_manager.py:170-176](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/mcp_server_manager.py#L170-L176); [ufo/rag/retriever.py:120-121](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/rag/retriever.py#L120-L121) (verified)
  - *To reach the next level:* Pin extension versions and verify hashes; avoid pickle-based index loading.
- **C L0:** No extension type is verified. — [ufo/client/mcp/mcp_server_manager.py:170-176](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/mcp_server_manager.py#L170-L176); [ufo/rag/retriever.py:120-121](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/rag/retriever.py#L120-L121) (verified)
  - *To reach the next level:* Verify at least MCP servers and retrieval indexes.
- **D L2:** Nothing third-party is enabled by default; extensions are added only by editing mcp.yaml in the install directory. — [config/ufo/mcp.yaml:28](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/mcp.yaml#L28); [config/ufo/rag.yaml:4](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/rag.yaml#L4) (verified)
  - *To reach the next level:* Show the exact package/command and permissions at enable time and keep it user-scope only.
- **B L2:** Stdio servers run as separate processes with only their configured env (MCP SDK default-environment behaviour), still as the same user. — [ufo/client/mcp/mcp_server_manager.py:172](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/mcp/mcp_server_manager.py#L172) (inferred)
  - *To reach the next level:* Sandbox each extension with its own scoped credentials.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.25 (high)

LLM API keys live in plaintext in config/ufo/agents.yaml (git-ignored) or environment variables, and the whole process environment is merged into the config object. There is no telemetry, but every session writes full prompts and screenshots to logs/ by default with no redaction. Some masking exists (config validator display, sensitive env-var blocks in the shell client), but not on the main log or model paths.

- **S L1:** Secrets come from plaintext YAML or env; masking exists only in the config validator display and the shell client's env-var guard. — [config/ufo/agents.yaml.template:10](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/agents.yaml.template#L10); [config/config_loader.py:476](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/config_loader.py#L476); [ufo/tools/validate_config.py:351](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/tools/validate_config.py#L351); [ufo/automator/app_apis/shell/shell_client.py:198](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/automator/app_apis/shell/shell_client.py#L198) (verified)
  - *To reach the next level:* Add redaction filters on logs and model-bound messages and type-level secret masking.
- **C L1:** Masking covers one display path; request/response logs, screenshots and model-bound messages are unprotected. — [ufo/module/basic.py:602-604](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L602-L604); [ufo/tools/validate_config.py:351](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/tools/validate_config.py#L351) (verified)
  - *To reach the next level:* Redact secrets in logs and transcripts.
- **D L1:** No telemetry, but full prompt payloads and screenshots are logged by default at DEBUG level without redaction. — [ufo/module/basic.py:602-604](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L602-L604); [config/ufo/system.yaml:60](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L60); searched `rg -n -i 'sentry|posthog|telemetry|applicationinsights|opentelemetry' --type py` in `.` → 0 hits (No telemetry or crash-reporting SDK in the Python code.) (verified)
  - *To reach the next level:* Make payload logging opt-in or redacted by default.
- **B L1:** Leaked LLM provider keys are long-lived; the GUI agent could open the config file containing them. — [config/ufo/agents.yaml.template:10](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/agents.yaml.template#L10) (verified)
  - *To reach the next level:* Use scoped, short-lived credentials.
- **Cap:** none

### C9 Audit & traceability — 0.45 (high)

Every step is written as a structured JSON record (action, arguments, results, timings, and whether the user confirmed) to logs/<task>/response.log, flushed on each write, alongside the full LLM requests. The logs sit in UFO's working directory where the desktop agent itself could edit them, carry no actor or approver identity, and are not tamper-evident.

- **S L2:** Structured per-step records with action, arguments and confirmation field. — [ufo/agents/processors/core/processing_middleware.py:126](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/core/processing_middleware.py#L126); [config/ufo/system.yaml:31](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L31) (verified)
  - *To reach the next level:* Add actor attribution (requesting principal, approver) and correlation IDs across Galaxy and devices.
- **C L2:** All built-in tool steps are logged per session; cross-device correlation and denied approvals are not clearly linked. — [ufo/agents/processors/core/processing_middleware.py:126](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/agents/processors/core/processing_middleware.py#L126); [ufo/module/basic.py:602-604](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L602-L604) (verified)
  - *To reach the next level:* Log approvals/denials and sub-agent calls in one correlated trail.
- **D L1:** Logging is on by default but stored under logs/ in the working directory, writable by the agent's own GUI tools. — [ufo/module/basic.py:480](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L480) (verified)
  - *To reach the next level:* Store logs outside anything the agent's tools can modify.
- **B L2:** Records are flushed per write and write failures are printed; nothing blocks actions on log failure. — [ufo/module/basic.py:92](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L92) (verified)
  - *To reach the next level:* Make the record durable per action and replayable, failing closed for high-risk actions.
- **Cap:** none

### C10 Limits & kill switch — 0.45 (high)

Step limits are enforced in code (50 steps per UFO session, 15 for Galaxy, one round), LLM calls time out at 60 seconds, and Galaxy caps concurrent tasks at six. But each device session gets its own fresh step budget, there is no cost cap, and the MCP tool timeout is 6000 seconds (the comment says 5 minutes) with timed-out calls left running in a thread pool. A stop-task handler exists in the Galaxy web UI.

- **S L2:** Iteration caps plus per-call LLM and tool timeouts are enforced; no cost cap or rate limits. — [ufo/module/basic.py:820](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/module/basic.py#L820); [galaxy/session/galaxy_session.py:423](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/galaxy/session/galaxy_session.py#L423); [config/ufo/system.yaml:8](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L8); [ufo/client/computer.py:70](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/computer.py#L70) (verified)
  - *To reach the next level:* Add wall-clock and token/cost caps plus rate limits on side-effecting tools.
- **C L2:** Top-level loops and tool calls are bounded, but each device sub-session starts its own budget. — [galaxy/session/galaxy_session.py:423](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/galaxy/session/galaxy_session.py#L423); [config/galaxy/constellation.yaml:8-9](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/galaxy/constellation.yaml#L8-L9) (verified)
  - *To reach the next level:* Count device sub-sessions against the Galaxy budget.
- **D L2:** Sensible step defaults, operator-configurable in YAML. — [config/ufo/system.yaml:15](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/ufo/system.yaml#L15); [config/galaxy/constellation.yaml:8-9](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/config/galaxy/constellation.yaml#L8-L9) (verified)
  - *To reach the next level:* Prevent delegation from resetting budgets.
- **B L1:** The 6000-second tool timeout only stops waiting; the thread keeps running, so in-flight work outlives the limit. — [ufo/client/computer.py:70](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/computer.py#L70); [ufo/client/computer.py:222-225](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/ufo/client/computer.py#L222-L225); [galaxy/webui/handlers/websocket_handlers.py:222-224](https://github.com/microsoft/ufo/blob/a795552d976c4c019d7c2f778a0effb5cef7de6b/galaxy/webui/handlers/websocket_handlers.py#L222-L224) (verified)
  - *To reach the next level:* Use tight tool timeouts that cancel the underlying call.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Raw UI control text and screenshots of web pages/emails (ufo/prompter/agent_prompter.py:268) · [B] sensitive data/systems: User's full desktop session, files and signed-in apps (ufo/client/mcp/local_servers/ui_mcp_server.py:201) · [C] state change / egress: Arbitrary keyboard/mouse input into any window, e.g. browser or Outlook (ufo/client/mcp/local_servers/ui_mcp_server.py:472) · Same default session? Yes

## Highest-impact improvements
1. Decide in code which actions need confirmation (keyboard_input/set_edit_text/click on send-like controls, run_shell, all HostAgent commands) instead of relying on the model's CONFIRM status. — C2 S L1→L3, +0.150 before caps (Playbook 5)
2. Route HostAgent and Galaxy constellation actions through the same confirmation gate (remove the TODO/always-True stubs). — C2 C L0→L2, +0.150 before caps (Playbook 5)
3. Block shell/terminal windows as keyboard_input targets or apply the command allowlist to typed text. — C4 C L0→L2, +0.150 before caps (Playbook 3 step 1)
4. Ship a documented default of running device agents in Windows Sandbox/VM or a dedicated low-privilege account. — C4 S L1→L2, +0.075 before caps (Playbook 3 step 1)
5. Cut the 6000s MCP tool timeout to minutes and cancel the underlying call on timeout. — C10 B L1→L2, +0.050 before caps (Playbook 3 step 3)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- Scored the Windows device-agent path; the Linux, mobile (adb) and hardware MCP servers were reviewed only partially. The Linux server requires the model to pass the MCP API key as a tool argument; how it is injected was not determined.
- Galaxy web UI frontend (TypeScript) and the learner/dataflow/record_processor tools were not examined in depth.
- No text aimed at AI reviewers was found in the repository.
