openclaw - ✅(Solved) Fix [Bug]: `trusted-operator` plugin HTTP routes escalate shared-secret callers to implicit admin scopes [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#78712Fetched 2026-05-07 03:33:28
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
2
Author
Timeline (top)
cross-referenced ×2commented ×1labeled ×1

For plugin HTTP routes that opt into gatewayRuntimeScopeSurface: "trusted-operator", scope resolution restores CLI_DEFAULT_OPERATOR_SCOPES (includes operator.admin) when requestAuth.trustDeclaredOperatorScopes is false. Shared-secret bearer auth (token / password) sets trustDeclaredOperatorScopes: false, so a caller can request narrow scopes (or no scope header) yet still execute plugin runtime logic with admin-equivalent scope set. This is a privilege escalation inside plugin route runtime scope mapping.

Root Cause

For plugin HTTP routes that opt into gatewayRuntimeScopeSurface: "trusted-operator", scope resolution restores CLI_DEFAULT_OPERATOR_SCOPES (includes operator.admin) when requestAuth.trustDeclaredOperatorScopes is false. Shared-secret bearer auth (token / password) sets trustDeclaredOperatorScopes: false, so a caller can request narrow scopes (or no scope header) yet still execute plugin runtime logic with admin-equivalent scope set. This is a privilege escalation inside plugin route runtime scope mapping.

PR fix notes

PR #78719: fix(gateway): prevent shared-secret callers from gaining admin scopes on trusted-operator plugin routes

Description (problem / solution / changelog)

Summary

Fixes #78712 — beta blocker privilege escalation in plugin HTTP route scope resolution.

Root cause: resolvePluginRouteRuntimeOperatorScopes short-circuited to CLI_DEFAULT_OPERATOR_SCOPES (including operator.admin) whenever surface === "trusted-operator" and !requestAuth.trustDeclaredOperatorScopes. Shared-secret bearer auth always sets trustDeclaredOperatorScopes: false, so a token/password caller could gain admin-equivalent scopes even when they declared narrow or no scopes.

Fix: Remove the early-return that bypassed resolveTrustedHttpOperatorScopes for untrusted callers. The function now delegates to resolveTrustedHttpOperatorScopes unconditionally for the trusted-operator surface:

  • Shared-secret callers (trustDeclaredOperatorScopes: false) → [] (no header) or their declared subset — never admin defaults
  • trusted-proxy callers (trustDeclaredOperatorScopes: true) → full operator defaults when no scope header present (unchanged behavior)

Files changed:

  • src/gateway/server/plugin-route-runtime-scopes.ts — 5-line fix, remove unused CLI_DEFAULT_OPERATOR_SCOPES import
  • src/gateway/server/plugin-route-runtime-scopes.test.ts — update test that asserted buggy behavior
  • src/gateway/server/plugins-http.runtime-scopes.test.ts — replace admin-escalation assertion with fail-closed assertion; add trusted-proxy positive case
  • src/gateway/server.plugin-http-auth.test.ts — update integration test to assert no admin escalation for shared-secret callers

Tests: 3435/3435 gateway tests pass.

Audit

  • Audit A (existing helper): resolveTrustedHttpOperatorScopes already handles this correctly — reused, no new helper needed.
  • Audit B (shared caller check): resolvePluginRouteRuntimeOperatorScopes is called from 2 sites (server-http.ts:440, server/plugins-http.ts:120). Both pass requestAuth as-is; no caller contract change.
  • Audit C (rival PR): No rival PR touching plugin-route-runtime-scopes.ts. Related PRs (#57889, #51413) touch WebSocket connect-policy, not plugin route scopes.

Changed files

  • src/gateway/server.plugin-http-auth.test.ts (modified, +3/-6)
  • src/gateway/server/plugin-route-runtime-scopes.test.ts (modified, +2/-9)
  • src/gateway/server/plugin-route-runtime-scopes.ts (modified, +1/-4)
  • src/gateway/server/plugins-http.runtime-scopes.test.ts (modified, +42/-4)

Code Example

Relevant code paths:

- `src/gateway/server/plugin-route-runtime-scopes.ts`
  - `resolvePluginRouteRuntimeOperatorScopes(...)` returns `CLI_DEFAULT_OPERATOR_SCOPES` when:
    - `surface === "trusted-operator"`
    - `!requestAuth.trustDeclaredOperatorScopes`
- `src/gateway/http-auth-utils.ts`
  - `checkGatewayHttpRequestAuth(...)` sets `trustDeclaredOperatorScopes` false for shared-secret methods
- `src/gateway/server/plugins-http.runtime-scopes.test.ts`
  - test case demonstrates trusted-operator route restoring admin/read/write defaults
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

Yes

Summary

For plugin HTTP routes that opt into gatewayRuntimeScopeSurface: "trusted-operator", scope resolution restores CLI_DEFAULT_OPERATOR_SCOPES (includes operator.admin) when requestAuth.trustDeclaredOperatorScopes is false. Shared-secret bearer auth (token / password) sets trustDeclaredOperatorScopes: false, so a caller can request narrow scopes (or no scope header) yet still execute plugin runtime logic with admin-equivalent scope set. This is a privilege escalation inside plugin route runtime scope mapping.

Steps to reproduce

  1. Register a plugin HTTP route with:
    • auth: "gateway"
    • gatewayRuntimeScopeSurface: "trusted-operator"
  2. Authenticate using shared-secret bearer auth (gateway token/password).
  3. Send request with narrow declared scopes (for example x-openclaw-scopes: operator.read) or omit the header.
  4. In route handler, read runtime scopes from plugin runtime request scope and check admin-only permission.
  5. Observe admin-capable scopes are present and admin-only checks pass.

Expected behavior

Shared-secret callers should not be silently up-leveled to admin scopes on plugin routes unless explicitly and safely configured with clear, auditable intent. Narrow declared scopes should remain narrow.

Actual behavior

trusted-operator surface restores full default operator scope set (including admin) for shared-secret auth paths, overriding the caller’s narrower scope intent.

OpenClaw version

2026.5.4

Operating system

Ubuntu 24.04 / Windows 11

Install method

npm global

Model

anthropic/claude-sonnet-4.5

Provider / routing chain

anthropic

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Relevant code paths:

- `src/gateway/server/plugin-route-runtime-scopes.ts`
  - `resolvePluginRouteRuntimeOperatorScopes(...)` returns `CLI_DEFAULT_OPERATOR_SCOPES` when:
    - `surface === "trusted-operator"`
    - `!requestAuth.trustDeclaredOperatorScopes`
- `src/gateway/http-auth-utils.ts`
  - `checkGatewayHttpRequestAuth(...)` sets `trustDeclaredOperatorScopes` false for shared-secret methods
- `src/gateway/server/plugins-http.runtime-scopes.test.ts`
  - test case demonstrates trusted-operator route restoring admin/read/write defaults

Impact and severity

Critical for deployments exposing plugin HTTP routes with trusted-operator: bearer-token callers can gain broader-than-requested privilege (including admin scope) in plugin runtime context, increasing blast radius of token compromise and violating least-privilege boundaries.

Additional information

Check duplicates for: gatewayRuntimeScopeSurface, trusted-operator, resolvePluginRouteRuntimeOperatorScopes, trustDeclaredOperatorScopes, and plugin route admin scope escalation.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Shared-secret callers should not be silently up-leveled to admin scopes on plugin routes unless explicitly and safely configured with clear, auditable intent. Narrow declared scopes should remain narrow.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: `trusted-operator` plugin HTTP routes escalate shared-secret callers to implicit admin scopes [1 pull requests, 1 comments, 2 participants]