From 6be80684417663a76a7948bd0da22b44d719a770 Mon Sep 17 00:00:00 2001 From: Prashant Bajpai <34747455+prashantasdeveloper@users.noreply.github.com> Date: Mon, 28 Sep 2026 12:55:26 +0530 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20use=20null-prototype=20ma?= =?UTF-8?q?ps=20in=20local=20API=20key=20and=20signer=20stores?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lookups in the local API key store and local signer address book now only match entries that were explicitly configured or added. --- .../local-store/repos/api-key.repo.spec.ts | 13 +++++++++++++ src/datastore/local-store/repos/api-key.repo.ts | 2 +- .../services/local-signing.service.spec.ts | 15 +++++++++++++++ src/signing/services/local-signing.service.ts | 2 +- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/datastore/local-store/repos/api-key.repo.spec.ts b/src/datastore/local-store/repos/api-key.repo.spec.ts index 48c5dcb9..d664a5a7 100644 --- a/src/datastore/local-store/repos/api-key.repo.spec.ts +++ b/src/datastore/local-store/repos/api-key.repo.spec.ts @@ -2,6 +2,7 @@ import { createMock } from '@golevelup/ts-jest'; import { ConfigService } from '@nestjs/config'; import { ApiKeyRepo } from '~/auth/repos/api-key.repo'; +import { AppNotFoundError } from '~/common/errors'; import { LocalApiKeysRepo } from '~/datastore/local-store/repos/api-key.repo'; import { defaultUser } from '~/users/user.consts'; @@ -23,4 +24,16 @@ describe('LocalApiKeyRepo', () => { return expect(repo.getUserByApiKey(config)).resolves.toEqual(defaultUser); }); + + it.each(['constructor', '__proto__', 'toString', 'valueOf', 'hasOwnProperty'])( + 'should not treat the Object.prototype property "%s" as a configured key', + key => { + const mockConfig = createMock(); + mockConfig.getOrThrow.mockReturnValue(config); + + const repo = new LocalApiKeysRepo(mockConfig); + + return expect(repo.getUserByApiKey(key)).rejects.toThrow(AppNotFoundError); + } + ); }); diff --git a/src/datastore/local-store/repos/api-key.repo.ts b/src/datastore/local-store/repos/api-key.repo.ts index 5f26cfcf..2df33069 100644 --- a/src/datastore/local-store/repos/api-key.repo.ts +++ b/src/datastore/local-store/repos/api-key.repo.ts @@ -11,7 +11,7 @@ import { defaultUser } from '~/users/user.consts'; @Injectable() export class LocalApiKeysRepo implements ApiKeyRepo { - private apiKeys: Record = {}; + private apiKeys: Record = Object.create(null); constructor(readonly config: ConfigService) { const givenApiKeys = config.getOrThrow('API_KEYS'); diff --git a/src/signing/services/local-signing.service.spec.ts b/src/signing/services/local-signing.service.spec.ts index 6b1873b3..02b5be97 100644 --- a/src/signing/services/local-signing.service.spec.ts +++ b/src/signing/services/local-signing.service.spec.ts @@ -75,6 +75,13 @@ describe('LocalSigningService', () => { it('should throw if an Account is not loaded', () => { expect(() => service.getAddressByHandle('badId')).toThrow(AppNotFoundError); }); + + it.each(['constructor', '__proto__', 'toString'])( + 'should throw for the Object.prototype property "%s" when it is not loaded', + handle => { + expect(() => service.getAddressByHandle(handle)).toThrow(AppNotFoundError); + } + ); }); describe('isAddress', () => { @@ -122,5 +129,13 @@ describe('LocalSigningService', () => { await expect(service.addSigner(handle, mnemonic)).rejects.toThrow(AppConflictError); }); + + it('should add a signer whose handle is an Object.prototype property name', async () => { + const handle = 'constructor'; + const expectedAddress = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5'; + + await expect(service.addSigner(handle, '//Alice')).resolves.toBe(expectedAddress); + await expect(service.getAddressByHandle(handle)).resolves.toBe(expectedAddress); + }); }); }); diff --git a/src/signing/services/local-signing.service.ts b/src/signing/services/local-signing.service.ts index 3ee3c403..cf667089 100644 --- a/src/signing/services/local-signing.service.ts +++ b/src/signing/services/local-signing.service.ts @@ -7,7 +7,7 @@ import { PolymeshService } from '~/polymesh/polymesh.service'; import { SigningService } from '~/signing/services/signing.service'; export class LocalSigningService extends SigningService { - private addressBook: Record = {}; + private addressBook: Record = Object.create(null); constructor( protected readonly signingManager: LocalSigningManager,