mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-28 01:21:36 +00:00
fix: preserve configured plugins in allowlist
This commit is contained in:
@@ -57,7 +57,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
expect(result.autoEnabledReasons).toEqual({});
|
||||
});
|
||||
|
||||
it("auto-enables built-in channels without appending to plugins.allow", () => {
|
||||
it("auto-enables built-in channels and preserves them in restrictive plugins.allow", () => {
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
channels: { slack: { botToken: "x" } },
|
||||
@@ -68,7 +68,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
|
||||
expect(result.config.channels?.slack?.enabled).toBe(true);
|
||||
expect(result.config.plugins?.entries?.slack).toBeUndefined();
|
||||
expect(result.config.plugins?.allow).toEqual(["telegram"]);
|
||||
expect(result.config.plugins?.allow).toEqual(["telegram", "slack"]);
|
||||
expect(result.autoEnabledReasons).toEqual({
|
||||
slack: ["slack configured"],
|
||||
});
|
||||
@@ -285,7 +285,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
expect(validateConfigObject(result.config).ok).toBe(true);
|
||||
});
|
||||
|
||||
it("does not append built-in WhatsApp to plugins.allow during auto-enable", () => {
|
||||
it("appends built-in WhatsApp to restrictive plugins.allow during auto-enable", () => {
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
channels: {
|
||||
@@ -301,10 +301,52 @@ describe("applyPluginAutoEnable core", () => {
|
||||
});
|
||||
|
||||
expect(result.config.channels?.whatsapp?.enabled).toBe(true);
|
||||
expect(result.config.plugins?.allow).toEqual(["telegram"]);
|
||||
expect(result.config.plugins?.allow).toEqual(["telegram", "whatsapp"]);
|
||||
expect(validateConfigObject(result.config).ok).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves configured plugin entries in restrictive plugins.allow", () => {
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["glueclaw"],
|
||||
entries: {
|
||||
discord: {
|
||||
config: {
|
||||
token: "x",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
env: makeIsolatedEnv(),
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.allow).toEqual(["glueclaw", "discord"]);
|
||||
expect(result.changes).toContain("discord plugin config present, added to plugin allowlist.");
|
||||
});
|
||||
|
||||
it("does not preserve stale configured plugin entries in restrictive plugins.allow", () => {
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
plugins: {
|
||||
allow: ["glueclaw"],
|
||||
entries: {
|
||||
"missing-plugin": {
|
||||
config: {
|
||||
token: "x",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
env: makeIsolatedEnv(),
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.allow).toEqual(["glueclaw"]);
|
||||
expect(result.changes).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not re-emit built-in auto-enable changes when rerun with plugins.allow set", () => {
|
||||
const first = applyPluginAutoEnable({
|
||||
config: {
|
||||
|
||||
@@ -256,8 +256,16 @@ function hasConfiguredPluginConfigEntry(cfg: OpenClawConfig): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function hasPluginEntries(cfg: OpenClawConfig): boolean {
|
||||
const entries = cfg.plugins?.entries;
|
||||
return !!entries && typeof entries === "object" && Object.keys(entries).length > 0;
|
||||
}
|
||||
|
||||
function configMayNeedPluginManifestRegistry(cfg: OpenClawConfig): boolean {
|
||||
const pluginEntries = cfg.plugins?.entries;
|
||||
if (Array.isArray(cfg.plugins?.allow) && cfg.plugins.allow.length > 0 && hasPluginEntries(cfg)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
pluginEntries &&
|
||||
Object.values(pluginEntries).some((entry) => isRecord(entry) && isRecord(entry.config))
|
||||
@@ -292,6 +300,9 @@ export function configMayNeedPluginAutoEnable(
|
||||
cfg: OpenClawConfig,
|
||||
env: NodeJS.ProcessEnv,
|
||||
): boolean {
|
||||
if (Array.isArray(cfg.plugins?.allow) && cfg.plugins.allow.length > 0 && hasPluginEntries(cfg)) {
|
||||
return true;
|
||||
}
|
||||
if (hasConfiguredPluginConfigEntry(cfg)) {
|
||||
return true;
|
||||
}
|
||||
@@ -500,6 +511,59 @@ function registerPluginEntry(cfg: OpenClawConfig, pluginId: string): OpenClawCon
|
||||
};
|
||||
}
|
||||
|
||||
function hasMaterialPluginEntryConfig(entry: unknown): boolean {
|
||||
if (!isRecord(entry)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
entry.enabled === true ||
|
||||
isRecord(entry.config) ||
|
||||
isRecord(entry.hooks) ||
|
||||
isRecord(entry.subagent) ||
|
||||
entry.apiKey !== undefined ||
|
||||
entry.env !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
function isKnownPluginId(pluginId: string, manifestRegistry: PluginManifestRegistry): boolean {
|
||||
if (normalizeChatChannelId(pluginId)) {
|
||||
return true;
|
||||
}
|
||||
return manifestRegistry.plugins.some((plugin) => plugin.id === pluginId);
|
||||
}
|
||||
|
||||
function materializeConfiguredPluginEntryAllowlist(params: {
|
||||
config: OpenClawConfig;
|
||||
changes: string[];
|
||||
manifestRegistry: PluginManifestRegistry;
|
||||
}): OpenClawConfig {
|
||||
let next = params.config;
|
||||
const allow = next.plugins?.allow;
|
||||
const entries = next.plugins?.entries;
|
||||
if (!Array.isArray(allow) || allow.length === 0 || !entries || typeof entries !== "object") {
|
||||
return next;
|
||||
}
|
||||
|
||||
for (const pluginId of Object.keys(entries).toSorted((left, right) =>
|
||||
left.localeCompare(right),
|
||||
)) {
|
||||
const entry = entries[pluginId];
|
||||
if (
|
||||
!hasMaterialPluginEntryConfig(entry) ||
|
||||
isPluginDenied(next, pluginId) ||
|
||||
isPluginExplicitlyDisabled(next, pluginId) ||
|
||||
allow.includes(pluginId) ||
|
||||
!isKnownPluginId(pluginId, params.manifestRegistry)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
next = ensurePluginAllowlisted(next, pluginId);
|
||||
params.changes.push(`${pluginId} plugin config present, added to plugin allowlist.`);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
function formatAutoEnableChange(entry: PluginAutoEnableCandidate): string {
|
||||
let reason = resolvePluginAutoEnableCandidateReason(entry).trim();
|
||||
const channelId = normalizeChatChannelId(entry.pluginId);
|
||||
@@ -557,8 +621,7 @@ export function materializePluginAutoEnableCandidatesInternal(params: {
|
||||
}
|
||||
|
||||
const allow = next.plugins?.allow;
|
||||
const allowMissing =
|
||||
builtInChannelId == null && Array.isArray(allow) && !allow.includes(entry.pluginId);
|
||||
const allowMissing = Array.isArray(allow) && !allow.includes(entry.pluginId);
|
||||
const alreadyEnabled =
|
||||
builtInChannelId != null
|
||||
? isBuiltInChannelAlreadyEnabled(next, builtInChannelId)
|
||||
@@ -568,9 +631,7 @@ export function materializePluginAutoEnableCandidatesInternal(params: {
|
||||
}
|
||||
|
||||
next = registerPluginEntry(next, entry.pluginId);
|
||||
if (!builtInChannelId) {
|
||||
next = ensurePluginAllowlisted(next, entry.pluginId);
|
||||
}
|
||||
next = ensurePluginAllowlisted(next, entry.pluginId);
|
||||
const reason = resolvePluginAutoEnableCandidateReason(entry);
|
||||
autoEnabledReasons.set(entry.pluginId, [
|
||||
...(autoEnabledReasons.get(entry.pluginId) ?? []),
|
||||
@@ -579,6 +640,12 @@ export function materializePluginAutoEnableCandidatesInternal(params: {
|
||||
changes.push(formatAutoEnableChange(entry));
|
||||
}
|
||||
|
||||
next = materializeConfiguredPluginEntryAllowlist({
|
||||
config: next,
|
||||
changes,
|
||||
manifestRegistry: params.manifestRegistry,
|
||||
});
|
||||
|
||||
const autoEnabledReasonRecord: Record<string, string[]> = Object.create(null);
|
||||
for (const [pluginId, reasons] of autoEnabledReasons) {
|
||||
if (!isBlockedObjectKey(pluginId)) {
|
||||
|
||||
Reference in New Issue
Block a user