fix(write): guarded xd approval evaluation failures
Schema-invalid JSON objects can reach mounted approval functions before xdev dispatch validates their arguments. Fall back to the exec tier when an approval function throws, preserving fail-closed prompting and allowing dispatch to surface its normal schema error. Added regression coverage for ast_edit payloads containing null paths. Fixes #5727
This commit is contained in:
@@ -402,8 +402,9 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
||||
// Decode the device JSON payload and evaluate the mounted tool's own
|
||||
// approval (which may be argument-dependent, e.g. ast_edit is read-tier
|
||||
// for internal-URL paths, debug is read-tier for inspection actions).
|
||||
// Malformed JSON, non-object payloads, and missing content stay exec so
|
||||
// the gate fails closed — the dispatch itself rejects them too.
|
||||
// Malformed JSON, non-object payloads, missing content, and approval
|
||||
// functions that reject schema-invalid objects stay exec so the gate
|
||||
// fails closed — the dispatch itself rejects invalid arguments too.
|
||||
const rawContent = (args as Partial<WriteParams>).content;
|
||||
if (typeof rawContent !== "string") return "exec";
|
||||
let parsed: unknown;
|
||||
@@ -413,7 +414,11 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
||||
return "exec";
|
||||
}
|
||||
if (!isRecord(parsed)) return "exec";
|
||||
return resolveToolTier(inst, parsed);
|
||||
try {
|
||||
return resolveToolTier(inst, parsed);
|
||||
} catch {
|
||||
return "exec";
|
||||
}
|
||||
}
|
||||
// Remote SSH writes open an outbound connection and run a remote shell —
|
||||
// gate them like the exec-tier `ssh` tool, ahead of the handler-write
|
||||
|
||||
@@ -115,11 +115,13 @@ describe("read and write route xd:// device URLs", () => {
|
||||
expect(tier("xd://debug", JSON.stringify({ action: "sessions" }))).toBe("read");
|
||||
expect(tier("xd://debug", JSON.stringify({ action: "launch", program: "./app" }))).toBe("exec");
|
||||
|
||||
// Fail closed: malformed JSON, non-object payloads, missing content,
|
||||
// and unknown devices all stay exec so the gate never under-prompts.
|
||||
// Fail closed: malformed JSON, non-object or schema-invalid payloads,
|
||||
// missing content, and unknown devices all stay exec so the gate never
|
||||
// under-prompts.
|
||||
expect(tier("xd://ast_edit", "{ not json")).toBe("exec");
|
||||
expect(tier("xd://ast_edit", "[1,2,3]")).toBe("exec");
|
||||
expect(tier("xd://ast_edit", '"a string"')).toBe("exec");
|
||||
expect(tier("xd://ast_edit", JSON.stringify({ paths: [null] }))).toBe("exec");
|
||||
expect(approval({ path: "xd://ast_edit" })).toBe("exec");
|
||||
expect(tier("xd://no_such_device", "{}")).toBe("exec");
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user