Skip to content

fix(plugins): block untrusted workspace setup-only channel loads#86953

Merged
clawsweeper[bot] merged 18 commits into
mainfrom
pr-64154-rework
Jun 1, 2026
Merged

fix(plugins): block untrusted workspace setup-only channel loads#86953
clawsweeper[bot] merged 18 commits into
mainfrom
pr-64154-rework

Conversation

@hxy91819

@hxy91819 hxy91819 commented May 26, 2026

Copy link
Copy Markdown
Member

Summary

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Current Main vs This PR

The #64154 design had two layers:

  1. Catalog callers should resolve channels through trusted helpers so setup/add flows do not select untrusted workspace shadows.
  2. The loader/registry should enforce the same trust boundary before any plugin module can execute, so lower-level callers cannot bypass the catalog layer.

Latest main still preserves the first layer. channels add, setup/onboarding, and channel setup plugin resolution use listTrustedChannelPluginCatalogEntries / getTrustedChannelPluginCatalogEntry; the setup wizard's installable display can still show workspace-only candidates through the lenient discovery helper without treating them as executable/installed.

This PR fills the second layer. It prevents explicit setup-only scoped loads from importing disabled workspace channel plugins, rejects disabled workspace channel registrations at the registry boundary, and keeps trusted workspace overrides working. It also ports #85774's try/catch around api.registerChannel(mergedSetupPlugin), which addresses a related but distinct resilience gap: trusted setup-entry plugins are allowed to load, but one malformed registration should not stop healthy sibling plugins from loading.

Diagram

graph TD
  SOURCES["Plugin source directories"] --> MANIFEST["Manifest registry<br/>(pluginId + origin + channels)"]

  MANIFEST -->|"setup / onboard / channels add"| TRUSTED["Trusted catalog helpers<br/>strict execution list<br/>untrusted workspace shadows fall back"]
  MANIFEST -->|"setup wizard display"| DISCOVERY["Setup discovery helper<br/>lenient display list<br/>workspace-only candidates visible"]
  MANIFEST -->|"gateway or explicit scoped setup load"| LOADER["loadOpenClawPlugins()"]

  TRUSTED --> SETUP["setup/add snapshot request"]
  DISCOVERY --> UI["installable UI bucket only<br/>not an execution decision"]
  SETUP --> LOADER

  LOADER --> GATE{"This PR:<br/>workspace channel plugin enabled?"}
  GATE -->|"disabled workspace"| BLOCK["reject before setup-only import<br/>no plugin code executes"]
  GATE -->|"trusted workspace or non-workspace"| IMPORT["import setup/runtime entry"]

  IMPORT --> REGISTER{"This PR + #85774:<br/>registerChannel succeeds?"}
  REGISTER -->|"yes"| REGISTRY["snapshot/runtime registry"]
  REGISTER -->|"throws"| DIAG["record plugin diagnostic<br/>continue loading siblings"]

  REGISTRY --> SNAPUSE["validateInput / applyAccountConfig<br/>write config / lifecycle callbacks"]
  REGISTRY --> RUNTIME["gateway/runtime channel lookup"]

  BLOCK:::fixed
  GATE:::fixed
  DIAG:::fixed
  TRUSTED:::kept
  DISCOVERY:::display
  UI:::display

  classDef fixed fill:#d4edda,stroke:#28a745,stroke-width:2px
  classDef kept fill:#d9edf7,stroke:#31708f,stroke-width:2px
  classDef display fill:#fff3cd,stroke:#ffc107,stroke-width:2px
Loading

