Skip to content

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

Closed
hxy91819 wants to merge 9 commits into
mainfrom
pr-64154-rework
Closed

fix(plugins): block untrusted workspace setup-only channel loads#82579
hxy91819 wants to merge 9 commits into
mainfrom
pr-64154-rework

Conversation

@hxy91819

@hxy91819 hxy91819 commented May 16, 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 on latest main after rebasing this PR.
  • Exact steps or command run after this patch:
    • OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test 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
    • OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test src/plugins/registry.channel-guard.test.ts src/commands/channel-setup/trusted-catalog.test.ts src/commands/channels.add.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 -- --reporter=verbose
    • pnpm check:changed
    • pnpm build
    • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
  • Evidence after fix: the targeted loader tests pass for untrusted/trusted workspace setup-only scoped loads and setup-entry registerChannel error isolation; the registry/catalog/channel setup tests pass across 65 channel setup cases; check:changed, build, and autoreview are clean.
  • 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, channels add workspace shadow fallback, fix(plugins): guard registerChannel in setup-entry path with try/catch #85774 setup-entry registerChannel error isolation, pnpm check:changed, pnpm build, autoreview.
  • 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) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

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.

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 fix(plugins): block untrusted workspace setup-only channel loads #82579 head)
  • 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 (PR #82579 head). 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).

@clawsweeper

clawsweeper Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Codex review: found issues before merge. Reviewed May 26, 2026, 11:35 AM ET / 15:35 UTC.

Summary
This PR hardens plugin channel setup loading by blocking disabled workspace channel plugins from setup-only scoped loads, adding a registry backstop for disabled workspace channel registrations, wrapping setup-entry channel registration failures as diagnostics, and adding regression coverage around catalog, loader, and registry paths.

PR surface: Source +46, Tests +558, Docs +1. Total +605 across 20 files.

Reproducibility: yes. source-reproducible: current main has a focused setup-only disabled-channel test, and the PR branch's enabled checks block that state for workspace-origin plugins. I did not run tests because this was a read-only review.

Review metrics: 1 noteworthy metric.

  • Setup trust gates changed: 2 hardening gates added. Both new gates decide whether workspace channel plugin code can execute or register during setup, so the trust and upgrade semantics need maintainer agreement before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🌊 off-meta tidepool
Patch quality: 🧂 unranked krab
Result: blocked by patch quality or review findings.

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

Rank-up moves:

  • Narrow the loader and registry workspace guards to preserve trusted disabled setup-only metadata while blocking untrusted workspace candidates.
  • Add regression coverage for an allowlisted workspace plugin with entries.<id>.enabled=false in the scoped setup-only path.
  • Remove the direct CHANGELOG.md entry before merge.

Risk before merge

  • Merging as-is can break existing setup/status flows for trusted workspace plugins that are allowlisted but disabled for runtime, because their setup-only channel metadata is blocked at both loader and registry boundaries.
  • The trust-boundary intent is security-sensitive: maintainers should explicitly confirm whether disabled-but-trusted workspace plugins may expose setup-only metadata before this fail-closed behavior lands.
  • The direct CHANGELOG.md edit conflicts with the repository’s release-owned changelog process and should be removed before merge.

Maintainer options:

  1. Narrow the workspace trust gate before merge (recommended)
    Update both the loader and registry guards to distinguish untrusted workspace default/allowlist rejection from explicitly scoped trusted workspace setup metadata, then add a regression test for an allowlisted workspace plugin with entries.<id>.enabled=false.
  2. Accept a stricter fail-closed contract
    If maintainers want disabled workspace plugins to expose no setup code at all, update the tests/docs and call out the upgrade behavior explicitly before merging.
  3. Pause in favor of a smaller security-only branch
    If the trusted-disabled setup contract is still unsettled, close or pause this branch and land only the non-controversial registerChannel error-isolation change separately.

Next step before merge
This needs maintainer handling because the remaining blocker is the security/compatibility contract for trusted-but-disabled workspace setup metadata, not just routine validation.

Security
Cleared: The diff is security-sensitive but adds no third-party code, secrets handling, permissions, package resolution, or network execution surface; the remaining blockers are compatibility defects in the hardening boundary.

Review findings

  • [P1] Preserve trusted disabled workspace setup loads — src/plugins/loader.ts:1921
  • [P2] Keep trusted disabled setup registrations allowed — src/plugins/registry.ts:916
  • [P3] Remove the direct changelog entry — CHANGELOG.md:36
Review details

Best possible solution:

Keep the untrusted workspace hardening and the setup-entry error isolation, but narrow the loader and registry checks so they reject only untrusted workspace candidates while preserving current scoped setup-only behavior for trusted disabled plugins; remove the direct changelog entry and keep release-note context in the PR body or squash message.

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

Yes, source-reproducible: current main has a focused setup-only disabled-channel test, and the PR branch's enabled checks block that state for workspace-origin plugins. I did not run tests because this was a read-only review.

Is this the best way to solve the issue?

No, not yet. The registerChannel try/catch is the right error-isolation shape, but the workspace hardening should distinguish trust cause from runtime enabled state rather than using enabled as a blanket proxy.

Full review comments:

  • [P1] Preserve trusted disabled workspace setup loads — src/plugins/loader.ts:1921
    This makes enabled the trust proxy for workspace setup-only loads. Current main allows an explicitly scoped disabled channel plugin to expose setup metadata, and the activation resolver can mark an allowlisted plugin enabled:false when entries.<id>.enabled=false; with this condition, an allowlisted workspace plugin disabled for runtime can no longer be inspected or setup-loaded. Please reject only the untrusted workspace case, not every disabled trusted workspace setup load.
    Confidence: 0.86
  • [P2] Keep trusted disabled setup registrations allowed — src/plugins/registry.ts:916
    This backstop also keys on !record.enabled, so even after the loader gate is narrowed, setup-only registration from an allowlisted workspace plugin disabled in config will be dropped before channelSetups is populated. The registry guard needs the same trusted-vs-untrusted distinction as the loader, otherwise setup/status flows still lose metadata for trusted disabled workspace plugins.
    Confidence: 0.8
  • [P3] Remove the direct changelog entry — CHANGELOG.md:36
    Root policy says CHANGELOG.md is release-owned for normal PRs; release-note context should stay in the PR body or squash/merge message. Please remove this direct changelog entry so release generation owns the final wording and attribution.
    Confidence: 0.95

Overall correctness: patch is incorrect
Overall confidence: 0.84

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add P1: This PR changes a plugin security boundary and currently has a blocking compatibility regression in setup-only workspace plugin loading.
  • add merge-risk: 🚨 compatibility: The new loader and registry checks can stop existing trusted workspace plugin setup/status flows when those plugins are allowlisted but disabled for runtime.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🌊 off-meta tidepool and patch quality is 🧂 unranked krab.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: The author is a MEMBER, so the external-contributor real-behavior proof gate does not apply; the PR body includes local targeted test/build/check claims but no live external-plugin proof.
  • remove rating: 🌊 off-meta tidepool: Current PR rating is rating: 🧂 unranked krab, so this older rating label is no longer current.

Label justifications:

  • P1: This PR changes a plugin security boundary and currently has a blocking compatibility regression in setup-only workspace plugin loading.
  • merge-risk: 🚨 compatibility: The new loader and registry checks can stop existing trusted workspace plugin setup/status flows when those plugins are allowlisted but disabled for runtime.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🌊 off-meta tidepool and patch quality is 🧂 unranked krab.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: The author is a MEMBER, so the external-contributor real-behavior proof gate does not apply; the PR body includes local targeted test/build/check claims but no live external-plugin proof.
Evidence reviewed

PR surface:

Source +46, Tests +558, Docs +1. Total +605 across 20 files.

View PR surface stats
Area Files Added Removed Net
Source 8 62 16 +46
Tests 11 558 0 +558
Docs 1 1 0 +1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 20 621 16 +605

What I checked:

  • Root review policy applied: The root policy marks plugin APIs, config loading, setup, startup checks, fallback behavior, and security boundaries as compatibility-sensitive, and says CHANGELOG.md is release-owned for normal PRs. (AGENTS.md:16, e96cde7e1493)
  • Scoped plugin policy applied: The plugin boundary guide says loader and registry changes must preserve manifest-first setup/discovery behavior and keep loader behavior aligned with documented plugin contracts. (src/plugins/AGENTS.md:1, e96cde7e1493)
  • PR loader gate overuses enabled state: The PR branch adds (candidate.origin !== "workspace" || enableState.enabled) to setup-only scoped channel loading, so any workspace plugin with enabled:false is blocked before the setup-only import path can run. (src/plugins/loader.ts:1921, b05a7b14fe9b)
  • PR registry guard repeats the same enabled-state proxy: The PR branch rejects channel registration whenever record.origin === "workspace" && !record.enabled, which also blocks setup-only channel metadata for trusted workspace plugins disabled in config. (src/plugins/registry.ts:916, b05a7b14fe9b)
  • Current main has an explicit disabled setup-only contract: Current main has a focused loader test where a selected disabled channel plugin does not load broadly, but does setup-load when scoped with includeSetupOnlyChannelPlugins and onlyPluginIds, producing one setup entry while remaining disabled. (src/plugins/loader.test.ts:5297, e96cde7e1493)
  • Current enable-state code distinguishes trust selection from enabled runtime state: Current main treats non-bundled allowlist membership as explicit selection, but entries.<id>.enabled=false still returns disabled, so enabled:false alone cannot distinguish untrusted workspace default-disable from trusted-but-disabled setup inspection. (src/plugins/config-activation-shared.ts:121, e96cde7e1493)

Likely related people:

  • Vincent Koc: Current-main blame for the central loader, registry, and trusted-catalog lines in this shallow checkout points to f0599fd. (role: recent area contributor; confidence: medium; commits: f0599fddac2f; files: src/plugins/loader.ts, src/plugins/registry.ts, src/commands/channel-setup/trusted-catalog.ts)
  • Peter Steinberger: Recent current-main log over the same loader and registry files shows a broad refactor touching this area after the grafted base history. (role: recent adjacent contributor; confidence: low; commits: cac0b2db18ea; files: src/plugins/loader.ts, src/plugins/registry.ts)
  • SebTardif: The PR discussion and body credit the setup-entry registerChannel isolation from the related fix(plugins): guard registerChannel in setup-entry path with try/catch #85774, which this branch ports into the same loader path. (role: adjacent hardening contributor; confidence: medium; commits: 0b3453ee7e19; files: src/plugins/loader.ts, src/plugins/loader.test.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.

@hxy91819

Copy link
Copy Markdown
Member Author

@codex review

@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: e6b62d7f8b

ℹ️ 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 src/plugins/loader.ts
(!enableState.enabled || forceSetupOnlyChannelPlugins);
const canLoadScopedSetupOnlyChannelPlugin =
scopedSetupOnlyChannelPluginRequested &&
(candidate.origin !== "workspace" || enableState.enabled) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Limit workspace setup-only gate to untrusted disable reason

This new predicate blocks all disabled workspace plugins from the scoped setup-only path, but that path is explicitly for loading disabled channel setup surfaces (scopedSetupOnlyChannelPluginRequested is driven by !enableState.enabled || forceSetupOnlyChannelPlugins). As written, a trusted workspace plugin that is disabled via config (for example plugins.entries.<id>.enabled=false) can no longer load its setup entry when explicitly scoped with onlyPluginIds, which regresses disabled-channel setup/inspection behavior; the condition should target only the untrusted workspace-default case instead of every disabled reason.

Useful? React with 👍 / 👎.

Comment thread src/plugins/registry.ts
return;
}
const id = plugin.id;
if (record.origin === "workspace" && !record.enabled) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep trusted disabled workspace setup registrations allowed

