fix(channel): harden local setup trust#89456
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 11, 2026, 3:32 AM ET / 07:32 UTC. Summary PR surface: Source +160, Tests +788, Other +324. Total +1272 across 13 files. Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Review detailsBest possible solution: Retry the Codex review after fixing the execution failure. Do we have a high-confidence way to reproduce the issue? Unclear. The review failed before ClawSweeper could establish a reproduction path. Is this the best way to solve the issue? Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction. AGENTS.md: unclear because the file could not be read completely. Codex review notes: model internal, reasoning high; reviewed against 418d7e1e83c5. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +160, Tests +788, Other +324. Total +1272 across 13 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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
|
|
I like the origin-aware fallback here — excluding |
182404f to
8622783
Compare
Dependency graph guard clearedThis PR no longer has blocked dependency graph changes. A future dependency graph change requires a fresh
|
d38ade1 to
e126b79
Compare
|
@clawsweeper automerge |
|
🦞🔧
Draft PRs stay fix-only until GitHub marks them ready for review. Pause with Automerge progress:
|
|
ClawSweeper 🐠 reef update Thanks for the work on this. ClawSweeper opened a replacement PR only because the source branch was not writable from the available bot permissions. branch tides, not contributor blame. Why replacement: ClawSweeper could not update the source PR branch directly; GitHub did not grant sufficient push rights to the bot for that branch.
fish notes: reasoning high; reviewed against bf86d9f. |
Summary
config/globalchannel plugins discovered throughplugins.load.pathsor linked installsmainblocks trustedplugins.load.pathssetup because the catalog never sees that channel, while this branch reaches the loader and passes all four trust cases{ pluginId, origin }, then fall back to a safe bundled/non-local copy instead of suppressing all copies of the same plugin id.dtsbuild break insrc/commands/channel-setup/trusted-catalog.ts, which was failing freshpnpm build:plugin-sdk:dts/ Docker image builds even when local incremental caches hid itChange Type
Scope
Linked Issue / PR
fix(plugins): block untrusted workspace setup-only channel loads)my_docs/my_docs/04-cases/2026-04-10-channel-workspace-shadow-case/pr-86953-autoreview-follow-up.zh.mdplugins.load.paths(origin: "config") and linked/local installs that surface asorigin: "global"Problem Statement
#86953 established the product rule that untrusted local channel plugins should not get imported during setup-only channel flows. That landed for workspace-origin plugins.
The remaining gap was the broader local-origin surface:
plugins.load.pathschannel plugins appear asorigin: "config"origin: "global"Those are still local code from the operator's point of view. If setup hardening only keys on
origin === "workspace", the trust boundary is inconsistent: one class of local plugin is blocked before import, while other local shadows can still win channel catalog resolution or suppress a safe bundled fallback.This branch also had a separate concrete defect in fresh builds:
src/commands/channel-setup/trusted-catalog.tsfailed declaration generation, so Docker image builds died inbuild:plugin-sdk:dtseven though local incremental caches could mask the problem.The fresh-build failure was:
Blackbox Proof
Harness:
openclaw-e2e-gospecs/plugintrust/local_origin_followup_test.goscripts/docker/test-plugintrust-local-origin.shscripts/fixtures/plugintrust/{workspace-shadow,load-paths-shadow}/OPENCLAW_E2E_TARGET_REPO=<openclaw checkout> go test ./specs/plugintrust -vThe harness bakes a fresh OpenClaw image from source, then asserts only on marker files written by fixture plugins:
PLUGINTRUST_SETUP_IMPORT_MARKER: module top-level import happenedPLUGINTRUST_SETUP_REGISTER_MARKER: setup hook actually ranClean
main(a4196a44454f) behavior:workspace + untrusted: PASS, trust gate blocks the untrusted workspace pluginworkspace + plugins.allow: PASS, trusted workspace plugin loadsload-paths + untrusted: PASS as probe-only, marker absent because catalog rejectsUnknown channel e2e-load-pathsbefore loaderload-paths + plugins.allow: FAIL, sameUnknown channel e2e-load-paths; trusted load-path plugin is still invisible to the catalog on mainThis branch behavior from the previously built fresh image:
That is the direct before/after proof for the regression this PR fixes: on
main, trustedplugins.load.pathschannels never reach setup because the catalog cannot resolve them; on this branch, the same scenario resolves and loads correctly while the untrusted cases still stay blocked.What Changed
extraPathsthrough channel catalog resolution so trusted channel setup/add flows can actually see channel plugins discovered fromplugins.load.paths.excludePluginRefs: [{ pluginId, origin }], so rejecting an untrusted local shadow only skips that exact local candidate and still allows a bundled copy of the same channel/plugin id to win.trusted-catalog.tsby replacing the unstablePick<Parameters<...>[1], ...>shape with an explicit local exclusions type and explicit local-origin narrowing.src/commands/channels.add.trust.e2e.test.tsas a main-repo L4 E2E regression for the realchannels addCLI path.bash scripts/e2e/channel-plugin-trust-docker.shas a focused Docker/package L6 manual proof script for theplugins.load.pathstrust boundary.Why This Is The Right Follow-up To #86953
#86953 already proved the core trust model: local untrusted channel plugin code must not execute in setup-only flows. This PR does not redefine that model; it applies the same invariant consistently to the remaining local origins the earlier PR intentionally left as a follow-up hardening item.
The
pr-86953-autoreview-follow-up.zh.mdnote called out exactly this direction: stop hardcoding one origin name and move the loader/catalog gate toward a unified local trust decision. This PR stays within that scope and keeps the current product behavior for trusted plugins:Verification
pnpm build:plugin-sdk:dtspnpm test src/commands/channel-setup/trusted-catalog.test.ts -- --reporter=verbosepnpm buildpnpm test src/commands/channels.add.trust.e2e.test.ts -- --reporter=verbosebash scripts/e2e/channel-plugin-trust-docker.shgit diff --checkenv npm_config_registry=https://registry.npmjs.org/ node scripts/generate-npm-shrinkwrap.mjs --checkopenclaw-e2e-goEvidence
pnpm build:plugin-sdk:dtsnow passes after thetrusted-catalog.tstype fix; this was the blocker for Docker image rebuilds.src/commands/channel-setup/trusted-catalog.test.ts.pnpm buildpasses after the declaration-build fix.src/commands/channels.add.trust.e2e.test.tscovers workspace/load-paths x trusted/untrusted, 4/4 passing through the realopenclaw channels addCLI path.bash scripts/e2e/channel-plugin-trust-docker.shbuilt the package artifacts, packed/checked the OpenClaw tarball, installed it into the functional Docker image, then verifiedplugins.load.pathsuntrusted blocks setup entry execution andplugins.allowpermits setup entry execution.npm-shrinkwrap.jsonwas regenerated/check-verified withhttps://registry.npmjs.org/; no local mirror URLs are present.pnpm check:changedcould not complete locally because the installed Crabbox binary is0.15.0while current Testbox delegation requires Crabbox>=0.22.0. Running the child command directly reached existing shrinkwrap guard behavior for changed plugin packages; no test/lint/type failure was observed before that guard.