From 1b6301c13be3f127a1e9a31a27af11eb6efab553 Mon Sep 17 00:00:00 2001 From: Cassio Sales Date: Tue, 16 Jun 2026 18:33:04 -0300 Subject: [PATCH] Raise decrypt errors after recording persistent ids Stop swallowing decryption failures in the push receiver client so library consumers still see authentication, missing crypto-key, and missing salt errors. The client still records the persistent id before raising so reconnects do not redeliver the same message. Also carries forward the existing GCM semicolon lint fix needed for yarn lint on master. CLI-801 --- src/client.js | 8 +---- src/gcm/index.js | 4 +-- test/client.test.js | 86 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 test/client.test.js diff --git a/src/client.js b/src/client.js index 79b0d7d..195643e 100644 --- a/src/client.js +++ b/src/client.js @@ -197,14 +197,8 @@ module.exports = class Client extends EventEmitter { ): case error.message.includes('crypto-key is missing'): case error.message.includes('salt is missing'): - // NOTE(ibash) Periodically we're unable to decrypt notifications. In - // all cases we've been able to receive future notifications using the - // same keys. So, we silently drop this notification. - console.warn( - 'Message dropped as it could not be decrypted: ' + error.message - ); this._persistentIds.push(object.persistentId); - return; + throw error; default: { throw error; } diff --git a/src/gcm/index.js b/src/gcm/index.js index 9260129..940082f 100644 --- a/src/gcm/index.js +++ b/src/gcm/index.js @@ -8,8 +8,8 @@ const { toBase64 } = require('../utils/base64'); // Hack to fix PHONE_REGISTRATION_ERROR #17 when bundled with webpack // https://github.com/dcodeIO/protobuf.js#browserify-integration -protobuf.util.Long = Long -protobuf.configure() +protobuf.util.Long = Long; +protobuf.configure(); const serverKey = toBase64(Buffer.from(fcmKey)); diff --git a/test/client.test.js b/test/client.test.js new file mode 100644 index 0000000..a19a481 --- /dev/null +++ b/test/client.test.js @@ -0,0 +1,86 @@ +jest.mock('../src/utils/decrypt'); + +const Client = require('../src/client'); +const decrypt = require('../src/utils/decrypt'); + +const KEYS = { + authSecret : 'auth-secret', + privateKey : 'private-key', +}; + +function createClient(persistentIds = []) { + return new Client({ keys : KEYS }, { persistentIds }); +} + +describe('Client', () => { + describe('_onDataMessage', () => { + beforeEach(() => { + decrypt.mockReset(); + }); + + [ + 'Unsupported state or unable to authenticate data', + 'crypto-key is missing', + 'salt is missing', + ].forEach(errorMessage => { + it(`raises "${errorMessage}" after recording the persistent id`, () => { + const client = createClient(); + const object = { persistentId : 'persistent-id' }; + const decryptionError = new Error(errorMessage); + let thrownError; + + decrypt.mockImplementation(() => { + throw decryptionError; + }); + + try { + client._onDataMessage(object); + } catch (error) { + thrownError = error; + } + + expect(thrownError).toBe(decryptionError); + expect(client._persistentIds).toEqual(['persistent-id']); + expect(decrypt).toHaveBeenCalledWith(object, KEYS); + }); + }); + + it('does not record unrelated decrypt errors', () => { + const client = createClient(); + const object = { persistentId : 'persistent-id' }; + const decryptionError = new Error('unexpected decrypt failure'); + let thrownError; + + decrypt.mockImplementation(() => { + throw decryptionError; + }); + + try { + client._onDataMessage(object); + } catch (error) { + thrownError = error; + } + + expect(thrownError).toBe(decryptionError); + expect(client._persistentIds).toEqual([]); + }); + + it('records and emits successfully decrypted messages', () => { + const client = createClient(); + const object = { persistentId : 'persistent-id' }; + const message = { title : 'Hello' }; + const onNotification = jest.fn(); + + decrypt.mockReturnValue(message); + client.on('ON_NOTIFICATION_RECEIVED', onNotification); + + client._onDataMessage(object); + + expect(client._persistentIds).toEqual(['persistent-id']); + expect(onNotification).toHaveBeenCalledWith({ + notification : message, + persistentId : 'persistent-id', + }); + }); + }); +});