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;