The important boundary is now shared: catalog helpers decide which channel catalog entries are trusted for setup/add flows, and the loader/registry enforce the same rule before runtime code can execute. If a trusted setup-entry channel still fails during registration, the error is isolated to that plugin and sibling plugins continue loading.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: untrusted workspace channel plugins should not execute during setup-only scoped loads; disabled workspace channel registrations should be rejected at registry assembly; trusted setup-entry channel registration failures should not abort sibling plugin loading.
  • Real environment tested: local Linux repo checkout rebased on upstream/main 98b8e85bebdc5438e9ea5718af5749be0cc742fb; PR head 9bfc1ca3c0e4e80fd5ae8a10d16a32b8dc4af972.
  • Exact steps or command run after this patch:
    • pnpm docs:list
    • pnpm test src/commands/onboard-channels.e2e.test.ts -- --reporter=verbose
    • pnpm test src/plugins/loader.test.ts src/plugins/registry.channel-guard.test.ts src/commands/channel-setup/trusted-catalog.test.ts src/commands/channel-setup/channel-plugin-resolution.test.ts src/commands/channel-setup/discovery.test.ts src/commands/channel-setup/workspace-shadow-bypass.test.ts src/commands/channel-setup/plugin-install.test.ts src/commands/channels.add.test.ts -- --reporter=verbose
    • pnpm build
    • pnpm check:changed
  • Evidence after fix: src/commands/onboard-channels.e2e.test.ts passed 16/16 after exposing listRawChannelPluginCatalogEntries through the same mock/actual fallback as the old catalog helper; the focused loader/registry/catalog/channel setup tests passed after the 2026-06-01 rebase; pnpm build passed on PR head 9bfc1ca3c0e4e80fd5ae8a10d16a32b8dc4af972. pnpm check:changed first could not delegate because the local Crabbox binary is 0.15.0 and the Testbox wrapper requires >=0.22.0; the local child run reached typecheck and failed only in untouched extensions/phone-control/index.test.ts extension-test mocks. CHANGELOG.md and stale transcripts tool-display metadata are no longer in the final branch diff.
  • Observed result after fix: disabled workspace shadows no longer execute through scoped setup-only loads; trusted workspace overrides remain available; disabled workspace channel registrations are rejected as a registry backstop; malformed trusted setup-entry channel registration records a diagnostic and sibling plugins keep loading.
  • What was not tested: live channel setup against a real external workspace plugin outside the loader test harness.
  • Before evidence (optional but encouraged): the same minimal setup-only repro failed on latest origin/main before this branch because the marker file was created during scoped setup-only load.

Root Cause (if applicable)

  • Root cause: the scoped setup-only loader path treated explicit onlyPluginIds as sufficient to load a disabled workspace channel plugin, so workspace trust was bypassed below the trusted-catalog call sites.
  • Missing detection / guardrail: registry assembly still accepted channel registration from disabled workspace plugins if a lower-level caller imported one.
  • Complementary fix(plugins): guard registerChannel in setup-entry path with try/catch #85774 root cause: the setup-entry loader loop caught surrounding registration failures, but api.registerChannel(mergedSetupPlugin) itself was unguarded, so one throwing trusted channel registration could abort sibling plugin loading.
  • Contributing context: latest main already absorbed the higher-level channels add / trusted-catalog call-site fixes from Plugins: keep untrusted workspace channel shadows out of setup-time loads #64154, but not this lower-level loader/registry backstop or the setup-entry error-isolation guard.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
  • Unit test
  • Seam / integration test
  • End-to-end test
  • Existing coverage already sufficient
  • Target test or file: src/plugins/loader.test.ts, src/plugins/registry.channel-guard.test.ts, src/commands/channel-setup/trusted-catalog.test.ts, and existing channel setup/add tests.
  • Scenario the test should lock in: explicit scoped setup-only loads must reject disabled workspace channel plugins but keep trusted workspace channel plugins available; registry assembly must reject disabled workspace channel registrations; trusted setup-entry registration errors must be recorded without aborting healthy sibling plugins.
  • Why this is the smallest reliable guardrail: the bugs live at loader/registry/catalog boundaries and require real plugin import behavior plus registration-mode assertions.
  • Existing test that already covers this (if any): only setup-loads a disabled channel plugin when the caller scopes to the selected plugin still covers the non-workspace path.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • Untrusted workspace channel shadows no longer execute during setup-only scoped loads.
  • Trusted workspace channel plugins remain available to setup-time scoped loading.
  • A broken trusted setup-entry channel registration now surfaces as a plugin diagnostic instead of stopping sibling plugin loading.

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: Node 22 local repo checkout
  • Model/provider: N/A
  • Integration/channel (if any): plugin loader / channel plugin registry / channel setup catalog
  • Relevant config (redacted): workspace plugins enabled; untrusted plugin omitted from allowlist

Steps

  1. Create a workspace channel plugin fixture with channels metadata and a side-effect marker write in register().
  2. Load it through loadOpenClawPlugins({ includeSetupOnlyChannelPlugins: true, forceSetupOnlyChannelPlugins: true, onlyPluginIds: [...] }).
  3. Compare disabled workspace behavior against trusted workspace behavior.
  4. Load a setup-entry channel whose channel config getter throws during registerChannel alongside a healthy sibling plugin.

