From ee4e0094a01d11a648bad75a637f642f409c74b3 Mon Sep 17 00:00:00 2001 From: Xavier Jouslin de Noray <xavier.jouslindenoray@savoirfairelinux.com> Date: Thu, 21 Dec 2023 08:08:11 -0500 Subject: [PATCH] Watcher: avoid doubles in plugins Change-Id: I08975a409e7e182df3e9726de35e0ef11d8ac818 --- src/interfaces/plugins.ts | 1 + src/services/certificate.manager.service.ts | 4 ++ src/services/plugins.manager.service.ts | 44 +++++++++++++++++++-- tests/plugins.manager.test.ts | 43 ++++++++++++++++++-- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/interfaces/plugins.ts b/src/interfaces/plugins.ts index 5e67d47..828a2d8 100644 --- a/src/interfaces/plugins.ts +++ b/src/interfaces/plugins.ts @@ -25,4 +25,5 @@ export interface Plugins { arches: string[]; timestamp: string; author: string; + signature: Buffer; } diff --git a/src/services/certificate.manager.service.ts b/src/services/certificate.manager.service.ts index c3b956d..20eb1f8 100644 --- a/src/services/certificate.manager.service.ts +++ b/src/services/certificate.manager.service.ts @@ -62,6 +62,10 @@ export class CertificateManager { return await this.readCRL(process.env.DATA_DIRECTORY + '/' + 'sfl.crl'); } + verifySignature(signature: Buffer, checkSignature: Buffer): boolean { + return Buffer.compare(signature, checkSignature) === 0; + } + private async readCertificate( path: string, file: string diff --git a/src/services/plugins.manager.service.ts b/src/services/plugins.manager.service.ts index ffe6caa..b3056d1 100644 --- a/src/services/plugins.manager.service.ts +++ b/src/services/plugins.manager.service.ts @@ -44,9 +44,22 @@ export class PluginsManager { return; } // eslint-disable-next-line - watcher.on('change', () => { - this.setPlugins().catch(e => { - console.log(e); + watcher.on('change', (_, filename: string | Buffer) => { + const filePath = + // eslint-disable-next-line + __dirname + + '/../..' + + process.env.DATA_DIRECTORY + + '/' + + filename.toString(); + // check if the file signature is valid and not in the list + this.checkPluginAlreadyPull(filePath).then(isValid => { + if (!isValid) { + return; + } + this.setPlugins().catch(e => { + console.log(e); + }); }); }); } @@ -55,7 +68,7 @@ export class PluginsManager { async getPlugins( arch: string, lang: string - ): Promise<Array<Omit<Plugins, 'arches'>>> { + ): Promise<Array<Omit<Omit<Plugins, 'arches'>, 'signature'>>> { if (this.plugins.length === 0) { await this.setPlugins(); } @@ -206,6 +219,7 @@ export class PluginsManager { timestamp, author: issuer.CN, background: plugin.background, + signature: await this.fileManager.readArchive(path, 'signatures.sig'), }; } catch (e) { console.error(e); @@ -296,4 +310,26 @@ export class PluginsManager { } this.plugins = plugins; } + + private async checkPluginAlreadyPull(pluginPath: string): Promise<boolean> { + const id = pluginPath.split('/').at(-1)?.split('.').at(0); + const arches = await this.architectureManager.getAllPluginArches( + pluginPath + ); + if (id === undefined || arches === undefined) { + return false; + } + const pluginAlreadyInstalled = await this.findPlugin(id, arches[0]); + if (pluginAlreadyInstalled === undefined) { + return true; + } + const signature = await this.fileManager.readArchive( + pluginPath, + 'signatures.sig' + ); + return !this.certificateManager.verifySignature( + pluginAlreadyInstalled.signature, + signature + ); + } } diff --git a/tests/plugins.manager.test.ts b/tests/plugins.manager.test.ts index 8088280..12597f9 100644 --- a/tests/plugins.manager.test.ts +++ b/tests/plugins.manager.test.ts @@ -57,7 +57,8 @@ describe('Plugins manager service tests', function () { arches: ['test'], timestamp: '', author: '', - background: '' + background: '', + signature: Buffer.from(''), }]; const expectedPlugin = [ @@ -89,6 +90,7 @@ describe('Plugins manager service tests', function () { background: 'test', timestamp: 'test', author: 'test', + signature: Buffer.from(''), } const expected = [{ @@ -99,8 +101,16 @@ describe('Plugins manager service tests', function () { pluginsManagerService['plugins'] = expected; transaltionStub.formatText.resolves(plugin.name); const actual = await pluginsManagerService.getPlugin("test", 'en'); - - expect(actual).toEqual(plugin); + expect(actual).toEqual({ + id: plugin.id, + name: plugin.name, + version: plugin.version, + description: plugin.description, + icon: plugin.icon, + background: plugin.background, + timestamp: plugin.timestamp, + author: plugin.author, + }); }); it('should return undefined if plugin not found', async () => { @@ -118,6 +128,7 @@ describe('Plugins manager service tests', function () { background: 'test', timestamp: 'test', author: 'test', + signature: Buffer.from(''), }; const expected = [{ @@ -214,6 +225,7 @@ describe('Plugins manager service tests', function () { background: 'test', timestamp: 'test', author: 'test', + signature: Buffer.from(''), }; const expected = [{ @@ -270,6 +282,7 @@ describe('Plugins manager service tests', function () { arches: ['x64'], timestamp: 'timestamp1', author: 'author1', + signature: Buffer.from(''), }, { id: 'plugin2', @@ -281,6 +294,7 @@ describe('Plugins manager service tests', function () { arches: ['x86', 'x64'], timestamp: 'timestamp2', author: 'author2', + signature: Buffer.from(''), }, { id: 'plugin3', @@ -292,6 +306,7 @@ describe('Plugins manager service tests', function () { arches: ['x64'], timestamp: 'timestamp3', author: 'author3', + signature: Buffer.from(''), }, ]; @@ -335,6 +350,28 @@ describe('Plugins manager service tests', function () { // Restore the original getNewPlugin method getNewPluginStub.restore(); }); + it('should return true if plugin is not already installed and signature is valid', async () => { + const pluginPath = 'path/to/plugin'; + const signature = Buffer.from('valid-signature'); + + architectureService.getAllPluginArches.resolves(['arch1', 'arch2']); + fileManager.readArchive.resolves(signature); + pluginsManagerService['plugins'] = []; + + const result = await pluginsManagerService['checkPluginAlreadyPull'](pluginPath); + + expect(result).toBe(true); + }); + + it('should return true if plugin is not already installed and signature is invalid', async () => { + const pluginPath = 'path/to/plugin'; + const signature = Buffer.from('invalid-signature'); + architectureService.getAllPluginArches.resolves(['arch1', 'arch2']); + fileManager.readArchive.resolves(signature); + pluginsManagerService['plugins'] = []; + const result = await pluginsManagerService['checkPluginAlreadyPull'](pluginPath); + expect(result).toBe(true); + }); }); \ No newline at end of file -- GitLab