mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: respect plugin allowlist for bundled deps
This commit is contained in:
@@ -70,6 +70,9 @@ Gateway startup runtime-dependency repair.
|
||||
Explicit disablement still wins: `plugins.entries.<id>.enabled: false`,
|
||||
`plugins.deny`, `plugins.enabled: false`, and `channels.<id>.enabled: false`
|
||||
prevent automatic bundled runtime-dependency repair for that plugin/channel.
|
||||
A non-empty `plugins.allow` also bounds default-enabled bundled runtime-dependency
|
||||
repair; explicit bundled channel enablement (`channels.<id>.enabled: true`) can
|
||||
still repair that channel's plugin dependencies.
|
||||
External plugins and custom load paths must still be installed through
|
||||
`openclaw plugins install`.
|
||||
|
||||
|
||||
@@ -286,6 +286,72 @@ describe("doctor bundled plugin runtime deps", () => {
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not report allowlist-excluded default-enabled bundled plugin deps", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeJson(path.join(root, "dist", "extensions", "openai", "package.json"), {
|
||||
dependencies: {
|
||||
"openai-only": "1.0.0",
|
||||
},
|
||||
});
|
||||
writeJson(path.join(root, "dist", "extensions", "openai", "openclaw.plugin.json"), {
|
||||
id: "openai",
|
||||
enabledByDefault: true,
|
||||
configSchema: { type: "object" },
|
||||
});
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
config: {
|
||||
plugins: { enabled: true, allow: ["browser"] },
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("lets explicit bundled channel enablement bypass runtime-deps allowlist gating", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeBundledChannelPlugin(root, "telegram", { "telegram-only": "1.0.0" });
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
config: {
|
||||
plugins: { enabled: true, allow: ["browser"] },
|
||||
channels: {
|
||||
telegram: { enabled: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing.map((dep) => `${dep.name}@${dep.version}`)).toEqual([
|
||||
"telegram-only@1.0.0",
|
||||
]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("does not let doctor channel recovery bypass restrictive plugin allowlists", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
writeBundledChannelPlugin(root, "telegram", { "telegram-only": "1.0.0" });
|
||||
|
||||
const result = scanBundledPluginRuntimeDeps({
|
||||
packageRoot: root,
|
||||
includeConfiguredChannels: true,
|
||||
config: {
|
||||
plugins: { enabled: true, allow: ["browser"] },
|
||||
channels: {
|
||||
telegram: { botToken: "123:abc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.missing).toEqual([]);
|
||||
expect(result.conflicts).toEqual([]);
|
||||
});
|
||||
|
||||
it("repairs missing deps during non-interactive doctor", async () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-doctor-bundled-"));
|
||||
writeJson(path.join(root, "package.json"), { name: "openclaw" });
|
||||
|
||||
@@ -907,10 +907,8 @@ function isBundledPluginConfiguredForRuntimeDeps(params: {
|
||||
if (entry?.enabled === false) {
|
||||
return false;
|
||||
}
|
||||
if (entry?.enabled === true) {
|
||||
return true;
|
||||
}
|
||||
let hasExplicitChannelDisable = false;
|
||||
let hasConfiguredChannel = false;
|
||||
for (const channelId of readBundledPluginChannels(params.pluginDir)) {
|
||||
const normalizedChannelId = normalizeOptionalLowercaseString(channelId);
|
||||
if (!normalizedChannelId) {
|
||||
@@ -932,15 +930,31 @@ function isBundledPluginConfiguredForRuntimeDeps(params: {
|
||||
channelConfig &&
|
||||
typeof channelConfig === "object" &&
|
||||
!Array.isArray(channelConfig) &&
|
||||
(params.includeConfiguredChannels ||
|
||||
(channelConfig as { enabled?: unknown }).enabled === true)
|
||||
(channelConfig as { enabled?: unknown }).enabled === true
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
channelConfig &&
|
||||
typeof channelConfig === "object" &&
|
||||
!Array.isArray(channelConfig) &&
|
||||
params.includeConfiguredChannels
|
||||
) {
|
||||
hasConfiguredChannel = true;
|
||||
}
|
||||
}
|
||||
if (hasExplicitChannelDisable) {
|
||||
return false;
|
||||
}
|
||||
if (plugins.allow.length > 0 && !plugins.allow.includes(params.pluginId)) {
|
||||
return false;
|
||||
}
|
||||
if (entry?.enabled === true) {
|
||||
return true;
|
||||
}
|
||||
if (hasConfiguredChannel) {
|
||||
return true;
|
||||
}
|
||||
return readBundledPluginEnabledByDefault(params.pluginDir);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user