fix: reject blank cron payloads

This commit is contained in:
Peter Steinberger
2026-05-31 23:18:03 -04:00
parent 76fa1b99c3
commit 45b5f876dd
3 changed files with 62 additions and 0 deletions

View File

@@ -195,6 +195,42 @@ describe("normalizeCronJobCreate", () => {
expectAnnounceDeliveryTarget(delivery, { channel: "telegram", to: "7200373102" });
});
it("normalizes whitespace-only payload text to empty strings so validation rejects it", () => {
const agentTurn = normalizeCronJobCreate({
name: "blank agent turn",
enabled: true,
schedule: { kind: "every", everyMs: 60_000 },
sessionTarget: "isolated",
wakeMode: "now",
payload: {
kind: "agentTurn",
message: " ",
},
}) as unknown as Record<string, unknown>;
expect(agentTurn.payload).toEqual({ kind: "agentTurn", message: "" });
expect(validateCronAddParams(agentTurn)).toBe(false);
const systemEvent = normalizeCronJobCreate({
name: "blank system event",
enabled: true,
schedule: { kind: "every", everyMs: 60_000 },
sessionTarget: "main",
wakeMode: "now",
payload: {
kind: "systemEvent",
text: " ",
},
}) as unknown as Record<string, unknown>;
expect(systemEvent.payload).toEqual({ kind: "systemEvent", text: "" });
expect(validateCronAddParams(systemEvent)).toBe(false);
const update = normalizeCronJobPatch({
payload: { kind: "agentTurn", message: " " },
}) as unknown as Record<string, unknown>;
expect(update.payload).toEqual({ kind: "agentTurn", message: "" });
expect(validateCronUpdateParams({ id: "job-1", patch: update })).toBe(false);
});
it("normalizes delivery accountId and strips blanks", () => {
const normalized = normalizeIsolatedAgentTurnCreateJob({
name: "delivery account",

View File

@@ -128,12 +128,16 @@ function coercePayload(payload: UnknownRecord) {
const trimmed = normalizeOptionalString(next.message) ?? "";
if (trimmed) {
next.message = trimmed;
} else {
next.message = "";
}
}
if (typeof next.text === "string") {
const trimmed = normalizeOptionalString(next.text) ?? "";
if (trimmed) {
next.text = trimmed;
} else {
next.text = "";
}
}
if ("model" in next) {

View File

@@ -404,6 +404,28 @@ describe("cron method validation", () => {
expectResponseError(respond, { code: "INVALID_REQUEST" });
});
it("rejects whitespace-only cron payloads before calling add", async () => {
const agentTurn = await invokeCronAdd(
agentTurnCronParams({
name: "blank agent turn",
payload: { kind: "agentTurn", message: " " },
}),
);
expect(agentTurn.context.cron.add).not.toHaveBeenCalled();
expectResponseError(agentTurn.respond, { code: "INVALID_REQUEST", messageIncludes: "message" });
const systemEvent = await invokeCronAdd({
name: "blank system event",
enabled: true,
schedule: { kind: "every", everyMs: 60_000 },
sessionTarget: "main",
wakeMode: "next-heartbeat",
payload: { kind: "systemEvent", text: " " },
});
expect(systemEvent.context.cron.add).not.toHaveBeenCalled();
expectResponseError(systemEvent.respond, { code: "INVALID_REQUEST", messageIncludes: "text" });
});
it("rejects ambiguous announce delivery on add when multiple channels are configured", async () => {
setRuntimeConfig(telegramSlackConfig({ includeMainSession: true }));