From b5c148dd8d697a13ce5423cc6e17fbd196905128 Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Fri, 4 Sep 2026 17:48:19 +0200 Subject: [PATCH] fix(frontend): cap the inferred username at what the column holds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `usr.username` is VARCHAR(50) while the provider name and email it is derived from run to 255, and `create_workspace` inserts the value untruncated — so a long first name failed the same way the invalid characters did: posted invisibly, refused on insert, with nothing on screen naming the field. Undefined instead, which the form already routes to the full one. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9 --- frontend/src/lib/workspaceCreation.test.ts | 7 +++++++ frontend/src/lib/workspaceCreation.ts | 17 ++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/workspaceCreation.test.ts b/frontend/src/lib/workspaceCreation.test.ts index 55230538e7..13d63d1fd1 100644 --- a/frontend/src/lib/workspaceCreation.test.ts +++ b/frontend/src/lib/workspaceCreation.test.ts @@ -52,4 +52,11 @@ describe('usernameFromName', () => { expect(usernameFromName('++')).toBeUndefined() expect(usernameFromName('')).toBeUndefined() }) + + it('answers undefined rather than a value the column cannot hold', () => { + // `usr.username` is VARCHAR(50) while the name and email it is derived from run to 255, + // and `create_workspace` inserts it untruncated. + expect(usernameFromName('a'.repeat(50))).toBe('a'.repeat(50)) + expect(usernameFromName('a'.repeat(51))).toBeUndefined() + }) }) diff --git a/frontend/src/lib/workspaceCreation.ts b/frontend/src/lib/workspaceCreation.ts index 3a39613870..da67afba6f 100644 --- a/frontend/src/lib/workspaceCreation.ts +++ b/frontend/src/lib/workspaceCreation.ts @@ -45,15 +45,22 @@ export interface UsernamePolicy { suggested?: string } +/** What `usr.username` holds, and neither the provider name nor the email is bounded by it. */ +const USERNAME_MAX_LENGTH = 50 + /** - * A username the `proper_username` constraint accepts: `^[\w-]+$`, so word characters and - * hyphens and nothing else. Everything else is dropped rather than substituted — `O'Connor` - * is `oconnor`, not `o-connor` — and a name with nothing left of it answers undefined, which - * is the caller's cue to ask for one instead of posting a value the database refuses. + * A username the whole `usr.username` contract accepts: the `proper_username` constraint + * (`^[\w-]+$`, so word characters and hyphens and nothing else) and the column's own 50 + * characters. Anything outside the class is dropped rather than substituted — `O'Connor` is + * `oconnor`, not `o-connor`. + * + * Undefined where nothing usable is left or where what is left is too long, which is the + * caller's cue to ask for one: `create_workspace` inserts this value with no truncation, so a + * name the column refuses would fail on insert with nothing on screen naming the field. */ export function usernameFromName(name: string): string | undefined { const cleaned = name.toLowerCase().replace(/[^\w-]/g, '') - return cleaned === '' ? undefined : cleaned + return cleaned === '' || cleaned.length > USERNAME_MAX_LENGTH ? undefined : cleaned } /**