Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(telegram): Enforce incoming message validation #332

Merged
merged 4 commits into from
Jun 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions e2e/dm.en.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {
injectTestDependencies,
} from "./helpers/dependencies.js";
import { Pool as MockPool } from "../src/db/__mocks__/pg.js";
import { TgChatType } from "../src/telegram/api/types.js";
import { LanguageCode } from "../src/recognition/types.js";
import type { TgChatType } from "../src/telegram/api/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -36,7 +37,7 @@ const enableSSL = false;
const appPort = 3100;
const dbPort = appPort + 1;

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

let chatType: TgChatType;
Expand Down
32 changes: 4 additions & 28 deletions e2e/dm.ru.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {
injectTestDependencies,
} from "./helpers/dependencies.js";
import { Pool as MockPool } from "../src/db/__mocks__/pg.js";
import { TgChatType } from "../src/telegram/api/types.js";
import { LanguageCode } from "../src/recognition/types.js";
import type { TgChatType } from "../src/telegram/api/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -36,7 +37,7 @@ const enableSSL = false;
const appPort = 3200;
const dbPort = appPort + 1;

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

let testLangId: LanguageCode;
Expand Down Expand Up @@ -514,31 +515,6 @@ describe("[russian language]", () => {
]);
});

it("responds on a voice message with broken duration", () => {
const voiceFileId = "some-file-id";
tgMessage.setVoice(
testMessageId,
voiceFileId,
"123" as unknown as number
);
const statModel = mockGetBotStatItem(
testPool,
tgMessage.chatId,
botStat.langId,
botStat
);

return Promise.all([
sendTelegramMessage(host, bot, tgMessage),
mockTgReceiveMessage(
telegramServer,
tgMessage.chatId,
statModel.langId,
LabelId.NoContent
),
]);
});

it("converts audio into text (it fits 90 sec limit)", () => {
const voiceFileId = "some-file-id";
const voiceFileDuration = 89;
Expand Down
3 changes: 2 additions & 1 deletion e2e/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "../src/testUtils/dependencies.js";
import { injectTestDependencies } from "./helpers/dependencies.js";
import { Pool as MockPool } from "../src/db/__mocks__/pg.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -30,7 +31,7 @@ const enableSSL = false;
const appPort = 3600;
const dbPort = appPort + 1;

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

let testPool: MockPool;
Expand Down
7 changes: 4 additions & 3 deletions e2e/fund.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {
InjectedTestFn,
injectTestDependencies,
} from "./helpers/dependencies.js";
import { TgChatType } from "../src/telegram/api/types.js";
import { LanguageCode } from "../src/recognition/types.js";
import type { TgChatType } from "../src/telegram/api/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -47,7 +48,7 @@ const dbConfig = {
const paymentToken = "stripe-token";
const testPool = new MockPool(dbConfig);

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

let chatType: TgChatType;
Expand Down
37 changes: 4 additions & 33 deletions e2e/group.en.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import {
injectTestDependencies,
} from "./helpers/dependencies.js";
import { Pool as MockPool } from "../src/db/__mocks__/pg.js";
import { TgChatType } from "../src/telegram/api/types.js";
import type { TgChatType } from "../src/telegram/api/types.js";
import { VoiceConverterOptions } from "../src/recognition/types.js";
import { LanguageCode } from "../src/recognition/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -45,7 +46,7 @@ const dbConfig = {
};
const testPool = new MockPool(dbConfig);

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

// Define dependencies
Expand Down Expand Up @@ -719,23 +720,6 @@ describe("[default language - english]", () => {
});
});

it("keeps calm on a voice file with broken duration", (done) => {
const voiceFileId = "some-file-id";
tgMessage.setVoice(
testMessageId,
voiceFileId,
"-34" as unknown as number
);

mockTgReceiveUnexpectedMessage(telegramServer, done);

sendTelegramMessage(host, bot, tgMessage).then(() => {
expect(nock.pendingMocks()).toHaveLength(1);
nock.cleanAll();
return done && done();
});
});

