diff --git a/packages/components/src/utils/validationSchemes.ts b/packages/components/src/utils/validationSchemes.ts index 3f0b2c115d..a6ac2c0522 100644 --- a/packages/components/src/utils/validationSchemes.ts +++ b/packages/components/src/utils/validationSchemes.ts @@ -1,3 +1,4 @@ +import { PROHIBITED_CHARACTERS } from "@umami/state"; import { type Network } from "@umami/tezos"; import { z } from "zod"; @@ -24,6 +25,10 @@ export const getNetworkValidationScheme = (availableNetworks: Network[], network : z .string() .min(1, "Name is required") + .max(256, "Name should not exceed 256 characters") + .refine(name => PROHIBITED_CHARACTERS.some(char => name.includes(char)), { + message: "Name contains special character(s)", + }) .refine(name => !availableNetworks.find(n => n.name === name), { message: "Network with this name already exists", }), diff --git a/packages/state/src/hooks/labels.test.ts b/packages/state/src/hooks/labels.test.ts index 2ddc953f10..00d8ed6969 100644 --- a/packages/state/src/hooks/labels.test.ts +++ b/packages/state/src/hooks/labels.test.ts @@ -9,7 +9,7 @@ import { } from "@umami/core"; import { MAINNET } from "@umami/tezos"; -import { useGetNextAvailableAccountLabels, useValidateName } from "./labels"; +import { PROHIBITED_CHARACTERS, useGetNextAvailableAccountLabels, useValidateName } from "./labels"; import { contactsActions, multisigsActions, networksActions } from "../slices"; import { type UmamiStore, makeStore } from "../store"; import { addTestAccounts, renderHook } from "../testUtils"; @@ -22,6 +22,22 @@ beforeEach(() => { describe("labelsHooks", () => { describe("useValidateName", () => { + it.each(PROHIBITED_CHARACTERS)("fails if name contains `%s` special character", char => { + const { + result: { current: validateName }, + } = renderHook(() => useValidateName(), { store }); + + expect(validateName(`Some ${char} name`)).toEqual("Name contains special character(s)"); + }); + + it("fails if name exceeds 256 characters", () => { + const { + result: { current: validateName }, + } = renderHook(() => useValidateName(), { store }); + + expect(validateName("a".repeat(257))).toEqual("Name should not exceed 256 characters"); + }); + describe.each([ { testCase: "with trailing whitespaces", withWhitespaces: true }, { testCase: "without trailing whitespaces", withWhitespaces: false }, diff --git a/packages/state/src/hooks/labels.ts b/packages/state/src/hooks/labels.ts index 7cd545cefc..7bb25cb469 100644 --- a/packages/state/src/hooks/labels.ts +++ b/packages/state/src/hooks/labels.ts @@ -1,12 +1,20 @@ import { useAllContacts } from "./contacts"; import { useAllAccounts } from "./getAccountData"; +export const PROHIBITED_CHARACTERS = `/\\"'<>:{}$#|\`\t\r\n`.split(""); + /** Hook for validating name for account or contact. */ export const useValidateName = (oldName?: string | undefined) => { const isUniqueLabel = useIsUniqueLabel(); return (name: string) => { const trimmedName = name.trim(); + if (trimmedName.length > 256) { + return "Name should not exceed 256 characters"; + } + if (PROHIBITED_CHARACTERS.some(char => trimmedName.includes(char))) { + return "Name contains special character(s)"; + } if (trimmedName.length === 0) { return "Name should not be empty"; }