Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

EVG-20817: Restrict special characters in project name & ID input #2099

Merged
merged 7 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/components/SpruceForm/customFormats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
validateEmail,
validateJira,
validateJiraURL,
validateNoSpecialCharacters,
validatePercentage,
validateRegexp,
validateSlack,
Expand All @@ -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,
});
6 changes: 6 additions & 0 deletions src/components/SpruceForm/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AjvError } from "@rjsf/core";
import { allowedSymbols } from "utils/validators";

export enum Errors {
Invisible = "invisible",
Expand Down Expand Up @@ -51,6 +52,11 @@ export const transformErrors = (errors: AjvError[]) =>
};
case "format":
switch (error.params.format) {
case "noSpecialCharacters":
return {
...error,
message: `Value can only contain numbers, letters and these symbols: ${allowedSymbols}.`,
};
case "noSpaces":
return {
...error,
Expand Down
11 changes: 6 additions & 5 deletions src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,12 @@ export const getProjectSettingsRoute = (
projectId: string,
tab?: ProjectSettingsTabRoutes
) => {
if (!tab) {
return `${paths.project}/${projectId}/${PageNames.Settings}`;
}

return `${paths.project}/${projectId}/${PageNames.Settings}/${tab}`;
// Encode projectId for backwards compatibilty.
// Encoding can be removed when all projectIDs
// are URL friendly withou encoding
const encodedProjectId = encodeURIComponent(projectId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description says we don't need to encode the URL, should this be included

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for backwards compatibility, I'll make a comment in the code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice -- since the ticket mentions that we'll work with project admins to change their project identifiers, can we update the comment to note that we'll delete this when that is done?

const root = `${paths.project}/${encodedProjectId}/${PageNames.Settings}`;
return tab ? `${root}/${tab}` : root;
};

export const getDistroSettingsRoute = (
Expand Down
4 changes: 2 additions & 2 deletions src/pages/projectSettings/sharedFormSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const getFormSchema = (
title: "Identifier",
default: "",
minLength: 1,
format: "noSpecialCharacters",
},
}),
batchTime: {
Expand Down
16 changes: 15 additions & 1 deletion src/utils/validators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,30 @@ 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 = new RegExp(`^[0-9a-zA-Z${allowedSymbols}]*$`);
return noSpecialCharacters.test(str);
};

export {
allowedSymbols,
validateDuration,
validateEmail,
validateJira,
validateJiraURL,
validateNoSpecialCharacters,
validateObjectId,
validatePercentage,
validateRegexp,
validateSSHPublicKey,
validateSlack,
validateSSHPublicKey,
validateURL,
validateURLTemplate,
};
24 changes: 20 additions & 4 deletions src/utils/validators/validators.test.ts
Original file line number Diff line number Diff line change
@@ -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("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("he/lloworld")).toBe(false);
expect(validateNoSpecialCharacters("hello%world")).toBe(false);
});
});

describe("validateObjectId", () => {
it("validates object ids", () => {
expect(validateObjectId("5f74d99ab2373627c047c5e5")).toBeTruthy();
Expand Down