fix(config): soften antigravity removal fallout (#25538)

Land #25538 by @chilu18 to keep legacy google-antigravity-auth config entries non-fatal after removal (see #25862).

Co-authored-by: chilu18 <chilu.machona@icloud.com>
This commit is contained in:
Peter Steinberger
2026-02-24 23:02:45 +00:00
parent 039ae0b77c
commit bf8ca07deb
3 changed files with 62 additions and 16 deletions

View File

@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Config/Plugins: treat stale removed `google-antigravity-auth` plugin references as compatibility warnings (not hard validation errors) across `plugins.entries`, `plugins.allow`, `plugins.deny`, and `plugins.slots.memory`, so startup no longer fails after antigravity removal. (#25538, #25862) Thanks @chilu18.
- Security/Workspace FS: normalize `@`-prefixed paths before workspace-boundary checks (including workspace-only read/write/edit and sandbox mount path guards), preventing absolute-path escape attempts from bypassing guard validation. This ships in the next npm release. Thanks @tdjackey for reporting.
- Security/Native images: enforce `tools.fs.workspaceOnly` for native prompt image auto-load (including history refs), preventing out-of-workspace sandbox mounts from being implicitly ingested as vision input. This ships in the next npm release. Thanks @tdjackey for reporting.
- Security/Exec approvals: bind `system.run` command display/approval text to full argv when shell-wrapper inline payloads carry positional argv values, and reject payload-only `rawCommand` mismatches for those wrapper-carrier forms, preventing hidden command execution under misleading approval text. This ships in the next npm release. Thanks @tdjackey for reporting.

View File

@@ -103,6 +103,48 @@ describe("config plugin validation", () => {
}
});
it("warns for removed legacy plugin ids instead of failing validation", async () => {
const home = await createCaseHome();
const removedId = "google-antigravity-auth";
const res = validateInHome(home, {
agents: { list: [{ id: "pi" }] },
plugins: {
enabled: false,
entries: { [removedId]: { enabled: true } },
allow: [removedId],
deny: [removedId],
slots: { memory: removedId },
},
});
expect(res.ok).toBe(true);
if (res.ok) {
expect(res.warnings).toEqual(
expect.arrayContaining([
{
path: `plugins.entries.${removedId}`,
message:
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
},
{
path: "plugins.allow",
message:
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
},
{
path: "plugins.deny",
message:
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
},
{
path: "plugins.slots.memory",
message:
"plugin removed: google-antigravity-auth (stale config entry ignored; remove it from plugins config)",
},
]),
);
}
});
it("surfaces plugin config diagnostics", async () => {
const home = await createCaseHome();
const pluginDir = path.join(home, "bad-plugin");

View File

@@ -22,6 +22,8 @@ import { findLegacyConfigIssues } from "./legacy.js";
import type { OpenClawConfig, ConfigValidationIssue } from "./types.js";
import { OpenClawSchema } from "./zod-schema.js";
const LEGACY_REMOVED_PLUGIN_IDS = new Set(["google-antigravity-auth"]);
function isWorkspaceAvatarPath(value: string, workspaceDir: string): boolean {
const workspaceRoot = path.resolve(workspaceDir);
const resolved = path.resolve(workspaceRoot, value);
@@ -313,6 +315,19 @@ function validateConfigObjectWithPluginsBase(
}
const { registry, knownIds, normalizedPlugins } = ensureRegistry();
const pushMissingPluginIssue = (path: string, pluginId: string) => {
if (LEGACY_REMOVED_PLUGIN_IDS.has(pluginId)) {
warnings.push({
path,
message: `plugin removed: ${pluginId} (stale config entry ignored; remove it from plugins config)`,
});
return;
}
issues.push({
path,
message: `plugin not found: ${pluginId}`,
});
};
const pluginsConfig = config.plugins;
@@ -320,10 +335,7 @@ function validateConfigObjectWithPluginsBase(
if (entries && isRecord(entries)) {
for (const pluginId of Object.keys(entries)) {
if (!knownIds.has(pluginId)) {
issues.push({
path: `plugins.entries.${pluginId}`,
message: `plugin not found: ${pluginId}`,
});
pushMissingPluginIssue(`plugins.entries.${pluginId}`, pluginId);
}
}
}
@@ -334,10 +346,7 @@ function validateConfigObjectWithPluginsBase(
continue;
}
if (!knownIds.has(pluginId)) {
issues.push({
path: "plugins.allow",
message: `plugin not found: ${pluginId}`,
});
pushMissingPluginIssue("plugins.allow", pluginId);
}
}
@@ -347,19 +356,13 @@ function validateConfigObjectWithPluginsBase(
continue;
}
if (!knownIds.has(pluginId)) {
issues.push({
path: "plugins.deny",
message: `plugin not found: ${pluginId}`,
});
pushMissingPluginIssue("plugins.deny", pluginId);
}
}
const memorySlot = normalizedPlugins.slots.memory;
if (typeof memorySlot === "string" && memorySlot.trim() && !knownIds.has(memorySlot)) {
issues.push({
path: "plugins.slots.memory",
message: `plugin not found: ${memorySlot}`,
});
pushMissingPluginIssue("plugins.slots.memory", memorySlot);
}
let selectedMemoryPluginId: string | null = null;