Skip to content

Cherry-pick batch: Gateway fixes and hardening (20 commits)#1848

Merged
alexey-pelykh merged 20 commits intomainfrom
staging/cherry-pick-1825
Mar 23, 2026
Merged

Cherry-pick batch: Gateway fixes and hardening (20 commits)#1848
alexey-pelykh merged 20 commits intomainfrom
staging/cherry-pick-1825

Conversation

@alexey-pelykh
Copy link
Copy Markdown

Cherry-pick batch from upstream

Issue: #1825
Commits: 18 cherry-picked, 2 skipped (empty after resolution), 1 fix commit

# Hash Subject Result
1 92fc8065e fix(gateway): remove re-introduced auth.mode=none pairing bypass SKIPPED (empty — fork already had the change)
2 9bffa3422 fix(gateway): skip device pairing when auth.mode=none RESOLVED (CHANGELOG.md DU)
3 7dc447f79 fix(gateway): strip unbound scopes for shared-auth connects RESOLVED (scope-clearing logic restored with fork params)
4 36f394c29 fix(gateway): increase WS handshake timeout from 3s to 10s RESOLVED (CHANGELOG + env var rename)
5 26e0a3ee9 fix(gateway): skip Control UI pairing when auth.mode=none RESOLVED (connect-policy + message-handler fork params)
6 57204b4fa fix(gateway): surface env override keys in exec approvals SKIPPED (empty — all changes in gutted exec-approval layer)
7 ccf16cd88 fix(gateway): clear trusted-proxy control ui scopes RESOLVED (control-ui test helpers + unused var cleanup)
8 3faaf8984 fix(gateway): guard interface discovery failures RESOLVED (CHANGELOG.md DU)
9 c0d4abc59 fix(gateway): suppress ciao interface assertions RESOLVED (CHANGELOG + bonjour files)
10 8cc0c9baf fix(gateway): run before_tool_call for HTTP tools RESOLVED (fork renames + pi-tools stubs)
11 57f1cf66a fix(gateway): skip seq-gap broadcast for stale post-lifecycle events RESOLVED (CHANGELOG.md DU)
12 4da617e17 fix(gateway): honor trusted proxy hook auth rate limits PICKED (clean)
13 ebed3bbde fix(gateway): enforce browser origin check regardless of proxy headers RESOLVED (CHANGELOG.md DU)
14 29fec8bb9 fix(gateway): harden health monitor account gating RESOLVED (server-channels content conflicts)
15 5fc43ff0e fix(gateway): bound unanswered client requests RESOLVED (CHANGELOG.md DU)
16 a1520d70f fix(gateway): propagate real gateway client into plugin subagent runtime RESOLVED (CHANGELOG + server-plugins content)
17 dafd61b5c fix(gateway): enforce caller-scope subsetting in device.token.rotate RESOLVED (CHANGELOG.md DU)
18 a69f6190a fix(gateway): pin plugin webhook route registry RESOLVED (CHANGELOG + server-runtime-state + new submodules)
19 c91d1622d fix(gateway): split conversation reset from admin reset RESOLVED (CHANGELOG + session-reset-service gutted imports)
20 a76e81019 fix(gateway): harden token fallback/reconnect behavior and docs RESOLVED (CHANGELOG + docs content)

Adaptation commit

  • Fork TS strictness fixes: renamed OpenClawConfigRemoteClawConfig, loadOpenClawPluginsloadRemoteClawPlugins
  • Created stub modules for gutted upstream layers (pi-tools, ACP, bootstrap-cache, pi-embedded)
  • Extracted plugin HTTP route submodules (path-context, route-match, route-auth)
  • Fixed test mocks for fork parameter changes (sharedAuthOk vs role)

🤖 Generated with Claude Code

ademczuk and others added 19 commits March 23, 2026 10:33
Fixes openclaw#42931

When gateway.auth.mode is set to "none", authentication succeeds with
method "none" but sharedAuthOk remains false because the auth-context
only recognises token/password/trusted-proxy methods. This causes all
pairing-skip conditions to fail, so Control UI browser connections get
closed with code 1008 "pairing required" despite auth being disabled.

Short-circuit the skipPairing check: if the operator explicitly
disabled authentication, device pairing (which is itself an auth
mechanism) must also be bypassed.

Fixes openclaw#42931

(cherry picked from commit 9bffa34)
…49262)

* fix(gateway): increase WS handshake timeout from 3s to 10s

The 3-second default is too aggressive when the event loop is under load
(concurrent sessions, compaction, agent turns), causing spurious
'gateway closed (1000)' errors on CLI commands like `openclaw cron list`.

Changes:
- Increase DEFAULT_HANDSHAKE_TIMEOUT_MS from 3_000 to 10_000
- Add OPENCLAW_HANDSHAKE_TIMEOUT_MS env var for user override (no VITEST gate)
- Keep OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS as fallback for existing tests

Fixes openclaw#46892

* fix: restore VITEST guard on test env var, use || for empty-string fallback, fix formatting

* fix: cover gateway handshake timeout env override (openclaw#49262) (thanks @fuller-stack-dev)

---------

Co-authored-by: Wilfred <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit 36f394c)
…nclaw#42931) (openclaw#47148)

When auth is completely disabled (mode=none), requiring device pairing
for Control UI operator sessions adds friction without security value
since any client can already connect without credentials.

