strong-ice-84145
09/11/2026, 8:57 PMfresh-machine-37269
09/11/2026, 8:57 PMfresh-machine-37269
09/11/2026, 9:00 PMACP_PROVIDERS) from "what Cloud offers" (SURFACED_ACP_PROVIDERS), and parametrizing the rejection tests off the registry diff so new upstream harnesses are covered automatically.
Blocking (P1): There's a TOCTOU gap ā _validate_acp_provider_surfaced resolves the profile and checks the provider, but the settings actually used to build the agent are re-resolved much later (_build_start_conversation_request_for_user / _build_acp_start_conversation_request). After the potentially long sandbox-start wait, a caller can overwrite the same profile ID to an unsupported provider and the later lookup builds it un-gated.
Other points (P2/P3):
⢠Redundant profile resolution on the HTTP path (router check + service check + build = up to three lookups), which also widens the P1 window.
⢠The allowlist isn't gated by get_deployment_mode() ā self-hosted/Replicated operators can't widen it even if they pin a wrapper for an extra harness.
⢠custom bypass is only documented in the constants module, not where the gate is enforced.
⢠is_acp_provider_surfaced (raw list) and surfaced_acp_providers() (registry-filtered) can disagree on a renamed/removed harness.
Plus a small nit about rebuilding the tuple constant on every call. Overall a solid, well-tested fix with one security-relevant gap worth closing before merge.strong-ice-84145
09/11/2026, 10:14 PMfresh-machine-37269
09/11/2026, 10:15 PMfresh-machine-37269
09/11/2026, 10:25 PM_start_app_conversation resolves the effective launch view once and threads that exact UserInfo snapshot through validation, the build, and provenance tagging, with a regression test that actually swaps the resolved revision between lookups and asserts the unsupported provider never reaches the build. The service-level enforcement covers integration-triggered starts (GitHub/GitLab/Jira/Slack), not just the HTTP endpoints.
I also left four non-blocking notes (the custom bypass, the 400 message omitting custom, the import-time allowlist snapshot, and the test asserting HTTPException rather than the specific error type), none of which block merge.
Review: github.com/OpenHands/enterprise/pull/336#ā¦strong-ice-84145
09/11/2026, 10:35 PMfresh-machine-37269
09/11/2026, 10:38 PMfeat: scope an agent profile to specific MCP servers) and posted an Approve.
Key points from my review:
⢠The seeding fix is the crux ā toAgentSettingsOverride now carries mcp_server_refs, so reopening a scoped profile restores the selection instead of silently widening to "all servers" on save.
⢠Tri-state is handled exactly: null = all, array = subset, [] = none (and ?? null correctly preserves the explicit-empty case).
⢠The prior order-sensitivity finding is fixed ā dirty tracking now uses sameScopeSelection (set comparison), with tests covering stored-order-vs-catalog-order divergence.
⢠Dangling-ref inline error is the right call, since a dangling MCP ref raises DanglingMcpServerRef at launch (unlike a stale secret_refs, which is a no-op).
⢠Placement is correct (hidden outside the profile editor), and CI is green across ubuntu + windows with 5646 tests passing.
I left two non-blocking nits (the row sub-label shows transport type rather than a useful description, and mcp_server_refs is now emitted on every embedded save even when untouched). Neither blocks merge.
Review: github.com/OpenHands/OpenHands/pull/17289#ā¦