diff --git a/.eslintrc.js b/.eslintrc.js index 4a52717e5bc5a..ce94492ede6e6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -345,8 +345,6 @@ const AXIOS_LEGACY_CONSUMERS = [ 'x-pack/solutions/security/plugins/security_solution/common/endpoint/format_axios_error.ts', 'x-pack/solutions/security/plugins/security_solution/common/endpoint/utils/**/*.{js,mjs,ts,tsx}', 'x-pack/solutions/security/plugins/security_solution/scripts/endpoint/**/*.{js,mjs,ts,tsx}', - 'x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts', - 'x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/**/*.{js,mjs,ts,tsx}', 'x-pack/solutions/security/plugins/security_solution/scripts/telemetry/**/*.{js,mjs,ts,tsx}', 'x-pack/solutions/security/plugins/security_solution/server/integration_tests/**/*.{js,mjs,ts,tsx}', 'x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/scripts/**/*.{js,mjs,ts,tsx}', diff --git a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts index 36272b0294f31..f3a17f01ab46f 100644 --- a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts +++ b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts @@ -18,14 +18,12 @@ import crypto from 'crypto'; import fs from 'fs'; import { exec } from 'child_process'; import { createFailError } from '@kbn/dev-cli-errors'; -import axios, { AxiosError } from 'axios'; import path from 'path'; import os from 'os'; import pRetry from 'p-retry'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants'; -import { catchAxiosErrorFormatAndThrow } from '../../common/endpoint/format_axios_error'; import { createToolingLogger } from '../../common/endpoint/data_loaders/utils'; import { renderSummaryTable } from './print_run'; import { @@ -58,9 +56,10 @@ let log: ToolingLog = new ToolingLog({ }); const API_HEADERS = Object.freeze({ + 'Content-Type': 'application/json', 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution', - [ELASTIC_HTTP_VERSION_HEADER]: [INITIAL_REST_VERSION], + [ELASTIC_HTTP_VERSION_HEADER]: INITIAL_REST_VERSION, }); const PROVIDERS = Object.freeze({ providerType: 'basic', @@ -83,15 +82,17 @@ export function proxyHealthcheck(proxyUrl: string): Promise { const fetchHealthcheck = async (attemptNum: number) => { log.info(`Retry number ${attemptNum} to check if Elasticsearch is green.`); - const response = await axios.get(`${proxyUrl}/healthcheck`); + const response = await fetch(`${proxyUrl}/healthcheck`); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + log.info(`The proxy service is available.`); return response.status === 200; }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError) { - log.info(`The proxy service is not available. A retry will be triggered soon...`); - } + onFailedAttempt: () => { + log.info(`The proxy service is not available. A retry will be triggered soon...`); }, retries: 4, factor: 2, @@ -106,19 +107,22 @@ export function waitForEsStatusGreen(esUrl: string, auth: string, runnerId: stri const fetchHealthStatusAttempt = async (attemptNum: number) => { log.info(`Retry number ${attemptNum} to check if Elasticsearch is green.`); - const response = await axios - .get(`${esUrl}/_cluster/health?wait_for_status=green&timeout=50s`, { - headers: { - Authorization: `Basic ${auth}`, - }, - }) - .catch(catchAxiosErrorFormatAndThrow); + const response = await fetch(`${esUrl}/_cluster/health?wait_for_status=green&timeout=50s`, { + headers: { + Authorization: `Basic ${auth}`, + }, + }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { status: string }; - log.info(`${runnerId}: Elasticsearch is ready with status ${response.data.status}.`); + log.info(`${runnerId}: Elasticsearch is ready with status ${data.status}.`); }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { log.info( `${runnerId}: The Elasticsearch URL is not yet reachable. A retry will be triggered soon...` ); @@ -140,22 +144,25 @@ export function waitForKibanaAvailable( ): Promise { const fetchKibanaStatusAttempt = async (attemptNum: number) => { log.info(`Retry number ${attemptNum} to check if kibana is available.`); - const response = await axios - .get(`${kbUrl}/api/status`, { - headers: { - Authorization: `Basic ${auth}`, - }, - }) - .catch(catchAxiosErrorFormatAndThrow); - if (response.data.status.overall.level !== 'available') { + const response = await fetch(`${kbUrl}/api/status`, { + headers: { + Authorization: `Basic ${auth}`, + }, + }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { status: { overall: { level: string } } }; + if (data.status.overall.level !== 'available') { throw new Error(`${runnerId}: Kibana is not available. A retry will be triggered soon...`); } else { - log.info(`${runnerId}: Kibana status overall is ${response.data.status.overall.level}.`); + log.info(`${runnerId}: Kibana status overall is ${data.status.overall.level}.`); } }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { log.info( `${runnerId}: The Kibana URL is not yet reachable. A retry will be triggered soon...` ); @@ -175,17 +182,18 @@ export function waitForEsAccess(esUrl: string, auth: string, runnerId: string): const fetchEsAccessAttempt = async (attemptNum: number) => { log.info(`Retry number ${attemptNum} to check if can be accessed.`); - await axios - .get(`${esUrl}`, { - headers: { - Authorization: `Basic ${auth}`, - }, - }) - .catch(catchAxiosErrorFormatAndThrow); + const response = await fetch(`${esUrl}`, { + headers: { + Authorization: `Basic ${auth}`, + }, + }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { log.info( `${runnerId}: The elasticsearch url is not yet reachable. A retry will be triggered soon...` ); @@ -209,15 +217,18 @@ function waitForKibanaLogin(kbUrl: string, credentials: Credentials): Promise { log.info(`Retry number ${attemptNum} to check if login can be performed.`); - axios - .post(`${kbUrl}/internal/security/login`, body, { - headers: API_HEADERS, - }) - .catch(catchAxiosErrorFormatAndThrow); + const response = await fetch(`${kbUrl}/internal/security/login`, { + method: 'POST', + headers: API_HEADERS, + body: JSON.stringify(body), + }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { log.info('Project is not reachable. A retry will be triggered soon...'); } else { log.error(`${error.message}`); diff --git a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/cloud_project_handler.ts b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/cloud_project_handler.ts index 28fea5ed88841..807987c2a4531 100644 --- a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/cloud_project_handler.ts +++ b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/cloud_project_handler.ts @@ -5,7 +5,6 @@ * 2.0. */ -import axios, { AxiosError } from 'axios'; import pRetry from 'p-retry'; import type { ProductType, @@ -65,48 +64,57 @@ export class CloudHandler extends ProjectHandler { } try { - const response = await axios.post( - `${this.baseEnvUrl}/api/v1/serverless/projects/security`, - body, - { - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - } - ); + const response = await fetch(`${this.baseEnvUrl}/api/v1/serverless/projects/security`, { + method: 'POST', + headers: { + Authorization: `ApiKey ${this.apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { + name: string; + id: string; + region_id: string; + endpoints: { elasticsearch: string; kibana: string }; + type: string; + }; return { - name: response.data.name, - id: response.data.id, - region: response.data.region_id, - es_url: `${response.data.endpoints.elasticsearch}:443`, - kb_url: `${response.data.endpoints.kibana}:443`, - product: response.data.type, + name: data.name, + id: data.id, + region: data.region_id, + es_url: `${data.endpoints.elasticsearch}:443`, + kb_url: `${data.endpoints.kibana}:443`, + product: data.type, }; } catch (error) { - if (error instanceof AxiosError) { - const errorData = JSON.stringify(error.response?.data); - this.log.error(`${error.response?.status}:${errorData}`); - } else { - this.log.error(`${error.message}`); - } + this.log.error(`${error.message}`); } } // Method to invoke the delete project API for serverless. async deleteSecurityProject(projectId: string, projectName: string): Promise { try { - await axios.delete(`${this.baseEnvUrl}/api/v1/serverless/projects/security/${projectId}`, { - headers: { - Authorization: `ApiKey ${this.apiKey}`, - }, - }); + const response = await fetch( + `${this.baseEnvUrl}/api/v1/serverless/projects/security/${projectId}`, + { + method: 'DELETE', + headers: { + Authorization: `ApiKey ${this.apiKey}`, + }, + } + ); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + this.log.info(`Project ${projectName} was successfully deleted!`); } catch (error) { - if (error instanceof AxiosError) { - this.log.error(`${error.response?.status}:${error.response?.data}`); - } else { - this.log.error(`${error.message}`); - } + this.log.error(`${error.message}`); } } @@ -115,25 +123,32 @@ export class CloudHandler extends ProjectHandler { this.log.info(`${runnerId} : Reseting credentials`); const fetchResetCredentialsStatusAttempt = async (attemptNum: number) => { - const response = await axios.post( + const response = await fetch( `${this.baseEnvUrl}/api/v1/serverless/projects/security/${projectId}/_reset-internal-credentials`, - {}, { + method: 'POST', headers: { Authorization: `ApiKey ${this.apiKey}`, + 'Content-Type': 'application/json', }, + body: JSON.stringify({}), } ); - this.log.info('Credentials have ben reset'); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { password: string; username: string }; + this.log.info('Credentials have been reset'); return { - password: response.data.password, - username: response.data.username, + password: data.password, + username: data.username, }; }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { this.log.info('Project is not reachable. A retry will be triggered soon..'); } else { this.log.error(`${error.message}`); @@ -151,7 +166,7 @@ export class CloudHandler extends ProjectHandler { waitForProjectInitialized(projectId: string): Promise { const fetchProjectStatusAttempt = async (attemptNum: number) => { this.log.info(`Retry number ${attemptNum} to check if project is initialized.`); - const response = await axios.get( + const response = await fetch( `${this.baseEnvUrl}/api/v1/serverless/projects/security/${projectId}/status`, { headers: { @@ -159,16 +174,21 @@ export class CloudHandler extends ProjectHandler { }, } ); - if (response.data.phase !== 'initialized') { - this.log.info(response.data); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { phase: string }; + if (data.phase !== 'initialized') { + this.log.info(data); throw new Error('Project is not initialized. A retry will be triggered soon...'); } else { this.log.info('Project is initialized'); } }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { this.log.info('Project is not reachable. A retry will be triggered soon...'); } else { this.log.error(`${error.message}`); diff --git a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/proxy_project_handler.ts b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/proxy_project_handler.ts index 14d62f80efdfd..e980f986e10a4 100644 --- a/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/proxy_project_handler.ts +++ b/x-pack/solutions/security/plugins/security_solution/scripts/run_cypress/project_handler/proxy_project_handler.ts @@ -5,7 +5,6 @@ * 2.0. */ -import axios, { AxiosError } from 'axios'; import pRetry from 'p-retry'; import type { ProductType, @@ -65,47 +64,61 @@ export class ProxyHandler extends ProjectHandler { } try { - const response = await axios.post(`${this.baseEnvUrl}/projects`, body, { + const response = await fetch(`${this.baseEnvUrl}/projects`, { + method: 'POST', headers: { Authorization: `Basic ${this.proxyAuth}`, + 'Content-Type': 'application/json', }, + body: JSON.stringify(body), }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { + name: string; + project_id: string; + region_id: string; + elasticsearch_endpoint: string; + kibana_endpoint: string; + project_type: string; + id: number; + organization_id: number; + organization_name: string; + }; return { - name: response.data.name, - id: response.data.project_id, - region: response.data.region_id, - es_url: `${response.data.elasticsearch_endpoint}:443`, - kb_url: `${response.data.kibana_endpoint}:443`, - product: response.data.project_type, - proxy_id: response.data.id, - proxy_org_id: response.data.organization_id, - proxy_org_name: response.data.organization_name, + name: data.name, + id: data.project_id, + region: data.region_id, + es_url: `${data.elasticsearch_endpoint}:443`, + kb_url: `${data.kibana_endpoint}:443`, + product: data.project_type, + proxy_id: data.id, + proxy_org_id: data.organization_id, + proxy_org_name: data.organization_name, }; } catch (error) { - if (error instanceof AxiosError) { - const errorData = JSON.stringify(error.response?.data); - this.log.error(`${error.response?.status}:${errorData}`); - } else { - this.log.error(`${error.message}`); - } + this.log.error(`${error.message}`); } } // Method to invoke the delete project API for serverless. async deleteSecurityProject(projectId: string, projectName: string): Promise { try { - await axios.delete(`${this.baseEnvUrl}/projects/${projectId}`, { + const response = await fetch(`${this.baseEnvUrl}/projects/${projectId}`, { + method: 'DELETE', headers: { Authorization: `Basic ${this.proxyAuth}`, }, }); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + this.log.info(`Project ${projectName} was successfully deleted!`); } catch (error) { - if (error instanceof AxiosError) { - this.log.error(`${error.response?.status}:${error.response?.data}`); - } else { - this.log.error(`${error.message}`); - } + this.log.error(`${error.message}`); } } @@ -114,25 +127,32 @@ export class ProxyHandler extends ProjectHandler { this.log.info(`${runnerId} : Reseting credentials`); const fetchResetCredentialsStatusAttempt = async (attemptNum: number) => { - const response = await axios.post( + const response = await fetch( `${this.baseEnvUrl}/projects/${projectId}/_reset-internal-credentials`, - {}, { + method: 'POST', headers: { Authorization: `Basic ${this.proxyAuth}`, + 'Content-Type': 'application/json', }, + body: JSON.stringify({}), } ); - this.log.info('Credentials have ben reset'); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { password: string; username: string }; + this.log.info('Credentials have been reset'); return { - password: response.data.password, - username: response.data.username, + password: data.password, + username: data.username, }; }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { this.log.info('Project is not reachable. A retry will be triggered soon..'); } else { this.log.error(`${error.message}`); @@ -150,21 +170,26 @@ export class ProxyHandler extends ProjectHandler { waitForProjectInitialized(projectId: string): Promise { const fetchProjectStatusAttempt = async (attemptNum: number) => { this.log.info(`Retry number ${attemptNum} to check if project is initialized.`); - const response = await axios.get(`${this.baseEnvUrl}/projects/${projectId}/status`, { + const response = await fetch(`${this.baseEnvUrl}/projects/${projectId}/status`, { headers: { Authorization: `Basic ${this.proxyAuth}`, }, }); - if (response.data.phase !== 'initialized') { - this.log.info(response.data); + if (!response.ok) { + throw new Error(`${response.status}:${await response.text()}`); + } + + const data = (await response.json()) as { phase: string }; + if (data.phase !== 'initialized') { + this.log.info(data); throw new Error('Project is not initialized. A retry will be triggered soon...'); } else { this.log.info('Project is initialized'); } }; const retryOptions = { - onFailedAttempt: (error: Error | AxiosError) => { - if (error instanceof AxiosError && error.code === 'ENOTFOUND') { + onFailedAttempt: (error: Error) => { + if ((error as { cause?: { code?: string } }).cause?.code === 'ENOTFOUND') { this.log.info('Project is not reachable. A retry will be triggered soon...'); } else { this.log.error(`${error.message}`);