fix: preserve allowlist guard for auto-enabled bundled channels (#60233) (thanks @dorukardahan)

This commit is contained in:
Peter Steinberger
2026-04-03 21:35:40 +09:00
parent cd08facd7a
commit bc137951e9
8 changed files with 58 additions and 18 deletions

View File

@@ -64,6 +64,7 @@ Docs: https://docs.openclaw.ai
- Agents/tool policy: stop `tools.profile` warnings from flagging runtime-gated baseline core tools as unknown when the coding profile is missing tools like `code_execution`, `x_search`, `image`, or `image_generate`, while still warning on explicit extra allowlist entries. Thanks @vincentkoc.
- Sessions/resolution: collapse alias-duplicate session-id matches before scoring, keep distinct structural ties ambiguous, and prefer current-store reuse when resolving equal cross-store duplicates so follow-up turns stop dropping or duplicating sessions on timestamp ties.
- Mobile pairing/bootstrap: keep setup bootstrap tokens alive through the initial node auto-pair so the same QR bootstrap token can finish operator approval, then revoke it after the full issued profile connects successfully. (#60221) Thanks @obviyus.
- Plugins/allowlists: let explicit bundled chat channel enablement bypass `plugins.allow`, while keeping auto-enabled channel activation and startup sidecars behind restrictive allowlists. (#60233) Thanks @dorukardahan.
## 2026.4.2

View File

@@ -252,6 +252,7 @@ describe("loadGatewayPlugins", () => {
});
expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({
config: {},
activationSourceConfig: undefined,
workspaceDir: "/tmp",
env: process.env,
});
@@ -308,6 +309,7 @@ describe("loadGatewayPlugins", () => {
expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({
config: autoEnabledConfig,
activationSourceConfig: undefined,
workspaceDir: "/tmp",
env: process.env,
});
@@ -343,6 +345,12 @@ describe("loadGatewayPlugins", () => {
config: rawConfig,
env: process.env,
});
expect(resolveGatewayStartupPluginIds).toHaveBeenCalledWith({
config: resolvedConfig,
activationSourceConfig: rawConfig,
workspaceDir: "/tmp",
env: process.env,
});
expect(loadOpenClawPlugins).toHaveBeenCalledWith(
expect.objectContaining({
config: resolvedConfig,

View File

@@ -423,6 +423,7 @@ export function loadGatewayPlugins(params: {
params.pluginIds ??
resolveGatewayStartupPluginIds({
config: resolvedConfig,
activationSourceConfig: params.activationSourceConfig,
workspaceDir: params.workspaceDir,
env: process.env,
});

View File

@@ -602,6 +602,7 @@ export async function startGatewayServer(
? []
: resolveGatewayStartupPluginIds({
config: gatewayPluginConfigAtStart,
activationSourceConfig: cfgAtStart,
workspaceDir: defaultWorkspaceDir,
env: process.env,
});

View File

@@ -70,22 +70,30 @@ function createManifestRegistryFixture() {
};
}
function expectStartupPluginIds(config: OpenClawConfig, expected: readonly string[]) {
function expectStartupPluginIds(params: {
config: OpenClawConfig;
activationSourceConfig?: OpenClawConfig;
expected: readonly string[];
}) {
expect(
resolveGatewayStartupPluginIds({
config,
config: params.config,
...(params.activationSourceConfig !== undefined
? { activationSourceConfig: params.activationSourceConfig }
: {}),
workspaceDir: "/tmp",
env: process.env,
}),
).toEqual(expected);
).toEqual(params.expected);
expect(loadPluginManifestRegistry).toHaveBeenCalled();
}
function expectStartupPluginIdsCase(params: {
config: OpenClawConfig;
activationSourceConfig?: OpenClawConfig;
expected: readonly string[];
}) {
expectStartupPluginIds(params.config, params.expected);
expectStartupPluginIds(params);
}
function createStartupConfig(params: {
@@ -211,4 +219,27 @@ describe("resolveGatewayStartupPluginIds", () => {
] as const)("%s", (_name, config, expected) => {
expectStartupPluginIdsCase({ config, expected });
});
it("keeps effective-only bundled sidecars behind restrictive allowlists", () => {
const rawConfig = createStartupConfig({
allowPluginIds: ["browser"],
});
const effectiveConfig = {
...rawConfig,
plugins: {
allow: ["browser"],
entries: {
"voice-call": {
enabled: true,
},
},
},
} as OpenClawConfig;
expectStartupPluginIdsCase({
config: effectiveConfig,
activationSourceConfig: rawConfig,
expected: ["demo-channel", "browser"],
});
});
});

View File

@@ -75,6 +75,7 @@ export function resolveConfiguredDeferredChannelPluginIds(params: {
export function resolveGatewayStartupPluginIds(params: {
config: OpenClawConfig;
activationSourceConfig?: OpenClawConfig;
workspaceDir?: string;
env: NodeJS.ProcessEnv;
}): string[] {
@@ -82,6 +83,9 @@ export function resolveGatewayStartupPluginIds(params: {
listPotentialConfiguredChannelIds(params.config, params.env).map((id) => id.trim()),
);
const pluginsConfig = normalizePluginsConfig(params.config.plugins);
const sourcePluginsConfig = normalizePluginsConfig(
(params.activationSourceConfig ?? params.config).plugins,
);
return loadPluginManifestRegistry({
config: params.config,
workspaceDir: params.workspaceDir,
@@ -100,6 +104,8 @@ export function resolveGatewayStartupPluginIds(params: {
config: pluginsConfig,
rootConfig: params.config,
enabledByDefault: plugin.enabledByDefault,
sourceConfig: sourcePluginsConfig,
sourceRootConfig: params.activationSourceConfig ?? params.config,
});
if (!activationState.enabled) {
return false;

View File

@@ -287,7 +287,8 @@ export function resolvePluginActivationState(params: {
});
const explicitlyConfiguredBundledChannel =
params.origin === "bundled" &&
isBundledChannelEnabledByChannelConfig(params.sourceRootConfig ?? params.rootConfig, params.id);
explicitSelection.explicitlyEnabled &&
explicitSelection.reason === "channel enabled in config";
if (!params.config.enabled) {
return {

View File

@@ -3,7 +3,6 @@ import fs from "node:fs";
import path from "node:path";
import { createJiti } from "jiti";
import type { ChannelPlugin } from "../channels/plugins/types.js";
import { normalizeChatChannelId } from "../channels/registry.js";
import { isChannelConfigured } from "../config/channel-configured.js";
import type { OpenClawConfig } from "../config/config.js";
import type { PluginInstallRecord } from "../config/types.plugins.js";
@@ -1167,12 +1166,8 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
config: normalized,
rootConfig: cfg,
enabledByDefault: manifestRecord.enabledByDefault,
...(normalizeChatChannelId(pluginId)
? {
sourceConfig: activationSourceNormalized,
sourceRootConfig: activationSourceConfig,
}
: {}),
sourceConfig: activationSourceNormalized,
sourceRootConfig: activationSourceConfig,
});
const entry = normalized.entries[pluginId];
const record = createPluginRecord({
@@ -1724,12 +1719,8 @@ export async function loadOpenClawPluginCliRegistry(
config: normalized,
rootConfig: cfg,
enabledByDefault: manifestRecord.enabledByDefault,
...(normalizeChatChannelId(pluginId)
? {
sourceConfig: activationSourceNormalized,
sourceRootConfig: activationSourceConfig,
}
: {}),
sourceConfig: activationSourceNormalized,
sourceRootConfig: activationSourceConfig,
});
const entry = normalized.entries[pluginId];
const record = createPluginRecord({