From 1db2668a96208046ffe316114f65d432e57db994 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Thu, 6 Feb 2025 14:55:57 +0000 Subject: Improved service handle validation logic (#7657) * fix validation logic for 3p pdses * fix bad import * add service handle validation test --- __tests__/lib/strings/handles.test.ts | 42 ++++++++++++++++++++++ src/lib/strings/handles.ts | 7 ++-- .../Settings/components/ChangeHandleDialog.tsx | 4 +-- src/screens/Signup/StepHandle.tsx | 6 ++-- tsconfig.json | 2 +- 5 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 __tests__/lib/strings/handles.test.ts diff --git a/__tests__/lib/strings/handles.test.ts b/__tests__/lib/strings/handles.test.ts new file mode 100644 index 000000000..4456fae94 --- /dev/null +++ b/__tests__/lib/strings/handles.test.ts @@ -0,0 +1,42 @@ +import {IsValidHandle, validateServiceHandle} from '#/lib/strings/handles' + +describe('handle validation', () => { + const valid = [ + ['ali', 'bsky.social'], + ['alice', 'bsky.social'], + ['a-lice', 'bsky.social'], + ['a-----lice', 'bsky.social'], + ['123', 'bsky.social'], + ['123456789012345678', 'bsky.social'], + ['alice', 'custom-pds.com'], + ['alice', 'my-custom-pds-with-long-name.social'], + ['123456789012345678', 'my-custom-pds-with-long-name.social'], + ] + it.each(valid)(`should be valid: %s.%s`, (handle, service) => { + const result = validateServiceHandle(handle, service) + expect(result.overall).toEqual(true) + }) + + const invalid = [ + ['al', 'bsky.social', 'frontLength'], + ['-alice', 'bsky.social', 'hyphenStartOrEnd'], + ['alice-', 'bsky.social', 'hyphenStartOrEnd'], + ['%%%', 'bsky.social', 'handleChars'], + ['1234567890123456789', 'bsky.social', 'frontLength'], + [ + '1234567890123456789', + 'my-custom-pds-with-long-name.social', + 'frontLength', + ], + ['al', 'my-custom-pds-with-long-name.social', 'frontLength'], + ['a'.repeat(300), 'toolong.com', 'totalLength'], + ] satisfies [string, string, keyof IsValidHandle][] + it.each(invalid)( + `should be invalid: %s.%s due to %s`, + (handle, service, expectedError) => { + const result = validateServiceHandle(handle, service) + expect(result.overall).toEqual(false) + expect(result[expectedError]).toEqual(false) + }, + ) +}) diff --git a/src/lib/strings/handles.ts b/src/lib/strings/handles.ts index bf329a869..754d219ed 100644 --- a/src/lib/strings/handles.ts +++ b/src/lib/strings/handles.ts @@ -42,10 +42,9 @@ export interface IsValidHandle { } // More checks from https://github.com/bluesky-social/atproto/blob/main/packages/pds/src/handle/index.ts#L72 -export function validateHandle( +export function validateServiceHandle( str: string, userDomain: string, - isServiceHandle?: boolean, ): IsValidHandle { const fullHandle = createFullHandle(str, userDomain) @@ -53,8 +52,8 @@ export function validateHandle( handleChars: !str || (VALIDATE_REGEX.test(fullHandle) && !str.includes('.')), hyphenStartOrEnd: !str.startsWith('-') && !str.endsWith('-'), - frontLength: str.length >= 3, - totalLength: fullHandle.length <= (isServiceHandle ? 30 : 253), + frontLength: str.length >= 3 && str.length <= 18, + totalLength: fullHandle.length <= 253, } return { diff --git a/src/screens/Settings/components/ChangeHandleDialog.tsx b/src/screens/Settings/components/ChangeHandleDialog.tsx index 37f6ed9ef..6450e24b2 100644 --- a/src/screens/Settings/components/ChangeHandleDialog.tsx +++ b/src/screens/Settings/components/ChangeHandleDialog.tsx @@ -17,8 +17,8 @@ import {useMutation, useQueryClient} from '@tanstack/react-query' import {HITSLOP_10} from '#/lib/constants' import {cleanError} from '#/lib/strings/errors' +import {createFullHandle, validateServiceHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles' -import {createFullHandle, validateHandle} from '#/lib/strings/handles' import {useFetchDid, useUpdateHandleMutation} from '#/state/queries/handle' import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile' import {useServiceQuery} from '#/state/queries/service' @@ -172,7 +172,7 @@ function ProvidedHandlePage({ const host = serviceInfo.availableUserDomains[0] const validation = useMemo( - () => validateHandle(subdomain, host, true), + () => validateServiceHandle(subdomain, host), [subdomain, host], ) diff --git a/src/screens/Signup/StepHandle.tsx b/src/screens/Signup/StepHandle.tsx index 1d04264ae..c06fa29d1 100644 --- a/src/screens/Signup/StepHandle.tsx +++ b/src/screens/Signup/StepHandle.tsx @@ -7,7 +7,7 @@ import {logEvent} from '#/lib/statsig/statsig' import { createFullHandle, maxServiceHandleLength, - validateHandle, + validateServiceHandle, } from '#/lib/strings/handles' import {useAgent} from '#/state/session' import {ScreenTransition} from '#/screens/Login/ScreenTransition' @@ -37,7 +37,7 @@ export function StepHandle() { value: handle, }) - const newValidCheck = validateHandle(handle, state.userDomain) + const newValidCheck = validateServiceHandle(handle, state.userDomain) if (!newValidCheck.overall) { return } @@ -97,7 +97,7 @@ export function StepHandle() { }) }, [dispatch, state.activeStep]) - const validCheck = validateHandle(draftValue, state.userDomain, true) + const validCheck = validateServiceHandle(draftValue, state.userDomain) return ( diff --git a/tsconfig.json b/tsconfig.json index 1c5e27eec..001b5247e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "jsx": "react-jsx", "module": "esnext", - "types": ["node"], + "types": ["node", "jest"], "paths": { "#/*": ["./src/*"], "lib/*": ["./src/lib/*"], -- cgit 1.4.1