Skip to content

Commit dd49c1a

Browse files
[8.19] chore(axios,security-solution): remove axios from telemetry/role scripts (#267944) (#268475)
# Backport This will backport the following commits from `main` to `8.19`: - [chore(axios,security-solution): remove axios from telemetry/role scripts (#267944)](#267944) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Aleh Zasypkin","email":"aleh.zasypkin@elastic.co"},"sourceCommit":{"committedDate":"2026-05-08T14:53:10Z","message":"chore(axios,security-solution): remove axios from telemetry/role scripts (#267944)\n\n## Summary\n\n> [!IMPORTANT]\n> **NOTE TO CODE OWNERS:** I'm modifying code I don't own day to day.\nPlease verify the changes still work as expected. For these migrations a\nquick run of the affected scripts/tests is worth more than a code-only\nreview.\n\nThis PR removes the `axios` dependency for files owned by\n`@elastic/security-solution`. Phase 4 of the axios migration tracked\nunder #266556.\n\n### Why\n\nNode.js 22 ships a native `fetch` API built on undici, and every browser\nKibana targets supports `fetch` natively. Removing axios cuts one\nruntime dependency and continues the per-team rollout that mirrors the\nearlier node-fetch migration\n([#250719](#250719) and siblings).\n\n### Changes\n\nThree files migrated, two files **deferred to a later phase**:\n\n- `scripts/telemetry/build_ebt_data_view.ts`: 1 axios.get + 1 axios.put.\nReplaced with `fetch`, `res.ok` check, and typed `await res.json()` for\nthe data-view fetch.\n- `scripts/telemetry/build_ebt_data_view.test.ts`: was mocking\n`axios.put` for `upsertRuntimeFields`. Switched to `jest.spyOn(global,\n'fetch')`. The 16 existing test cases pass unchanged in intent:\nname/type/url assertions adapted to read the `fetch(url, init)` argument\nshape (`init.body` is now `JSON.stringify(payload)` rather than the\nthird axios arg). Header equality and dotted-name handling assertions\nunchanged.\n-\n`server/lib/detection_engine/scripts/roles_users/create_role_and_user.ts`:\n2 axios.put calls. Replaced with `fetch` and an explicit non-2xx throw\nthat preserves status+body in the error message.\n\nRemoved the `scripts/telemetry/**` and\n`server/lib/detection_engine/scripts/**` entries from\n`AXIOS_LEGACY_CONSUMERS` in `.eslintrc.js`. New axios usage in either of\nthose directories is now blocked by the existing global ban.\n\n**Deferred to a later phase**:\n`server/integration_tests/configuration.test.ts` and\n`server/integration_tests/telemetry.test.ts` mock `axios` at the module\nlevel for code under `server/lib/telemetry/*`, which is owned by\n`@elastic/security-data-analytics` and not yet migrated. Test mocks must\nflip in lockstep with the production code they intercept; these two\ntests will migrate alongside that team's PR.\n\n### Behavior parity\n\nNative fetch does not throw on non-2xx, so each call site explicitly\nchecks `res.ok` / `res.status`. Errors thrown inside the migrated\nscripts now have the form `${status}:${body}` to keep the same\ndiagnostic content the original axios errors carried. The diff is\nintentionally minimal: variable names, comment placement, try-catch\nstructure, and error-handling shape from the original axios code are\npreserved.","sha":"312e26c1241eb65a63c33c6d5acc3593ef6956d1","branchLabelMapping":{"^v9.5.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["chore","release_note:skip","backport:all-open","v9.5.0"],"title":"chore(axios,security-solution): remove axios from telemetry/role scripts","number":267944,"url":"https://github.com/elastic/kibana/pull/267944","mergeCommit":{"message":"chore(axios,security-solution): remove axios from telemetry/role scripts (#267944)\n\n## Summary\n\n> [!IMPORTANT]\n> **NOTE TO CODE OWNERS:** I'm modifying code I don't own day to day.\nPlease verify the changes still work as expected. For these migrations a\nquick run of the affected scripts/tests is worth more than a code-only\nreview.\n\nThis PR removes the `axios` dependency for files owned by\n`@elastic/security-solution`. Phase 4 of the axios migration tracked\nunder #266556.\n\n### Why\n\nNode.js 22 ships a native `fetch` API built on undici, and every browser\nKibana targets supports `fetch` natively. Removing axios cuts one\nruntime dependency and continues the per-team rollout that mirrors the\nearlier node-fetch migration\n([#250719](#250719) and siblings).\n\n### Changes\n\nThree files migrated, two files **deferred to a later phase**:\n\n- `scripts/telemetry/build_ebt_data_view.ts`: 1 axios.get + 1 axios.put.\nReplaced with `fetch`, `res.ok` check, and typed `await res.json()` for\nthe data-view fetch.\n- `scripts/telemetry/build_ebt_data_view.test.ts`: was mocking\n`axios.put` for `upsertRuntimeFields`. Switched to `jest.spyOn(global,\n'fetch')`. The 16 existing test cases pass unchanged in intent:\nname/type/url assertions adapted to read the `fetch(url, init)` argument\nshape (`init.body` is now `JSON.stringify(payload)` rather than the\nthird axios arg). Header equality and dotted-name handling assertions\nunchanged.\n-\n`server/lib/detection_engine/scripts/roles_users/create_role_and_user.ts`:\n2 axios.put calls. Replaced with `fetch` and an explicit non-2xx throw\nthat preserves status+body in the error message.\n\nRemoved the `scripts/telemetry/**` and\n`server/lib/detection_engine/scripts/**` entries from\n`AXIOS_LEGACY_CONSUMERS` in `.eslintrc.js`. New axios usage in either of\nthose directories is now blocked by the existing global ban.\n\n**Deferred to a later phase**:\n`server/integration_tests/configuration.test.ts` and\n`server/integration_tests/telemetry.test.ts` mock `axios` at the module\nlevel for code under `server/lib/telemetry/*`, which is owned by\n`@elastic/security-data-analytics` and not yet migrated. Test mocks must\nflip in lockstep with the production code they intercept; these two\ntests will migrate alongside that team's PR.\n\n### Behavior parity\n\nNative fetch does not throw on non-2xx, so each call site explicitly\nchecks `res.ok` / `res.status`. Errors thrown inside the migrated\nscripts now have the form `${status}:${body}` to keep the same\ndiagnostic content the original axios errors carried. The diff is\nintentionally minimal: variable names, comment placement, try-catch\nstructure, and error-handling shape from the original axios code are\npreserved.","sha":"312e26c1241eb65a63c33c6d5acc3593ef6956d1"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.5.0","branchLabelMappingKey":"^v9.5.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/267944","number":267944,"mergeCommit":{"message":"chore(axios,security-solution): remove axios from telemetry/role scripts (#267944)\n\n## Summary\n\n> [!IMPORTANT]\n> **NOTE TO CODE OWNERS:** I'm modifying code I don't own day to day.\nPlease verify the changes still work as expected. For these migrations a\nquick run of the affected scripts/tests is worth more than a code-only\nreview.\n\nThis PR removes the `axios` dependency for files owned by\n`@elastic/security-solution`. Phase 4 of the axios migration tracked\nunder #266556.\n\n### Why\n\nNode.js 22 ships a native `fetch` API built on undici, and every browser\nKibana targets supports `fetch` natively. Removing axios cuts one\nruntime dependency and continues the per-team rollout that mirrors the\nearlier node-fetch migration\n([#250719](#250719) and siblings).\n\n### Changes\n\nThree files migrated, two files **deferred to a later phase**:\n\n- `scripts/telemetry/build_ebt_data_view.ts`: 1 axios.get + 1 axios.put.\nReplaced with `fetch`, `res.ok` check, and typed `await res.json()` for\nthe data-view fetch.\n- `scripts/telemetry/build_ebt_data_view.test.ts`: was mocking\n`axios.put` for `upsertRuntimeFields`. Switched to `jest.spyOn(global,\n'fetch')`. The 16 existing test cases pass unchanged in intent:\nname/type/url assertions adapted to read the `fetch(url, init)` argument\nshape (`init.body` is now `JSON.stringify(payload)` rather than the\nthird axios arg). Header equality and dotted-name handling assertions\nunchanged.\n-\n`server/lib/detection_engine/scripts/roles_users/create_role_and_user.ts`:\n2 axios.put calls. Replaced with `fetch` and an explicit non-2xx throw\nthat preserves status+body in the error message.\n\nRemoved the `scripts/telemetry/**` and\n`server/lib/detection_engine/scripts/**` entries from\n`AXIOS_LEGACY_CONSUMERS` in `.eslintrc.js`. New axios usage in either of\nthose directories is now blocked by the existing global ban.\n\n**Deferred to a later phase**:\n`server/integration_tests/configuration.test.ts` and\n`server/integration_tests/telemetry.test.ts` mock `axios` at the module\nlevel for code under `server/lib/telemetry/*`, which is owned by\n`@elastic/security-data-analytics` and not yet migrated. Test mocks must\nflip in lockstep with the production code they intercept; these two\ntests will migrate alongside that team's PR.\n\n### Behavior parity\n\nNative fetch does not throw on non-2xx, so each call site explicitly\nchecks `res.ok` / `res.status`. Errors thrown inside the migrated\nscripts now have the form `${status}:${body}` to keep the same\ndiagnostic content the original axios errors carried. The diff is\nintentionally minimal: variable names, comment placement, try-catch\nstructure, and error-handling shape from the original axios code are\npreserved.","sha":"312e26c1241eb65a63c33c6d5acc3593ef6956d1"}}]}] BACKPORT--> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@elastic.co>
1 parent ef1fb30 commit dd49c1a

4 files changed

Lines changed: 55 additions & 48 deletions

File tree

.eslintrc.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,7 @@ const AXIOS_LEGACY_CONSUMERS = [
345345
'x-pack/solutions/security/plugins/security_solution/common/endpoint/format_axios_error.ts',
346346
'x-pack/solutions/security/plugins/security_solution/common/endpoint/utils/**/*.{js,mjs,ts,tsx}',
347347
'x-pack/solutions/security/plugins/security_solution/scripts/endpoint/**/*.{js,mjs,ts,tsx}',
348-
'x-pack/solutions/security/plugins/security_solution/scripts/telemetry/**/*.{js,mjs,ts,tsx}',
349348
'x-pack/solutions/security/plugins/security_solution/server/integration_tests/**/*.{js,mjs,ts,tsx}',
350-
'x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/scripts/**/*.{js,mjs,ts,tsx}',
351349
'x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/**/*.{js,mjs,ts,tsx}',
352350
'x-pack/solutions/security/test/osquery_cypress/utils.ts',
353351
'x-pack/solutions/security/test/security_solution_api_integration/config/services/**/*.{js,mjs,ts,tsx}',

x-pack/solutions/security/plugins/security_solution/scripts/telemetry/build_ebt_data_view.test.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,9 @@
66
*/
77
/* eslint-disable @typescript-eslint/no-explicit-any */
88

9-
import axios from 'axios';
109
import { flattenSchema, upsertRuntimeFields } from './build_ebt_data_view';
1110

12-
jest.mock('axios', () => ({
13-
__esModule: true,
14-
default: {
15-
put: jest.fn(),
16-
},
17-
}));
11+
const mockedFetch = jest.spyOn(global, 'fetch');
1812

1913
describe('upsertRuntimeFields', () => {
2014
const url = 'http://fake_url';
@@ -26,7 +20,7 @@ describe('upsertRuntimeFields', () => {
2620

2721
beforeEach(() => {
2822
jest.resetAllMocks();
29-
(axios.put as jest.Mock).mockResolvedValue({});
23+
mockedFetch.mockResolvedValue(new Response(null, { status: 200 }));
3024
});
3125

3226
test('sends one PUT per string field with correct payload and headers', async () => {
@@ -38,26 +32,30 @@ describe('upsertRuntimeFields', () => {
3832

3933
await upsertRuntimeFields(fields, url, headers);
4034

41-
expect(axios.put).toHaveBeenCalledTimes(3);
35+
expect(mockedFetch).toHaveBeenCalledTimes(3);
4236

43-
const calls = (axios.put as jest.Mock).mock.calls.map(([callUrl, payload, opts]) => ({
44-
callUrl,
45-
name: payload.name,
46-
type: payload.runtimeField?.type,
47-
opts,
48-
}));
37+
const calls = mockedFetch.mock.calls.map(([callUrl, opts]) => {
38+
const payload = JSON.parse(opts?.body as string);
39+
return {
40+
callUrl,
41+
method: opts?.method,
42+
name: payload.name,
43+
type: payload.runtimeField?.type,
44+
headers: opts?.headers,
45+
};
46+
});
4947

5048
const names = new Set(calls.map((c) => c.name));
5149
const types = new Set(calls.map((c) => c.type));
5250
const urls = new Set(calls.map((c) => c.callUrl));
53-
const allHeadersOk = calls.every(
54-
(c) => JSON.stringify(c.opts?.headers) === JSON.stringify(headers)
55-
);
51+
const allHeadersOk = calls.every((c) => JSON.stringify(c.headers) === JSON.stringify(headers));
52+
const allMethodsOk = calls.every((c) => c.method === 'PUT');
5653

5754
expect(names).toEqual(new Set(['properties.a', 'properties.nested.b', 'properties.deep.x.y']));
5855
expect(types).toEqual(new Set(['keyword', 'long', 'date']));
5956
expect(urls).toEqual(new Set([url]));
6057
expect(allHeadersOk).toBe(true);
58+
expect(allMethodsOk).toBe(true);
6159
});
6260

6361
test('ignores non-string field values', async () => {
@@ -70,15 +68,16 @@ describe('upsertRuntimeFields', () => {
7068

7169
await upsertRuntimeFields(fields as any, url, headers);
7270

73-
expect(axios.put).toHaveBeenCalledTimes(1);
74-
const [callUrl, payload, opts] = (axios.put as jest.Mock).mock.calls[0];
71+
expect(mockedFetch).toHaveBeenCalledTimes(1);
72+
const [callUrl, opts] = mockedFetch.mock.calls[0];
7573

7674
expect(callUrl).toBe(url);
77-
expect(payload).toEqual({
75+
expect(opts?.method).toBe('PUT');
76+
expect(JSON.parse(opts?.body as string)).toEqual({
7877
name: 'properties.ok',
7978
runtimeField: { type: 'ip' },
8079
});
81-
expect(opts).toEqual({ headers });
80+
expect(opts?.headers).toEqual(headers);
8281
});
8382

8483
test('handles dotted field names correctly', async () => {
@@ -88,7 +87,8 @@ describe('upsertRuntimeFields', () => {
8887

8988
await upsertRuntimeFields(fields, url, headers);
9089

91-
const [, payload] = (axios.put as jest.Mock).mock.calls[0];
90+
const [, opts] = mockedFetch.mock.calls[0];
91+
const payload = JSON.parse(opts?.body as string);
9292
expect(payload.name).toBe('properties.one.two.three');
9393
expect(payload.runtimeField.type).toBe('double');
9494
});

x-pack/solutions/security/plugins/security_solution/scripts/telemetry/build_ebt_data_view.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
*/
77

88
import { ToolingLog } from '@kbn/tooling-log';
9-
import axios from 'axios';
109
import { events as genAiEvents } from '@kbn/elastic-assistant-plugin/server/lib/telemetry/event_based_telemetry';
1110

1211
import { isObject } from 'lodash';
@@ -62,13 +61,16 @@ async function cli(): Promise<void> {
6261

6362
try {
6463
logger.info(`Fetching data view "${dataViewName}"...`);
65-
const {
66-
data: { data_view: ourDataView },
67-
} = await axios.get(dataViewApiUrl, {
64+
const response = await fetch(dataViewApiUrl, {
6865
headers: requestHeaders,
6966
});
67+
if (!response.ok) {
68+
throw new Error(`${response.status}:${await response.text()}`);
69+
}
70+
71+
const responseBody = (await response.json()) as { data_view: unknown };
7072

71-
if (!ourDataView) {
73+
if (!responseBody.data_view) {
7274
throw new Error(
7375
`Data view "${dataViewName}" not found, check your data view is spelled correctly and is defined in the ${spaceId} space`
7476
);
@@ -195,9 +197,14 @@ export async function upsertRuntimeFields(
195197
};
196198

197199
try {
198-
await axios.put(requestUrl, payload, {
200+
const response = await fetch(requestUrl, {
201+
method: 'PUT',
199202
headers: requestHeaders,
203+
body: JSON.stringify(payload),
200204
});
205+
if (!response.ok) {
206+
throw new Error(`${response.status}:${await response.text()}`);
207+
}
201208
} catch (error) {
202209
throw new Error(`Error upserting field '${fieldName}: ${fieldType}' - ${error.message}`);
203210
}

x-pack/solutions/security/plugins/security_solution/server/lib/detection_engine/scripts/roles_users/create_role_and_user.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
* 2.0.
66
*/
77

8-
import axios from 'axios';
98
import yargs from 'yargs';
109
import { ToolingLog } from '@kbn/tooling-log';
1110
import {
@@ -46,20 +45,22 @@ async function cli(): Promise<void> {
4645
const requestHeaders = {
4746
Authorization: `Basic ${btoa(`${USERNAME}:${PASSWORD}`)}`,
4847
'kbn-xsrf': 'xxx',
48+
'Content-Type': 'application/json',
4949
};
5050

5151
try {
5252
logger.info(`Creating role "${role}"...`);
53-
await axios.put(
54-
`${KIBANA_URL}/api/security/role/${role}`,
55-
{
53+
const response = await fetch(`${KIBANA_URL}/api/security/role/${role}`, {
54+
method: 'PUT',
55+
headers: requestHeaders,
56+
body: JSON.stringify({
5657
elasticsearch: selectedRoleDefinition.elasticsearch,
5758
kibana: selectedRoleDefinition.kibana,
58-
},
59-
{
60-
headers: requestHeaders,
61-
}
62-
);
59+
}),
60+
});
61+
if (!response.ok) {
62+
throw new Error(`${response.status}:${await response.text()}`);
63+
}
6364

6465
logger.info(`Role "${role}" has been created`);
6566
} catch (e) {
@@ -69,18 +70,19 @@ async function cli(): Promise<void> {
6970

7071
try {
7172
logger.info(`Creating user "${userName}"...`);
72-
await axios.put(
73-
`${ELASTICSEARCH_URL}/_security/user/${userName}`,
74-
{
73+
const response = await fetch(`${ELASTICSEARCH_URL}/_security/user/${userName}`, {
74+
method: 'PUT',
75+
headers: requestHeaders,
76+
body: JSON.stringify({
7577
password,
7678
roles: [role],
7779
full_name: role,
7880
email: `role@example.com`,
79-
},
80-
{
81-
headers: requestHeaders,
82-
}
83-
);
81+
}),
82+
});
83+
if (!response.ok) {
84+
throw new Error(`${response.status}:${await response.text()}`);
85+
}
8486

8587
logger.info(`User "${userName}" has been created (password "${password}")`);
8688
} catch (e) {

0 commit comments

Comments
 (0)