From 7a6e706b1959d544c2d76717290f33215b0c303a Mon Sep 17 00:00:00 2001 From: stanisloe Date: Thu, 4 Apr 2024 12:38:30 +0100 Subject: [PATCH 1/4] fix: validate private key min length --- .../Setup/ImportAccount/SeedPhraseImport.tsx | 19 +++++++------- apps/extension/src/utils/index.ts | 25 +++++++++++-------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx index 8b8b7e066..e9edaed51 100644 --- a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx +++ b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx @@ -47,27 +47,28 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { Array.from(mnemonicsRange) ); - const privateKeyError = (() => { - const validation = validatePrivateKey(filterPrivateKeyPrefix(privateKey)); + const validatePkAndFormatErrorMessage = (key: string): string => { + const validation = validatePrivateKey(filterPrivateKeyPrefix(key)); if (validation.ok) { return ""; } else { switch (validation.error.t) { - case "TooLong": - return `Private key must be no more than - ${validation.error.maxLength} characters long`; + case "WrongLength": + return `Private key must be ${validation.error.length} characters long`; case "BadCharacter": return "Private key may only contain characters 0-9, a-f"; default: return assertNever(validation.error); } } - })(); + }; + + const privateKeyError = validatePkAndFormatErrorMessage(privateKey); const isSubmitButtonDisabled = - mnemonicType === MnemonicTypes.PrivateKey - ? privateKey === "" || privateKeyError !== "" - : mnemonics.slice(0, mnemonicType).some((mnemonic) => !mnemonic); + mnemonicType === MnemonicTypes.PrivateKey ? + privateKey === "" || privateKeyError !== "" + : mnemonics.slice(0, mnemonicType).some((mnemonic) => !mnemonic); const onPaste = useCallback( (index: number, e: React.ClipboardEvent) => { diff --git a/apps/extension/src/utils/index.ts b/apps/extension/src/utils/index.ts index 960379731..407c08144 100644 --- a/apps/extension/src/utils/index.ts +++ b/apps/extension/src/utils/index.ts @@ -81,24 +81,27 @@ export const validateProps = (object: T, props: (keyof T)[]): void => { }); }; -const PRIVATE_KEY_MAX_LENGTH = 64; +const PRIVATE_KEY_LENGTH = 64; export type PrivateKeyError = - | { t: "TooLong"; maxLength: number } + | { t: "WrongLength"; length: number } | { t: "BadCharacter" }; // Very basic private key validation export const validatePrivateKey = ( privateKey: string -): Result => - privateKey.length > PRIVATE_KEY_MAX_LENGTH - ? Result.err({ t: "TooLong", maxLength: PRIVATE_KEY_MAX_LENGTH }) - : !/^[0-9a-f]*$/.test(privateKey) - ? Result.err({ t: "BadCharacter" }) - : Result.ok(null); +): Result => { + if (privateKey.length != PRIVATE_KEY_LENGTH) { + return Result.err({ t: "WrongLength", length: PRIVATE_KEY_LENGTH }); + } else if (!/^[0-9a-f]*$/.test(privateKey)) { + return Result.err({ t: "BadCharacter" }); + } else { + return Result.ok(null); + } +}; // Remove prefix from private key, which may be present when exporting keys from CLI export const filterPrivateKeyPrefix = (privateKey: string): string => - privateKey.length === PRIVATE_KEY_MAX_LENGTH + 2 - ? privateKey.replace(/^00/, "") - : privateKey; + privateKey.length === PRIVATE_KEY_LENGTH + 2 ? + privateKey.replace(/^00/, "") + : privateKey; From 89fa456bcc1e7dd2101c1c840cdd4a07a4ba5d78 Mon Sep 17 00:00:00 2001 From: stanisloe Date: Thu, 4 Apr 2024 12:39:05 +0100 Subject: [PATCH 2/4] fix: validate pk before submit --- .../src/Setup/ImportAccount/SeedPhraseImport.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx index e9edaed51..e84a1c210 100644 --- a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx +++ b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx @@ -119,11 +119,16 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { const onSubmit = useCallback(async () => { if (mnemonicType === MnemonicTypes.PrivateKey) { - // TODO: validate here - onConfirm({ - t: "PrivateKey", - privateKey: filterPrivateKeyPrefix(privateKey), - }); + const privateKeyError = validatePkAndFormatErrorMessage(privateKey); + if (privateKeyError) { + setMnemonicError(privateKeyError); + } else { + setMnemonicError(undefined); + onConfirm({ + t: "PrivateKey", + privateKey: filterPrivateKeyPrefix(privateKey), + }); + } } else { const actualMnemonics = mnemonics.slice(0, mnemonicType); const phrase = actualMnemonics.join(" "); From a7f92661c87ed802315fe10878a950f437588d45 Mon Sep 17 00:00:00 2001 From: stanisloe Date: Thu, 4 Apr 2024 15:57:00 +0100 Subject: [PATCH 3/4] style: improve validation error message --- apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx index e84a1c210..21ed90704 100644 --- a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx +++ b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx @@ -54,7 +54,10 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { } else { switch (validation.error.t) { case "WrongLength": - return `Private key must be ${validation.error.length} characters long`; + return ( + `Private key must be ${validation.error.length} characters long. ` + + `You provided a key of length ${key.length}.` + ); case "BadCharacter": return "Private key may only contain characters 0-9, a-f"; default: From b83d6c44f257e42f5e8d3af239c9feac88da913a Mon Sep 17 00:00:00 2001 From: stanisloe Date: Tue, 9 Apr 2024 11:44:51 +0100 Subject: [PATCH 4/4] fix: address pr comments --- .../Setup/ImportAccount/SeedPhraseImport.tsx | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx index 21ed90704..c8ee0c7af 100644 --- a/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx +++ b/apps/extension/src/Setup/ImportAccount/SeedPhraseImport.tsx @@ -47,30 +47,34 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { Array.from(mnemonicsRange) ); - const validatePkAndFormatErrorMessage = (key: string): string => { + const validatePk = (key: string): { isValid: boolean; msg: string } => { const validation = validatePrivateKey(filterPrivateKeyPrefix(key)); if (validation.ok) { - return ""; + return { isValid: true, msg: "" }; } else { + let msg = ""; switch (validation.error.t) { case "WrongLength": - return ( + msg = `Private key must be ${validation.error.length} characters long. ` + - `You provided a key of length ${key.length}.` - ); + `You provided a key of length ${key.length}.`; + break; case "BadCharacter": - return "Private key may only contain characters 0-9, a-f"; + msg = "Private key may only contain characters 0-9, a-f"; + break; default: - return assertNever(validation.error); + msg = assertNever(validation.error); + break; } + return { isValid: false, msg: msg }; } }; - const privateKeyError = validatePkAndFormatErrorMessage(privateKey); + const pkValidationResult = validatePk(privateKey); const isSubmitButtonDisabled = mnemonicType === MnemonicTypes.PrivateKey ? - privateKey === "" || privateKeyError !== "" + privateKey === "" || !pkValidationResult.isValid : mnemonics.slice(0, mnemonicType).some((mnemonic) => !mnemonic); const onPaste = useCallback( @@ -122,9 +126,9 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { const onSubmit = useCallback(async () => { if (mnemonicType === MnemonicTypes.PrivateKey) { - const privateKeyError = validatePkAndFormatErrorMessage(privateKey); - if (privateKeyError) { - setMnemonicError(privateKeyError); + const pkValidationResult = validatePk(privateKey); + if (!pkValidationResult.isValid) { + setMnemonicError(pkValidationResult.msg); } else { setMnemonicError(undefined); onConfirm({ @@ -235,7 +239,7 @@ export const SeedPhraseImport: React.FC = ({ onConfirm }) => { value={privateKey} placeholder="Enter your private key" onChange={(e) => setPrivateKey(e.target.value)} - error={privateKeyError} + error={pkValidationResult.msg} /> )}