it("keeps calm on an audio file with wrong mime type", (done) => {
const voiceFileId = "some-file-id";
const voiceFileDuration = 59;
Expand All @@ -755,19 +739,6 @@ describe("[default language - english]", () => {
});
});

it("keeps calm on an audio file with broken duration", (done) => {
const voiceFileId = "some-file-id";
tgMessage.setAudio(testMessageId, voiceFileId, "-1" as unknown as number);

mockTgReceiveUnexpectedMessage(telegramServer, done);

sendTelegramMessage(host, bot, tgMessage).then(() => {
expect(nock.pendingMocks()).toHaveLength(1);
nock.cleanAll();
return done && done();
});
});

it("converts audio into text (it fits 90 sec limit) - no username", () => {
const voiceFileId = "some-file-id";
const voiceFileDuration = 89;
Expand Down
7 changes: 4 additions & 3 deletions e2e/group.ru.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import {
injectTestDependencies,
} from "./helpers/dependencies.js";
import { Pool as MockPool } from "../src/db/__mocks__/pg.js";
import { TgChatType } from "../src/telegram/api/types.js";
import type { TgChatType } from "../src/telegram/api/types.js";
import { VoiceConverterOptions } from "../src/recognition/types.js";
import { LanguageCode } from "../src/recognition/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -45,7 +46,7 @@ const dbConfig = {
};
const testPool = new MockPool(dbConfig);

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

// Define dependencies
Expand Down
3 changes: 2 additions & 1 deletion e2e/health.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
InjectedTestFn,
injectTestDependencies,
} from "./helpers/dependencies.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand Down Expand Up @@ -51,7 +52,7 @@ let localhostUrl: string;

const path = "/health";

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