This guard rejects channel registration for any disabled workspace record, regardless of why it is disabled. That means even trusted workspace plugins intentionally disabled in config cannot register setup-time channels when a caller explicitly requests setup-only loading, so channelSetups becomes empty for those plugins and setup/status flows lose expected metadata. The backstop should be keyed to untrusted workspace disablement, not blanket !record.enabled for workspace origin.

Useful? React with 👍 / 👎.

@SebTardif

Copy link
Copy Markdown
Contributor

Cross-referencing from #85774 (now closed as superseded by this PR):

#85774 added a try/catch around registerChannel(mergedSetupPlugin) in the setup-entry loader path. Current main still has this call unguarded, so if a trusted setup-entry plugin throws during registerChannel (e.g. a listAccountIds getter error in channel normalization), it takes down sibling plugin loading.

This PR addresses the trust policy side (blocking untrusted workspace loads), but the error resilience gap for trusted plugins remains. It may be worth including the try/catch + recordPluginError + continue guard from #85774 as part of this rework, so a buggy trusted plugin can't crash the loader either.

@hxy91819

Copy link
Copy Markdown
Member Author

Cross-referencing from #85774 (now closed as superseded by this PR):

#85774 added a try/catch around registerChannel(mergedSetupPlugin) in the setup-entry loader path. Current main still has this call unguarded, so if a trusted setup-entry plugin throws during registerChannel (e.g. a listAccountIds getter error in channel normalization), it takes down sibling plugin loading.

This PR addresses the trust policy side (blocking untrusted workspace loads), but the error resilience gap for trusted plugins remains. It may be worth including the try/catch + recordPluginError + continue guard from #85774 as part of this rework, so a buggy trusted plugin can't crash the loader either.

Thanks, I will take this.

hxy91819 and others added 6 commits May 26, 2026 18:13
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>
@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: L and removed size: M labels May 26, 2026
@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label 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.

@hxy91819

Copy link
Copy Markdown
Member Author

/clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@openclaw-barnacle openclaw-barnacle Bot added the cli CLI command changes label May 26, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. 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. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels May 26, 2026
@clawsweeper

clawsweeper Bot commented May 26, 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:

@hxy91819

Copy link
Copy Markdown
Member Author

Closing this PR in favor of a replacement PR from the same branch and commit history so the repository automation can process a fresh opened PR event. The replacement will be linked from this thread.

@hxy91819 hxy91819 closed this May 26, 2026
@hxy91819

Copy link
Copy Markdown
Member Author

Replacement PR: #86953

Same head branch: pr-64154-rework
Same head commit at replacement creation: b05a7b1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes commands Command implementations maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: L status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants