From 5f65b7628b2aa142eb8e7f210a931a0449ead60e Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Thu, 12 Oct 2023 13:38:54 -0400 Subject: [PATCH 1/7] make project name and id input more strict --- src/components/SpruceForm/customFormats.ts | 11 +++++---- src/components/SpruceForm/errors.ts | 6 +++++ src/pages/projectSettings/sharedFormSchema.ts | 4 ++-- src/utils/validators/index.ts | 13 +++++++++- src/utils/validators/validators.test.ts | 24 +++++++++++++++---- 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/components/SpruceForm/customFormats.ts b/src/components/SpruceForm/customFormats.ts index 3edd4956ad..38e92f383e 100644 --- a/src/components/SpruceForm/customFormats.ts +++ b/src/components/SpruceForm/customFormats.ts @@ -5,6 +5,7 @@ const { validateEmail, validateJira, validateJiraURL, + validateNoSpecialCharacters, validatePercentage, validateRegexp, validateSlack, @@ -13,16 +14,16 @@ const { } = validators; export const customFormats = (jiraHost: string) => ({ + noSpecialCharacters: validateNoSpecialCharacters, // Permit empty string but disallow whitespace noSpaces: /^$|^\S+$/, - // Permit url - validURL: validateURL, - validURLTemplate: validateURLTemplate, validDuration: validateDuration, - validPercentage: validatePercentage, + validEmail: validateEmail, validJiraTicket: validateJira, validJiraURL: (url: string) => validateJiraURL(jiraHost, url), + validPercentage: validatePercentage, validRegex: validateRegexp, validSlack: validateSlack, - validEmail: validateEmail, + validURL: validateURL, + validURLTemplate: validateURLTemplate, }); diff --git a/src/components/SpruceForm/errors.ts b/src/components/SpruceForm/errors.ts index 3eea1a1a14..5fe0fb00fb 100644 --- a/src/components/SpruceForm/errors.ts +++ b/src/components/SpruceForm/errors.ts @@ -51,6 +51,12 @@ export const transformErrors = (errors: AjvError[]) => }; case "format": switch (error.params.format) { + case "noSpecialCharacters": + return { + ...error, + message: + "Value can only contain numbers, letters, periods and hyphens.", + }; case "noSpaces": return { ...error, diff --git a/src/pages/projectSettings/sharedFormSchema.ts b/src/pages/projectSettings/sharedFormSchema.ts index 3ffe662eec..f58e6d270d 100644 --- a/src/pages/projectSettings/sharedFormSchema.ts +++ b/src/pages/projectSettings/sharedFormSchema.ts @@ -3,7 +3,7 @@ export const projectName = { type: "string" as "string", title: "Project Name", minLength: 1, - format: "noSpaces", + format: "noSpecialCharacters", }, uiSchema: { "ui:data-cy": "project-name-input", @@ -14,7 +14,7 @@ export const projectId = { schema: { type: "string" as "string", title: "Project ID", - format: "noSpaces", + format: "noSpecialCharacters", }, uiSchema: { "ui:data-cy": "project-id-input", diff --git a/src/utils/validators/index.ts b/src/utils/validators/index.ts index a786fa90b6..9af2a3a581 100644 --- a/src/utils/validators/index.ts +++ b/src/utils/validators/index.ts @@ -132,16 +132,27 @@ const validateRegexp = (regexp: string): boolean => { } }; +/** + * `validateNoSpecialCharacters` tests if a provided string contains no special characters + * @param str - The string to test. + * @returns - true if the string has no special characters and false otherwise + */ +const validateNoSpecialCharacters = (str: string): boolean => { + const noSpecialCharacters = /^[0-9a-zA-Z-._]*$/; + return noSpecialCharacters.test(str); +}; + export { validateDuration, validateEmail, validateJira, validateJiraURL, + validateNoSpecialCharacters, validateObjectId, validatePercentage, validateRegexp, - validateSSHPublicKey, validateSlack, + validateSSHPublicKey, validateURL, validateURLTemplate, }; diff --git a/src/utils/validators/validators.test.ts b/src/utils/validators/validators.test.ts index 061ca8a63f..4be7ce014d 100644 --- a/src/utils/validators/validators.test.ts +++ b/src/utils/validators/validators.test.ts @@ -1,13 +1,29 @@ import { - validateRegexp, + validateJira, + validateJiraURL, + validateNoSpecialCharacters, validateObjectId, + validateRegexp, + validateSlack, validateSSHPublicKey, - validateJiraURL, - validateJira, validateURL, - validateSlack, } from "."; +describe("validateNoSpecialCharacters", () => { + it("returns true if string has no special characters", () => { + expect(validateNoSpecialCharacters("")).toBe(true); + expect(validateNoSpecialCharacters("hello_world")).toBe(true); + expect(validateNoSpecialCharacters("hello-world123")).toBe(true); + expect(validateNoSpecialCharacters("helloworld.123")).toBe(true); + expect(validateNoSpecialCharacters("helloWorld123")).toBe(true); + + expect(validateNoSpecialCharacters(" ")).toBe(false); + expect(validateNoSpecialCharacters("s ")).toBe(false); + expect(validateNoSpecialCharacters("he/lloworld")).toBe(false); + expect(validateNoSpecialCharacters("hello%world")).toBe(false); + }); +}); + describe("validateObjectId", () => { it("validates object ids", () => { expect(validateObjectId("5f74d99ab2373627c047c5e5")).toBeTruthy(); From 6b8a90714464242b3352ac7b16e156455f196b34 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Thu, 12 Oct 2023 13:39:56 -0400 Subject: [PATCH 2/7] encode projectId for backwards compatibility --- src/constants/routes.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 257538b0b7..795cfc81e5 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -227,11 +227,9 @@ export const getProjectSettingsRoute = ( projectId: string, tab?: ProjectSettingsTabRoutes ) => { - if (!tab) { - return `${paths.project}/${projectId}/${PageNames.Settings}`; - } - - return `${paths.project}/${projectId}/${PageNames.Settings}/${tab}`; + const encodedProjectId = encodeURIComponent(projectId); + const root = `${paths.project}/${encodedProjectId}/${PageNames.Settings}`; + return tab ? `${root}/${tab}` : root; }; export const getDistroSettingsRoute = ( From 93719e0b0fd454d7b17db6e6fa02a2cf8f3c7833 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Thu, 12 Oct 2023 14:33:37 -0400 Subject: [PATCH 3/7] allow more symbols --- src/components/SpruceForm/errors.ts | 2 +- src/utils/validators/index.ts | 2 +- src/utils/validators/validators.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/SpruceForm/errors.ts b/src/components/SpruceForm/errors.ts index 5fe0fb00fb..605c2b11ea 100644 --- a/src/components/SpruceForm/errors.ts +++ b/src/components/SpruceForm/errors.ts @@ -55,7 +55,7 @@ export const transformErrors = (errors: AjvError[]) => return { ...error, message: - "Value can only contain numbers, letters, periods and hyphens.", + "Value can only contain numbers, letters and these symbols: -._~().", }; case "noSpaces": return { diff --git a/src/utils/validators/index.ts b/src/utils/validators/index.ts index 9af2a3a581..00ab9e8dc3 100644 --- a/src/utils/validators/index.ts +++ b/src/utils/validators/index.ts @@ -138,7 +138,7 @@ const validateRegexp = (regexp: string): boolean => { * @returns - true if the string has no special characters and false otherwise */ const validateNoSpecialCharacters = (str: string): boolean => { - const noSpecialCharacters = /^[0-9a-zA-Z-._]*$/; + const noSpecialCharacters = /^[0-9a-zA-Z-._~()]*$/; return noSpecialCharacters.test(str); }; diff --git a/src/utils/validators/validators.test.ts b/src/utils/validators/validators.test.ts index 4be7ce014d..45ad6277e7 100644 --- a/src/utils/validators/validators.test.ts +++ b/src/utils/validators/validators.test.ts @@ -12,13 +12,13 @@ import { describe("validateNoSpecialCharacters", () => { it("returns true if string has no special characters", () => { expect(validateNoSpecialCharacters("")).toBe(true); - expect(validateNoSpecialCharacters("hello_world")).toBe(true); + expect(validateNoSpecialCharacters("helloworld-_~)(")).toBe(true); expect(validateNoSpecialCharacters("hello-world123")).toBe(true); expect(validateNoSpecialCharacters("helloworld.123")).toBe(true); + expect(validateNoSpecialCharacters("hellowo~rld.123")).toBe(true); expect(validateNoSpecialCharacters("helloWorld123")).toBe(true); expect(validateNoSpecialCharacters(" ")).toBe(false); - expect(validateNoSpecialCharacters("s ")).toBe(false); expect(validateNoSpecialCharacters("he/lloworld")).toBe(false); expect(validateNoSpecialCharacters("hello%world")).toBe(false); }); From 045fb5ec7b0f9045035b94ac627a1af40317343b Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Fri, 13 Oct 2023 10:52:20 -0400 Subject: [PATCH 4/7] refactor --- src/components/SpruceForm/errors.ts | 4 ++-- src/utils/validators/index.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/SpruceForm/errors.ts b/src/components/SpruceForm/errors.ts index 605c2b11ea..3776466e6c 100644 --- a/src/components/SpruceForm/errors.ts +++ b/src/components/SpruceForm/errors.ts @@ -1,4 +1,5 @@ import { AjvError } from "@rjsf/core"; +import { allowedSymbols } from "utils/validators"; export enum Errors { Invisible = "invisible", @@ -54,8 +55,7 @@ export const transformErrors = (errors: AjvError[]) => case "noSpecialCharacters": return { ...error, - message: - "Value can only contain numbers, letters and these symbols: -._~().", + message: `Value can only contain numbers, letters and these symbols: ${allowedSymbols}.`, }; case "noSpaces": return { diff --git a/src/utils/validators/index.ts b/src/utils/validators/index.ts index 00ab9e8dc3..9def5f64c9 100644 --- a/src/utils/validators/index.ts +++ b/src/utils/validators/index.ts @@ -132,17 +132,20 @@ const validateRegexp = (regexp: string): boolean => { } }; +const allowedSymbols = "-._~()"; + /** * `validateNoSpecialCharacters` tests if a provided string contains no special characters * @param str - The string to test. * @returns - true if the string has no special characters and false otherwise */ const validateNoSpecialCharacters = (str: string): boolean => { - const noSpecialCharacters = /^[0-9a-zA-Z-._~()]*$/; + const noSpecialCharacters = new RegExp(`^[0-9a-zA-Z${allowedSymbols}]*$`); return noSpecialCharacters.test(str); }; export { + allowedSymbols, validateDuration, validateEmail, validateJira, From f3e07d6120ac36f52aaf564f9f7ecd564e13b26d Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Fri, 13 Oct 2023 10:55:37 -0400 Subject: [PATCH 5/7] comment --- src/constants/routes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 795cfc81e5..24f41b510d 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -227,6 +227,7 @@ export const getProjectSettingsRoute = ( projectId: string, tab?: ProjectSettingsTabRoutes ) => { + // Encode projectId for backwards compatibilty const encodedProjectId = encodeURIComponent(projectId); const root = `${paths.project}/${encodedProjectId}/${PageNames.Settings}`; return tab ? `${root}/${tab}` : root; From 616e40b30d65c6880f7b743786d3d6b6c28be240 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Fri, 13 Oct 2023 13:33:08 -0400 Subject: [PATCH 6/7] add formatting --- src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx b/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx index c21b3a230c..76d97f8e80 100644 --- a/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx +++ b/src/pages/projectSettings/tabs/GeneralTab/getFormSchema.tsx @@ -82,6 +82,7 @@ export const getFormSchema = ( title: "Identifier", default: "", minLength: 1, + format: "noSpecialCharacters", }, }), batchTime: { From c8a682e97242227b368bfb1d9f1733263d65f063 Mon Sep 17 00:00:00 2001 From: Arjun Patel Date: Fri, 13 Oct 2023 13:58:18 -0400 Subject: [PATCH 7/7] add comment --- src/constants/routes.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/constants/routes.ts b/src/constants/routes.ts index 24f41b510d..6f8305ea0e 100644 --- a/src/constants/routes.ts +++ b/src/constants/routes.ts @@ -227,7 +227,9 @@ export const getProjectSettingsRoute = ( projectId: string, tab?: ProjectSettingsTabRoutes ) => { - // Encode projectId for backwards compatibilty + // Encode projectId for backwards compatibilty. + // Encoding can be removed when all projectIDs + // are URL friendly withou encoding const encodedProjectId = encodeURIComponent(projectId); const root = `${paths.project}/${encodedProjectId}/${PageNames.Settings}`; return tab ? `${root}/${tab}` : root;