describe("[health]", () => {
Expand Down
4 changes: 2 additions & 2 deletions e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { readFileSync } from "node:fs";
import { resolve as resolvePath } from "node:path";
import { fileURLToPath } from "node:url";
import { LanguageCode } from "../src/recognition/types.js";
import type { LanguageCode } from "../src/recognition/types.js";
import { LabelId } from "../src/text/labels.js";
import { randomIntFromInterval } from "../src/common/timer.js";
import {
import type {
TgCallbackQuery,
TgChatType,
TgMessage,
Expand Down
3 changes: 2 additions & 1 deletion e2e/lifecycle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
InjectedTestFn,
} from "./helpers/dependencies.js";
import { HealthSsl, HealthStatus } from "../src/server/types.js";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand All @@ -36,7 +37,7 @@ const dbPort = appPort + 1;

const path = "/lifecycle";

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

let server: InstanceType<InjectedFn["ExpressServer"]>;
Expand Down
2 changes: 1 addition & 1 deletion e2e/requests/db/botStat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from "@jest/globals";
import { LanguageCode } from "../../../src/recognition/types.js";
import type { LanguageCode } from "../../../src/recognition/types.js";
import { BotStatRecordModel } from "../../helpers.js";
import { randomIntFromInterval } from "../../../src/common/timer.js";
import { Pool as MockPool } from "../../../src/db/__mocks__/pg.js";
Expand Down
19 changes: 12 additions & 7 deletions e2e/requests/telegram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import {
TelegramMessageModel,
} from "../helpers.js";
import { TextModel } from "../../src/text/index.js";
import { LanguageCode } from "../../src/recognition/types.js";
import type { LanguageCode } from "../../src/recognition/types.js";
import { LabelId } from "../../src/text/labels.js";
import { botCommands } from "../../src/telegram/data.js";
import { flattenPromise } from "../../src/common/helpers.js";
import { TelegramButtonModel } from "../../src/telegram/types.js";
import { parseDonationPayload } from "../../src/telegram/helpers.js";
import type { TgUpdate } from "../../src/telegram/api/types.js";

const text = new TextModel();
const telegramApiResponseOk = JSON.stringify({ ok: true });
Expand All @@ -30,11 +31,13 @@ export const sendTelegramMessage = (
bot: TelegramBotModel,
msg: TelegramMessageModel
): Promise<void> => {
const payload: TgUpdate = {
update_id: 12434,
message: msg.toApi(),
};
return host
.post(bot.getPath())
.send({
message: msg.toApi(),
})
.send(payload)
.then((res) => {
expect(res.status).toBe(200);
expect(res.body).toEqual({});
Expand All @@ -46,11 +49,13 @@ export const sendTelegramCallbackMessage = (
bot: TelegramBotModel,
msg: TelegramMessageModel
): Promise<void> => {
const payload: TgUpdate = {
update_id: 12434,
callback_query: msg.toCallbackApi(),
};
return host
.post(bot.getPath())
.send({
callback_query: msg.toCallbackApi(),
})
.send(payload)
.then((res) => {
expect(res.status).toBe(200);
expect(res.body).toEqual({});
Expand Down
3 changes: 2 additions & 1 deletion e2e/scheduler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from "../src/testUtils/dependencies.js";
import { HealthDto, HealthSsl, HealthStatus } from "../src/server/types.js";
import { SpiedFunction } from "jest-mock";
import type { VoidPromise } from "../src/common/types.js";

jest.unstable_mockModule(
"../src/logger/index",
Expand Down Expand Up @@ -64,7 +65,7 @@ let waiter: InstanceType<InjectedFn["WaiterForCalls"]>;
let hostUrl: string;
const enableSSL = false;

let stopHandler: () => Promise<void> = () =>
let stopHandler: VoidPromise = () =>
Promise.reject(new Error("Server did not start"));

describe("[uptime daemon]", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/common/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Logger } from "../logger/index.js";
import { sleepFor } from "./timer.js";
import { LanguageCode } from "../recognition/types.js";
import type { LanguageCode } from "../recognition/types.js";

const logger = new Logger("run-retry");

Expand Down
2 changes: 2 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type Prettify<T> = {
[key in keyof T]: T[key];
};

export type VoidPromise = () => Promise<void>;
2 changes: 1 addition & 1 deletion src/db/usages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "@jest/globals";
import { Pool as MockPool } from "./__mocks__/pg.js";
import { injectDependencies, InjectedFn } from "../testUtils/dependencies.js";
import { LanguageCode } from "../recognition/types.js";
import type { LanguageCode } from "../recognition/types.js";

jest.unstable_mockModule(
"../logger/index",
Expand Down
2 changes: 1 addition & 1 deletion src/donate/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LanguageCode } from "../recognition/types.js";
import type { LanguageCode } from "../recognition/types.js";

export interface PaymentService {
readonly isReady: boolean;
Expand Down
3 changes: 2 additions & 1 deletion src/process/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { onExit } from "signal-exit";
import { Logger } from "../logger/index.js";
import type { VoidPromise } from "../common/types.js";

const logger = new Logger("stop-listener");

export class StopListener {
private triggers: Array<() => Promise<void>> = [];
private triggers: VoidPromise[] = [];

constructor() {
onExit((code, signal) => {
Expand Down
5 changes: 3 additions & 2 deletions src/scheduler/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Logger } from "../logger/index.js";
import type { VoidPromise } from "../common/types.js";

const logger = new Logger("daemon");

Expand All @@ -7,7 +8,7 @@ export class ScheduleDaemon<TickData> {
private readonly printId: string;
private handler: NodeJS.Timeout | null = null;
private shouldStop?: (data: TickData) => boolean;
private onFinish?: () => Promise<void>;
private onFinish?: VoidPromise;

public get isRunning(): boolean {
return !!this.handler;
Expand All @@ -22,7 +23,7 @@ export class ScheduleDaemon<TickData> {

public setStopHandler(
shouldStop: (data: TickData) => boolean,
onFinish: () => Promise<void>
onFinish: VoidPromise
): this {
this.shouldStop = shouldStop;
this.onFinish = onFinish;
Expand Down
Loading