Skip to content

Commit

Permalink
🐛 Regex to avoid substring matching on route paths (#1337)
Browse files Browse the repository at this point in the history
This updates the formatPath function to use a regex pattern that matches
:k followed by either a / or the end of the string ($). This way, it
won't inadvertently replace parts of other placeholders.

Resolves #1335

---------

Signed-off-by: ibolton336 <ibolton@redhat.com>
  • Loading branch information
ibolton336 authored Sep 11, 2023
1 parent 22b29a6 commit f52d7e2
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 30 deletions.
10 changes: 0 additions & 10 deletions client/src/app/Paths.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
export const formatPath = (path: Paths, data: any) => {
let url = path as string;

for (const k of Object.keys(data)) {
url = url.replace(":" + k, data[k]);
}

return url;
};

export enum Paths {
base = "/",
notFound = "/not-found",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
import { AssessmentStakeholdersForm } from "../assessment-stakeholders-form";
import { CustomWizardFooter } from "../custom-wizard-footer";
import { getApplicationById, patchAssessment } from "@app/api/rest";
import { formatPath, Paths } from "@app/Paths";
import { Paths } from "@app/Paths";
import { NotificationsContext } from "@app/components/NotificationsContext";
import { getAxiosErrorMessage } from "@app/utils/utils";
import { formatPath, getAxiosErrorMessage } from "@app/utils/utils";
import { WizardStepNavDescription } from "../wizard-step-nav-description";
import { QuestionnaireForm } from "../questionnaire-form";
import { ConfirmDialog } from "@app/components/ConfirmDialog";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import BanIcon from "@patternfly/react-icons/dist/esm/icons/ban-icon";
import InfoCircleIcon from "@patternfly/react-icons/dist/esm/icons/info-circle-icon";

import { useAssessApplication } from "@app/hooks";
import { formatPath, Paths, ReviewRoute } from "@app/Paths";
import { Paths, ReviewRoute } from "@app/Paths";
import {
getApplicationByIdPromise,
getAssessmentById,
getAssessmentsPromise,
getReviewId,
} from "@app/api/rest";
import { Application, Assessment, Review } from "@app/api/models";
import { getAxiosErrorMessage } from "@app/utils/utils";
import { formatPath, getAxiosErrorMessage } from "@app/utils/utils";
import { ApplicationReviewPage } from "./components/application-review-page";
import { ApplicationDetails } from "./components/application-details";
import { ReviewForm } from "./components/review-form";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,12 @@ import { ToolbarBulkSelector } from "@app/components/ToolbarBulkSelector";
import { ApplicationDependenciesFormContainer } from "@app/components/ApplicationDependenciesFormContainer";
import { ConfirmDialog } from "@app/components/ConfirmDialog";
import { NotificationsContext } from "@app/components/NotificationsContext";
import { dedupeFunction, getAxiosErrorMessage } from "@app/utils/utils";
import { Paths, formatPath } from "@app/Paths";
import {
dedupeFunction,
formatPath,
getAxiosErrorMessage,
} from "@app/utils/utils";
import { Paths } from "@app/Paths";
import keycloak from "@app/keycloak";
import {
RBAC,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";

import { useLocalTableControls } from "@app/hooks/table-controls";
Expand All @@ -9,17 +8,13 @@ import {
TableRowContentWithControls,
} from "@app/components/TableControls";
import { NoDataEmptyState } from "@app/components/NoDataEmptyState";
import {
Application,
Assessment,
InitialAssessment,
Questionnaire,
} from "@app/api/models";
import { Application, InitialAssessment, Questionnaire } from "@app/api/models";
import { Button } from "@patternfly/react-core";
import { Paths, formatPath } from "@app/Paths";
import { Paths } from "@app/Paths";
import { useHistory } from "react-router-dom";
import { useFetchQuestionnaires } from "@app/queries/questionnaires";
import { useCreateAssessmentMutation } from "@app/queries/assessments";
import { formatPath } from "@app/utils/utils";
export interface AssessmentActionsTableProps {
application?: Application;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import {

import { IconedStatus } from "@app/components/IconedStatus";
import { ConfirmDialog } from "@app/components/ConfirmDialog";
import { formatPath, Paths } from "@app/Paths";
import { Paths } from "@app/Paths";
import { ApplicationImportSummary } from "@app/api/models";
import { formatDate } from "@app/utils/utils";
import { formatDate, formatPath } from "@app/utils/utils";
import { ImportApplicationsForm } from "../components/import-applications-form";
import { useLegacyPaginationState } from "@app/hooks/useLegacyPaginationState";
import {
Expand Down
22 changes: 19 additions & 3 deletions client/src/app/utils/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { act, render } from "@testing-library/react";
import { renderHook } from "@testing-library/react-hooks";
import { AxiosError } from "axios";
import {
getAxiosErrorMessage,
getValidatedFromError,
getValidatedFromErrors,
getToolbarChipKey,
customURLValidation,
gitUrlRegex,
standardURLRegex,
formatPath,
} from "./utils";
import { Paths } from "@app/Paths";

describe("utils", () => {
// getAxiosErrorMessage
Expand Down Expand Up @@ -158,3 +157,20 @@ describe("utils", () => {
expect(standardURLRegex.test(url)).toBe(true);
});
});
describe("formatPath function", () => {
it("should replace path parameters with values", () => {
const path = Paths.applicationsImportsDetails;
const data = { importId: "import123" };
const result = formatPath(path, data);

expect(result).toBe("/applications/application-imports/import123");
});

it("should handle missing data", () => {
const path = Paths.applicationsAssessment;
const data = {};
const result = formatPath(path, data);

expect(result).toBe("/applications/assessment/:assessmentId");
});
});
14 changes: 13 additions & 1 deletion client/src/app/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AxiosError } from "axios";
import { FormGroupProps, ToolbarChip } from "@patternfly/react-core";
import { ToolbarChip } from "@patternfly/react-core";
import { StringSchema } from "yup";
import { Paths } from "@app/Paths";

// Axios error

Expand Down Expand Up @@ -123,3 +124,14 @@ export const customURLValidation = (schema: StringSchema) => {
}
});
};

export const formatPath = (path: Paths, data: any) => {
let url = path as string;

for (const k of Object.keys(data)) {
const regex = new RegExp(`:${k}(/|$)`, "g");
url = url.replace(regex, data[k] + "$1");
}

return url;
};

0 comments on commit f52d7e2

Please sign in to comment.