From 744ed517c60253270f227fb8a9f8b4374635bf2d Mon Sep 17 00:00:00 2001 From: Ian Bolton Date: Fri, 19 Apr 2024 10:14:45 -0400 Subject: [PATCH] - Upgrade axios package to fix CVE - Remove axios-mock package Signed-off-by: Ian Bolton --- client/package.json | 3 +- client/src/app/api/rest.ts | 39 ++++---- .../__tests__/analysis-wizard.test.tsx | 19 +++- .../__tests__/application-form.test.tsx | 26 ++--- client/src/app/pages/controls/tags/tags.tsx | 4 +- .../__tests__/identity-form.test.tsx | 15 ++- .../proxies/__tests__/proxy-form.test.tsx | 98 +++++++++---------- client/src/app/test-config/mockInstance.ts | 4 - client/src/app/utils/utils.test.ts | 27 ----- client/src/app/utils/utils.ts | 15 +-- package-lock.json | 38 ++++--- package.json | 3 +- 12 files changed, 131 insertions(+), 160 deletions(-) delete mode 100644 client/src/app/test-config/mockInstance.ts diff --git a/client/package.json b/client/package.json index de932e6874..dbc3a3fe32 100644 --- a/client/package.json +++ b/client/package.json @@ -34,7 +34,7 @@ "@react-keycloak/web": "^3.4.0", "@tanstack/react-query": "^4.22.0", "@tanstack/react-query-devtools": "^4.22.0", - "axios": "^0.21.2", + "axios": "^1.6.8", "dayjs": "^1.11.7", "ejs": "^3.1.7", "fast-xml-parser": "^4.0.3", @@ -71,7 +71,6 @@ "@types/react-measure": "^2.0.12", "@types/react-router-dom": "^5.1.7", "@types/tinycolor2": "^1.4.6", - "axios-mock-adapter": "^1.19.0", "browserslist": "^4.19.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", "copy-webpack-plugin": "^12.0.2", diff --git a/client/src/app/api/rest.ts b/client/src/app/api/rest.ts index 68c10d1b3b..df92de2d33 100644 --- a/client/src/app/api/rest.ts +++ b/client/src/app/api/rest.ts @@ -1,6 +1,4 @@ -// hub OpenAPI definition: https://github.com/konveyor/tackle2-hub/blob/main/docs/openapi3.json - -import axios, { AxiosPromise } from "axios"; +import axios, { AxiosPromise, RawAxiosRequestHeaders } from "axios"; import { AnalysisDependency, @@ -107,14 +105,18 @@ export const QUESTIONNAIRES = HUB + "/questionnaires"; export const ARCHETYPES = HUB + "/archetypes"; -// PATHFINDER -export const PATHFINDER = "/hub/pathfinder"; export const ASSESSMENTS = HUB + "/assessments"; -const jsonHeaders = { headers: { Accept: "application/json" } }; -const formHeaders = { headers: { Accept: "multipart/form-data" } }; -const fileHeaders = { headers: { Accept: "application/json" } }; -const yamlHeaders = { headers: { Accept: "application/x-yaml" } }; +const jsonHeaders: RawAxiosRequestHeaders = { + Accept: "application/json", +}; +const formHeaders: RawAxiosRequestHeaders = { + Accept: "multipart/form-data", +}; +const fileHeaders: RawAxiosRequestHeaders = { Accept: "application/json" }; +const yamlHeaders: RawAxiosRequestHeaders = { + Accept: "application/x-yaml", +}; type Direction = "asc" | "desc"; @@ -137,7 +139,7 @@ export const getApplicationDependencies = ( return axios .get(`${APPLICATION_DEPENDENCY}`, { params, - headers: jsonHeaders.headers, + headers: jsonHeaders, }) .then((response) => response.data); }; @@ -243,7 +245,7 @@ export const deleteAssessment = (id: number) => { }; export const getIdentities = () => { - return axios.get(`${IDENTITIES}`, jsonHeaders); + return axios.get(`${IDENTITIES}`, { headers: jsonHeaders }); }; export const createIdentity = (obj: New) => { @@ -322,8 +324,7 @@ export function getTaskById( format: string, merged: boolean = false ): Promise { - const headers = - format === "yaml" ? { ...yamlHeaders.headers } : { ...jsonHeaders.headers }; + const headers = format === "yaml" ? { ...yamlHeaders } : { ...jsonHeaders }; const responseType = format === "yaml" ? "text" : "json"; let url = `${TASKS}/${id}`; @@ -371,11 +372,9 @@ export const uploadFileTaskgroup = ({ formData: any; file: any; }) => { - return axios.post( - `${TASKGROUPS}/${id}/bucket/${path}`, - formData, - formHeaders - ); + return axios.post(`${TASKGROUPS}/${id}/bucket/${path}`, formData, { + headers: formHeaders, + }); }; export const removeFileTaskgroup = ({ @@ -430,7 +429,9 @@ export const createFile = ({ file: IReadFile; }) => axios - .post(`${FILES}/${file.fileName}`, formData, fileHeaders) + .post(`${FILES}/${file.fileName}`, formData, { + headers: fileHeaders, + }) .then((response) => { return response.data; }); diff --git a/client/src/app/pages/applications/analysis-wizard/__tests__/analysis-wizard.test.tsx b/client/src/app/pages/applications/analysis-wizard/__tests__/analysis-wizard.test.tsx index 94bb00cf6f..a2e1cd4a0d 100644 --- a/client/src/app/pages/applications/analysis-wizard/__tests__/analysis-wizard.test.tsx +++ b/client/src/app/pages/applications/analysis-wizard/__tests__/analysis-wizard.test.tsx @@ -2,11 +2,9 @@ import React from "react"; import "@testing-library/jest-dom"; import { render, screen, waitFor } from "@app/test-config/test-utils"; import { AnalysisWizard } from "../analysis-wizard"; -import { TASKGROUPS } from "@app/api/rest"; -import mock from "@app/test-config/mockInstance"; import userEvent from "@testing-library/user-event"; - -mock.onAny().reply(200, []); +import { server } from "@mocks/server"; +import { rest } from "msw"; const applicationData1 = { id: 1, @@ -53,6 +51,13 @@ const taskgroupData = { }; describe("", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + afterEach(() => { + server.resetHandlers(); + }); + let isAnalyzeModalOpen = true; const setAnalyzeModalOpen = (toggle: boolean) => (isAnalyzeModalOpen = toggle); @@ -157,7 +162,11 @@ describe("", () => { }, ]; - mock.onPost(`${TASKGROUPS}`).reply(200, taskgroupData); + server.use( + rest.get("/hub/taskgroups", (req, res, ctx) => { + return res(ctx.json([taskgroupData])); + }) + ); render( { const mockChangeValue = jest.fn(); + beforeAll(() => server.listen({ onUnhandledRequest: "warn" })); + + beforeEach(() => { + jest.clearAllMocks(); + }); + afterEach(() => { + server.resetHandlers(); + }); + server.use( + rest.get("/hub/businessservices", (req, res, ctx) => { + return res(ctx.status(200), ctx.json([{ id: 1, name: "service" }])); + }) + ); it("Validation tests", async () => { - const businessServices: BusinessService[] = [{ id: 1, name: "service" }]; - - mock - .onGet(`${BUSINESS_SERVICES}`) - .reply(200, businessServices) - .onAny() - .reply(200, []); - render( ); diff --git a/client/src/app/pages/controls/tags/tags.tsx b/client/src/app/pages/controls/tags/tags.tsx index 2a8e3d5c77..804662449f 100644 --- a/client/src/app/pages/controls/tags/tags.tsx +++ b/client/src/app/pages/controls/tags/tags.tsx @@ -91,7 +91,7 @@ export const Tags: React.FC = () => { const onDeleteTagError = (error: AxiosError) => { if ( error.response?.status === 500 && - error.response?.data.error === "FOREIGN KEY constraint failed" + error.message === "FOREIGN KEY constraint failed" ) { pushNotification({ title: "Cannot delete a used tag", @@ -121,7 +121,7 @@ export const Tags: React.FC = () => { const onDeleteTagCategoryError = (error: AxiosError) => { if ( error.response?.status === 500 && - error.response?.data.error === "FOREIGN KEY constraint failed" + error.message === "FOREIGN KEY constraint failed" ) { pushNotification({ title: "Cannot delete a used tag", diff --git a/client/src/app/pages/identities/components/identity-form/__tests__/identity-form.test.tsx b/client/src/app/pages/identities/components/identity-form/__tests__/identity-form.test.tsx index 73978806cf..20e69a236c 100644 --- a/client/src/app/pages/identities/components/identity-form/__tests__/identity-form.test.tsx +++ b/client/src/app/pages/identities/components/identity-form/__tests__/identity-form.test.tsx @@ -6,17 +6,16 @@ import { fireEvent, } from "@app/test-config/test-utils"; -import { IDENTITIES } from "@app/api/rest"; -import mock from "@app/test-config/mockInstance"; - import { IdentityForm } from ".."; import "@testing-library/jest-dom"; +import { server } from "@mocks/server"; -const data: any[] = []; +describe("Component: identity-form", () => { + beforeAll(() => server.listen({ onUnhandledRequest: "bypass" })); -mock.onGet(`${IDENTITIES}`).reply(200, data); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); -describe("Component: identity-form", () => { const mockChangeValue = jest.fn(); it("Display form on initial load", async () => { @@ -176,7 +175,7 @@ describe("Component: identity-form", () => { expect(createButton).toBeDisabled(); }); - it.skip("Identity form validation test - source - key upload", async () => { + it("Identity form validation test - source - key upload", async () => { render(); const identityNameInput = await screen.findByLabelText("Name *"); @@ -231,7 +230,7 @@ describe("Component: identity-form", () => { expect(createButton).toBeEnabled(); }); - it.skip("Identity form validation test - maven", async () => { + it("Identity form validation test - maven", async () => { render(); const identityNameInput = await screen.findByLabelText("Name *"); diff --git a/client/src/app/pages/proxies/__tests__/proxy-form.test.tsx b/client/src/app/pages/proxies/__tests__/proxy-form.test.tsx index 432c70cae7..ece850d38c 100644 --- a/client/src/app/pages/proxies/__tests__/proxy-form.test.tsx +++ b/client/src/app/pages/proxies/__tests__/proxy-form.test.tsx @@ -8,40 +8,30 @@ import { } from "@app/test-config/test-utils"; import { Proxies } from "../proxies"; -import MockAdapter from "axios-mock-adapter"; -import { IDENTITIES, PROXIES } from "@app/api/rest"; -import axios from "axios"; -import { Proxy, Identity } from "@app/api/models"; import userEvent from "@testing-library/user-event"; -import { ProxyForm } from "../proxy-form"; -import mock from "@app/test-config/mockInstance"; - -const identitiesData: Identity[] = []; -mock.onGet(`${IDENTITIES}`).reply(200, identitiesData); - -const proxiesData = [ - { - host: "", - kind: "http", - port: 0, - excluded: [], - identity: null, - id: 1, - enabled: false, - }, - { - host: "", - kind: "https", - port: 0, - excluded: [], - identity: null, - id: 1, - enabled: false, - }, -]; -mock.onGet(`${PROXIES}`).reply(200, proxiesData); +import { server } from "@mocks/server"; +import { rest } from "msw"; describe("Component: proxy-form", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + afterEach(() => { + server.resetHandlers(); + }); + server.use( + rest.get("/hub/identities", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([ + { id: 0, name: "proxy-cred", kind: "proxy" }, + { id: 1, name: "maven-cred", kind: "maven" }, + { id: 2, name: "source-cred", kind: "source" }, + ]) + ); + }) + ); + it("Display switch statements on initial load", async () => { render(); await screen.findByLabelText("HTTP proxy"); @@ -49,7 +39,7 @@ describe("Component: proxy-form", () => { await screen.findByLabelText("HTTPS proxy"); }); - it.skip("Show HTTP proxy form when switch button clicked", async () => { + it("Show HTTP proxy form when switch button clicked", async () => { render(); const httpProxySwitch = await screen.findByLabelText("HTTP proxy"); @@ -62,7 +52,7 @@ describe("Component: proxy-form", () => { ); }); - it.skip("Show HTTPS proxy form when switch button clicked", async () => { + it("Show HTTPS proxy form when switch button clicked", async () => { render(); const httpsProxySwitch = await screen.findByLabelText("HTTPS proxy"); @@ -75,14 +65,19 @@ describe("Component: proxy-form", () => { ); }); - it.skip("Select http proxy identity", async () => { - const identitiesData: Identity[] = [ - { id: 0, name: "proxy-cred", kind: "proxy" }, - { id: 1, name: "maven-cred", kind: "maven" }, - { id: 2, name: "source-cred", kind: "source" }, - ]; - - mock.onGet(`${IDENTITIES}`).reply(200, identitiesData); + it("Select http proxy identity", async () => { + server.use( + rest.get("/hub/identities", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([ + { id: 0, name: "proxy-cred", kind: "proxy" }, + { id: 1, name: "maven-cred", kind: "maven" }, + { id: 2, name: "source-cred", kind: "source" }, + ]) + ); + }) + ); render(); const httpProxySwitch = await screen.findByLabelText("HTTP proxy"); @@ -112,14 +107,19 @@ describe("Component: proxy-form", () => { expect(sourceCred).toBeNull(); // it doesn't exist }); - it.skip("Select https proxy identity", async () => { - const identitiesData: Identity[] = [ - { id: 0, name: "proxy-cred", kind: "proxy" }, - { id: 1, name: "maven-cred", kind: "maven" }, - { id: 2, name: "source-cred", kind: "source" }, - ]; - - mock.onGet(`${IDENTITIES}`).reply(200, identitiesData); + it("Select https proxy identity", async () => { + server.use( + rest.get("/hub/identities", (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json([ + { id: 0, name: "proxy-cred", kind: "proxy" }, + { id: 1, name: "maven-cred", kind: "maven" }, + { id: 2, name: "source-cred", kind: "source" }, + ]) + ); + }) + ); render(); const httpsProxySwitch = await screen.findByLabelText("HTTPS proxy"); diff --git a/client/src/app/test-config/mockInstance.ts b/client/src/app/test-config/mockInstance.ts deleted file mode 100644 index f614569e8a..0000000000 --- a/client/src/app/test-config/mockInstance.ts +++ /dev/null @@ -1,4 +0,0 @@ -import MockAdapter from "axios-mock-adapter"; -import axios from "axios"; - -export default new MockAdapter(axios); diff --git a/client/src/app/utils/utils.test.ts b/client/src/app/utils/utils.test.ts index 99c634afa3..1cd13176c9 100644 --- a/client/src/app/utils/utils.test.ts +++ b/client/src/app/utils/utils.test.ts @@ -22,7 +22,6 @@ describe("utils", () => { isAxiosError: true, name: "error", message: errorMsg, - config: {}, toJSON: () => ({}), }; @@ -30,32 +29,6 @@ describe("utils", () => { expect(errorMessage).toBe(errorMsg); }); - it("getAxiosErrorMessage: should pick body message", () => { - const errorMsg = "Internal server error"; - - const mockAxiosError: AxiosError = { - isAxiosError: true, - name: "error", - message: "Network error", - config: {}, - response: { - data: { - errorMessage: errorMsg, - }, - status: 400, - statusText: "", - headers: {}, - config: {}, - }, - toJSON: () => ({}), - }; - - const errorMessage = getAxiosErrorMessage(mockAxiosError); - expect(errorMessage).toBe(errorMsg); - }); - - // getValidatedFromError - it("getValidatedFromError: given value should return 'error'", () => { const error = "Any value"; diff --git a/client/src/app/utils/utils.ts b/client/src/app/utils/utils.ts index 0e57049095..bbdc611fa5 100644 --- a/client/src/app/utils/utils.ts +++ b/client/src/app/utils/utils.ts @@ -6,19 +6,10 @@ import { Paths } from "@app/Paths"; // Axios error export const getAxiosErrorMessage = (axiosError: AxiosError) => { - if ( - axiosError.response && - axiosError.response.data && - axiosError.response.data.errorMessage - ) { - return axiosError.response.data.errorMessage; - } else if ( - axiosError.response?.data?.error && - typeof axiosError?.response?.data?.error === "string" - ) { - return axiosError?.response?.data?.error; - } else { + if (axiosError.response && axiosError.response.data && axiosError.message) { return axiosError.message; + } else { + return "Network error"; } }; diff --git a/package-lock.json b/package-lock.json index 997e0ab6dd..aa11c07a68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,7 @@ "@react-keycloak/web": "^3.4.0", "@tanstack/react-query": "^4.22.0", "@tanstack/react-query-devtools": "^4.22.0", - "axios": "^0.21.2", + "axios": "^1.6.8", "dayjs": "^1.11.7", "ejs": "^3.1.7", "fast-xml-parser": "^4.0.3", @@ -110,7 +110,6 @@ "@types/react-measure": "^2.0.12", "@types/react-router-dom": "^5.1.7", "@types/tinycolor2": "^1.4.6", - "axios-mock-adapter": "^1.19.0", "browserslist": "^4.19.1", "case-sensitive-paths-webpack-plugin": "^2.4.0", "copy-webpack-plugin": "^12.0.2", @@ -141,6 +140,16 @@ "webpack-merge": "^5.10.0" } }, + "client/node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "common": { "name": "@konveyor-ui/common", "version": "0.1.0", @@ -3777,8 +3786,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/attr-accept": { "version": "2.2.2", @@ -3804,23 +3812,11 @@ "version": "0.21.4", "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "peer": true, "dependencies": { "follow-redirects": "^1.14.0" } }, - "node_modules/axios-mock-adapter": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", - "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "is-buffer": "^2.0.5" - }, - "peerDependencies": { - "axios": ">= 0.17.0" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4715,7 +4711,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -6118,7 +6113,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -7698,7 +7692,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -13473,6 +13466,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index 6c0b0cba77..ef9ba61161 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "typescript": "^5.1.6" }, "overrides": { - "follow-redirects": "^1.15.6" + "follow-redirects": "^1.15.6", + "axios": "^1.6.8" } }