From 178dc379a8b268790329a6092a3ed6d0df738d6f Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Wed, 1 Jan 2025 15:36:00 +0700 Subject: [PATCH] fix: username validation --- app/actions.ts | 8 +++++++- app/api/wallets/route.ts | 6 ++++++ app/db.ts | 34 ++++++++++++++++++++++++++-------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/actions.ts b/app/actions.ts index b6bb5c6..3511121 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -1,6 +1,6 @@ "use server"; -import { saveConnectionSecret } from "./db"; +import { saveConnectionSecret, UsernameTakenError } from "./db"; import { getAlbyHubUrl, getDailyWalletLimit, getDomain } from "./utils"; export type Reserves = { @@ -135,6 +135,12 @@ export async function createWallet( }; } catch (error) { console.error(error); + + // only expose known errors + if (error instanceof UsernameTakenError) { + return { wallet: undefined, error: error.message }; + } + return { wallet: undefined, error: "internal error" }; } } diff --git a/app/api/wallets/route.ts b/app/api/wallets/route.ts index 4e94b9f..7c5a389 100644 --- a/app/api/wallets/route.ts +++ b/app/api/wallets/route.ts @@ -22,6 +22,12 @@ export async function POST(request: Request) { // force lowercase username if (createWalletRequest.username) { createWalletRequest.username = createWalletRequest.username.toLowerCase(); + + if (!/^[a-z0-9]+$/.test(createWalletRequest.username)) { + return new Response("only letters and numbers in username are allowed", { + status: 400, + }); + } } const { wallet, error } = await createWallet( diff --git a/app/db.ts b/app/db.ts index 4432222..de48ac8 100644 --- a/app/db.ts +++ b/app/db.ts @@ -1,9 +1,16 @@ import { PrismaClient } from "@prisma/client"; import { nwc } from "@getalby/sdk"; import { getPublicKey } from "nostr-tools"; +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; const prisma = new PrismaClient(); +export class UsernameTakenError extends Error { + constructor() { + super("username taken"); + } +} + export async function saveConnectionSecret( username: string | undefined, connectionSecret: string @@ -15,14 +22,25 @@ export async function saveConnectionSecret( const pubkey = getPublicKey(parsed.secret); username = username || pubkey.substring(0, 6); - const result = await prisma.connectionSecret.create({ - data: { - id: connectionSecret, - username, - pubkey, - }, - }); - return { username: result.username }; + try { + const result = await prisma.connectionSecret.create({ + data: { + id: connectionSecret, + username, + pubkey, + }, + }); + return { username: result.username }; + } catch (error) { + console.error("failed to save wallet", error); + if ( + error instanceof PrismaClientKnownRequestError && + error.code === "P2002" // unique constraint + ) { + throw new UsernameTakenError(); + } + throw error; + } } export async function findWalletConnection(query: { username: string }) {