diff --git a/src/interfaces/plugins.ts b/src/interfaces/plugins.ts index 5e67d4795ec61d2e0ac66c798a5dfffbfb47cff6..828a2d8fa2dc625fdaf83759f205d5bab93a401b 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 c3b956d15cf46325d3b8637f5b8539f67fd9386d..20eb1f8b142aaf4f230b2c74b5f7ac08258708e3 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 ffe6caafc98f0dd0911046e632fe6fb53ea29e0f..b3056d139cae1b7416b3a7a35f9910ca2f687a92 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 80882809549d04d96f49134940962e0eadbcbea8..12597f936b28b5b0eb44cbc64df6763f24b73bfb 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