Skip to content

fix(plugins): gate runtime provider discovery by plugins.allow#76085

Closed
dougbtv wants to merge 2 commits intoopenclaw:mainfrom
track-forge:downstream/fix-allowlist-runtime-bypass
Closed

fix(plugins): gate runtime provider discovery by plugins.allow#76085
dougbtv wants to merge 2 commits intoopenclaw:mainfrom
track-forge:downstream/fix-allowlist-runtime-bypass

Conversation

@dougbtv
Copy link
Copy Markdown
Contributor

@dougbtv dougbtv commented May 2, 2026

Summary

  • Adds plugins.bundledMode: "respect-allow" config option that makes runtime provider plugin discovery honor plugins.allow for bundled plugins
  • When set, bundled provider plugins are filtered by the allowlist on chat-turn discovery, matching startup behavior
  • Default "compat" preserves existing force-load-all behavior for backwards compatibility

Root cause: isProviderPluginEligibleForSetupDiscovery() short-circuits to true for all origin: "bundled" plugins without checking the allowlist. Then withActivatedPluginIds() unconditionally adds every discovered plugin ID to config.plugins.allow, blowing open any user-set allowlist. Together these cause all ~12 bundled provider plugins to load on every first message regardless of the user's plugins.allow setting.

Fix layers:

  1. providers.ts — thread shouldFilterBundledByAllowlist into discovery eligibility, derived from bundledMode === "respect-allow"
  2. activation-context.ts — don't add plugin IDs to allow set that aren't already in the user's allowlist when respect-allow
  3. bundled-compat.ts — skip compat allowlist/enablement injection when respect-allow
  4. types.plugins.ts — add bundledMode field to PluginsConfig

Test plan

  • Existing models-config.providers.plugin-allowlist-compat.test.ts passes (3/3) — compat behavior preserved
  • New test: respect-allow mode returns only allowed plugins from resolveEnabledProviderPluginIds
  • New test: respect-allow filters bundled plugins in resolveDiscoveredProviderPluginIds
  • New test: default/compat mode returns all bundled provider plugins (backwards compat)
  • pnpm build — no new errors in changed files
  • Formatting clean via oxfmt

Closes #75575

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels May 2, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 2, 2026

Codex review: needs changes before merge.

Summary
The PR adds an opt-in plugins.bundledMode: "respect-allow" mode so bundled provider runtime discovery can honor plugins.allow, while preserving default compat behavior and adding schema/help wiring plus focused tests.

Reproducibility: yes. A high-confidence source-level reproduction exists on current main: configure a restrictive plugins.allow, then bundled provider discovery returns non-workspace plugins before allowlist checks and compat activation can add discovered IDs back into the effective config.

Next step before merge
A repair worker can make a narrow PR-branch follow-up because the remaining blocker is docs/changelog alignment for a user-facing config option.

Security
Cleared: No concrete security or supply-chain regression was found; the diff changes plugin activation/config code and tests without new dependencies, workflows, downloads, package scripts, or secret handling.

Review findings

  • [P2] Document bundledMode before release — src/config/types.plugins.ts:56
Review details

Best possible solution:

Land the default-preserving plugins.bundledMode: "respect-allow" implementation with strict schema/help, focused runtime tests, public config docs, and an Unreleased changelog entry.

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

Yes. A high-confidence source-level reproduction exists on current main: configure a restrictive plugins.allow, then bundled provider discovery returns non-workspace plugins before allowlist checks and compat activation can add discovered IDs back into the effective config.

Is this the best way to solve the issue?

Yes as a direction, but not yet as a shippable patch. The opt-in mode is the narrow maintainable fix because it preserves legacy defaults while using existing activation seams; it needs docs/changelog coverage before merge.

Full review comments:

  • [P2] Document bundledMode before release — src/config/types.plugins.ts:56
    The patch introduces a user-facing plugins.bundledMode config key, but the published plugin/config docs and changelog still have no entry. docs/tools/plugin.md and docs/gateway/configuration-reference.md currently describe plugins.allow as exclusive without explaining default bundled-provider compat or the new opt-in, so users cannot discover the fix and release notes will omit a user-facing behavior change.
    Confidence: 0.92

Overall correctness: patch is incorrect
Overall confidence: 0.86