Expected

  • Disabled workspace channel plugin stays disabled and does not execute.
  • Trusted workspace channel plugin remains available to setup-only loading.
  • A throwing trusted setup-entry channel records a diagnostic and does not prevent sibling plugins from loading.

Actual

  • This branch matches the expected behavior.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets in test output
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: untrusted/trusted workspace scoped setup-only regressions, disabled workspace registry guard, trusted-catalog filtering semantics, onboarding channel setup e2e catalog mock compatibility, channels add workspace shadow fallback, fix(plugins): guard registerChannel in setup-entry path with try/catch #85774 setup-entry registerChannel error isolation, and pnpm build after the 2026-06-01 rebase. pnpm check:changed has a current local proof gap in untouched extensions/phone-control/index.test.ts extension-test typecheck.
  • Edge cases checked: trusted workspace override still loads in setup-only mode; non-workspace disabled plugin behavior stays unchanged; setup wizard discovery still keeps workspace-only install candidates visible without treating them as installed/executable.
  • What you did not verify: a live end-to-end channel setup outside the loader test harness.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No) No. This is an intentional security hardening break for disabled workspace channel plugins that were previously reachable through setup-only scoped loads.
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No automatic migration. Operators who intentionally develop or run a workspace/linked channel plugin must explicitly enable/trust that plugin before setup-time code can run.
  • If yes, exact upgrade steps: use the normal plugin enable/trust flow for the workspace plugin before running channel setup. The behavior is documented in docs/cli/plugins.md.

Risks and Mitigations

  • Risk: a trusted workspace plugin could accidentally get blocked if the trust check were too broad.
  • Mitigation: regression coverage explicitly keeps the trusted workspace scoped-load path green.
  • Risk: setup discovery could accidentally hide workspace-only install candidates.
  • Mitigation: trusted-catalog and discovery tests keep the strict execution list separate from the lenient setup wizard display list.
  • Risk: an existing disabled workspace channel plugin that relied on setup-only execution now fails closed.
  • Mitigation: this is the intended security behavior; the docs now state that workspace-origin plugins are not imported or executed until explicitly enabled/trusted.

Proof 1: Pre-Fix Reproduction on origin/main

  • Behavior addressed: Untrusted workspace channel plugin (evi1-channel-probe) executes its register() function via the setup-only scoped load path when includeSetupOnlyChannelPlugins: true is passed to loadOpenClawPlugins, bypassing the trust boundary.
  • Real environment tested: Crabbox docker, provider=docker, container=391883b7652bcb464c523f64ddcabb9bcad47f92e434737b7cc5391f7bc0922f, run=cbx_6dadb5d505eb, checkout SHA=ead847f6065ac36031f9e7a95dd1ee90f171f79c (origin/main)
  • Exact steps or command run after this patch: (1) git fetch --depth=1 origin main && git checkout FETCH_HEAD inside Crabbox container; (2) pnpm install --frozen-lockfile && pnpm build; (3) Deploy evi1-channel-probe plugin to /tmp/proof1-workspace/.openclaw/extensions/evi1-channel-probe/; (4) Confirm /tmp/openclaw-marker-evidence absent; (5) Run node probe-untrusted.mjs with onlyPluginIds=["evi1-channel-probe"], includeSetupOnlyChannelPlugins=true, activate=false, cache=false; (6) Check /tmp/openclaw-marker-evidence existence.
  • Evidence after fix: Marker file /tmp/openclaw-marker-evidence CREATED with content "evi1-probe-executed\n". Plugin status="disabled" with channelSetups=[{id:"evi1-channel-probe"}] — the setup-only path imported and executed the untrusted workspace plugin code.
  • Observed result after fix: On origin/main (pre-fix), the untrusted workspace channel plugin code ran despite being disabled. The plugin appeared in channelSetups (not channels), status was "disabled", yet register() executed and created the marker file. This confirms the bypass vulnerability.
  • What was not tested: CLI blackbox path, real channels add flow

