diff --git a/.eslintrc.js b/.eslintrc.js index d46463f64f7ef..f5abbf59b543c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -470,8 +470,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 e7eacea62e067..10c54954e61a8 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, @@ -110,19 +111,22 @@ export function waitForEsStatusGreen( 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(`${projectId}: Elasticsearch is ready with status ${response.data.status}.`); + log.info(`${projectId}: 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( `${projectId}: The Elasticsearch URL is not yet reachable. A retry will be triggered soon...` ); @@ -144,22 +148,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(`${projectId}: Kibana is not available. A retry will be triggered soon...`); } else { - log.info(`${projectId}: Kibana status overall is ${response.data.status.overall.level}.`); + log.info(`${projectId}: 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( `${projectId}: The Kibana URL is not yet reachable. A retry will be triggered soon...` ); @@ -179,17 +186,18 @@ export function waitForEsAccess(esUrl: string, auth: string, projectId: 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( `${projectId}: The elasticsearch url is not yet reachable. A retry will be triggered soon...` ); @@ -213,15 +221,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 c2f0bb251ec3c..ef4960a5025d1 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, @@ -76,48 +75,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}`); } } @@ -126,25 +134,32 @@ export class CloudHandler extends ProjectHandler { this.log.info(`${projectId} : 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({}), } ); + 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}`); @@ -162,7 +177,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: { @@ -170,16 +185,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.warning(`${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 693a3cb08415e..0935ef49decba 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, @@ -76,47 +75,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}`); } } @@ -125,25 +138,32 @@ export class ProxyHandler extends ProjectHandler { this.log.info(`${projectId} : 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({}), } ); + 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}`); @@ -161,21 +181,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.warning(`${error.message}`);