Acceptance criteria:

  • pnpm test src/agents/models-config.providers.plugin-allowlist-compat.test.ts src/plugins/providers.test.ts
  • pnpm exec oxfmt --check --threads=1 src/config/schema.help.ts src/config/types.plugins.ts src/config/zod-schema.ts src/plugins/activation-context.ts src/plugins/bundled-compat.ts src/plugins/providers.ts src/agents/models-config.providers.plugin-allowlist-compat.test.ts src/plugins/providers.test.ts
  • pnpm format:docs:check
  • pnpm docs:check-mdx
  • pnpm config:docs:check

What I checked:

  • Current main bypasses bundled provider allowlists: isProviderPluginEligibleForSetupDiscovery() returns true for every non-workspace plugin before allowlist or activation checks, so bundled provider discovery is not filtered by plugins.allow on main. (src/plugins/providers.ts:290, 0b09cfb8cd24)
  • Current main compat paths widen activation: withActivatedPluginIds() adds every discovered setup plugin to allow and entries, and the bundled compat helpers expand allowlists/enablement for plugin IDs passed through runtime activation. (src/plugins/activation-context.ts:81, 0b09cfb8cd24)
  • Latest PR head fixed the earlier schema blocker: The PR diff now adds bundledMode to the strict plugins zod object and adds plugins.bundledMode help text, matching the author’s follow-up comment about commit c45ec3d. (src/config/zod-schema.ts:1059, c45ec3d9132e)
  • Web provider paths use the shared compat seam: The web-search and web-fetch runtime paths call resolveBundledPluginCompatibleLoadValues() / resolveEnabledBundledManifestContractPlugins(), which apply the shared bundled compat helpers changed by this PR, so the earlier web-path concern is not clearly a functional blocker after re-review. (src/plugins/web-provider-resolution-shared.ts:157, 0b09cfb8cd24)
  • Public docs and changelog are still missing the new option: The visible PR diff changes schema/help/types/runtime/tests only; current public docs still describe plugins.allow as exclusive without plugins.bundledMode, and CHANGELOG.md has no entry for this user-facing fix. Public docs: docs/tools/plugin.md. (docs/tools/plugin.md:249, 0b09cfb8cd24)

Likely related people:

  • steipete: Current checked-out history and blame for the central provider/activation/compat files point to commit 40f2bf3 by Peter Steinberger, and the provided related context also shows maintainer work in nearby provider-runtime repair paths. (role: recent maintainer / current-main owner; confidence: medium; commits: 40f2bf395059; files: src/plugins/providers.ts, src/plugins/activation-context.ts, src/plugins/bundled-compat.ts)

Remaining risk / open question:

  • The visible PR diff does not add direct web_search/web_fetch respect-allow tests; source review indicates the shared compat helper should cover those paths, but CI or targeted tests should still prove it.
  • No validation commands were run in this read-only review.

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

dougbtv added 2 commits May 2, 2026 09:47
…llowlist

When plugins.bundledMode is set to "respect-allow", runtime provider
discovery paths honor plugins.allow for bundled plugins instead of
force-loading all providers. Default "compat" preserves existing behavior.

Closes openclaw#75575
Addresses review feedback: adds bundledMode to the strict plugins zod
object so the config option passes validation, and adds schema.help
documentation for the field.
@dougbtv dougbtv force-pushed the downstream/fix-allowlist-runtime-bypass branch from d106fc7 to c45ec3d Compare May 2, 2026 13:50
@dougbtv
Copy link
Copy Markdown
Contributor Author

dougbtv commented May 2, 2026

Thanks for the thorough review. Addressed both P2 findings:

P2: Register bundledMode in config validation — Fixed in c45ec3d. Added bundledMode as an optional enum to the strict plugins zod schema in zod-schema.ts, and added help text in schema.help.ts.

P2: Gate web provider runtime paths — Already covered by the existing patch. The web provider paths (web-provider-resolution-shared.ts, web-content-extractors.runtime.ts) flow through resolveBundledPluginCompatibleLoadValuescreateBundledPluginCompatConfigwithBundledPluginAllowlistCompat / withBundledPluginEnablementCompat. Both of those compat functions already have bundledMode: "respect-allow" guards from the first commit. The enablement: "always" setting used by web providers still goes through withBundledPluginEnablementCompat which filters against the allowSet when respect-allow is active.

