mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:01:01 +00:00
fix(plugins): add bundledMode to gate runtime provider discovery by allowlist
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 #75575
This commit is contained in:
committed by
Peter Steinberger
parent
81035e651b
commit
f738663c79
@@ -84,6 +84,30 @@ describe("implicit provider plugin allowlist compatibility", () => {
|
||||
).toEqual(["kilocode", "moonshot", "openrouter"]);
|
||||
});
|
||||
|
||||
it("respects allowlist for bundled plugins when bundledMode is respect-allow", () => {
|
||||
const config = withBundledPluginEnablementCompat({
|
||||
config: withBundledPluginAllowlistCompat({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["openrouter"],
|
||||
bundledMode: "respect-allow",
|
||||
},
|
||||
},
|
||||
pluginIds: ["kilocode", "moonshot"],
|
||||
}),
|
||||
pluginIds: ["kilocode", "moonshot"],
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveEnabledProviderPluginIds({
|
||||
config,
|
||||
registry: providerRegistry,
|
||||
manifestRegistry: providerManifestRegistry,
|
||||
onlyPluginIds: PROVIDER_PLUGIN_IDS,
|
||||
}),
|
||||
).toEqual(["openrouter"]);
|
||||
});
|
||||
|
||||
it("still honors explicit plugin denies over compat allowlist injection", () => {
|
||||
const config = withBundledPluginEnablementCompat({
|
||||
config: withBundledPluginAllowlistCompat({
|
||||
|
||||
@@ -51,6 +51,16 @@ export type PluginsConfig = {
|
||||
allow?: string[];
|
||||
/** Optional plugin denylist (plugin ids). */
|
||||
deny?: string[];
|
||||
/**
|
||||
* Controls whether bundled plugins bypass `allow` / `entries` on runtime
|
||||
* provider discovery paths.
|
||||
*
|
||||
* - `"compat"` (default): bundled provider plugins are force-loaded on
|
||||
* every chat turn regardless of the allowlist (legacy behavior).
|
||||
* - `"respect-allow"`: bundled provider plugins are gated by `allow` and
|
||||
* `entries.<id>.enabled` the same way third-party plugins are.
|
||||
*/
|
||||
bundledMode?: "compat" | "respect-allow";
|
||||
load?: PluginsLoadConfig;
|
||||
slots?: PluginSlotsConfig;
|
||||
entries?: Record<string, PluginEntryConfig>;
|
||||
|
||||
@@ -78,7 +78,11 @@ export function withActivatedPluginIds(params: {
|
||||
if (params.pluginIds.length === 0) {
|
||||
return params.config;
|
||||
}
|
||||
const allow = new Set(params.config?.plugins?.allow ?? []);
|
||||
const originalAllow = params.config?.plugins?.allow ?? [];
|
||||
const respectAllow =
|
||||
params.config?.plugins?.bundledMode === "respect-allow" && originalAllow.length > 0;
|
||||
const originalAllowSet = respectAllow ? new Set(originalAllow) : undefined;
|
||||
const allow = new Set(originalAllow);
|
||||
const entries = {
|
||||
...params.config?.plugins?.entries,
|
||||
};
|
||||
@@ -87,6 +91,9 @@ export function withActivatedPluginIds(params: {
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
if (originalAllowSet && !originalAllowSet.has(normalized)) {
|
||||
continue;
|
||||
}
|
||||
allow.add(normalized);
|
||||
const existingEntry = entries[normalized];
|
||||
entries[normalized] = {
|
||||
|
||||
@@ -6,6 +6,9 @@ export function withBundledPluginAllowlistCompat(params: {
|
||||
config: OpenClawConfig | undefined;
|
||||
pluginIds: readonly string[];
|
||||
}): OpenClawConfig | undefined {
|
||||
if (params.config?.plugins?.bundledMode === "respect-allow") {
|
||||
return params.config;
|
||||
}
|
||||
const allow = params.config?.plugins?.allow;
|
||||
if (!Array.isArray(allow) || allow.length === 0) {
|
||||
return params.config;
|
||||
@@ -39,6 +42,8 @@ export function withBundledPluginEnablementCompat(params: {
|
||||
}): OpenClawConfig | undefined {
|
||||
const existingEntries = params.config?.plugins?.entries ?? {};
|
||||
const forcePluginsEnabled = params.config?.plugins?.enabled === false;
|
||||
const respectAllow = params.config?.plugins?.bundledMode === "respect-allow";
|
||||
const allowSet = respectAllow ? new Set(params.config?.plugins?.allow ?? []) : undefined;
|
||||
let changed = false;
|
||||
const nextEntries: Record<string, PluginEntryConfig> = { ...existingEntries };
|
||||
|
||||
@@ -46,6 +51,9 @@ export function withBundledPluginEnablementCompat(params: {
|
||||
if (existingEntries[pluginId] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
if (allowSet && !allowSet.has(pluginId)) {
|
||||
continue;
|
||||
}
|
||||
nextEntries[pluginId] = { enabled: true };
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -593,6 +593,75 @@ describe("resolvePluginProviders", () => {
|
||||
).toEqual(["legacy-auth-owner"]);
|
||||
});
|
||||
|
||||
it("filters bundled provider plugins by allowlist when bundledMode is respect-allow", () => {
|
||||
setManifestPlugins([
|
||||
createManifestProviderPlugin({
|
||||
id: "kilocode",
|
||||
providerIds: ["kilocode"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({
|
||||
id: "moonshot",
|
||||
providerIds: ["moonshot"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({
|
||||
id: "openrouter",
|
||||
providerIds: ["openrouter"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const discovered = resolveDiscoveredProviderPluginIds({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["openrouter"],
|
||||
bundledMode: "respect-allow",
|
||||
},
|
||||
},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
});
|
||||
|
||||
expect(discovered).toEqual(["openrouter"]);
|
||||
});
|
||||
|
||||
it("returns all bundled provider plugins in compat mode (default)", () => {
|
||||
setManifestPlugins([
|
||||
createManifestProviderPlugin({
|
||||
id: "kilocode",
|
||||
providerIds: ["kilocode"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({
|
||||
id: "moonshot",
|
||||
providerIds: ["moonshot"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
createManifestProviderPlugin({
|
||||
id: "openrouter",
|
||||
providerIds: ["openrouter"],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const discovered = resolveDiscoveredProviderPluginIds({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["openrouter"],
|
||||
},
|
||||
},
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
});
|
||||
|
||||
expect(discovered).toEqual(["kilocode", "moonshot", "openrouter"]);
|
||||
});
|
||||
|
||||
it("treats explicit empty provider scopes as scoped-empty in provider helpers", () => {
|
||||
expect(
|
||||
resolveEnabledProviderPluginIds({
|
||||
|
||||
@@ -255,6 +255,7 @@ export function resolveDiscoveredProviderPluginIds(params: {
|
||||
const { registry, onlyPluginIdSet } = loadScopedProviderRegistry(params);
|
||||
const providerSurfacePluginIds = resolveProviderSurfacePluginIdSet({ ...params, registry });
|
||||
const shouldFilterUntrustedWorkspacePlugins = params.includeUntrustedWorkspacePlugins === false;
|
||||
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledMode === "respect-allow";
|
||||
const normalizedConfig = normalizePluginsConfigWithRegistry(params.config?.plugins, registry);
|
||||
return listRegistryPluginIds(registry, (plugin) => {
|
||||
if (
|
||||
@@ -268,6 +269,7 @@ export function resolveDiscoveredProviderPluginIds(params: {
|
||||
return isProviderPluginEligibleForSetupDiscovery({
|
||||
plugin,
|
||||
shouldFilterUntrustedWorkspacePlugins,
|
||||
shouldFilterBundledByAllowlist,
|
||||
normalizedConfig,
|
||||
rootConfig: params.config,
|
||||
});
|
||||
@@ -277,10 +279,15 @@ export function resolveDiscoveredProviderPluginIds(params: {
|
||||
function isProviderPluginEligibleForSetupDiscovery(params: {
|
||||
plugin: PluginRegistryRecord;
|
||||
shouldFilterUntrustedWorkspacePlugins: boolean;
|
||||
shouldFilterBundledByAllowlist: boolean;
|
||||
normalizedConfig: NormalizedPluginsConfig;
|
||||
rootConfig?: PluginLoadOptions["config"];
|
||||
}): boolean {
|
||||
if (!params.shouldFilterUntrustedWorkspacePlugins || params.plugin.origin !== "workspace") {
|
||||
if (params.plugin.origin === "workspace") {
|
||||
if (!params.shouldFilterUntrustedWorkspacePlugins) {
|
||||
return true;
|
||||
}
|
||||
} else if (!params.shouldFilterBundledByAllowlist) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
@@ -306,12 +313,14 @@ export function resolveDiscoverableProviderOwnerPluginIds(params: {
|
||||
includeUntrustedWorkspacePlugins?: boolean;
|
||||
}): string[] {
|
||||
const shouldFilterUntrustedWorkspacePlugins = params.includeUntrustedWorkspacePlugins === false;
|
||||
const shouldFilterBundledByAllowlist = params.config?.plugins?.bundledMode === "respect-allow";
|
||||
return resolveProviderOwnerPluginIds({
|
||||
...params,
|
||||
isEligible: (plugin, normalizedConfig) =>
|
||||
isProviderPluginEligibleForSetupDiscovery({
|
||||
plugin,
|
||||
shouldFilterUntrustedWorkspacePlugins,
|
||||
shouldFilterBundledByAllowlist,
|
||||
normalizedConfig,
|
||||
rootConfig: params.config,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user