From d099ccedad812b8a4ccf7a586f2429f7d07e8091 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 26 Aug 2020 11:27:57 +0200 Subject: [PATCH] Additional AccountId length check (protect users against themselves) (#2520) * Additional AccountId length check * Allow all-0 * Spaceing. --- packages/types/src/generic/AccountId.spec.ts | 10 ++++++++-- packages/types/src/generic/AccountId.ts | 13 +++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/types/src/generic/AccountId.spec.ts b/packages/types/src/generic/AccountId.spec.ts index 01ae1415c0..c87107f4b8 100644 --- a/packages/types/src/generic/AccountId.spec.ts +++ b/packages/types/src/generic/AccountId.spec.ts @@ -36,6 +36,12 @@ describe('AccountId', (): void => { expect(a.toString()).toBe(expected); }); + it('fails with non-32-byte lengths', (): void => { + expect( + () => registry.createType('AccountId', '0x1234') + ).toThrow(/Invalid AccountId provided, expected 32 bytes/); + }); + testDecode( 'AccountId', registry.createType('AccountId', '0x0102030405060708010203040506070801020304050607080102030405060708'), @@ -72,7 +78,7 @@ describe('AccountId', (): void => { testEncode('toHex', '0x0102030405060708010203040506070801020304050607080102030405060708'); testEncode('toJSON', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); testEncode('toString', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); - testEncode('toString', '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM', '0x00'); + testEncode('toString', '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM', '0x0000000000000000000000000000000000000000000000000000000000000000'); testEncode('toU8a', Uint8Array.from([ 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 @@ -92,7 +98,7 @@ describe('AccountId', (): void => { ); const data = registry.createType('StorageData', jsonVec.params.result.changes[0][1]); - const list = registry.createType('Vec', data).map((accountId): string => accountId.toString()); + const list = registry.createType('Vec', data).map((accountId) => accountId.toString()); expect(list).toEqual([ '7qVJujLF3EDbZt5WfQXWvueFedMS4Vfk2Hb4GyR8jwksTLup', diff --git a/packages/types/src/generic/AccountId.ts b/packages/types/src/generic/AccountId.ts index 28dc40878b..6ccaea8496 100644 --- a/packages/types/src/generic/AccountId.ts +++ b/packages/types/src/generic/AccountId.ts @@ -4,13 +4,13 @@ import { AnyString, AnyU8a, Registry } from '../types'; -import { hexToU8a, isHex, isString, isU8a, u8aToU8a } from '@polkadot/util'; +import { hexToU8a, isHex, isString, isU8a, u8aToU8a, assert } from '@polkadot/util'; import { decodeAddress, encodeAddress } from '@polkadot/util-crypto'; import U8aFixed from '../codec/U8aFixed'; /** @internal */ -function decodeAccountId (value: AnyU8a | AnyString): AnyU8a { +function decodeAccountId (value: AnyU8a | AnyString): Uint8Array { if (isU8a(value) || Array.isArray(value)) { return u8aToU8a(value); } else if (isHex(value)) { @@ -19,7 +19,7 @@ function decodeAccountId (value: AnyU8a | AnyString): AnyU8a { return decodeAddress((value as string).toString()); } - return value; + throw new Error('Unknown type passed to AccountId constructor'); } /** @@ -31,7 +31,12 @@ function decodeAccountId (value: AnyU8a | AnyString): AnyU8a { */ export default class AccountId extends U8aFixed { constructor (registry: Registry, value: AnyU8a = new Uint8Array()) { - super(registry, decodeAccountId(value), 256); + const decoded = decodeAccountId(value); + + // Part of stream containing >= 32 bytes or a all empty (defaults) + assert(decoded.length >= 32 || !decoded.some((b) => b), `Invalid AccountId provided, expected 32 bytes, found ${decoded.length}`); + + super(registry, decoded, 256); } public static encode (value: Uint8Array, ss58Format?: number): string {