diff --git a/packages/server-client/__tests__/file.test.ts b/packages/server-client/__tests__/file.test.ts index 2ea6d01e..6c606c97 100644 --- a/packages/server-client/__tests__/file.test.ts +++ b/packages/server-client/__tests__/file.test.ts @@ -1,6 +1,5 @@ import nock from 'nock'; import { FileClient } from '../lib'; -import { BASE_URL } from './common'; import { Auth } from '@vonage/auth'; import { mkdirSync, readFileSync, existsSync } from 'fs'; import { rm } from 'fs/promises'; @@ -10,7 +9,7 @@ const FILE_PATH = `${process.cwd()}/test-path`; describe('File tests', () => { let client: FileClient; - let scope: nock.Scope; + // let scope: nock.Scope; beforeEach(() => { if (!existsSync(FILE_PATH)) { @@ -21,11 +20,6 @@ describe('File tests', () => { new Auth(keyAuth), ); - scope = nock(BASE_URL, { - reqheaders: { - authorization: (value) => value.startsWith('Bearer '), - }, - }).persist(); }); afterEach(async () => { @@ -37,18 +31,48 @@ describe('File tests', () => { }); }); - test('Can download file with url', async () => { + test('Can download file with vonage domain', async () => { const content = 'Ford, I think I\'m a couch'; const file = `${FILE_PATH}/my-file.txt`; - scope - .get('/v1/files/00000000-0000-0000-0000-000000000001') + nock('https://api.vonage.com', { + reqheaders: { + authorization: (value) => value.startsWith('Bearer '), + }, + }).persist() + .get('/v3/files/00000000-0000-0000-0000-000000000001') .reply(200, content); expect(existsSync(file)).toBeFalsy(); expect( await client.downloadFile( - 'https://api.nexmo.com/v1/files/00000000-0000-0000-0000-000000000001', + 'https://api.vonage.com/v3/files/00000000-0000-0000-0000-000000000001', + file, + ), + ).toBeUndefined(); + + expect(existsSync(file)).toBeTruthy(); + expect(readFileSync(file).toString()).toEqual(content); + expect(nock.isDone()).toBeTruthy(); + }); + + test('Can download file with nexmo domain', async () => { + const content = 'Ford, I think I\'m a couch'; + const file = `${FILE_PATH}/my-file.txt`; + nock('https://api.nexmo.com', { + reqheaders: { + authorization: (value) => value.startsWith('Bearer '), + }, + }) + .persist() + .get('/v3/files/00000000-0000-0000-0000-000000000001') + .reply(200, content); + + expect(existsSync(file)).toBeFalsy(); + + expect( + await client.downloadFile( + 'https://api.nexmo.com/v3/files/00000000-0000-0000-0000-000000000001', file, ), ).toBeUndefined(); @@ -61,8 +85,13 @@ describe('File tests', () => { test('Can download file with id', async () => { const content = 'Ford, I think I\'m a couch'; const file = `${FILE_PATH}/my-file.txt`; - scope - .get('/v1/files/00000000-0000-0000-0000-000000000001') + nock('https://api.nexmo.com', { + reqheaders: { + authorization: (value) => value.startsWith('Bearer '), + }, + }) + .persist() + .get('/v3/files/00000000-0000-0000-0000-000000000001') .reply(200, content); expect(existsSync(file)).toBeFalsy(); @@ -83,11 +112,16 @@ describe('File tests', () => { const file2 = `${FILE_PATH}/my-file2.txt`; const content2 = 'I know how you feel.'; - scope - .get('/v1/files/00000000-0000-0000-0000-000000000001') + nock('https://api.nexmo.com', { + reqheaders: { + authorization: (value) => value.startsWith('Bearer '), + }, + }) + .persist() + .get('/v3/files/00000000-0000-0000-0000-000000000001') .delay(1000) .reply(200, content) - .get('/v1/files/00000000-0000-0000-0000-000000000002') + .get('/v3/files/00000000-0000-0000-0000-000000000002') .delay(800) .reply(200, content2); @@ -107,4 +141,17 @@ describe('File tests', () => { expect(nock.isDone()).toBeTruthy(); }); + + test('Will throw error when url is not nexmo or vonage domain', async () => { + const file = `${FILE_PATH}/my-file.txt`; + + expect(existsSync(file)).toBeFalsy(); + + await expect( + client.downloadFile('https://example.com', file), + ).rejects.toThrow('The domain https://example.com/ is invalid for file download. Only vonage.com and nexmo.com are allowed.'); + + expect(existsSync(file)).toBeFalsy(); + expect(nock.isDone()).toBeTruthy(); + }); }); diff --git a/packages/server-client/lib/fileClient.ts b/packages/server-client/lib/fileClient.ts index 562117c8..99dac852 100644 --- a/packages/server-client/lib/fileClient.ts +++ b/packages/server-client/lib/fileClient.ts @@ -30,20 +30,30 @@ export class FileClient extends Client { */ async downloadFile(file: string, path: string): Promise { log(`Downloading file: ${file}`); - let fileId = file; + let fileURL: URL; try { - const fileURL = new URL(file); - fileId = fileURL.pathname.split('/').pop() || ''; + fileURL = new URL(file); + log('Downloading file by URL'); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (_) { - log('Not a url'); + log('Downloading file by ID'); + fileURL = new URL(`${this.config.apiHost}/v3/files/${file}`); + } + + log(`File URL ${fileURL}`); + const hostname = fileURL.hostname.split('.').slice(-2).join('.'); + + if (!['vonage.com', 'nexmo.com'].includes(hostname)) { + throw new Error( + `The domain ${fileURL} is invalid for file download. Only vonage.com and nexmo.com are allowed.`, + ); } - log(`File Id ${fileId}`); const request = await this.addAuthenticationToRequest({ - url: `${this.config.apiHost}/v1/files/${fileId}`, + url: fileURL.toString(), method: HTTPMethods.GET, } as VetchOptions); + log('File download request', request); const response = await fetch(