diff --git a/scripts/genesis/AccountCreator.ts b/scripts/genesis/AccountCreator.ts index 96e416ff..e12c4aaf 100644 --- a/scripts/genesis/AccountCreator.ts +++ b/scripts/genesis/AccountCreator.ts @@ -233,6 +233,7 @@ export class LocalDevAccountCreator { } const ED25519_PUBLIC_KEY_HEX_LENGTH = 66 // 0x prefix + 64 hex chars (32 bytes) +const VALIDATOR_ID_REGEX = /^[1-9][0-9]*$/ function parseOverridePublicKeys(input: string): Map { const map = new Map() @@ -252,9 +253,12 @@ function parseOverridePublicKeys(input: string): Map { throw new Error(`Invalid format in overridePublicKeys: ${entry} (missing ID or key)`) } - const id = Number(idStr) + if (!VALIDATOR_ID_REGEX.test(idStr)) { + throw new Error(`Invalid validator ID in overridePublicKeys: ${idStr}`) + } - if (isNaN(id) || id < 1) { + const id = Number(idStr) + if (!Number.isSafeInteger(id)) { throw new Error(`Invalid validator ID in overridePublicKeys: ${idStr}`) } if (map.has(id)) { diff --git a/tests/unit/localdev-account-creator.test.ts b/tests/unit/localdev-account-creator.test.ts new file mode 100644 index 00000000..7d753e8f --- /dev/null +++ b/tests/unit/localdev-account-creator.test.ts @@ -0,0 +1,40 @@ +// Copyright 2026 Circle Internet Group, Inc. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { expect } from 'chai' +import { LocalDevAccountCreator } from '../../scripts/genesis/AccountCreator' + +const publicKey = `0x${'11'.repeat(32)}` + +describe('LocalDevAccountCreator', () => { + describe('overridePublicKeys', () => { + it('accepts canonical decimal validator IDs', () => { + const creator = new LocalDevAccountCreator({ overridePublicKeys: `1:${publicKey},42:${publicKey}` }) + + expect(creator.overridePublicKeys.get(1)).to.equal(publicKey) + expect(creator.overridePublicKeys.get(42)).to.equal(publicKey) + }) + + it('rejects non-canonical validator IDs', () => { + for (const id of ['1.5', '1e0', '01', '-1', 'NaN', 'Infinity', '']) { + expect( + () => new LocalDevAccountCreator({ overridePublicKeys: `${id}:${publicKey}` }), + `${id} should be rejected`, + ).to.throw(/Invalid validator ID|missing ID/) + } + }) + }) +})