Also rebased onto latest main (picks up the cron wake-now fix).

Copy link
Copy Markdown
Contributor

@martingarramon martingarramon left a comment

Choose a reason for hiding this comment

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

Solid layered fix — the 4 enforcement sites (providers.ts:265,322, activation-context.ts:84, bundled-compat.ts:9,46) are consistent on the bundledMode === "respect-allow" check, default "compat" keeps existing behavior intact. ✓

Two non-blocking questions:

  1. Empty-allow + respect-allow asymmetry. activation-context.ts:80-83 gates respectAllow on originalAllow.length > 0, so { bundledMode: "respect-allow", allow: [] } falls through and adds every discovered plugin ID to the allow set. But bundled-compat.ts:9-12 short-circuits unconditionally on respect-allow. The two files disagree on what "empty allow + respect-allow" means — activation-context reads as "allow everything", bundled-compat reads as "inject nothing". Intentional? If so, a one-line note locks the contract; if not, alignment tightens the boundary.

  2. Long-term default plan. PR body calls "compat" legacy behavior. Does the migration eventually flip the default to "respect-allow", or is opt-in permanent? Affects downstream docs framing — the gap stays open for any user who never reads about the new flag.

CI failures on current head match the SIGTERM-class infra noise hitting other PRs today; re-trigger before merge.

@dougbtv
Copy link
Copy Markdown
Contributor Author

dougbtv commented May 2, 2026

Checked the two CI failures (checks-node-core-runtime-infra and its rollup checks-node-core) — they are infrastructure-level Docker container management issues, not test failures. All checks that exercise our changed code paths (config-boundary, plugin-boundary, provider-runtime-boundary, plugin-sdk, contracts) are green.

@dougbtv
Copy link
Copy Markdown
Contributor Author

dougbtv commented May 2, 2026

Thanks for the thorough review @martingarramon — both great questions.

1. Empty-allow + respect-allow asymmetry. Intentional — the two files answer different questions. activation-context.ts asks "should I filter by the allowlist?" — no allowlist means nothing to filter by, so allow everything. bundled-compat.ts asks "should I inject legacy compat behavior?" — you said respect-allow, so no injection regardless. The edge case of { bundledMode: "respect-allow", allow: [] } is a valid transitional state (mid-configuration), and "allow everything" is the safe fallback there. Happy to add a one-line note locking that contract if you think it helps.

2. Long-term default plan. compat is probably the right permanent default — most users never configure plugins.allow and just want things to work. respect-allow is an operational control for constrained environments (containers, air-gapped, read-only filesystems, resource-limited pods) where you care precisely about what gets loaded and what triggers runtime dep installation. It is not a future default flip — it is a deployment-time opt-in for environments where plugin loading has real cost.

@martingarramon
Copy link
Copy Markdown
Contributor

Yes, please add the contract note — locks the asymmetry as deliberate so future "fix" PRs don't try to symmetrize it. The operational-control framing also answers my second question — agreed compat stays default; respect-allow reads as a deployment knob for constrained environments, not a path to a default flip.

CI infra distinction noted; the changed-code paths going green is what I was after.

@steipete
Copy link
Copy Markdown
Contributor

steipete commented May 4, 2026

Closing this as superseded by #77194.

#77194 carries the same core fix for #75575, plus the missing public docs/generated config surface, doctor migration/warning coverage, web provider public-artifact fallback coverage, and current CI is green on that head.

The remaining review work should stay on #77194 rather than split across two PRs. Current blockers there are narrow:

  • normalize plugins.allow aliases before filtering bundled public-artifact web providers, so aliases such as google-gemini-cli and minimax-portal keep matching their owner plugin ids
  • preserve plugins.enabled: false when allowlist filtering rejects all requested bundled plugins, instead of re-enabling plugin loading through the compat helper

Thanks @dougbtv for the original report and implementation work; the follow-up PR keeps that path moving with the broader docs/doctor/schema coverage.

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

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Honor plugins.allow on runtime provider paths (currently force-loaded) — propose plugins.bundledMode opt-in

3 participants