Proof 2: Post-Fix Verification on PR Head

  • Behavior addressed: The PR fix blocks untrusted workspace channel plugins from executing during setup-only scoped loads; disabled workspace channel registrations are rejected at registry assembly; trusted workspace plugins with plugins.allow still work; broken trusted setup-entry registerChannel records a diagnostic without aborting sibling plugins.
  • Real environment tested: Crabbox docker, provider=docker, container=df29879e2faef7ac8794cba519c1988aff410316a3718d90c6f638cd9977ce15, lease=cbx_0f687f5da081, checkout SHA=b05a7b14fe9bd6c119b3b7fe60a1967d0987804c (PR head at time of test; refreshed fix(plugins): block untrusted workspace setup-only channel loads #86953 head is 9bfc1ca3c0e4e80fd5ae8a10d16a32b8dc4af972)
  • Exact steps or command run after this patch: (1) git fetch origin pull/82579/head:pr-head && git checkout pr-head inside Crabbox container; (2) pnpm install --frozen-lockfile && pnpm build; (3) grep -r "channel registration rejected for disabled workspace plugin" dist/ — fix code found in dist/loader-DM-dphfh.js; (4) Deploy evi1-channel-probe (untrusted), confirm marker ABSENT after probe; (5) Deploy evi1-trusted-probe (with plugins.allow), confirm marker EXISTS; (6) Deploy evi1-broken-register + evi1-sibling-probe (with plugins.allow), confirm sibling marker EXISTS and diagnostic recorded; (7) Run focused tests: pnpm test src/plugins/loader.test.ts (142 pass), pnpm test src/plugins/registry.channel-guard.test.ts (2 pass), pnpm test src/commands/channel-setup/trusted-catalog.test.ts (5 pass), pnpm test src/commands/channel-setup/workspace-shadow-bypass.test.ts (6 pass); (8) pnpm check:changed — 0 errors.
  • Evidence after fix: Untrusted probe: MARKER_ABSENT, exit code 0 (clean block, not crash). Plugin status="disabled", channelSetups=[]. Trusted probe: MARKER_EXISTS, content "evi1-probe-executed\n". Plugin status="loaded", channels and channelSetups populated. Error isolation: broken-register records diagnostic (level=error, pluginId=evi1-broken-register), process exit code 0. Sibling marker EXISTS, content "evi1-sibling-probe-executed\n".
  • Observed result after fix: The fix blocks untrusted workspace channel plugin execution. Untrusted plugin stays disabled with empty channelSetups. Trusted workspace plugin with plugins.allow still loads and registers channels. Broken trusted registerChannel records a diagnostic and sibling plugins continue loading. All focused tests pass.
  • What was not tested: CLI blackbox path, real channels add flow, 3 pre-existing test failures in channel-auth.test.ts (2) and channels.remove.test.ts (1) unrelated to this PR

Crabbox cross-reference: Proof 1 provider=docker, container=391883b7652bcb464c523f64ddcabb9bcad47f92e434737b7cc5391f7bc0922f, run=cbx_6dadb5d505eb, SHA=ead847f6065ac36031f9e7a95dd1ee90f171f79c (origin/main). Proof 2 provider=docker, container=df29879e2faef7ac8794cba519c1988aff410316a3718d90c6f638cd9977ce15, lease=cbx_0f687f5da081, SHA=b05a7b14fe9bd6c119b3b7fe60a1967d0987804c (#82579 head; superseded by #86953, refreshed head 2a9a2d79fd498be5fc62e83634a544f873b67b4c). Proof 1 SHA (ead847f) is an ancestor of Proof 2 SHA (b05a7b1) via the main branch lineage. Probe infrastructure file SHA-256 hashes are identical between both proofs (VAL-CROSS-01 confirmed).


Blackbox Verification Evidence (2026-06-01)

PR Head SHA Tested

00f3c695439a7211d293b6f6ce203e4c0bea3a9f

Exact Commands Run

Blackbox probe (Linux, local worktree):

cd /tmp/proof-86953
pnpm install --frozen-lockfile
pnpm build
node blackbox-channel-guard-probe.mjs

Whitebox unit tests:

node scripts/run-vitest.mjs src/plugins/registry.channel-guard.test.ts --reporter=verbose
node scripts/run-vitest.mjs src/plugins/loader.test.ts -- -t "blocks untrusted setup-only workspace channel plugins|keeps trusted setup-only workspace channel plugins available|records a diagnostic when registerChannel throws in the setup-entry path|keeps healthy sibling channel plugins loadable when a setup entry throws" --reporter=verbose
git diff --check

Marker-File Before/After Evidence

Test Marker File Before Load After Load Content Interpretation
TEST1: disabled workspace (evil-workspace-probe, no allow/enable) test1-top-level.txt ABSENT ABSENT N/A Module top-level code did not execute
TEST2: allowlisted workspace (trusted-workspace-probe, plugins.allow) test2-top-level.txt ABSENT EXISTS trusted-workspace-probe-register-executed Trust-granted plugin loaded correctly
TEST3: disabled workspace (loader-level block) test3-top-level.txt ABSENT ABSENT N/A Module never imported, no register() called
TEST4: broken register + sibling (both allowlisted) test4-broken.txt ABSENT EXISTS (in-test) broken-register code ran Broken plugin loaded (allowlisted) but diagnostic recorded
TEST4: sibling plugin test4-sibling.txt ABSENT EXISTS (in-test) healthy-sibling-probe-register-executed Sibling loaded successfully despite broken neighbor
TEST5: entries-enabled workspace (enabled-workspace-probe, plugins.entries.*.enabled=true) test5-top-level.txt ABSENT EXISTS enabled-workspace-probe-register-executed Explicit enablement works

Whether Disabled Workspace Setup Code Executed

No. In TEST1 and TEST3, the disabled workspace channel plugin (evil-workspace-probe, evil-registry-probe) was discovered but not enabled or allowlisted. After the loadOpenClawPlugins call with includeSetupOnlyChannelPlugins: true, forceSetupOnlyChannelPlugins: true, onlyPluginIds: [...]:

  • No marker file was written — module top-level code did not execute
  • No register() callback ran — no register-executed marker
  • Plugin status was "disabled" with origin: "workspace"
  • channelSetups and channels were both empty — no channel registration occurred

The loader-level guard (canLoadScopedSetupOnlyChannelPlugin requiring candidate.origin !== "workspace" || enableState.enabled) correctly blocks disabled workspace plugins before module import. The registry defense-in-depth guard at registerChannel was not reached because the module was never imported.

Whether Enabled/Allowlisted Setup Still Worked

Yes.

  • Allowlisted (plugins.allow): TEST2 confirmed the allowlisted workspace plugin loaded with status "loaded", channelSetups populated, and marker file written.
  • Explicit entries enablement (plugins.entries.*.enabled = true): TEST5 confirmed the explicitly enabled workspace plugin loaded with status "loaded" and marker file written.
  • Error isolation: TEST4 confirmed a broken registerChannel in one allowlisted plugin records a diagnostic but does not prevent a healthy sibling from loading.

Whitebox Unit Test Results

src/plugins/registry.channel-guard.test.ts: 3 passed (3 total)
src/plugins/loader.test.ts (focused): 4 passed | 148 skipped (4 matched)

Tests verified:

  • blocks untrusted setup-only workspace channel plugins when explicitly scoped
  • keeps trusted setup-only workspace channel plugins available when explicitly scoped
  • records a diagnostic when registerChannel throws in the setup-entry path
  • keeps healthy sibling channel plugins loadable when a setup entry throws

Registry guard tests:

  • rejects channel registration from disabled workspace plugins

  • rejects disabled workspace registration before reading channel data

  • keeps channel registration available for trusted workspace plugins

  • git diff --check: clean, no whitespace errors.

Gaps or Skipped Checks

  • No CLI blackbox path tested: The probe used loadOpenClawPlugins() directly rather than openclaw channels add or openclaw onboard CLI commands. The programmatic API exercises the same loader/registry guard code path; the CLI path is a thin wrapper calling the same loadChannelSetupPluginRegistrySnapshotForChannel / loadChannelSetupPluginRegistry entry points.
  • No plugins.load.paths testing: Per the prompt, this is a separate follow-up hardening topic and was explicitly excluded.
  • No live external workspace plugin: The probe used synthetic fixture plugins rather than an externally installed npm workspace plugin. The origin detection code uses filesystem path (origin: "workspace" from .openclaw/extensions/), which the fixtures exercise correctly.
  • Test4 broken-register probe uses listAccountIds getter throwing. The actual fix(plugins): guard registerChannel in setup-entry path with try/catch #85774 guard also covers api.registerChannel(mergedSetupPlugin) throwing; this is covered by the whitebox unit test records a diagnostic when registerChannel throws in the setup-entry path.

@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes commands Command implementations size: L maintainer Maintainer-authored PR labels May 26, 2026
@clawsweeper

clawsweeper Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

Codex review: passed. Reviewed June 1, 2026, 5:24 AM ET / 09:24 UTC.

Summary
This PR blocks disabled workspace-origin channel plugins from setup-only scoped imports, rejects their channel registrations at registry assembly, documents the trust rule, and adds regression coverage.

PR surface: Source +46, Tests +610, Docs +13. Total +669 across 22 files.

Reproducibility: yes. source inspection gives a high-confidence reproduction path: current main's setup-only scoped loader gate lacks a workspace-enabled check, so an explicitly scoped disabled workspace channel plugin can be imported before this PR. I did not run the repro locally in this read-only review.

Review metrics: 1 noteworthy metric.

  • Workspace setup load policy: 1 fail-closed execution boundary changed. Disabled workspace-origin channel plugins that could previously execute through explicit setup-only scoped loads now require enablement and allowlisting.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Let required checks settle on head 11438bc.
  • [P1] Have maintainers explicitly accept the fail-closed workspace-plugin setup behavior before merge.

Risk before merge

  • [P1] Merging intentionally fail-closes disabled workspace-origin channel setup-only scoped imports; existing local channel setup flows that relied on explicit scoped setup loads must enable and allowlist the workspace plugin first.
  • [P1] The diff changes the plugin execution trust boundary, so maintainers should explicitly accept the compatibility/security tradeoff even though the source review and focused tests support the patch.
  • [P1] The latest head was force-pushed after the previous ClawSweeper review, so exact-head required checks should settle before landing.

Maintainer options:

  1. Accept the fail-closed trust boundary (recommended)
    Land with the documented enable and allowlist path once required checks pass, because the old behavior could execute disabled workspace-origin channel code during setup-only scoped loads.
  2. Preserve old setup convenience with an explicit mode
    If maintainers decide existing disabled-workspace setup flows must keep working by default, revise the PR to add an explicit strict or trusted-only setup mode and cover both modes with tests.
  3. Pause if plugin trust policy is unsettled
    If the permanent workspace-plugin trust contract is not decided, keep this PR open or pause it rather than merging a boundary that downstream setup flows may need to design around.

Next step before merge

  • No ClawSweeper repair lane is needed because there are no actionable patch findings; the remaining action is maintainer/check-gated merge handling for an accepted compatibility risk.

Security
Cleared: The diff narrows workspace plugin execution and documents the trust rule; I found no dependency, workflow, secret, or supply-chain broadening in the reviewed patch.

Review details

Best possible solution:

Land the loader and registry backstops with the docs/tests intact once maintainers accept the fail-closed workspace-plugin policy and exact-head checks pass.

Do we have a high-confidence way to reproduce the issue?

Yes, source inspection gives a high-confidence reproduction path: current main's setup-only scoped loader gate lacks a workspace-enabled check, so an explicitly scoped disabled workspace channel plugin can be imported before this PR. I did not run the repro locally in this read-only review.

Is this the best way to solve the issue?

Yes, this is the best fix shape for the reported boundary: enforce before import in the loader and add a registry backstop, while keeping trusted workspace overrides and documenting the operator path. The only remaining decision is whether maintainers accept the intentional fail-closed upgrade behavior.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 2bac970abc8e.

Label changes

Label justifications:

  • P1: The PR closes a plugin execution trust-boundary bypass in setup-only channel loading, which is urgent security-hardening with real workflow impact.
  • merge-risk: 🚨 compatibility: Existing workspace channel setup flows that relied on disabled plugins executing through scoped setup-only loads will fail closed after merge.
  • merge-risk: 🚨 security-boundary: The diff changes where untrusted workspace-origin plugin code is allowed to execute, so the trust boundary itself is the merge-sensitive surface.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 🚀 automerge armed: This PR is in ClawSweeper's automerge lane. Sufficient (terminal): The PR body includes after-fix terminal proof for focused loader/registry/catalog/channel setup tests, build, and changed checks, with before evidence for the marker-file repro; exact-head checks should still settle after the latest force-push.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal proof for focused loader/registry/catalog/channel setup tests, build, and changed checks, with before evidence for the marker-file repro; exact-head checks should still settle after the latest force-push.
Evidence reviewed

PR surface:

Source +46, Tests +610, Docs +13. Total +669 across 22 files.

View PR surface stats
Area Files Added Removed Net
Source 8 62 16 +46
Tests 13 623 13 +610
Docs 1 13 0 +13
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 22 698 29 +669

What I checked:

  • Repository policy applied: Read full root AGENTS.md plus scoped docs, channels, plugins, and test policies; the review applied the compatibility/security-boundary guidance for plugin loading, setup flows, docs, and tests. (AGENTS.md:1, 2bac970abc8e)
  • Maintainer notes checked: The only maintainer note found was Telegram-specific and did not apply to plugin loader/channel setup changes. (.agents/maintainer-notes/telegram.md:1, 2bac970abc8e)
  • Current-main loader gap: Current main allows scoped setup-only channel loads whenever a scoped channel plugin is requested and the setup-entry requirement is met; there is no workspace-origin enabled check in this gate. (src/plugins/loader.ts:2099, c429a3c4726f)
  • Current-main setup registration gap: Current main calls api.registerChannel(mergedSetupPlugin) without the error-isolation guard that this PR ports for setup-entry channel registration failures. (src/plugins/loader.ts:2564, c429a3c4726f)
  • PR loader enforcement: The PR adds the workspace trust check before setup-only scoped channel plugin import, so disabled workspace-origin candidates do not execute through this path. (src/plugins/loader.ts:2105, 11438bc1a062)
  • PR registry backstop: The PR rejects channel registration from disabled workspace plugin records before reading registration data, adding a second boundary if a lower-level caller imports one. (src/plugins/registry.ts:930, 11438bc1a062)

Likely related people:

  • vincentkoc: Current-main blame on the loader, registry, trusted catalog, and raw catalog surfaces points to Vincent Koc's recent refactor/maintenance commit on the relevant code paths. (role: recent area contributor; confidence: high; commits: c429a3c4726f; files: src/plugins/loader.ts, src/plugins/registry.ts, src/commands/channel-setup/trusted-catalog.ts)
  • gumadeiras: Gustavo Madeira Santana authored the setup-runtime stabilization commit tied by history to the setup-entry registerChannel path this PR hardens. (role: setup-runtime behavior contributor; confidence: medium; commits: 78ac1184274e; files: src/plugins/loader.ts)
  • zsx: The trusted-catalog workspace-shadow filtering layer traces to the earlier commit that excluded workspace shadows from channel setup catalog lookups. (role: trusted catalog behavior contributor; confidence: medium; commits: 1fede43b948d; files: src/commands/channel-setup/trusted-catalog.ts, src/commands/channels/add.ts)
  • hxy91819: The same member author opened this PR and the related unmerged replacement attempts for the loader/registry backstop, so they are the best branch-level follow-up contact even though current-main provenance is shared. (role: current hardening follow-up owner; confidence: medium; commits: bbf8421a1e42, 8ad86c921a1e, 88294938446f; files: src/plugins/loader.ts, src/plugins/registry.ts, src/channels/plugins/catalog.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b05a7b14fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CHANGELOG.md Outdated
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 26, 2026
@clawsweeper

clawsweeper Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress.

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.
What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label May 26, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 26, 2026
@hxy91819

Copy link
Copy Markdown
Member Author

/clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels May 27, 2026
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation and removed agents Agent runtime and tooling labels May 27, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. and removed merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels May 27, 2026
@hxy91819

Copy link
Copy Markdown
Member Author

@clawsweeper autofix

@clawsweeper

clawsweeper Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

🦞🔧
ClawSweeper autofix is enabled.

This is fix-only; I will not merge this PR.

@clawsweeper clawsweeper Bot added the clawsweeper:autofix Maintainer opted this PR into bounded ClawSweeper-reviewed autofix without merge label May 27, 2026
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jun 1, 2026
hxy91819 and others added 18 commits June 1, 2026 09:17
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port the registerChannel guard from #85774 so a permitted but broken setup-entry channel reports a diagnostic without aborting sibling plugin loads.

Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@clawsweeper clawsweeper Bot force-pushed the pr-64154-rework branch from 7a7bc3b to 11438bc Compare June 1, 2026 09:17
@clawsweeper clawsweeper Bot added clawsweeper:merge-ready ClawSweeper found the PR merge-ready but a human gate is still closed and removed clawsweeper:merge-ready ClawSweeper found the PR merge-ready but a human gate is still closed labels Jun 1, 2026
@clawsweeper clawsweeper Bot merged commit 004835f into main Jun 1, 2026
168 checks passed
@clawsweeper clawsweeper Bot deleted the pr-64154-rework branch June 1, 2026 09:26
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 2, 2026
…nclaw#86953)

Summary:
- This PR blocks disabled workspace-origin channel plugins from setup-only scoped imports, rejects their channel registrations at registry assembly, documents the trust rule, and adds regression coverage.
- PR surface: Source +46, Tests +610, Docs +13. Total +669 across 22 files.
- Reproducibility: yes. source inspection gives a high-confidence reproduction path: current main's setup-only ... ce channel plugin can be imported before this PR. I did not run the repro locally in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(plugins): cover workspace channel registry guard
- PR branch already contained follow-up commit before automerge: fix(plugins): isolate setup channel registration errors
- PR branch already contained follow-up commit before automerge: fix(channels): mark raw catalog listing internal
- PR branch already contained follow-up commit before automerge: test(channels): cover trusted catalog filtering
- PR branch already contained follow-up commit before automerge: test(channels): mock raw catalog helper
- PR branch already contained follow-up commit before automerge: docs(changelog): credit setup channel hardening

Validation:
- ClawSweeper review passed for head 11438bc.
- Required merge gates passed before the squash merge.

Prepared head SHA: 11438bc
Review: openclaw#86953 (comment)

Co-authored-by: masonxhuang <masonxhuang@tencent.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
…nclaw#86953)

Summary:
- This PR blocks disabled workspace-origin channel plugins from setup-only scoped imports, rejects their channel registrations at registry assembly, documents the trust rule, and adds regression coverage.
- PR surface: Source +46, Tests +610, Docs +13. Total +669 across 22 files.
- Reproducibility: yes. source inspection gives a high-confidence reproduction path: current main's setup-only ... ce channel plugin can be imported before this PR. I did not run the repro locally in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(plugins): cover workspace channel registry guard
- PR branch already contained follow-up commit before automerge: fix(plugins): isolate setup channel registration errors
- PR branch already contained follow-up commit before automerge: fix(channels): mark raw catalog listing internal
- PR branch already contained follow-up commit before automerge: test(channels): cover trusted catalog filtering
- PR branch already contained follow-up commit before automerge: test(channels): mock raw catalog helper
- PR branch already contained follow-up commit before automerge: docs(changelog): credit setup channel hardening

Validation:
- ClawSweeper review passed for head 11438bc.
- Required merge gates passed before the squash merge.

Prepared head SHA: 11438bc
Review: openclaw#86953 (comment)

Co-authored-by: masonxhuang <masonxhuang@tencent.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…nclaw#86953)

Summary:
- This PR blocks disabled workspace-origin channel plugins from setup-only scoped imports, rejects their channel registrations at registry assembly, documents the trust rule, and adds regression coverage.
- PR surface: Source +46, Tests +610, Docs +13. Total +669 across 22 files.
- Reproducibility: yes. source inspection gives a high-confidence reproduction path: current main's setup-only ... ce channel plugin can be imported before this PR. I did not run the repro locally in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(plugins): cover workspace channel registry guard
- PR branch already contained follow-up commit before automerge: fix(plugins): isolate setup channel registration errors
- PR branch already contained follow-up commit before automerge: fix(channels): mark raw catalog listing internal
- PR branch already contained follow-up commit before automerge: test(channels): cover trusted catalog filtering
- PR branch already contained follow-up commit before automerge: test(channels): mock raw catalog helper
- PR branch already contained follow-up commit before automerge: docs(changelog): credit setup channel hardening

Validation:
- ClawSweeper review passed for head 11438bc.
- Required merge gates passed before the squash merge.

Prepared head SHA: 11438bc
Review: openclaw#86953 (comment)

Co-authored-by: masonxhuang <masonxhuang@tencent.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge cli CLI command changes commands Command implementations docs Improvements or additions to documentation maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L status: 🚀 automerge armed This PR is in ClawSweeper's automerge lane.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants