fix(cron): validate Telegram delivery targets to reject invalid formats

Reject cron jobs with Telegram delivery targets using slash delimiters
(e.g. "-10012345/6789") which are not supported. Only accept valid
formats: plain chat ID, colon delimiter (:), or topic marker (:topic:).

Added validateTelegramDeliveryTarget() function and tests.
This commit is contained in:
Evgeny Zislis 2026-02-20 16:58:07 +02:00 committed by Ayaan Zaidi
parent 23598e0e3a
commit d58661f8bb
2 changed files with 92 additions and 0 deletions

View File

@ -152,6 +152,79 @@ describe("applyJobPatch", () => {
).not.toThrow();
expect(job.delivery).toEqual({ mode: "webhook", to: "https://example.invalid/trim" });
});
it("rejects Telegram delivery with invalid target (slash delimiter)", () => {
const job = createIsolatedAgentTurnJob("job-telegram-invalid", {
mode: "announce",
channel: "telegram",
to: "-10012345/6789",
});
expect(() => applyJobPatch(job, { enabled: true })).toThrow(
'Invalid Telegram delivery target "-10012345/6789". Use colon (:) as delimiter, not slash or other characters. Valid formats: -1001234567890, -1001234567890:123, -1001234567890:topic:123',
);
});
it("rejects Telegram delivery with other invalid characters", () => {
const job = createIsolatedAgentTurnJob("job-telegram-invalid-space", {
mode: "announce",
channel: "telegram",
to: "-10012345 6789",
});
expect(() => applyJobPatch(job, { enabled: true })).toThrow(
'Invalid Telegram delivery target "-10012345 6789". Use colon (:) as delimiter, not slash or other characters. Valid formats: -1001234567890, -1001234567890:123, -1001234567890:topic:123',
);
});
it("accepts Telegram delivery with valid target (plain chat id)", () => {
const job = createIsolatedAgentTurnJob("job-telegram-valid", {
mode: "announce",
channel: "telegram",
to: "-1001234567890",
});
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
});
it("accepts Telegram delivery with valid target (colon delimiter)", () => {
const job = createIsolatedAgentTurnJob("job-telegram-valid-colon", {
mode: "announce",
channel: "telegram",
to: "-1001234567890:123",
});
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
});
it("accepts Telegram delivery with valid target (topic marker)", () => {
const job = createIsolatedAgentTurnJob("job-telegram-valid-topic", {
mode: "announce",
channel: "telegram",
to: "-1001234567890:topic:456",
});
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
});
it("accepts Telegram delivery without target", () => {
const job = createIsolatedAgentTurnJob("job-telegram-no-target", {
mode: "announce",
channel: "telegram",
});
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
});
it("accepts Telegram delivery with @username", () => {
const job = createIsolatedAgentTurnJob("job-telegram-username", {
mode: "announce",
channel: "telegram",
to: "@mybot",
});
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
});
});
function createMockState(now: number): CronServiceState {

View File

@ -83,6 +83,19 @@ export function assertSupportedJobSpec(job: Pick<CronJob, "sessionTarget" | "pay
}
}
const INVALID_TELEGRAM_TARGET_CHARS = ["/", "\\", " ", ",", ";"];
function validateTelegramDeliveryTarget(to: string | undefined): string | undefined {
if (!to) {
return undefined;
}
const hasInvalidChar = INVALID_TELEGRAM_TARGET_CHARS.some((char) => to.includes(char));
if (hasInvalidChar) {
return `Invalid Telegram delivery target "${to}". Use colon (:) as delimiter, not slash or other characters. Valid formats: -1001234567890, -1001234567890:123, -1001234567890:topic:123`;
}
return undefined;
}
function assertDeliverySupport(job: Pick<CronJob, "sessionTarget" | "delivery">) {
if (!job.delivery) {
return;
@ -98,6 +111,12 @@ function assertDeliverySupport(job: Pick<CronJob, "sessionTarget" | "delivery">)
if (job.sessionTarget !== "isolated") {
throw new Error('cron channel delivery config is only supported for sessionTarget="isolated"');
}
if (job.delivery.channel === "telegram") {
const telegramError = validateTelegramDeliveryTarget(job.delivery.to);
if (telegramError) {
throw new Error(telegramError);
}
}
}
export function findJobOrThrow(state: CronServiceState, id: string) {