Add authMode parameter to shouldSkipControlUiPairing so the bypass
fires only for Control UI + operator role + auth.mode=none. This avoids
the openclaw#43478 regression where a top-level OR disabled pairing for ALL
websocket clients.

(cherry picked from commit 26e0a3e)
Closes openclaw#44180.
Refs openclaw#47590.
Co-authored-by: Peter Steinberger <[email protected]>

(cherry picked from commit 3faaf89)
Closes openclaw#38628.
Refs openclaw#47159, openclaw#52431.
Co-authored-by: Peter Steinberger <[email protected]>

(cherry picked from commit c0d4abc)
…penclaw#43751)

* fix: stop stale gateway seq-gap errors (openclaw#43751) (thanks @caesargattuso)

* fix: keep agent.request run ids session-scoped

---------

Co-authored-by: Ayaan Zaidi <[email protected]>
(cherry picked from commit 57f1cf6)
In trusted-proxy mode, enforceOriginCheckForAnyClient was set to false
whenever proxy headers were present. This allowed browser-originated
WebSocket connections from untrusted origins to bypass origin validation
entirely, as the check only ran for control-ui and webchat client types.

An attacker serving a page from an untrusted origin could connect through
a trusted reverse proxy, inherit proxy-injected identity, and obtain
operator.admin access via the sharedAuthOk / roleCanSkipDeviceIdentity
path without any origin restriction.

Remove the hasProxyHeaders exemption so origin validation runs for all
browser-originated connections regardless of how the request arrived.

Fixes GHSA-5wcw-8jjv-m286

(cherry picked from commit ebed3bb)
* gateway: harden health monitor account gating

* gateway: tighten health monitor account-id guard

(cherry picked from commit 29fec8b)
* fix(gateway): bound unanswered client requests

* fix(gateway): skip default timeout for expectFinal requests

* fix(gateway): preserve gateway call timeouts

* fix(gateway): localize request timeout policy

* fix(gateway): clamp explicit request timeouts

* fix(gateway): clamp default request timeout

(cherry picked from commit 5fc43ff)
Plugin subagent dispatch used a hardcoded synthetic client carrying
operator.admin, operator.approvals, and operator.pairing for all
runtime.subagent.* calls. Plugin HTTP routes with auth:"plugin" require
no gateway auth by design, so an unauthenticated external request could
drive admin-only gateway methods (sessions.delete, agent.run) through
the subagent runtime.

Propagate the real gateway client into the plugin runtime request scope
when one is available. Plugin HTTP routes now run inside a scoped
runtime client: auth:"plugin" routes receive a non-admin synthetic
operator.write client; gateway-authenticated routes retain admin-capable
scopes. The security boundary is enforced at the HTTP handler level.

Fixes GHSA-xw77-45gv-p728

(cherry picked from commit a1520d7)
device.token.rotate accepted attacker-controlled scopes and forwarded
them to rotateDeviceToken without verifying the caller held those
scopes. A pairing-scoped token could rotate up to operator.admin on
any already-paired device whose approvedScopes included admin.

Add a caller-scope subsetting check before rotateDeviceToken: the
requested scopes must be a subset of client.connect.scopes via the
existing roleScopesAllow helper. Reject with missing scope: <scope>
if not.

Also add server.device-token-rotate-authz.test.ts covering both the
priv-esc path and the admin-to-node-invoke chain.

Fixes GHSA-4jpw-hj22-2xmc

(cherry picked from commit dafd61b)
…claw#42507)

* fix(gateway): harden token fallback and auth reconnect handling

* docs(gateway): clarify auth retry and token-drift recovery

* fix(gateway): tighten auth reconnect gating across clients

* fix: harden gateway token retry (openclaw#42507) (thanks @joshavant)

(cherry picked from commit a76e810)
- Replace OpenClawConfig with RemoteClawConfig in server-channels and
  server-runtime-state
- Replace loadOpenClawPlugins with loadRemoteClawPlugins in server-plugins
  and remove unsupported runtimeOptions field and dead subagent runtime code
- Export HookClientIpConfig type from server-http and thread it through
  server/hooks into server-runtime-state and server.impl
- Create plugins-http/ submodules (path-context, route-match, route-auth)
  extracted from the monolithic plugins-http.ts by upstream refactor
- Create stub modules for gutted upstream layers: acp/control-plane/manager,
  agents/bootstrap-cache, agents/pi-embedded, agents/internal-events
- Strip thinkingLevel, reasoningLevel, skillsSnapshot from SessionEntry
  literals in agent.ts and session-reset-service.ts (Pi-specific fields)
- Remove internalEvents from agent ingress opts and loadGatewayModelCatalog
  from sessions patch call (not present in fork types)
- Fix connect-policy tests to pass booleans instead of role strings for
  the sharedAuthOk parameter (fork changed the function signature)
- Add isHealthMonitorEnabled to ChannelManager mocks in test files
- Widen runBeforeToolCallHook mock return type to accept blocked: true
- Add explicit string types for msg params in server-plugins logger

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@alexey-pelykh alexey-pelykh enabled auto-merge (squash) March 23, 2026 10:07
@alexey-pelykh alexey-pelykh merged commit fdfbc83 into main Mar 23, 2026
7 checks passed
@alexey-pelykh alexey-pelykh deleted the staging/cherry-pick-1825 branch March 23, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants