From f264b02b250accd77b58901fd7cb9b205507611d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 22 Dec 2022 01:10:54 +0100 Subject: [PATCH] check for max buffer length plus testmodus shifted to unittest --- backend/src/federation/index.test.ts | 324 +++++++++++++++++++++++++-- backend/src/federation/index.ts | 70 ++---- 2 files changed, 323 insertions(+), 71 deletions(-) diff --git a/backend/src/federation/index.test.ts b/backend/src/federation/index.test.ts index 01eae77e1..813f6f155 100644 --- a/backend/src/federation/index.test.ts +++ b/backend/src/federation/index.test.ts @@ -207,13 +207,13 @@ describe('federation', () => { }) describe('with receiving array of string-arrays', () => { - beforeEach(() => { + beforeEach(async () => { jest.clearAllMocks() const strArray: string[][] = [ [`api`, `url`, `invalid type in array test`], [`wrong`, `api`, `url`], ] - socketEventMocks.data(Buffer.from(strArray.toString())) + await socketEventMocks.data(Buffer.from(strArray.toString())) }) it('logs the received data', () => { @@ -232,7 +232,7 @@ describe('federation', () => { describe('with receiving JSON-Array with too much entries', () => { let jsonArray: { api: string; url: string }[] - beforeEach(() => { + beforeEach(async () => { jest.clearAllMocks() jsonArray = [ { api: 'v1_0', url: 'too much versions at the same time test' }, @@ -242,7 +242,7 @@ describe('federation', () => { { api: 'v1_0', url: 'url5' }, { api: 'v1_0', url: 'url6' }, ] - socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) }) it('logs the received data', () => { @@ -260,10 +260,10 @@ describe('federation', () => { }) }) - describe('with receiving wrong but tolerated property test', () => { + describe('with receiving wrong but tolerated property data', () => { let jsonArray: any[] let result: DbCommunity[] = [] - beforeEach(async () => { + beforeAll(async () => { jest.clearAllMocks() jsonArray = [ { @@ -277,8 +277,7 @@ describe('federation', () => { wrong: 'wrong but tolerated property test', }, ] - console.log(`jsonArray ${JSON.stringify(jsonArray)}`) - socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) result = await DbCommunity.find() }) @@ -286,12 +285,6 @@ describe('federation', () => { await cleanDB() }) - it('logs the received data', () => { - expect(logger.info).toBeCalledWith( - 'data: [{"wrong":"wrong but tolerated property test","api":"v1_0","url":"url1"},{"api":"v2_0","url":"url2","wrong":"wrong but tolerated property test"}]', - ) - }) - it('has two Communty entries in database', () => { expect(result).toHaveLength(2) }) @@ -329,6 +322,309 @@ describe('federation', () => { }) }) + describe('with receiving data but missing api property', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { test1: 'missing api proterty test', url: 'any url definition as string' }, + { api: 'some api', test2: 'missing url property test' }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`, + ) + }) + }) + + describe('with receiving data but missing url property', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { api: 'some api', test2: 'missing url property test' }, + { test1: 'missing api proterty test', url: 'any url definition as string' }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`, + ) + }) + }) + + describe('with receiving data but wrong type of api property', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { api: 1, url: 'wrong property type tests' }, + { api: 'urltyptest', url: 2 }, + { api: 1, url: 2 }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`, + ) + }) + }) + + describe('with receiving data but wrong type of url property', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { api: 'urltyptest', url: 2 }, + { api: 1, url: 'wrong property type tests' }, + { api: 1, url: 2 }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`, + ) + }) + }) + + describe('with receiving data but wrong type of both properties', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { api: 1, url: 2 }, + { api: 'urltyptest', url: 2 }, + { api: 1, url: 'wrong property type tests' }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received invalid apiVersion-Definition: ${JSON.stringify(jsonArray[0])}`, + ) + }) + }) + + describe('with receiving data but too long api string', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { api: 'toolong api', url: 'some valid url' }, + { + api: 'valid api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + { + api: 'toolong api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received apiVersion with content longer than max length: ${JSON.stringify( + jsonArray[0], + )}`, + ) + }) + }) + + describe('with receiving data but too long url string', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { + api: 'api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + { api: 'toolong api', url: 'some valid url' }, + { + api: 'toolong api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + + it('logs a warning of invalid apiVersion-Definition', () => { + expect(logger.warn).toBeCalledWith( + `received apiVersion with content longer than max length: ${JSON.stringify( + jsonArray[0], + )}`, + ) + }) + }) + + describe('with receiving data but both properties with too long strings', () => { + let jsonArray: any[] + beforeEach(async () => { + jest.clearAllMocks() + jsonArray = [ + { + api: 'toolong api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + { + api: 'api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + { api: 'toolong api', url: 'some valid url' }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + }) + + it('logs the received data', () => { + expect(logger.info).toBeCalledWith(`data: ${JSON.stringify(jsonArray)}`) + }) + }) + + describe('with receiving data of exact max allowed properties length', () => { + let jsonArray: any[] + let result: DbCommunity[] = [] + beforeAll(async () => { + jest.clearAllMocks() + jsonArray = [ + { + api: 'valid api', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + { + api: 'api', + url: 'this is a too long url definition with exact one character more than the allowed two hundert and fiftyfive characters. and here begins the fill characters with no sense of content menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmic', + }, + { api: 'toolong api', url: 'some valid url' }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + result = await DbCommunity.find() + }) + + afterAll(async () => { + await cleanDB() + }) + + it('has one Communty entry in database', () => { + expect(result).toHaveLength(1) + }) + + it(`has an entry with max content length for api and url`, () => { + expect(result).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + publicKey: expect.any(Buffer), + apiVersion: 'valid api', + endPoint: + 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + lastAnnouncedAt: expect.any(Date), + createdAt: expect.any(Date), + updatedAt: null, + }), + ]), + ) + }) + }) + + /* + describe('with receiving data of exact max allowed buffer length', () => { + let jsonArray: any[] + let result: DbCommunity[] = [] + beforeAll(async () => { + jest.clearAllMocks() + jsonArray = [ + { + api: 'valid api1', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + { + api: 'valid api2', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + { + api: 'valid api3', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + { + api: 'valid api4', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + { + api: 'valid api5', + url: 'this is a valid url definition with exact the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmiga menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofriertesmich', + }, + ] + await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) + result = await DbCommunity.find() + }) + + afterAll(async () => { + // await cleanDB() + }) + + it('has five Communty entries in database', () => { + expect(result).toHaveLength(5) + }) + + it(`has an entry with max content length for api and url`, () => { + expect(result).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + publicKey: expect.any(Buffer), + apiVersion: 'valid api1', + endPoint: + 'this is a valid url definition with the max allowed length of two hundert and fiftyfive characters. and here begins the fill characters with no sense of content kuhwarmigasmilchdirek menschhabicheinhungerdassichnichtweiswoichheutnachtschlafensollsofrierts', + lastAnnouncedAt: expect.any(Date), + createdAt: expect.any(Date), + updatedAt: null, + }), + ]), + ) + }) + }) + */ + describe('with proper data', () => { let result: DbCommunity[] = [] beforeAll(async () => { diff --git a/backend/src/federation/index.ts b/backend/src/federation/index.ts index fb2817262..4e3dca9de 100644 --- a/backend/src/federation/index.ts +++ b/backend/src/federation/index.ts @@ -26,46 +26,6 @@ type CommunityApi = { export const startDHT = async (topic: string): Promise => { try { - let testModeCtrl = 0 - const testModeData = [ - `hello here is a new community and i don't know how to communicate with you`, - [`invalid type test`, `api`, `url`], - [ - [`api`, `url`, `invalid type in array test`], - [`wrong`, `api`, `url`], - ], - [ - { api: ApiVersionType.V1_0, url: 'too much versions at the same time test' }, - { api: ApiVersionType.V1_0, url: 'url2' }, - { api: ApiVersionType.V1_0, url: 'url3' }, - { api: ApiVersionType.V1_0, url: 'url4' }, - { api: ApiVersionType.V1_0, url: 'url5' }, - { api: ApiVersionType.V2_0, url: 'url6' }, - ], - [ - { wrong: 'wrong but tolerated property test', api: ApiVersionType.V1_0, url: 'url1' }, - { api: ApiVersionType.V2_0, url: 'url2', wrong: 'wrong but tolerated property test' }, - ], - [ - { test1: 'missing api proterty test', url: 'any url definition as string' }, - { api: 'some api', test2: 'missing url property test' }, - ], - [ - { api: 1, url: 'wrong property type tests' }, - { api: 'urltyptest', url: 2 }, - { api: 1, url: 2 }, - ], - [ - { - api: ApiVersionType.V1_0, - url: CONFIG.FEDERATION_COMMUNITY_URL + ApiVersionType.V1_0, - }, - { - api: ApiVersionType.V2_0, - url: CONFIG.FEDERATION_COMMUNITY_URL + ApiVersionType.V2_0, - }, - ], - ] const TOPIC = DHT.hash(Buffer.from(topic)) const keyPair = DHT.keyPair(getSeed()) logger.info(`keyPairDHT: publicKey=${keyPair.publicKey.toString('hex')}`) @@ -89,12 +49,19 @@ export const startDHT = async (topic: string): Promise => { socket.on('data', async (data: Buffer) => { try { + // console.log(`data.len=${data.length}, ${data.toString('ascii')}`) + if (data.length > 1426) { + logger.warn( + `received more than max allowed length of data buffer: ${data.length} / 1426`, + ) + return + } logger.info(`data: ${data.toString('ascii')}`) const recApiVersions: CommunityApi[] = JSON.parse(data.toString('ascii')) // TODO better to introduce the validation by https://github.com/typestack/class-validato if (recApiVersions && Array.isArray(recApiVersions) && recApiVersions.length < 5) { - recApiVersions.forEach(async (recApiVersion) => { + for (const recApiVersion of recApiVersions) { if ( !recApiVersion.api || typeof recApiVersion.api !== 'string' || @@ -102,7 +69,7 @@ export const startDHT = async (topic: string): Promise => { typeof recApiVersion.url !== 'string' ) { logger.warn( - `received invalid apiVersion-Definition:${JSON.stringify(recApiVersion)}`, + `received invalid apiVersion-Definition: ${JSON.stringify(recApiVersion)}`, ) // in a forEach-loop use return instead of continue return @@ -110,7 +77,7 @@ export const startDHT = async (topic: string): Promise => { // TODO better to introduce the validation on entity-Level by https://github.com/typestack/class-validator if (recApiVersion.api.length > 10 || recApiVersion.url.length > 255) { logger.warn( - `received apiVersion with content longer than max length:${JSON.stringify( + `received apiVersion with content longer than max length: ${JSON.stringify( recApiVersion, )}`, ) @@ -135,8 +102,9 @@ export const startDHT = async (topic: string): Promise => { overwrite: ['end_point', 'last_announced_at'], }) .execute() + // console.log(`upserted...`, variables) logger.info(`federation community upserted successfully...`) - }) + } } else { logger.warn( `received totaly wrong or too much apiVersions-Definition JSON-String:${JSON.stringify( @@ -208,19 +176,7 @@ export const startDHT = async (topic: string): Promise => { }) socket.on('open', function () { - if (CONFIG.FEDERATION_DHT_TEST_SOCKET === true) { - logger.info( - `test-mode for socket handshake is activated...Test:(${testModeCtrl + 1}/${ - testModeData.length - })`, - ) - socket.write(Buffer.from(JSON.stringify(testModeData[testModeCtrl++]))) - if (testModeCtrl >= testModeData.length) { - testModeCtrl = 0 - } - } else { - socket.write(Buffer.from(JSON.stringify(ownApiVersions))) - } + socket.write(Buffer.from(JSON.stringify(ownApiVersions))) successfulRequests.push(remotePubKey) }) })