mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-09 15:35:17 +00:00
Validate Telegram delivery targets to reject invalid formats (#21930)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 02c9b1c3dd
Co-authored-by: kesor <7056+kesor@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
@@ -81,6 +81,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Cron/Run log: clean up settled per-path run-log write queue entries so long-running cron uptime does not retain stale promise bookkeeping in memory.
|
||||
- Cron/Run log: harden `cron.runs` run-log path resolution by rejecting path-separator `id`/`jobId` inputs and enforcing reads within the per-cron `runs/` directory.
|
||||
- Cron/Announce: when announce delivery target resolution fails (for example multiple configured channels with no explicit target), skip injecting fallback `Cron (error): ...` into the main session so runs fail cleanly without accidental last-route sends. (#24074)
|
||||
- Cron/Telegram: validate cron `delivery.to` with shared Telegram target parsing and resolve legacy `@username`/`t.me` targets to numeric IDs at send-time for deterministic delivery target writeback. (#21930) Thanks @kesor.
|
||||
- Cron/Isolation: force fresh session IDs for isolated cron runs so `sessionTarget="isolated"` executions never reuse prior run context. (#23470) Thanks @echoVic.
|
||||
- Plugins/Install: strip `workspace:*` devDependency entries from copied plugin manifests before `npm install --omit=dev`, preventing `EUNSUPPORTEDPROTOCOL` install failures for npm-published channel plugins (including Feishu and MS Teams).
|
||||
- Feishu/Plugins: restore bundled Feishu SDK availability for global installs and strip `openclaw: workspace:*` from plugin `devDependencies` during plugin-version sync so npm-installed Feishu plugins do not fail dependency install. (#23611, #23645, #23603)
|
||||
|
||||
@@ -152,6 +152,87 @@ describe("applyJobPatch", () => {
|
||||
).not.toThrow();
|
||||
expect(job.delivery).toEqual({ mode: "webhook", to: "https://example.invalid/trim" });
|
||||
});
|
||||
|
||||
it("rejects Telegram delivery with invalid target (chatId/topicId format)", () => {
|
||||
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 for topics, not slash. Valid formats: -1001234567890, -1001234567890:123, -1001234567890:topic:123, @username, https://t.me/username',
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts Telegram delivery with t.me URL", () => {
|
||||
const job = createIsolatedAgentTurnJob("job-telegram-tme", {
|
||||
mode: "announce",
|
||||
channel: "telegram",
|
||||
to: "https://t.me/mychannel",
|
||||
});
|
||||
|
||||
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
|
||||
});
|
||||
|
||||
it("accepts Telegram delivery with t.me URL (no https)", () => {
|
||||
const job = createIsolatedAgentTurnJob("job-telegram-tme-no-https", {
|
||||
mode: "announce",
|
||||
channel: "telegram",
|
||||
to: "t.me/mychannel",
|
||||
});
|
||||
|
||||
expect(() => applyJobPatch(job, { enabled: true })).not.toThrow();
|
||||
});
|
||||
|
||||
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 {
|
||||
|
||||
@@ -83,6 +83,23 @@ export function assertSupportedJobSpec(job: Pick<CronJob, "sessionTarget" | "pay
|
||||
}
|
||||
}
|
||||
|
||||
const TELEGRAM_TME_URL_REGEX = /^https?:\/\/t\.me\/|t\.me\//i;
|
||||
const TELEGRAM_SLASH_TOPIC_REGEX = /^-?\d+\/\d+$/;
|
||||
|
||||
function validateTelegramDeliveryTarget(to: string | undefined): string | undefined {
|
||||
if (!to) {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = to.trim();
|
||||
if (TELEGRAM_TME_URL_REGEX.test(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
if (TELEGRAM_SLASH_TOPIC_REGEX.test(trimmed)) {
|
||||
return `Invalid Telegram delivery target "${to}". Use colon (:) as delimiter for topics, not slash. Valid formats: -1001234567890, -1001234567890:123, -1001234567890:topic:123, @username, https://t.me/username`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function assertDeliverySupport(job: Pick<CronJob, "sessionTarget" | "delivery">) {
|
||||
if (!job.delivery) {
|
||||
return;
|
||||
@@ -98,6 +115,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) {
|
||||
|
||||
Reference in New Issue
Block a user