From c4c1362432f16e0a5fed1145fcbdbca63a30200a Mon Sep 17 00:00:00 2001 From: Kuchizu Date: Sat, 31 Oct 2026 23:53:19 +0300 Subject: [PATCH 1/3] Escape Markdown in Telegram notification titles --- workers/telegram/src/templates/event.ts | 11 ++++++++++- workers/telegram/src/templates/several-events.ts | 3 ++- workers/telegram/tests/provider.test.ts | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/workers/telegram/src/templates/event.ts b/workers/telegram/src/templates/event.ts index 46da0cba..b3677eea 100644 --- a/workers/telegram/src/templates/event.ts +++ b/workers/telegram/src/templates/event.ts @@ -1,5 +1,14 @@ import type { EventsTemplateVariables, TemplateEventData } from 'hawk-worker-sender/types/template-variables'; +/** + * Escape characters that break Telegram legacy Markdown parsing + * + * @param text - raw text + */ +export function escapeMarkdown(text: string): string { + return text.replace(/[_*`[]/g, '\\$&'); +} + /** * Return tpl with data substitutions * @@ -17,7 +26,7 @@ export default function render(tplData: EventsTemplateVariables): string { location = '\n' + '```\n' + location + '\n```'; // markdown code block } - return ''.concat(event.payload.title, + return ''.concat(escapeMarkdown(event.payload.title), '\n', location || '', '\n', diff --git a/workers/telegram/src/templates/several-events.ts b/workers/telegram/src/templates/several-events.ts index 06325564..2283f156 100644 --- a/workers/telegram/src/templates/several-events.ts +++ b/workers/telegram/src/templates/several-events.ts @@ -1,4 +1,5 @@ import type { EventsTemplateVariables } from 'hawk-worker-sender/types/template-variables'; +import { escapeMarkdown } from './event'; /** * Return tpl with data substitutions @@ -10,7 +11,7 @@ export default function render(tplData: EventsTemplateVariables): string { let message = tplData.events.length + ' new events\n\n'; tplData.events.forEach(({ event, newCount }) => { - message += `(${newCount}) ${event.payload.title} \n`; + message += `(${newCount}) ${escapeMarkdown(event.payload.title)} \n`; }); message += `\n[View all events](${projectUrl}) | *${tplData.project.name}*`; diff --git a/workers/telegram/tests/provider.test.ts b/workers/telegram/tests/provider.test.ts index 51ba45c4..842a1861 100644 --- a/workers/telegram/tests/provider.test.ts +++ b/workers/telegram/tests/provider.test.ts @@ -2,7 +2,7 @@ import { EventNotification, SeveralEventsNotification } from 'hawk-worker-sender import { DecodedGroupedEvent, ProjectDBScheme } from '@hawk.so/types'; import TelegramProvider from 'hawk-worker-telegram/src/provider'; import templates from '../src/templates'; -import EventTpl from '../src/templates/event'; +import EventTpl, { escapeMarkdown } from '../src/templates/event'; import { ObjectId } from 'mongodb'; /** @@ -13,6 +13,11 @@ describe('TelegramProvider', () => { * Check templates rendering */ describe('templates', () => { + it('should escape Markdown entities in event titles', () => { + expect(escapeMarkdown('No such file: /home/market_static/*.csv [`x`]')) + .toBe('No such file: /home/market\\_static/\\*.csv \\[\\`x\\`]'); + }); + /** * Check that rendering of a single event message works without errors */ From f9a688ae669bd6e2b54f15394c6d34a023885580 Mon Sep 17 00:00:00 2001 From: Kuchizu Date: Sat, 31 Oct 2026 23:58:47 +0300 Subject: [PATCH 2/3] Pin current date in paymaster tests to avoid month overflow --- workers/paymaster/tests/index.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/workers/paymaster/tests/index.test.ts b/workers/paymaster/tests/index.test.ts index 23a1bd69..127f3401 100644 --- a/workers/paymaster/tests/index.test.ts +++ b/workers/paymaster/tests/index.test.ts @@ -543,7 +543,7 @@ describe('PaymasterWorker', () => { /** * Arrange */ - const currentDate = new Date(); + const currentDate = new Date('2005-12-22'); const paidUntil = new Date(currentDate.getTime()); paidUntil.setMonth(paidUntil.getMonth() + 3); @@ -590,7 +590,7 @@ describe('PaymasterWorker', () => { /** * Arrange */ - const currentDate = new Date(); + const currentDate = new Date('2005-12-22'); const paidUntil = new Date(currentDate.getTime()); paidUntil.setDate(paidUntil.getDate() + 1); @@ -647,7 +647,7 @@ describe('PaymasterWorker', () => { /** * Arrange */ - const currentDate = new Date(); + const currentDate = new Date('2005-12-22'); const lastChargeDate = new Date(currentDate.getTime()); lastChargeDate.setMonth(lastChargeDate.getMonth() - 1); // Set last charge date to 1 month ago From d743b2735f027f24ef5d325cbba5777c57172f38 Mon Sep 17 00:00:00 2001 From: Kuchizu Date: Mon, 2 Nov 2026 23:53:44 +0300 Subject: [PATCH 3/3] Escape project name in Telegram notification footer --- workers/telegram/src/templates/event.ts | 2 +- workers/telegram/src/templates/several-events.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/telegram/src/templates/event.ts b/workers/telegram/src/templates/event.ts index b3677eea..8213e5b8 100644 --- a/workers/telegram/src/templates/event.ts +++ b/workers/telegram/src/templates/event.ts @@ -30,6 +30,6 @@ export default function render(tplData: EventsTemplateVariables): string { '\n', location || '', '\n', - `[View details](${eventURL}) `, `| *${tplData.project.name}*`, ` | ${eventInfo.newCount} new (${eventInfo.event.totalCount} total)` + `[View details](${eventURL}) `, `| ${escapeMarkdown(tplData.project.name)}`, ` | ${eventInfo.newCount} new (${eventInfo.event.totalCount} total)` ); } diff --git a/workers/telegram/src/templates/several-events.ts b/workers/telegram/src/templates/several-events.ts index 2283f156..bb138f15 100644 --- a/workers/telegram/src/templates/several-events.ts +++ b/workers/telegram/src/templates/several-events.ts @@ -14,7 +14,7 @@ export default function render(tplData: EventsTemplateVariables): string { message += `(${newCount}) ${escapeMarkdown(event.payload.title)} \n`; }); - message += `\n[View all events](${projectUrl}) | *${tplData.project.name}*`; + message += `\n[View all events](${projectUrl}) | ${escapeMarkdown(tplData.project.name)}`; return message; }