CVE-2026-42400
Description
Uncontrolled Resource Consumption (CWE-400) in Kibana can lead to denial of service via Excessive Allocation (CAPEC-130). An authenticated user can send a specially crafted compressed request payload that is processed prior to authorization checks, causing excessive memory and CPU resource consumption that can result in a Kibana instance becoming unresponsive or crashing.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2026-42400 allows authenticated users to cause denial of service in Kibana via crafted compressed request payloads.
Vulnerability
The vulnerability is an Uncontrolled Resource Consumption (CWE-400) in Kibana that can be triggered via Excessive Allocation (CAPEC-130). An authenticated user can send a specially crafted compressed request payload that is processed prior to authorization checks, causing excessive memory and CPU consumption. Affected versions include all Kibana 8.x from 8.0.0 to 8.19.15, 9.x from 9.0.0 to 9.3.4, and 9.4.0 to 9.4.1 [1].
Exploitation
An attacker needs valid authentication credentials to the Kibana instance and network access. The exploit involves sending a specially crafted compressed HTTP request payload. Because the request is processed before authorization checks, even low-privilege users can trigger the vulnerability. No user interaction from other users is required [1].
Impact
Successful exploitation leads to denial of service. The excessive memory and CPU resource consumption can cause the Kibana instance to become unresponsive or crash, affecting availability. There is no impact on confidentiality or integrity [1].
Mitigation
The issue is fixed in Kibana versions 8.19.16, 9.3.5, and 9.4.2, released as part of Elastic Security Advisory ESA-2026-35 [1]. For users unable to upgrade, no workarounds are available. Elastic Cloud Serverless was patched before public disclosure [1].
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
8ce8a83387719[8.19] Fix the HTTP route registration to limit the compressed payloads (#257721) (#269721)
6 files changed · +67 −4
docs/setup/settings.asciidoc+3 −2 modified@@ -464,8 +464,9 @@ The number of milliseconds to wait for additional data before restarting the <<server-socketTimeout, `server.socketTimeout`>> counter. *Default: `"120000"`* [[server-maxPayload]] `server.maxPayload`:: -The maximum payload size in bytes -for incoming server requests. *Default: `1048576`* +The maximum payload size in bytes for incoming server requests. +This option controls the maximum payload size Kibana can handle, rather than the incoming request size, which also limits the inflated size when compression is used. +*Default: `1048576`* `server.name`:: A human-readable display name that
src/core/packages/http/server-internal/src/http_server.test.ts+39 −0 modified@@ -28,6 +28,7 @@ import { createServer } from '@kbn/server-http-tools'; import { HttpConfig } from './http_config'; import { HttpServer } from './http_server'; import { Readable } from 'stream'; +import { gzipSync } from 'zlib'; import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; import moment from 'moment'; import { of, Observable, BehaviorSubject } from 'rxjs'; @@ -1153,6 +1154,44 @@ describe('body options', () => { }); }); + test('should reject a gzip-bomb that inflates beyond `maxBytes`', async () => { + const { registerRouter, server: innerServer } = await server.setup({ config$ }); + + const router = new Router('', logger, enhanceWithContext, routerOptions); + router.post( + { + path: '/', + validate: { body: schema.object({ test: schema.string() }) }, + options: { body: { maxBytes: 64 } }, + security: { + authz: { + requiredPrivileges: ['foo'], + }, + }, + }, + (context, req, res) => res.ok({ body: req.route }) + ); + registerRouter(router); + + await server.start(); + await supertest(innerServer.listener) + .post('/') + .set('Content-Encoding', 'gzip') + .set('Content-Type', 'application/json') + .send( + gzipSync( + JSON.stringify({ + test: '0'.repeat(1024), + }) + ) + ) + .expect(413, { + statusCode: 413, + error: 'Request Entity Too Large', + message: 'Payload content length greater than maximum allowed: 64', + }); + }); + test('should not parse the content in the request', async () => { const { registerRouter, server: innerServer } = await server.setup({ config$ });
src/core/packages/http/server-internal/src/http_server.ts+6 −0 modified@@ -798,6 +798,12 @@ export class HttpServer { parse, timeout: timeout?.payload, multipart: true, + compression: maxBytes + ? { + gzip: { maxOutputLength: maxBytes }, + deflate: { maxOutputLength: maxBytes }, + } + : undefined, } : undefined, timeout: {
src/platform/packages/shared/kbn-server-http-tools/src/get_server_options.test.ts+12 −0 modified@@ -107,4 +107,16 @@ describe('getServerOptions', () => { expect(getServerOptions(httpConfig).routes!.payload!.timeout).toEqual(9007); }); + + it('configures max payload size', () => { + const options = getServerOptions( + createConfig({ + maxPayload: ByteSizeValue.parse('1kb'), + }) + ); + + expect(options).toHaveProperty('routes.compression.deflate.maxOutputLength', 1024); + expect(options).toHaveProperty('routes.compression.gzip.maxOutputLength', 1024); + expect(options).toHaveProperty('routes.payload.maxBytes', 1024); + }); });
src/platform/packages/shared/kbn-server-http-tools/src/get_server_options.ts+6 −1 modified@@ -25,6 +25,7 @@ export function getServerOptions(config: IHttpConfig, { configureTLS = true } = headers: corsAllowedHeaders, } : false; + const maxPayload = config.maxPayload.getValueInBytes(); const options: ServerOptions = { host: config.host, @@ -39,9 +40,13 @@ export function getServerOptions(config: IHttpConfig, { configureTLS = true } = privacy: 'private', otherwise: 'private, no-cache, no-store, must-revalidate', }, + compression: { + deflate: { maxOutputLength: maxPayload }, + gzip: { maxOutputLength: maxPayload }, + }, cors, payload: { - maxBytes: config.maxPayload.getValueInBytes(), + maxBytes: maxPayload, timeout: config.payloadTimeout, }, validate: {
src/platform/plugins/shared/saved_objects_management/server/routes/scroll_count.ts+1 −1 modified@@ -25,7 +25,7 @@ export const registerScrollForCountRoute = (router: IRouter) => { }, validate: { body: schema.object({ - typesToInclude: schema.arrayOf(schema.string()), + typesToInclude: schema.arrayOf(schema.string({ maxLength: 1000 })), searchString: schema.maybe(schema.string()), references: schema.maybe( schema.arrayOf(
566ae1eecbd4[9.3] auto-implement: [Query Rules] Fix: Unbounded array in schema validation (#265495) (#266659)
1 file changed · +6 −4
x-pack/solutions/search/plugins/search_query_rules/server/routes.ts+6 −4 modified@@ -129,11 +129,12 @@ export function defineRoutes({ logger, router }: { logger: Logger; router: IRout schema.object({ type: schema.string(), metadata: schema.maybe(schema.string()), - values: schema.maybe(schema.arrayOf(schema.string())), - }) + values: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 100 })), + }), + { maxSize: 100 } ), actions: schema.object({ - ids: schema.maybe(schema.arrayOf(schema.string())), + ids: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10000 })), docs: schema.maybe( schema.arrayOf( schema.object({ @@ -143,7 +144,8 @@ export function defineRoutes({ logger, router }: { logger: Logger; router: IRout ) ), }), - }) + }), + { maxSize: 100 } ), }) ),
72d20e4b1651[9.3] [Security Solution][Timeline] fix Kibana DoS via Timeline Bulk Export (#260265) (#265222)
11 files changed · +246 −22
oas_docs/output/kibana.serverless.yaml+2 −0 modified@@ -86700,6 +86700,8 @@ paths: ids: items: type: string + maxItems: 1000 + minItems: 1 nullable: true type: array description: The IDs of the Timelines to export.
oas_docs/output/kibana.yaml+2 −0 modified@@ -85238,6 +85238,8 @@ paths: ids: items: type: string + maxItems: 1000 + minItems: 1 nullable: true type: array description: The IDs of the Timelines to export.
x-pack/solutions/security/plugins/security_solution/common/api/timeline/export_timelines/export_timelines_route.gen.ts+1 −1 modified@@ -27,6 +27,6 @@ export type ExportTimelinesRequestQueryInput = z.input<typeof ExportTimelinesReq export type ExportTimelinesRequestBody = z.infer<typeof ExportTimelinesRequestBody>; export const ExportTimelinesRequestBody = z.object({ - ids: z.array(z.string()).nullable().optional(), + ids: z.array(z.string()).min(1).max(1000).nullable().optional(), }); export type ExportTimelinesRequestBodyInput = z.input<typeof ExportTimelinesRequestBody>;
x-pack/solutions/security/plugins/security_solution/common/api/timeline/export_timelines/export_timelines_route.schema.yaml+2 −0 modified@@ -33,6 +33,8 @@ paths: ids: nullable: true type: array + minItems: 1 + maxItems: 1000 items: type: string responses:
x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml+2 −0 modified@@ -552,6 +552,8 @@ paths: ids: items: type: string + maxItems: 1000 + minItems: 1 nullable: true type: array description: The IDs of the Timelines to export.
x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml+2 −0 modified@@ -552,6 +552,8 @@ paths: ids: items: type: string + maxItems: 1000 + minItems: 1 nullable: true type: array description: The IDs of the Timelines to export.
x-pack/solutions/security/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/helpers.test.ts+80 −0 added@@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FrameworkRequest } from '../../../../framework'; +import type { TimelineResponse } from '../../../../../../common/api/timeline'; +import { getExportTimelineByObjectIds } from './helpers'; +import { getSelectedTimelines } from '../../../saved_object/timelines'; +import * as noteLib from '../../../saved_object/notes'; +import * as pinnedEventLib from '../../../saved_object/pinned_events'; + +jest.mock('../../../saved_object/timelines', () => ({ + getSelectedTimelines: jest.fn(), +})); + +jest.mock('../../../saved_object/notes', () => ({ + getNotesByTimelineId: jest.fn(), +})); + +jest.mock('../../../saved_object/pinned_events', () => ({ + getAllPinnedEventsByTimelineId: jest.fn(), +})); + +describe('export timelines helpers', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('enriches notes and pinned events in bounded batches', async () => { + const timelines = Array.from({ length: 11 }, (_, index) => ({ + savedObjectId: `timeline-${index}`, + status: 'active', + })) as unknown as TimelineResponse[]; + + (getSelectedTimelines as jest.Mock).mockResolvedValue({ + timelines, + errors: [], + }); + + const pendingNotes: Array<() => void> = []; + const pendingPinnedEvents: Array<() => void> = []; + (noteLib.getNotesByTimelineId as jest.Mock).mockImplementation((_, timelineId: string) => { + return new Promise((resolve) => { + pendingNotes.push(() => resolve([{ timelineId }])); + }); + }); + (pinnedEventLib.getAllPinnedEventsByTimelineId as jest.Mock).mockImplementation( + (_, timelineId: string) => { + return new Promise((resolve) => { + pendingPinnedEvents.push(() => resolve([{ timelineId, eventId: `event-${timelineId}` }])); + }); + } + ); + + const exportPromise = getExportTimelineByObjectIds({ + frameworkRequest: {} as FrameworkRequest, + ids: timelines.map((timeline) => timeline.savedObjectId), + }); + + await Promise.resolve(); + + expect(noteLib.getNotesByTimelineId).toHaveBeenCalledTimes(10); + expect(pinnedEventLib.getAllPinnedEventsByTimelineId).toHaveBeenCalledTimes(10); + + pendingNotes.splice(0, 10).forEach((resolveNote) => resolveNote()); + pendingPinnedEvents.splice(0, 10).forEach((resolvePinnedEvent) => resolvePinnedEvent()); + await new Promise(process.nextTick); + + expect(noteLib.getNotesByTimelineId).toHaveBeenCalledTimes(11); + expect(pinnedEventLib.getAllPinnedEventsByTimelineId).toHaveBeenCalledTimes(11); + + pendingNotes.forEach((resolveNote) => resolveNote()); + pendingPinnedEvents.forEach((resolvePinnedEvent) => resolvePinnedEvent()); + + await expect(exportPromise).resolves.toContain('timeline-0'); + }); +});
x-pack/solutions/security/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/helpers.ts+36 −18 modified@@ -22,6 +22,8 @@ import * as pinnedEventLib from '../../../saved_object/pinned_events'; import { getSelectedTimelines } from '../../../saved_object/timelines'; +const EXPORT_TIMELINE_ENRICHMENT_BATCH_SIZE = 10; + const getGlobalEventNotesByTimelineId = (currentNotes: Note[]): ExportedNotes => { const initialNotes: ExportedNotes = { eventNotes: [], @@ -42,34 +44,50 @@ const getPinnedEventsIdsByTimelineId = (currentPinnedEvents: PinnedEvent[]): str return currentPinnedEvents.map((event) => event.eventId) ?? []; }; +const getTimelineNotesAndPinnedEvents = async ( + request: FrameworkRequest, + exportedIds: string[] +): Promise<{ notes: Note[]; pinnedEvents: PinnedEvent[] }> => { + const notes: Note[] = []; + const pinnedEvents: PinnedEvent[] = []; + + for (let index = 0; index < exportedIds.length; index += EXPORT_TIMELINE_ENRICHMENT_BATCH_SIZE) { + const timelineIdsBatch = exportedIds.slice( + index, + index + EXPORT_TIMELINE_ENRICHMENT_BATCH_SIZE + ); + const [batchNotes, batchPinnedEvents] = await Promise.all([ + Promise.all( + timelineIdsBatch.map((timelineId) => noteLib.getNotesByTimelineId(request, timelineId)) + ), + Promise.all( + timelineIdsBatch.map((timelineId) => + pinnedEventLib.getAllPinnedEventsByTimelineId(request, timelineId) + ) + ), + ]); + notes.push(...batchNotes.flat()); + pinnedEvents.push(...batchPinnedEvents.flat()); + } + + return { notes, pinnedEvents }; +}; + const getTimelinesFromObjects = async ( request: FrameworkRequest, ids?: string[] | null ): Promise<Array<TimelineResponse | ExportTimelineNotFoundError>> => { const { timelines, errors } = await getSelectedTimelines(request, ids); const exportedIds = timelines.map((t) => t.savedObjectId); + const timelinesById = new Map(timelines.map((timeline) => [timeline.savedObjectId, timeline])); - const [notes, pinnedEvents] = await Promise.all([ - Promise.all(exportedIds.map((timelineId) => noteLib.getNotesByTimelineId(request, timelineId))), - Promise.all( - exportedIds.map((timelineId) => - pinnedEventLib.getAllPinnedEventsByTimelineId(request, timelineId) - ) - ), - ]); - - const myNotes = notes.reduce<Note[]>((acc, note) => [...acc, ...note], []); - - const myPinnedEventIds = pinnedEvents.reduce<PinnedEvent[]>( - (acc, pinnedEventId) => [...acc, ...pinnedEventId], - [] - ); + const { notes, pinnedEvents } = await getTimelineNotesAndPinnedEvents(request, exportedIds); const myResponse = exportedIds.reduce<TimelineResponse[]>((acc, timelineId) => { - const myTimeline = timelines.find((t) => t.savedObjectId === timelineId); + const myTimeline = timelinesById.get(timelineId); if (myTimeline != null) { - const timelineNotes = myNotes.filter((n) => n.timelineId === timelineId); - const timelinePinnedEventIds = myPinnedEventIds.filter((p) => p.timelineId === timelineId); + const timelineNotes = notes.filter((n) => n.timelineId === timelineId); + const timelinePinnedEventIds = pinnedEvents.filter((p) => p.timelineId === timelineId); const exportedTimeline = omit(['status', 'excludedRowRendererIds'], myTimeline); return [ ...acc,
x-pack/solutions/security/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts+55 −1 modified@@ -52,6 +52,12 @@ describe('export timelines', () => { let clients: MockClients; let context: SecuritySolutionRequestHandlerContextMock; + const registerRoute = (overrides?: Partial<ReturnType<typeof createMockConfig>>) => { + const routeConfig = { ...createMockConfig(), ...overrides }; + exportTimelinesRoute(server.router, routeConfig); + return routeConfig; + }; + beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); @@ -62,7 +68,6 @@ describe('export timelines', () => { (convertSavedObjectToSavedPinnedEvent as unknown as jest.Mock).mockReturnValue( mockPinnedEvents() ); - exportTimelinesRoute(server.router, createMockConfig()); }); afterEach(() => { @@ -72,6 +77,7 @@ describe('export timelines', () => { describe('status codes', () => { test('returns 200 when finding selected timelines', async () => { + registerRoute(); const response = await server.inject( getExportTimelinesRequest(), requestContextMock.convertContext(context) @@ -80,6 +86,7 @@ describe('export timelines', () => { }); test('catch error when status search throws error', async () => { + registerRoute(); clients.savedObjectsClient.bulkGet.mockReset(); clients.savedObjectsClient.bulkGet.mockRejectedValue(new Error('Test error')); const response = await server.inject( @@ -92,10 +99,56 @@ describe('export timelines', () => { status_code: 500, }); }); + + test('returns 400 when requested ids exceed export size limit', async () => { + const config = registerRoute({ maxTimelineImportExportSize: 1 }); + const response = await server.inject( + requestMock.create({ + method: 'post', + path: TIMELINE_EXPORT_URL, + query: { + file_name: 'mock_export_timeline.ndjson', + }, + body: { + ids: ['id-1', 'id-2'], + }, + }), + requestContextMock.convertContext(context) + ); + + expect(response.status).toEqual(400); + expect(response.body).toEqual({ + message: `Can't export more than ${config.maxTimelineImportExportSize} timelines`, + status_code: 400, + }); + }); + + test('deduplicates ids before applying export size limit', async () => { + registerRoute({ maxTimelineImportExportSize: 1 }); + const response = await server.inject( + requestMock.create({ + method: 'post', + path: TIMELINE_EXPORT_URL, + query: { + file_name: 'mock_export_timeline.ndjson', + }, + body: { + ids: ['f0e58720-57b6-11ea-b88d-3f1a31716be8', 'f0e58720-57b6-11ea-b88d-3f1a31716be8'], + }, + }), + requestContextMock.convertContext(context) + ); + + expect(response.status).toEqual(200); + expect(clients.savedObjectsClient.bulkGet).toHaveBeenCalledWith([ + { id: 'f0e58720-57b6-11ea-b88d-3f1a31716be8', type: 'siem-ui-timeline' }, + ]); + }); }); describe('request validation', () => { test('return validation error for request body', async () => { + registerRoute(); const request = requestMock.create({ method: 'get', path: TIMELINE_EXPORT_URL, @@ -107,6 +160,7 @@ describe('export timelines', () => { }); test('return validation error for request params', async () => { + registerRoute(); const request = requestMock.create({ method: 'get', path: TIMELINE_EXPORT_URL,
x-pack/solutions/security/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.ts+4 −2 modified@@ -49,8 +49,10 @@ export const exportTimelinesRoute = (router: SecuritySolutionPluginRouter, confi const frameworkRequest = await buildFrameworkRequest(context, request); const exportSizeLimit = config.maxTimelineImportExportSize; + const normalizedIds = + request.body?.ids != null ? [...new Set(request.body.ids)] : request.body?.ids; - if (request.body?.ids != null && request.body.ids.length > exportSizeLimit) { + if (normalizedIds != null && normalizedIds.length > exportSizeLimit) { return siemResponse.error({ statusCode: 400, body: `Can't export more than ${exportSizeLimit} timelines`, @@ -59,7 +61,7 @@ export const exportTimelinesRoute = (router: SecuritySolutionPluginRouter, confi const responseBody = await getExportTimelineByObjectIds({ frameworkRequest, - ids: request.body?.ids, + ids: normalizedIds, }); return response.ok({
x-pack/solutions/security/test/security_solution_api_integration/test_suites/investigation/timeline/tests/timeline_privileges.ts+60 −0 modified@@ -7,6 +7,8 @@ import expect from '@kbn/expect'; import type { CreateTimelinesResponse } from '@kbn/security-solution-plugin/common/api/timeline'; +import { TIMELINE_EXPORT_URL } from '@kbn/security-solution-plugin/common/constants'; +import type TestAgent from 'supertest/lib/agent'; import type { FtrProviderContextWithSpaces } from '../../../../ftr_provider_context_with_spaces'; import { getTimelines, @@ -35,6 +37,15 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { const isServerless = config.get('serverless'); const isEss = !isServerless; + const MAX_TIMELINE_EXPORT_IDS = 1000; + + const exportTimeline = async (supertest: TestAgent, ids: string[]) => + supertest + .post(`${TIMELINE_EXPORT_URL}?file_name=timelines_export.ndjson`) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ ids }); + describe('Timeline privileges', () => { before(async () => { if (isEss) { @@ -104,6 +115,55 @@ export default function ({ getService }: FtrProviderContextWithSpaces) { }); }); + describe('export timelines', () => { + let getTimelineId = () => ''; + + before(async () => { + const superTest = await utils.createSuperTestWithUser(users.secTimelineAllUser); + const { + body: { savedObjectId }, + } = await createBasicTimeline(superTest, 'timeline for export'); + getTimelineId = () => savedObjectId; + }); + + canWriteOrReadUsers.forEach((user) => { + it(`user "${user.username}" can export timelines`, async () => { + const superTest = await utils.createSuperTestWithUser(user); + const exportTimelineResponse = await exportTimeline(superTest, [getTimelineId()]); + expect(exportTimelineResponse.status).to.be(200); + }); + }); + + cannotAccessUsers.forEach((user) => { + it(`user "${user.username}" cannot export timelines`, async () => { + const superTest = await utils.createSuperTestWithUser(user); + const exportTimelineResponse = await exportTimeline(superTest, [getTimelineId()]); + expect(exportTimelineResponse.status).to.be(403); + }); + }); + + it('rejects export requests above the max ids limit for timeline read users', async () => { + const superTest = await utils.createSuperTestWithUser(users.secTimelineReadUser); + const oversizedIds = Array.from( + { length: MAX_TIMELINE_EXPORT_IDS + 1 }, + (_, index) => `non-existent-timeline-${index}` + ); + const exportTimelineResponse = await exportTimeline(superTest, oversizedIds); + expect(exportTimelineResponse.status).to.be(400); + }); + + it('accepts duplicate timeline ids for timeline read users', async () => { + const superTest = await utils.createSuperTestWithUser(users.secTimelineReadUser); + const timelineId = getTimelineId(); + const exportTimelineResponse = await exportTimeline(superTest, [ + timelineId, + timelineId, + timelineId, + ]); + expect(exportTimelineResponse.status).to.be(200); + }); + }); + describe('create and delete timelines', () => { canWriteUsers.forEach((user) => { it(`user "${user.username}" can create and delete timelines`, async () => {
86425ef4bb93Add maxSize and maxLength constraints to schema validations (#250851) (#269001)
48 files changed · +166 −116
src/platform/plugins/shared/console/server/routes/api/console/convert_request_to_language/index.ts+6 −5 modified@@ -16,17 +16,18 @@ import { acceptedHttpVerb, nonEmptyString } from '../proxy/validation_config'; const routeValidationConfig = { query: schema.object({ - language: schema.string(), - esHost: schema.string(), - kibanaHost: schema.string(), + language: schema.string({ maxLength: 100 }), + esHost: schema.string({ maxLength: 1000 }), + kibanaHost: schema.string({ maxLength: 1000 }), }), body: schema.maybe( schema.arrayOf( schema.object({ method: acceptedHttpVerb, url: nonEmptyString, - data: schema.arrayOf(schema.string()), - }) + data: schema.arrayOf(schema.string({ maxLength: 10000 }), { maxSize: 1000 }), + }), + { maxSize: 100 } ) ), };
x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_create_route.ts+3 −3 modified@@ -21,9 +21,9 @@ export const registerCreateRoute = ({ }: RouteDependencies) => { const bodySchema = schema.object({ id: schema.string({ maxLength: 1000 }), - remoteCluster: schema.string(), - leaderIndexPatterns: schema.arrayOf(schema.string()), - followIndexPattern: schema.string(), + remoteCluster: schema.string({ maxLength: 1000 }), + leaderIndexPatterns: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + followIndexPattern: schema.string({ maxLength: 1000 }), }); router.post(
x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_update_route.ts+2 −2 modified@@ -20,13 +20,13 @@ export const registerUpdateRoute = ({ lib: { handleEsError }, }: RouteDependencies) => { const paramsSchema = schema.object({ - id: schema.string(), + id: schema.string({ maxLength: 1000 }), }); const bodySchema = schema.object({ active: schema.boolean(), remoteCluster: schema.string(), - leaderIndexPatterns: schema.arrayOf(schema.string()), + leaderIndexPatterns: schema.arrayOf(schema.string(), { maxSize: 1000 }), followIndexPattern: schema.string(), });
x-pack/platform/plugins/private/cross_cluster_replication/server/routes/api/follower_index/register_create_route.ts+7 −7 modified@@ -22,18 +22,18 @@ export const registerCreateRoute = ({ }: RouteDependencies) => { const bodySchema = schema.object({ name: schema.string({ maxLength: 1000 }), - remoteCluster: schema.string(), - leaderIndex: schema.string(), + remoteCluster: schema.string({ maxLength: 1000 }), + leaderIndex: schema.string({ maxLength: 1000 }), maxReadRequestOperationCount: schema.maybe(schema.number()), maxOutstandingReadRequests: schema.maybe(schema.number()), - maxReadRequestSize: schema.maybe(schema.string()), // byte value + maxReadRequestSize: schema.maybe(schema.string({ maxLength: 1000 })), maxWriteRequestOperationCount: schema.maybe(schema.number()), - maxWriteRequestSize: schema.maybe(schema.string()), // byte value + maxWriteRequestSize: schema.maybe(schema.string({ maxLength: 1000 })), maxOutstandingWriteRequests: schema.maybe(schema.number()), maxWriteBufferCount: schema.maybe(schema.number()), - maxWriteBufferSize: schema.maybe(schema.string()), // byte value - maxRetryDelay: schema.maybe(schema.string()), // time value - readPollTimeout: schema.maybe(schema.string()), // time value + maxWriteBufferSize: schema.maybe(schema.string({ maxLength: 1000 })), + maxRetryDelay: schema.maybe(schema.string({ maxLength: 1000 })), + readPollTimeout: schema.maybe(schema.string({ maxLength: 1000 })), }); router.post(
x-pack/platform/plugins/private/data_usage/common/rest_types/data_streams.ts+2 −1 modified@@ -21,7 +21,8 @@ export const DataStreamsResponseSchema = { schema.object({ name: schema.string(), storageSizeBytes: schema.number(), - }) + }), + { maxSize: 1000 } ), };
x-pack/platform/plugins/private/data_usage/common/rest_types/usage_metrics.ts+6 −2 modified@@ -72,6 +72,7 @@ export const UsageMetricsRequestSchema = schema.object({ to: DateSchema, metricTypes: schema.arrayOf(schema.string(), { minSize: 1, + maxSize: 1000, validate: (values) => { const trimmedValues = values.map((v) => v.trim()); if (trimmedValues.some((v) => !v.length)) { @@ -82,6 +83,7 @@ export const UsageMetricsRequestSchema = schema.object({ }, }), dataStreams: schema.arrayOf(schema.string(), { + maxSize: 1000, validate: (values) => { if (values.map((v) => v.trim()).some((v) => !v.length)) { return 'list cannot contain empty values'; @@ -104,9 +106,11 @@ export const UsageMetricsResponseSchema = { schema.object({ x: schema.number(), y: schema.number(), - }) + }), + { maxSize: 1000 } ), - }) + }), + { maxSize: 1000 } ) ), };
x-pack/platform/plugins/private/data_usage/server/config.ts+1 −0 modified@@ -38,6 +38,7 @@ export const configSchema = schema.object({ */ enableExperimental: schema.arrayOf(schema.string(), { defaultValue: () => [], + maxSize: 1000, }), });
x-pack/platform/plugins/private/index_lifecycle_management/server/config.ts+2 −2 modified@@ -23,7 +23,7 @@ const schemaLatest = schema.object( enabled: schema.boolean({ defaultValue: true }), }), // Cloud requires the ability to hide internal node attributes from users. - filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), + filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [], maxSize: 1000 }), /** * Disables the plugin. * Added back in 8.8. @@ -57,7 +57,7 @@ const schema7x = schema.object( enabled: schema.boolean({ defaultValue: true }), }), // Cloud requires the ability to hide internal node attributes from users. - filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), + filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [], maxSize: 1000 }), }, { defaultValue: undefined } );
x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/index/register_remove_route.ts+1 −1 modified@@ -24,7 +24,7 @@ async function removeLifecycle(client: ElasticsearchClient, indexNames: string[] } const bodySchema = schema.object({ - indexNames: schema.arrayOf(schema.string()), + indexNames: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerRemoveRoute({
x-pack/platform/plugins/private/index_lifecycle_management/server/routes/api/index/register_retry_route.ts+1 −1 modified@@ -25,7 +25,7 @@ async function retryLifecycle(client: ElasticsearchClient, indexNames: string[]) } const bodySchema = schema.object({ - indexNames: schema.arrayOf(schema.string()), + indexNames: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerRetryRoute({ router, license, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/private/remote_clusters/server/routes/api/add_route.ts+3 −3 modified@@ -20,11 +20,11 @@ const bodyValidation = schema.object({ name: schema.string({ maxLength: 1000 }), skipUnavailable: schema.boolean(), mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), - seeds: schema.nullable(schema.arrayOf(schema.string())), + seeds: schema.nullable(schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 })), nodeConnections: schema.nullable(schema.number()), - proxyAddress: schema.nullable(schema.string()), + proxyAddress: schema.nullable(schema.string({ maxLength: 1000 })), proxySocketConnections: schema.nullable(schema.number()), - serverName: schema.nullable(schema.string()), + serverName: schema.nullable(schema.string({ maxLength: 1000 })), }); type RouteBody = TypeOf<typeof bodyValidation>;
x-pack/platform/plugins/private/remote_clusters/server/routes/api/update_route.ts+1 −1 modified@@ -18,7 +18,7 @@ import { licensePreRoutingFactory } from '../../lib/license_pre_routing_factory' const bodyValidation = schema.object({ skipUnavailable: schema.boolean(), mode: schema.oneOf([schema.literal(PROXY_MODE), schema.literal(SNIFF_MODE)]), - seeds: schema.nullable(schema.arrayOf(schema.string())), + seeds: schema.nullable(schema.arrayOf(schema.string(), { maxSize: 1000 })), nodeConnections: schema.nullable(schema.number()), proxyAddress: schema.nullable(schema.string()), proxySocketConnections: schema.nullable(schema.number()),
x-pack/platform/plugins/private/rollup/server/routes/api/jobs/register_delete_route.ts+1 −1 modified@@ -25,7 +25,7 @@ export const registerDeleteRoute = ({ }, validate: { body: schema.object({ - jobIds: schema.arrayOf(schema.string()), + jobIds: schema.arrayOf(schema.string(), { maxSize: 1000 }), }), }, },
x-pack/platform/plugins/private/rollup/server/routes/api/jobs/register_start_route.ts+1 −1 modified@@ -25,7 +25,7 @@ export const registerStartRoute = ({ }, validate: { body: schema.object({ - jobIds: schema.arrayOf(schema.string()), + jobIds: schema.arrayOf(schema.string(), { maxSize: 1000 }), }), query: schema.maybe( schema.object({
x-pack/platform/plugins/private/rollup/server/routes/api/jobs/register_stop_route.ts+1 −1 modified@@ -25,7 +25,7 @@ export const registerStopRoute = ({ }, validate: { body: schema.object({ - jobIds: schema.arrayOf(schema.string()), + jobIds: schema.arrayOf(schema.string(), { maxSize: 1000 }), }), query: schema.object({ waitForCompletion: schema.maybe(schema.string()),
x-pack/platform/plugins/private/rollup/server/routes/api/search/register_search_route.ts+3 −2 modified@@ -26,9 +26,10 @@ export const registerSearchRoute = ({ validate: { body: schema.arrayOf( schema.object({ - index: schema.string(), + index: schema.string({ maxLength: 1000 }), query: schema.any(), - }) + }), + { maxSize: 1000 } ), }, },
x-pack/platform/plugins/private/snapshot_restore/server/routes/api/restore.ts+2 −2 modified@@ -98,8 +98,8 @@ export function registerRestoreRoutes({ // Restore snapshot const restoreParamsSchema = schema.object({ - repository: schema.string(), - snapshot: schema.string(), + repository: schema.string({ maxLength: 1000 }), + snapshot: schema.string({ maxLength: 1000 }), }); router.post(
x-pack/platform/plugins/private/snapshot_restore/server/routes/api/snapshots.ts+2 −1 modified@@ -240,7 +240,8 @@ export function registerSnapshotsRoutes({ schema.object({ repository: schema.string(), snapshot: schema.string(), - }) + }), + { maxSize: 1000 } ); // DELETE one or multiple snapshots
x-pack/platform/plugins/private/snapshot_restore/server/routes/api/validate_schemas.ts+48 −21 modified@@ -8,21 +8,30 @@ import { schema } from '@kbn/config-schema'; export const nameParameterSchema = schema.object({ - name: schema.string(), + name: schema.string({ maxLength: 1000 }), }); const snapshotConfigSchema = schema.object({ - indices: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), + indices: schema.maybe( + schema.oneOf([ + schema.string({ maxLength: 1000 }), + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + ]) + ), ignoreUnavailable: schema.maybe(schema.boolean()), includeGlobalState: schema.maybe(schema.boolean()), - featureStates: schema.maybe(schema.arrayOf(schema.string())), + featureStates: schema.maybe( + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }) + ), partial: schema.maybe(schema.boolean()), - metadata: schema.maybe(schema.recordOf(schema.string(), schema.string())), + metadata: schema.maybe( + schema.recordOf(schema.string({ maxLength: 1000 }), schema.string({ maxLength: 1000 })) + ), }); const snapshotRetentionSchema = schema.object({ expireAfterValue: schema.maybe(schema.oneOf([schema.number(), schema.literal('')])), - expireAfterUnit: schema.maybe(schema.string()), + expireAfterUnit: schema.maybe(schema.string({ maxLength: 1000 })), maxCount: schema.maybe(schema.oneOf([schema.number(), schema.literal('')])), minCount: schema.maybe(schema.oneOf([schema.number(), schema.literal('')])), }); @@ -55,36 +64,45 @@ export const snapshotListSchema = schema.object({ export const policySchema = schema.object({ name: schema.string({ maxLength: 1000 }), snapshotName: schema.string({ maxLength: 1000 }), - schedule: schema.string(), - repository: schema.string(), + schedule: schema.string({ maxLength: 1000 }), + repository: schema.string({ maxLength: 1000 }), config: schema.maybe(snapshotConfigSchema), retention: schema.maybe(snapshotRetentionSchema), isManagedPolicy: schema.boolean(), }); // Only validate required settings, everything else is optional -const fsRepositorySettings = schema.object({ location: schema.string() }, { unknowns: 'allow' }); +const fsRepositorySettings = schema.object( + { location: schema.string({ maxLength: 1000 }) }, + { unknowns: 'allow' } +); const readOnlyRepositorySettings = schema.object({ - url: schema.string(), + url: schema.string({ maxLength: 1000 }), }); // Only validate required settings, everything else is optional -const s3RepositorySettings = schema.object({ bucket: schema.string() }, { unknowns: 'allow' }); +const s3RepositorySettings = schema.object( + { bucket: schema.string({ maxLength: 1000 }) }, + { unknowns: 'allow' } +); // Only validate required settings, everything else is optional const hdsRepositorySettings = schema.object( { - uri: schema.string(), - path: schema.string(), + uri: schema.string({ maxLength: 1000 }), + path: schema.string({ maxLength: 1000 }), }, { unknowns: 'allow' } ); const azureRepositorySettings = schema.object({}, { unknowns: 'allow' }); // Only validate required settings, everything else is optional -const gcsRepositorySettings = schema.object({ bucket: schema.string() }, { unknowns: 'allow' }); +const gcsRepositorySettings = schema.object( + { bucket: schema.string({ maxLength: 1000 }) }, + { unknowns: 'allow' } +); const sourceRepositorySettings = schema.oneOf([ fsRepositorySettings, @@ -95,15 +113,15 @@ const sourceRepositorySettings = schema.oneOf([ gcsRepositorySettings, schema.object( { - delegateType: schema.string(), + delegateType: schema.string({ maxLength: 1000 }), }, { unknowns: 'allow' } ), ]); export const repositorySchema = schema.object({ name: schema.string({ maxLength: 1000 }), - type: schema.string(), + type: schema.string({ maxLength: 1000 }), settings: schema.oneOf([ fsRepositorySettings, readOnlyRepositorySettings, @@ -116,14 +134,23 @@ export const repositorySchema = schema.object({ }); export const restoreSettingsSchema = schema.object({ - indices: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), - renamePattern: schema.maybe(schema.string()), - renameReplacement: schema.maybe(schema.string()), + indices: schema.maybe( + schema.oneOf([ + schema.string({ maxLength: 1000 }), + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + ]) + ), + renamePattern: schema.maybe(schema.string({ maxLength: 1000 })), + renameReplacement: schema.maybe(schema.string({ maxLength: 1000 })), includeGlobalState: schema.maybe(schema.boolean()), - featureStates: schema.maybe(schema.arrayOf(schema.string())), + featureStates: schema.maybe( + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }) + ), partial: schema.maybe(schema.boolean()), - indexSettings: schema.maybe(schema.string()), - ignoreIndexSettings: schema.maybe(schema.arrayOf(schema.string())), + indexSettings: schema.maybe(schema.string({ maxLength: 1000 })), + ignoreIndexSettings: schema.maybe( + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }) + ), ignoreUnavailable: schema.maybe(schema.boolean()), includeAliases: schema.maybe(schema.boolean()), });
x-pack/platform/plugins/private/transform/server/routes/api_schemas/common.ts+4 −3 modified@@ -13,8 +13,9 @@ import { TRANSFORM_STATE } from '../../../common/constants'; export const transformIdsSchema = schema.arrayOf( schema.object({ - id: schema.string(), - }) + id: schema.string({ maxLength: 1000 }), + }), + { maxSize: 1000 } ); export type TransformIdsSchema = TypeOf<typeof transformIdsSchema>; @@ -38,7 +39,7 @@ export const dataViewTitleSchema = schema.object({ export type DataViewTitleSchema = TypeOf<typeof dataViewTitleSchema>; export const transformIdParamSchema = schema.object({ - transformId: schema.string(), + transformId: schema.string({ maxLength: 1000 }), }); export type TransformIdParamSchema = TypeOf<typeof transformIdParamSchema>;
x-pack/platform/plugins/private/transform/server/routes/api_schemas/delete_transforms.ts+2 −1 modified@@ -20,7 +20,8 @@ export const deleteTransformsRequestSchema = schema.object({ schema.object({ id: schema.string(), state: transformStateSchema, - }) + }), + { maxSize: 1000 } ), deleteDestIndex: schema.maybe(schema.boolean()), deleteDestDataView: schema.maybe(schema.boolean()),
x-pack/platform/plugins/private/transform/server/routes/api_schemas/field_histograms.ts+1 −1 modified@@ -15,7 +15,7 @@ export const fieldHistogramsRequestSchema = schema.object({ /** Query to match documents in the index. */ query: schema.any(), /** The fields to return histogram data. */ - fields: schema.arrayOf(schema.any()), + fields: schema.arrayOf(schema.any(), { maxSize: 1000 }), /** Optional runtime fields */ runtimeMappings: runtimeMappingsSchema, /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */
x-pack/platform/plugins/private/transform/server/routes/api_schemas/reset_transforms.ts+2 −1 modified@@ -19,7 +19,8 @@ export const resetTransformsRequestSchema = schema.object({ schema.object({ id: schema.string(), state: transformStateSchema, - }) + }), + { maxSize: 1000 } ), });
x-pack/platform/plugins/private/transform/server/routes/api_schemas/stop_transforms.ts+2 −1 modified@@ -15,7 +15,8 @@ export const stopTransformsRequestSchema = schema.arrayOf( schema.object({ id: schema.string(), state: transformStateSchema, - }) + }), + { maxSize: 1000 } ); export type StopTransformsRequestSchema = TypeOf<typeof stopTransformsRequestSchema>;
x-pack/platform/plugins/private/transform/server/routes/api_schemas/transforms.ts+17 −13 modified@@ -26,7 +26,8 @@ export const getTransformsRequestSchema = schema.arrayOf( schema.object({ id: schema.string(), state: transformStateSchema, - }) + }), + { maxSize: 1000 } ); export type GetTransformsRequestSchema = TypeOf<typeof getTransformsRequestSchema>; @@ -39,8 +40,8 @@ export interface GetTransformsResponseSchema { // schemas shared by parts of the preview, create and update endpoint export const destSchema = schema.object({ - index: schema.string(), - pipeline: schema.maybe(schema.string()), + index: schema.string({ maxLength: 1000 }), + pipeline: schema.maybe(schema.string({ maxLength: 1000 })), }); export const pivotSchema = schema.object({ @@ -50,8 +51,8 @@ export const pivotSchema = schema.object({ }); export const latestFunctionSchema = schema.object({ - unique_key: schema.arrayOf(schema.string()), - sort: schema.string(), + unique_key: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + sort: schema.string({ maxLength: 1000 }), }); export type PivotConfig = TypeOf<typeof pivotSchema>; @@ -60,8 +61,8 @@ export type LatestFunctionConfig = TypeOf<typeof latestFunctionSchema>; export const retentionPolicySchema = schema.object({ time: schema.object({ - field: schema.string(), - max_age: schema.string(), + field: schema.string({ maxLength: 1000 }), + max_age: schema.string({ maxLength: 1000 }), }), }); @@ -78,14 +79,17 @@ export const settingsSchema = schema.object({ export const sourceSchema = schema.object({ runtime_mappings: runtimeMappingsSchema, - index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), - query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + index: schema.oneOf([ + schema.string({ maxLength: 1000 }), + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + ]), + query: schema.maybe(schema.recordOf(schema.string({ maxLength: 1000 }), schema.any())), }); export const syncSchema = schema.object({ time: schema.object({ - delay: schema.maybe(schema.string()), - field: schema.string(), + delay: schema.maybe(schema.string({ maxLength: 1000 })), + field: schema.string({ maxLength: 1000 }), }), }); @@ -110,9 +114,9 @@ export const _metaSchema = schema.object( // PUT transforms/{transformId} export const putTransformsRequestSchema = schema.object( { - description: schema.maybe(schema.string()), + description: schema.maybe(schema.string({ maxLength: 1000 })), dest: destSchema, - frequency: schema.maybe(schema.string()), + frequency: schema.maybe(schema.string({ maxLength: 1000 })), /** * Pivot and latest are mutually exclusive, i.e. exactly one must be specified in the transform configuration */
x-pack/platform/plugins/private/upgrade_assistant/server/routes/cluster_settings.ts+1 −1 modified@@ -25,7 +25,7 @@ export function registerClusterSettingsRoute({ }, validate: { body: schema.object({ - settings: schema.arrayOf(schema.string()), + settings: schema.arrayOf(schema.string(), { maxSize: 1000 }), }), }, },
x-pack/platform/plugins/private/upgrade_assistant/server/routes/migrate_data_streams/data_stream_routes.ts+1 −1 modified@@ -245,7 +245,7 @@ export function registerMigrateDataStreamRoutes({ }, validate: { body: schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string(), { maxSize: 1000 }), }), params: schema.object({ dataStreamName: schema.string(),
x-pack/platform/plugins/private/upgrade_assistant/server/routes/update_index_settings.ts+1 −1 modified@@ -25,7 +25,7 @@ export function registerUpdateSettingsRoute({ router }: RouteDependencies) { indexName: schema.string(), }), body: schema.object({ - settings: schema.arrayOf(schema.string()), + settings: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }), }, },
x-pack/platform/plugins/private/upgrade_assistant/server/routes/update_index.ts+3 −2 modified@@ -34,11 +34,12 @@ export function registerUpdateIndexRoute({ }, validate: { params: schema.object({ - index: schema.string(), + index: schema.string({ maxLength: 1000 }), }), body: schema.object({ operations: schema.arrayOf( - schema.oneOf([schema.literal('blockWrite'), schema.literal('unfreeze')]) + schema.oneOf([schema.literal('blockWrite'), schema.literal('unfreeze')]), + { maxSize: 1000 } ), }), },
x-pack/platform/plugins/private/watcher/server/routes/api/register_list_fields_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { Fields } from '../../models/fields'; import { RouteDependencies } from '../../types'; const bodySchema = schema.object({ - indexes: schema.arrayOf(schema.string()), + indexes: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); function fetchFields(dataClient: IScopedClusterClient, indexes: string[]) {
x-pack/platform/plugins/private/watcher/server/routes/api/watches/register_delete_route.ts+1 −1 modified@@ -11,7 +11,7 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { RouteDependencies } from '../../../types'; const bodySchema = schema.object({ - watchIds: schema.arrayOf(schema.string()), + watchIds: schema.arrayOf(schema.string(), { maxSize: 1000 }), }); type DeleteWatchPromiseArray = Promise<{
x-pack/platform/plugins/private/watcher/server/routes/api/watch/register_save_route.ts+2 −2 modified@@ -12,12 +12,12 @@ import { serializeJsonWatch, serializeThresholdWatch } from '../../../../common/ import { RouteDependencies } from '../../../types'; const paramsSchema = schema.object({ - id: schema.string(), + id: schema.string({ maxLength: 1000 }), }); const bodySchema = schema.object( { - type: schema.string(), + type: schema.string({ maxLength: 1000 }), isNew: schema.boolean(), isActive: schema.boolean(), },
x-pack/platform/plugins/shared/index_management/server/routes/api/component_templates/schema_validation.ts+2 −2 modified@@ -16,7 +16,7 @@ export const componentTemplateSchema = schema.object({ lifecycle: schema.maybe( schema.object({ enabled: schema.boolean(), - data_retention: schema.maybe(schema.string()), + data_retention: schema.maybe(schema.string({ maxLength: 1000 })), }) ), // Allowing unknowns here to support data stream options that are not yet defined in the schema @@ -25,7 +25,7 @@ export const componentTemplateSchema = schema.object({ version: schema.maybe(schema.number()), _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), _kbnMeta: schema.object({ - usedBy: schema.arrayOf(schema.string()), + usedBy: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), isManaged: schema.boolean(), }), deprecated: schema.maybe(schema.boolean()),
x-pack/platform/plugins/shared/index_management/server/routes/api/data_streams/register_delete_route.ts+1 −1 modified@@ -11,7 +11,7 @@ import { RouteDependencies } from '../../../types'; import { addBasePath } from '..'; const bodySchema = schema.object({ - dataStreams: schema.arrayOf(schema.string()), + dataStreams: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/data_streams/register_put_route.ts+3 −3 modified@@ -21,8 +21,8 @@ export const getEsWarningText = (warning: string): string | null => { export function registerPutDataRetention({ router, lib: { handleEsError } }: RouteDependencies) { const bodySchema = schema.object({ - dataStreams: schema.arrayOf(schema.string()), - dataRetention: schema.maybe(schema.string()), + dataStreams: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), + dataRetention: schema.maybe(schema.string({ maxLength: 1000 })), enabled: schema.maybe(schema.boolean()), }); @@ -83,7 +83,7 @@ export function registerPutDataStreamFailureStore({ lib: { handleEsError }, }: RouteDependencies) { const bodySchema = schema.object({ - dataStreams: schema.arrayOf(schema.string()), + dataStreams: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), dsFailureStore: schema.boolean(), });
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_clear_cache_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerClearCacheRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_close_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerCloseRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_create_route.ts+2 −2 modified@@ -12,8 +12,8 @@ import { RouteDependencies } from '../../../types'; import { addInternalBasePath } from '..'; const bodySchema = schema.object({ - indexName: schema.string(), - indexMode: schema.string(), + indexName: schema.string({ maxLength: 1000 }), + indexMode: schema.string({ maxLength: 1000 }), }); export function registerCreateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_delete_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_flush_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerFlushRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_forcemerge_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), maxNumSegments: schema.maybe(schema.number()), });
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_open_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerOpenRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_refresh_route.ts+1 −1 modified@@ -12,7 +12,7 @@ import { addBasePath } from '..'; import { executeAsyncByChunks } from './helpers'; const bodySchema = schema.object({ - indices: schema.arrayOf(schema.string()), + indices: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), }); export function registerRefreshRoute({ router, lib: { handleEsError } }: RouteDependencies) {
x-pack/platform/plugins/shared/index_management/server/routes/api/indices/register_reload_route.ts+1 −1 modified@@ -15,7 +15,7 @@ import { addBasePath } from '..'; const bodySchema = schema.maybe( schema.object({ - indexNames: schema.maybe(schema.arrayOf(schema.string())), + indexNames: schema.maybe(schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 })), }) );
x-pack/platform/plugins/shared/index_management/server/routes/api/templates/register_delete_route.ts+2 −1 modified@@ -18,7 +18,8 @@ const bodySchema = schema.object({ name: schema.string(), isLegacy: schema.maybe(schema.boolean()), type: schema.maybe(schema.string()), - }) + }), + { maxSize: 1000 } ), });
x-pack/platform/plugins/shared/index_management/server/routes/api/templates/validate_schemas.ts+11 −9 modified@@ -9,13 +9,13 @@ import { schema } from '@kbn/config-schema'; export const templateSchema = schema.object({ name: schema.string({ maxLength: 1000 }), - indexPatterns: schema.arrayOf(schema.string()), + indexPatterns: schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }), version: schema.maybe(schema.number()), order: schema.maybe(schema.number()), priority: schema.maybe(schema.number()), - indexMode: schema.maybe(schema.string()), + indexMode: schema.maybe(schema.string({ maxLength: 1000 })), // Not present for legacy templates - allowAutoCreate: schema.maybe(schema.string()), + allowAutoCreate: schema.maybe(schema.string({ maxLength: 1000 })), template: schema.maybe( schema.object({ settings: schema.maybe(schema.object({}, { unknowns: 'allow' })), @@ -24,15 +24,17 @@ export const templateSchema = schema.object({ lifecycle: schema.maybe( schema.object({ enabled: schema.boolean(), - data_retention: schema.maybe(schema.string()), + data_retention: schema.maybe(schema.string({ maxLength: 1000 })), }) ), // Allowing unknowns here to support data stream options that are not yet defined in the schema data_stream_options: schema.maybe(schema.object({}, { unknowns: 'allow' })), }) ), - composedOf: schema.maybe(schema.arrayOf(schema.string())), - ignoreMissingComponentTemplates: schema.maybe(schema.arrayOf(schema.string())), + composedOf: schema.maybe(schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 })), + ignoreMissingComponentTemplates: schema.maybe( + schema.arrayOf(schema.string({ maxLength: 1000 }), { maxSize: 1000 }) + ), dataStream: schema.maybe( schema.object( { @@ -44,12 +46,12 @@ export const templateSchema = schema.object({ _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), ilmPolicy: schema.maybe( schema.object({ - name: schema.maybe(schema.string()), - rollover_alias: schema.maybe(schema.string()), + name: schema.maybe(schema.string({ maxLength: 1000 })), + rollover_alias: schema.maybe(schema.string({ maxLength: 1000 })), }) ), _kbnMeta: schema.object({ - type: schema.string(), + type: schema.string({ maxLength: 1000 }), hasDatastream: schema.maybe(schema.boolean()), isLegacy: schema.maybe(schema.boolean()), }),
x-pack/platform/plugins/shared/ingest_pipelines/server/routes/api/shared/pipeline_schema.ts+5 −3 modified@@ -8,9 +8,11 @@ import { schema } from '@kbn/config-schema'; export const pipelineSchema = { - description: schema.maybe(schema.string()), - processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + description: schema.maybe(schema.string({ maxLength: 1000 })), + processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any()), { maxSize: 1000 }), version: schema.maybe(schema.number()), - on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), + on_failure: schema.maybe( + schema.arrayOf(schema.recordOf(schema.string(), schema.any()), { maxSize: 1000 }) + ), _meta: schema.maybe(schema.object({}, { unknowns: 'allow' })), };
x-pack/platform/plugins/shared/ingest_pipelines/server/routes/api/simulate.ts+1 −1 modified@@ -13,7 +13,7 @@ import { pipelineSchema } from './shared'; const bodySchema = schema.object({ pipeline: schema.object(pipelineSchema), - documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any()), { maxSize: 1000 }), verbose: schema.maybe(schema.boolean()), });
79b7a1b14d0cca34dedcfe61[9.4] [Entity Store] Change defaults to conservative values (#270617) (#270915)
4 files changed · +10 −14
oas_docs/output/kibana.serverless.yaml+2 −2 modified@@ -70384,12 +70384,12 @@ paths: pattern: '[smdh]$' type: string maxLogsPerPage: - default: 40000 + default: 50000 maximum: 9007199254740991 minimum: 1 type: integer maxLogsPerWindow: - default: 500000 + default: 100000 maximum: 9007199254740991 minimum: 0 type: integer
oas_docs/output/kibana.yaml+2 −2 modified@@ -74377,12 +74377,12 @@ paths: pattern: '[smdh]$' type: string maxLogsPerPage: - default: 40000 + default: 50000 maximum: 9007199254740991 minimum: 1 type: integer maxLogsPerWindow: - default: 500000 + default: 100000 maximum: 9007199254740991 minimum: 0 type: integer
x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_client.test.ts+4 −8 modified@@ -112,6 +112,7 @@ function createMockGlobalStateClient( lookbackPeriod: string; delay: string; maxTimeWindowSize: string; + maxLogsPerWindow: number; excludedIndexPatterns: string[]; additionalIndexPatterns: string[]; }> @@ -125,6 +126,9 @@ function createMockGlobalStateClient( // Default to a very large cap so existing tests run as a single sub-window. The dedicated // sub-window cap describe block overrides this to exercise capping behavior. maxTimeWindowSize: logExtractionOverrides?.maxTimeWindowSize ?? '999d', + // Default to 0 (disabled) so volume-cap logic doesn't interfere with unrelated tests. + // The dedicated volume-cap describe block overrides this via setupVolCapTest. + maxLogsPerWindow: logExtractionOverrides?.maxLogsPerWindow ?? 0, }); const state = { logsExtraction } as EntityStoreGlobalState; return { @@ -975,9 +979,6 @@ describe('LogsExtractionClient', () => { ['2025-01-15T11:00:01.000Z', 'hash2', '2025-01-15T11:00:01.000Z', 'entity2'], ], }; - // effectiveMaxLogsPerPage = min(40000, maxLogsPerWindow=1) = 1. - // Probe LIMIT 1 → total_logs = 1 → sliceLogCount = 1. - // totalLogs = 1 >= maxLogsPerWindow=1 → cap fires. setupVolCapTest({ maxLogsPerWindow: 1, maxLogsPerWindowCapBehavior: 'defer' }); mockExtractSuccessSequence(mainExtractionResponse, 1); mockIngestEntities.mockResolvedValue(undefined); @@ -1014,9 +1015,6 @@ describe('LogsExtractionClient', () => { ['2025-01-15T11:00:01.000Z', 'hash2', '2025-01-15T11:00:01.000Z', 'entity2'], ], }; - // effectiveMaxLogsPerPage = min(40000, maxLogsPerWindow=1) = 1. - // Probe LIMIT 1 → total_logs = 1 → sliceLogCount = 1. - // totalLogs = 1 >= maxLogsPerWindow=1 → cap fires. setupVolCapTest({ maxLogsPerWindow: 1, maxLogsPerWindowCapBehavior: 'drop' }); mockExtractSuccessSequence(mainExtractionResponse, 1); mockIngestEntities.mockResolvedValue(undefined); @@ -1108,7 +1106,6 @@ describe('LogsExtractionClient', () => { expect(result.success).toBe(true); if (!result.success) return; expect(result.logsCapApplied).toBe(true); - // effectiveMaxLogsPerPage = min(40000, maxLogsPerWindow=1) = 1 → sliceLogCount = 1 expect(result.logsProcessed).toBe(1); // defer: lastSearchTimestamp is where the loop stopped, NOT the window end expect(result.lastSearchTimestamp).toBe(lastPageTimestamp); @@ -1142,7 +1139,6 @@ describe('LogsExtractionClient', () => { expect(result.success).toBe(true); if (!result.success) return; expect(result.logsCapApplied).toBe(true); - // effectiveMaxLogsPerPage = min(40000, maxLogsPerWindow=1) = 1 → sliceLogCount = 1 expect(result.logsProcessed).toBe(1); // drop: lastSearchTimestamp is advanced to the window end expect(result.lastSearchTimestamp).toBe(toDateISO);
x-pack/solutions/security/plugins/entity_store/server/domain/saved_objects/global_state/constants.ts+2 −2 modified@@ -15,11 +15,11 @@ export const LOG_EXTRACTION_FREQUENCY_DEFAULT = '1m'; // Max amount of entities to extract in one ESQL query export const LOG_EXTRACTION_DOCS_LIMIT_DEFAULT = 10000; // Max raw log documents per logs to be processed in a query (inside elastic search) -export const LOG_EXTRACTION_MAX_LOGS_PER_PAGE_DEFAULT = 40000; +export const LOG_EXTRACTION_MAX_LOGS_PER_PAGE_DEFAULT = 50_000; export const LOG_EXTRACTION_TIMEOUT_DEFAULT = '59s'; export const LOG_EXTRACTION_MAX_TIME_WINDOW_SIZE_DEFAULT = '15m'; // Max total raw log documents to process per task run; 0 = no cap -export const LOG_EXTRACTION_MAX_LOGS_PER_WINDOW_DEFAULT = 500_000; +export const LOG_EXTRACTION_MAX_LOGS_PER_WINDOW_DEFAULT = 100_000; export const LOG_EXTRACTION_CAP_BEHAVIOR_DEFAULT = 'drop' as const; export type LogExtractionConfig = z.infer<typeof LogExtractionConfig>;
209c12d77d1b[Scout] Update test config manifests (#270528)
1 file changed · +130 −18
x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/.meta/ui/standard.json+130 −18 modified@@ -1,5 +1,5 @@ { - "sha1": "50b5dfba62ac64690de376d8bc22cd52d75b4e93", + "sha1": "d17dc6f8487721a630d816876f7496bd8d02b0cc", "tests": [ { "id": "709f30ae5fb788f-05a1663c9e84f0e", @@ -221,7 +221,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 71, + "line": 74, "column": 7 } }, @@ -235,7 +235,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 77, + "line": 80, "column": 7 } }, @@ -249,7 +249,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 100, + "line": 103, "column": 7 } }, @@ -263,7 +263,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 108, + "line": 111, "column": 7 } }, @@ -277,7 +277,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 112, + "line": 115, "column": 7 } }, @@ -291,7 +291,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 118, + "line": 121, "column": 7 } }, @@ -305,7 +305,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 137, + "line": 141, "column": 7 } }, @@ -319,7 +319,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 144, + "line": 148, "column": 7 } }, @@ -333,7 +333,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 152, + "line": 156, "column": 7 } }, @@ -347,7 +347,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 161, + "line": 165, "column": 7 } }, @@ -361,7 +361,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 177, + "line": 181, "column": 7 } }, @@ -375,7 +375,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 192, + "line": 196, "column": 7 } }, @@ -389,7 +389,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 202, + "line": 206, "column": 7 } }, @@ -403,7 +403,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 218, + "line": 222, "column": 7 } }, @@ -417,7 +417,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 226, + "line": 233, "column": 7 } }, @@ -431,7 +431,7 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 233, + "line": 240, "column": 7 } }, @@ -445,7 +445,119 @@ ], "location": { "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/discovery.spec.ts", - "line": 243, + "line": 250, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-79d3cf13237ee2d", + "title": "Discover ES|QL should switch the query bar to ES|QL and display the default sample query", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 155, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-75f10f82cef9fbf", + "title": "Discover ES|QL should display a metric visualization for ES|QL STATS queries (count and sum)", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 174, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-1081a7397d42c88", + "title": "Discover ES|QL should open the inline edit visualization flyout for an ES|QL chart", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 189, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-4b67968e425b300", + "title": "Discover ES|QL should save an ES|QL visualization to a new dashboard from Discover", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 210, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-dc0e7a2eb6e2df6", + "title": "Discover ES|QL should edit, explore in Discover, and copy an ES|QL panel from a dashboard", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 230, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-f21c2c6ffe20444", + "title": "Discover ES|QL should restrict sidebar fields and grid columns to KEEP-listed fields", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 284, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-8bbbfff9121d488", + "title": "Discover ES|QL should embed a saved ES|QL Discover session on a dashboard and interact with its table", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 305, + "column": 7 + } + }, + { + "id": "7b807c730155cc2-ab47176f22b71bd", + "title": "Discover ES|QL should save an ES|QL bar histogram to a dashboard and edit it inline", + "expectedStatus": "passed", + "tags": [ + "@local-stateful-classic", + "@cloud-stateful-classic" + ], + "location": { + "file": "x-pack/platform/packages/private/kbn-scout-release-testing/test/scout/ui/tests/discover/esql.spec.ts", + "line": 347, "column": 7 } },
db396449a69d[9.3] [Scout] Don't create test track if no load candidates are identified (#270584) (#270599)
3 files changed · +29 −4
src/platform/packages/shared/kbn-scout/src/cli/create_test_tracks.ts+15 −3 modified@@ -549,19 +549,31 @@ export const createTestTracks: Command<void> = { : [...new Set(loads.map((load) => load.config.server.configSet))]; // Each server config set gets its own track - return configSets.map((configSet) => { + return configSets.flatMap((configSet): TestTrack[] => { log.info( `Building test track for test target '${target.tag}' with server config set '${configSet}'` ); + + const enabledLoads = loads.filter( + (load) => load.enabled && load.config.server.configSet === configSet + ); + + if (enabledLoads.length === 0) { + log.warning( + `No enabled test loads found for test target '${target.tag}' and server config set '${configSet}'` + ); + return []; + } + const track = buildTrack( Math.max(minimumRuntime, runtimeTarget), estimatedLaneSetupDuration, target, - loads.filter((load) => load.enabled && load.config.server.configSet === configSet), + enabledLoads, log ); track.metadata.server = { configSet }; - return track; + return [track]; }); }) .toArray();
src/platform/packages/shared/kbn-scout/src/execution/test_track.test.ts+10 −0 modified@@ -19,6 +19,16 @@ describe('TestTrack', () => { expect(track.leastLoadedOpenLane).toBe(undefined); }); + it('should produce valid stats for an empty track specification', () => { + const track = new TestTrack({ runtimeTarget: 10 }); + const spec = track.specification; + + expect(spec.stats.lane.count).toBe(0); + expect(spec.stats.lane.saturationPercent).toBe(0); + expect(spec.stats.combinedRuntime.target).toBe(0); + expect(spec.lanes).toEqual([]); + }); + it('closes the lane when one load fills it entirely', () => { const track = new TestTrack({ runtimeTarget: 10 });
src/platform/packages/shared/kbn-scout/src/execution/test_track.ts+4 −1 modified@@ -236,7 +236,10 @@ export class TestTrack { stats: { lane: { count: this.laneCount, - saturationPercent: parseFloat(((expectedRuntime / provisionedRuntime) * 100).toFixed(2)), + saturationPercent: + provisionedRuntime !== 0 + ? parseFloat(((expectedRuntime / provisionedRuntime) * 100).toFixed(2)) + : 0, longestEstimate: longestLaneEstimate, shortestEstimate: shortestLaneEstimate, },
Vulnerability mechanics
No source-code context for this CVE — mechanics is only generated when we can read the actual fix diff. Without that, the four sections (root cause, attack vector, affected code, fix) would be speculation rather than analysis.
References
1News mentions
0No linked articles in our index yet.