From 44ae0519d1c3cf5e218ff5f9bff7387a99ded516 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 27 Apr 2025 14:34:26 -0700 Subject: [PATCH] Revise ssh host alias field handling/validation --- backend/src/ee/routes/v1/ssh-host-router.ts | 22 +++---------------- .../ee/services/ssh-host/ssh-host-service.ts | 4 ++-- .../ee/services/ssh-host/ssh-host-types.ts | 4 ++-- frontend/src/hooks/api/sshHost/types.ts | 4 ++-- .../SshHostsPage/components/SshHostModal.tsx | 10 ++++----- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/backend/src/ee/routes/v1/ssh-host-router.ts b/backend/src/ee/routes/v1/ssh-host-router.ts index f8dda656e..9db642d4d 100644 --- a/backend/src/ee/routes/v1/ssh-host-router.ts +++ b/backend/src/ee/routes/v1/ssh-host-router.ts @@ -1,4 +1,3 @@ -import slugify from "@sindresorhus/slugify"; import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; @@ -8,6 +7,7 @@ import { isValidHostname } from "@app/ee/services/ssh-host/ssh-host-validators"; import { SSH_HOSTS } from "@app/lib/api-docs"; import { ms } from "@app/lib/ms"; import { publicSshCaLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; +import { slugSchema } from "@app/server/lib/schemas"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -102,15 +102,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => { message: "Hostname must be a valid hostname" }) .describe(SSH_HOSTS.CREATE.hostname), - alias: z - .string() - .trim() - .nullable() - .default(null) - .refine((v) => v == null || slugify(v) === v, { - message: "Alias must be a valid slug" - }) - .describe(SSH_HOSTS.CREATE.alias), + alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.CREATE.alias).default(""), userCertTtl: z .string() .refine((val) => ms(val) > 0, "TTL must be a positive number") @@ -185,15 +177,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => { }) .optional() .describe(SSH_HOSTS.UPDATE.hostname), - alias: z - .string() - .trim() - .nullable() - .refine((v) => v == null || slugify(v) === v, { - message: "Alias must be a valid slug" - }) - .optional() - .describe(SSH_HOSTS.UPDATE.alias), + alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.UPDATE.alias).optional(), userCertTtl: z .string() .refine((val) => ms(val) > 0, "TTL must be a positive number") diff --git a/backend/src/ee/services/ssh-host/ssh-host-service.ts b/backend/src/ee/services/ssh-host/ssh-host-service.ts index f1b14a9cb..92f1f5236 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-service.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-service.ts @@ -193,7 +193,7 @@ export const sshHostServiceFactory = ({ { projectId, hostname, - alias, + alias: alias === "" ? null : alias, userCertTtl, hostCertTtl, userSshCaId, @@ -300,7 +300,7 @@ export const sshHostServiceFactory = ({ sshHostId, { hostname, - alias, + alias: alias === "" ? null : alias, userCertTtl, hostCertTtl }, diff --git a/backend/src/ee/services/ssh-host/ssh-host-types.ts b/backend/src/ee/services/ssh-host/ssh-host-types.ts index 58f4db9fb..a4826cd72 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-types.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-types.ts @@ -4,7 +4,7 @@ export type TListSshHostsDTO = Omit; export type TCreateSshHostDTO = { hostname: string; - alias: string | null; + alias?: string; userCertTtl: string; hostCertTtl: string; loginMappings: { @@ -20,7 +20,7 @@ export type TCreateSshHostDTO = { export type TUpdateSshHostDTO = { sshHostId: string; hostname?: string; - alias?: string | null; + alias?: string; userCertTtl?: string; hostCertTtl?: string; loginMappings?: { diff --git a/frontend/src/hooks/api/sshHost/types.ts b/frontend/src/hooks/api/sshHost/types.ts index 6997d7c60..ebeb5130a 100644 --- a/frontend/src/hooks/api/sshHost/types.ts +++ b/frontend/src/hooks/api/sshHost/types.ts @@ -16,7 +16,7 @@ export type TSshHost = { export type TCreateSshHostDTO = { projectId: string; hostname: string; - alias: string | null; + alias?: string; userCertTtl?: string; hostCertTtl?: string; loginMappings: { @@ -30,7 +30,7 @@ export type TCreateSshHostDTO = { export type TUpdateSshHostDTO = { sshHostId: string; hostname?: string; - alias?: string | null; + alias?: string; userCertTtl?: string; hostCertTtl?: string; loginMappings?: { diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx index 0c90518f4..ede0a35a0 100644 --- a/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx @@ -134,15 +134,15 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { return; } - const processedAlias = alias.trim() || null; + const trimmedAlias = alias.trim(); // check if there is already a different host with the same non-null alias - if (processedAlias) { + if (trimmedAlias) { const existingAliases = sshHosts?.filter((h) => h.id !== sshHost?.id && h.alias !== null).map((h) => h.alias) || []; - if (existingAliases.includes(processedAlias)) { + if (existingAliases.includes(trimmedAlias)) { createNotification({ text: "A host with this alias already exists.", type: "error" @@ -155,7 +155,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { await updateMutateAsync({ sshHostId: sshHost.id, hostname, - alias: processedAlias, + alias: trimmedAlias, userCertTtl, loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ loginUser, @@ -168,7 +168,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { await createMutateAsync({ projectId, hostname, - alias: processedAlias, + alias: trimmedAlias, userCertTtl, loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ loginUser,