Compare commits
2 Commits
main
...
issue-3838
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d466c26f1 | ||
|
|
fc617945ff |
@ -35,6 +35,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Gateway/tools invoke malformed-tool hardening: classify tool schema validation failures (including Zod-style errors) as controlled `400 tool_error` responses instead of internal failures, preventing malformed tool payloads from escalating into unstable runtime behavior. (#38384)
|
||||||
- Onboarding/headless Linux daemon probe hardening: treat `systemctl --user is-enabled` probe failures as non-fatal during daemon install flow so onboarding no longer crashes on SSH/headless VPS environments before showing install guidance. (#37297) Thanks @acarbajal-web.
|
- Onboarding/headless Linux daemon probe hardening: treat `systemctl --user is-enabled` probe failures as non-fatal during daemon install flow so onboarding no longer crashes on SSH/headless VPS environments before showing install guidance. (#37297) Thanks @acarbajal-web.
|
||||||
- Memory/QMD mcporter Windows spawn hardening: when `mcporter.cmd` launch fails with `spawn EINVAL`, retry via bare `mcporter` shell resolution so QMD recall can continue instead of falling back to builtin memory search. (#27402) Thanks @i0ivi0i.
|
- Memory/QMD mcporter Windows spawn hardening: when `mcporter.cmd` launch fails with `spawn EINVAL`, retry via bare `mcporter` shell resolution so QMD recall can continue instead of falling back to builtin memory search. (#27402) Thanks @i0ivi0i.
|
||||||
- Tools/web_search Brave language-code validation: align `search_lang` handling with Brave-supported codes (including `zh-hans`, `zh-hant`, `en-gb`, and `pt-br`), map common alias inputs (`zh`, `ja`) to valid Brave values, and reject unsupported codes before upstream requests to prevent 422 failures. (#37260) Thanks @heyanming.
|
- Tools/web_search Brave language-code validation: align `search_lang` handling with Brave-supported codes (including `zh-hans`, `zh-hant`, `en-gb`, and `pt-br`), map common alias inputs (`zh`, `ja`) to valid Brave values, and reject unsupported codes before upstream requests to prevent 422 failures. (#37260) Thanks @heyanming.
|
||||||
|
|||||||
@ -117,6 +117,22 @@ vi.mock("../agents/openclaw-tools.js", () => {
|
|||||||
if (mode === "auth") {
|
if (mode === "auth") {
|
||||||
throw toolAuthorizationError("mode forbidden");
|
throw toolAuthorizationError("mode forbidden");
|
||||||
}
|
}
|
||||||
|
if (mode === "zod") {
|
||||||
|
const err = new Error("invalid tool payload") as Error & {
|
||||||
|
issues?: unknown[];
|
||||||
|
toolInputError?: boolean;
|
||||||
|
};
|
||||||
|
err.name = "ZodError";
|
||||||
|
err.issues = [{ path: ["amount"], message: "Required", code: "invalid_type" }];
|
||||||
|
err.toolInputError = true;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
if (mode === "zod-internal") {
|
||||||
|
const err = new Error("schema mismatch") as Error & { issues?: unknown[] };
|
||||||
|
err.name = "ZodError";
|
||||||
|
err.issues = [{ path: ["result"], message: "Invalid", code: "invalid_type" }];
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
if (mode === "crash") {
|
if (mode === "crash") {
|
||||||
throw new Error("boom");
|
throw new Error("boom");
|
||||||
}
|
}
|
||||||
@ -540,7 +556,7 @@ describe("POST /tools/invoke", () => {
|
|||||||
expect(resMain.status).toBe(200);
|
expect(resMain.status).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("maps tool input/auth errors to 400/403 and unexpected execution errors to 500", async () => {
|
it("maps tool input/auth errors and explicit schema-input errors to 400/403", async () => {
|
||||||
cfg = {
|
cfg = {
|
||||||
...cfg,
|
...cfg,
|
||||||
agents: {
|
agents: {
|
||||||
@ -570,6 +586,28 @@ describe("POST /tools/invoke", () => {
|
|||||||
expect(authBody.error?.type).toBe("tool_error");
|
expect(authBody.error?.type).toBe("tool_error");
|
||||||
expect(authBody.error?.message).toBe("mode forbidden");
|
expect(authBody.error?.message).toBe("mode forbidden");
|
||||||
|
|
||||||
|
const zodRes = await invokeToolAuthed({
|
||||||
|
tool: "tools_invoke_test",
|
||||||
|
args: { mode: "zod" },
|
||||||
|
sessionKey: "main",
|
||||||
|
});
|
||||||
|
expect(zodRes.status).toBe(400);
|
||||||
|
const zodBody = await zodRes.json();
|
||||||
|
expect(zodBody.ok).toBe(false);
|
||||||
|
expect(zodBody.error?.type).toBe("tool_error");
|
||||||
|
expect(zodBody.error?.message).toBe("invalid tool payload");
|
||||||
|
|
||||||
|
const internalZodRes = await invokeToolAuthed({
|
||||||
|
tool: "tools_invoke_test",
|
||||||
|
args: { mode: "zod-internal" },
|
||||||
|
sessionKey: "main",
|
||||||
|
});
|
||||||
|
expect(internalZodRes.status).toBe(500);
|
||||||
|
const internalZodBody = await internalZodRes.json();
|
||||||
|
expect(internalZodBody.ok).toBe(false);
|
||||||
|
expect(internalZodBody.error?.type).toBe("tool_error");
|
||||||
|
expect(internalZodBody.error?.message).toBe("tool execution failed");
|
||||||
|
|
||||||
const crashRes = await invokeToolAuthed({
|
const crashRes = await invokeToolAuthed({
|
||||||
tool: "tools_invoke_test",
|
tool: "tools_invoke_test",
|
||||||
args: { mode: "crash" },
|
args: { mode: "crash" },
|
||||||
|
|||||||
@ -117,6 +117,18 @@ function resolveToolInputErrorStatus(err: unknown): number | null {
|
|||||||
const status = (err as { status?: unknown }).status;
|
const status = (err as { status?: unknown }).status;
|
||||||
return typeof status === "number" ? status : 400;
|
return typeof status === "number" ? status : 400;
|
||||||
}
|
}
|
||||||
|
// Some tool implementations throw raw ZodError objects on malformed model/tool payloads.
|
||||||
|
// Only map to 400 when explicitly marked as tool-input validation; otherwise preserve 500.
|
||||||
|
if (typeof err === "object" && err !== null) {
|
||||||
|
const record = err as { name?: unknown; issues?: unknown; toolInputError?: unknown };
|
||||||
|
if (
|
||||||
|
record.name === "ZodError" &&
|
||||||
|
Array.isArray(record.issues) &&
|
||||||
|
record.toolInputError === true
|
||||||
|
) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (typeof err !== "object" || err === null || !("name" in err)) {
|
if (typeof err !== "object" || err === null || !("name" in err)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user