mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:58:38 +00:00
fix(config): fail closed on invalid config load (#9040, thanks @joetomasone)
Land #9040 by @joetomasone. Add fail-closed config loading, compat coverage, and changelog entry for #5052. Co-authored-by: Joe Tomasone <joe@tomasone.com>
This commit is contained in:
@@ -39,6 +39,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Security/Config: fail closed when `loadConfig()` hits validation or read errors so invalid configs cannot silently fall back to permissive runtime defaults. (#9040) Thanks @joetomasone.
|
||||||
- LINE/`requireMention` group gating: align inbound and reply-stage LINE group policy resolution across raw, `group:`, and `room:` keys (including account-scoped group config), preserve plugin-backed reply-stage fallback behavior, and add regression coverage for prefixed-only group/room config plus reply-stage policy resolution. (#35847) Thanks @kirisame-wang.
|
- LINE/`requireMention` group gating: align inbound and reply-stage LINE group policy resolution across raw, `group:`, and `room:` keys (including account-scoped group config), preserve plugin-backed reply-stage fallback behavior, and add regression coverage for prefixed-only group/room config plus reply-stage policy resolution. (#35847) Thanks @kirisame-wang.
|
||||||
- Onboarding/local setup: default unset local `tools.profile` to `coding` instead of `messaging`, restoring file/runtime tools for fresh local installs while preserving explicit user-set profiles. (from #38241, overlap with #34958) Thanks @cgdusek.
|
- Onboarding/local setup: default unset local `tools.profile` to `coding` instead of `messaging`, restoring file/runtime tools for fresh local installs while preserving explicit user-set profiles. (from #38241, overlap with #34958) Thanks @cgdusek.
|
||||||
- Gateway/Telegram stale-socket restart guard: only apply stale-socket restarts to channels that publish event-liveness timestamps, preventing Telegram providers from being misclassified as stale solely due to long uptime and avoiding restart/pairing storms after upgrade. (openclaw#38464)
|
- Gateway/Telegram stale-socket restart guard: only apply stale-socket restarts to channels that publish event-liveness timestamps, preventing Telegram providers from being misclassified as stale solely due to long uptime and avoiding restart/pairing storms after upgrade. (openclaw#38464)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ describe("config io paths", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("logs invalid config path details and returns empty config", async () => {
|
it("logs invalid config path details and throws on invalid config", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
const configDir = path.join(home, ".openclaw");
|
const configDir = path.join(home, ".openclaw");
|
||||||
await fs.mkdir(configDir, { recursive: true });
|
await fs.mkdir(configDir, { recursive: true });
|
||||||
@@ -159,7 +159,7 @@ describe("config io paths", () => {
|
|||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(io.loadConfig()).toEqual({});
|
expect(() => io.loadConfig()).toThrow("Invalid config");
|
||||||
expect(logger.error).toHaveBeenCalledWith(
|
expect(logger.error).toHaveBeenCalledWith(
|
||||||
expect.stringContaining(`Invalid config at ${configPath}:\\n`),
|
expect.stringContaining(`Invalid config at ${configPath}:\\n`),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -831,10 +831,11 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
|||||||
}
|
}
|
||||||
const error = err as { code?: string };
|
const error = err as { code?: string };
|
||||||
if (error?.code === "INVALID_CONFIG") {
|
if (error?.code === "INVALID_CONFIG") {
|
||||||
return {};
|
// Fail closed so invalid configs cannot silently fall back to permissive defaults.
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
deps.logger.error(`Failed to read config at ${configPath}`, err);
|
deps.logger.error(`Failed to read config at ${configPath}`, err);
|
||||||
return {};
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/config/io.validation-fails-closed.test.ts
Normal file
57
src/config/io.validation-fails-closed.test.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { clearConfigCache, loadConfig } from "./config.js";
|
||||||
|
import { withTempHomeConfig } from "./test-helpers.js";
|
||||||
|
|
||||||
|
describe("config validation fail-closed behavior", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
clearConfigCache();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws INVALID_CONFIG instead of returning an empty config", async () => {
|
||||||
|
await withTempHomeConfig(
|
||||||
|
{
|
||||||
|
agents: { list: [{ id: "main" }] },
|
||||||
|
nope: true,
|
||||||
|
channels: {
|
||||||
|
whatsapp: {
|
||||||
|
dmPolicy: "allowlist",
|
||||||
|
allowFrom: ["+1234567890"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||||
|
let thrown: unknown;
|
||||||
|
try {
|
||||||
|
loadConfig();
|
||||||
|
} catch (err) {
|
||||||
|
thrown = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(thrown).toBeInstanceOf(Error);
|
||||||
|
expect((thrown as { code?: string } | undefined)?.code).toBe("INVALID_CONFIG");
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still loads valid security settings unchanged", async () => {
|
||||||
|
await withTempHomeConfig(
|
||||||
|
{
|
||||||
|
agents: { list: [{ id: "main" }] },
|
||||||
|
channels: {
|
||||||
|
whatsapp: {
|
||||||
|
dmPolicy: "allowlist",
|
||||||
|
allowFrom: ["+1234567890"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
const cfg = loadConfig();
|
||||||
|
expect(cfg.channels?.whatsapp?.dmPolicy).toBe("allowlist");
|
||||||
|
expect(cfg.channels?.whatsapp?.allowFrom).toEqual(["+1234567890"]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user