diff --git a/backend/src/server/routes/v1/project-env-router.ts b/backend/src/server/routes/v1/project-env-router.ts index b2f7ffc2c..db35e16f6 100644 --- a/backend/src/server/routes/v1/project-env-router.ts +++ b/backend/src/server/routes/v1/project-env-router.ts @@ -1,3 +1,4 @@ +import slugify from "@sindresorhus/slugify"; import { z } from "zod"; import { ProjectEnvironmentsSchema } from "@app/db/schemas"; @@ -24,8 +25,10 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { name: z.string().trim(), slug: z .string() - .regex(/^[^./]*$/g) .trim() + .refine((v) => slugify(v) === v, { + message: "Slug must be a valid slug" + }) }), response: { 200: z.object({ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5e30edfc3..4b7907b60 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -38,6 +38,7 @@ "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", "@reduxjs/toolkit": "^1.8.3", + "@sindresorhus/slugify": "^2.2.1", "@stripe/react-stripe-js": "^1.16.3", "@stripe/stripe-js": "^1.46.0", "@tanstack/react-query": "^4.23.0", @@ -5774,6 +5775,57 @@ "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, + "node_modules/@sindresorhus/slugify": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", + "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", + "dependencies": { + "@sindresorhus/transliterate": "^1.0.0", + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/slugify/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", + "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/transliterate/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@storybook/addon-actions": { "version": "7.6.8", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6a1666f05..cae75f0c8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,6 +46,7 @@ "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.0.7", "@reduxjs/toolkit": "^1.8.3", + "@sindresorhus/slugify": "^2.2.1", "@stripe/react-stripe-js": "^1.16.3", "@stripe/stripe-js": "^1.46.0", "@tanstack/react-query": "^4.23.0", diff --git a/frontend/src/components/v2/InfisicalSecretInput/SecretReferenceSelect.tsx b/frontend/src/components/v2/InfisicalSecretInput/SecretReferenceSelect.tsx index da8cff98f..454e3600c 100644 --- a/frontend/src/components/v2/InfisicalSecretInput/SecretReferenceSelect.tsx +++ b/frontend/src/components/v2/InfisicalSecretInput/SecretReferenceSelect.tsx @@ -166,7 +166,7 @@ export default function SecretReferenceSelect({ })} {listReference.length !== 0 && ( - + )} diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx index 2525d57ef..f12dcfa4d 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx @@ -1,5 +1,6 @@ import { Controller, useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; +import slugify from "@sindresorhus/slugify"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; @@ -16,7 +17,14 @@ type Props = { const schema = yup.object({ environmentName: yup.string().label("Environment Name").required(), - environmentSlug: yup.string().label("Environment Slug").matches(/^[^./]*$/g, { message: "Invalid [.] or [/] not allowed"}).required() + environmentSlug: yup + .string() + .label("Environment Slug") + .test({ + test: (slug) => slugify(slug as string) === slug, + message: "Slug must be a valid slug" + }) + .required() }); export type FormData = yup.InferType;