VYPR
Medium severity6.5NVD Advisory· Published May 28, 2026

CVE-2026-42400

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

1
  • Elastic/Kibanallm-fuzzy
    Range: >=8.19.0 <8.19.16, >=9.3.0 <9.3.5, >=9.4.0 <9.4.2

Patches

8
ce8a83387719

[8.19] Fix the HTTP route registration to limit the compressed payloads (#257721) (#269721)

https://github.com/elastic/kibanaMichael DokolinMay 18, 2026Fixed in 8.19.16via llm-release-walk
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)

https://github.com/elastic/kibanaKibana MachineApr 30, 2026Fixed in 9.3.5via llm-release-walk
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)

https://github.com/elastic/kibanaAgustina Nahir RuidiazApr 24, 2026Fixed in 9.3.5via llm-release-walk
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 () => {
    
86425ef4bb93

Add maxSize and maxLength constraints to schema validations (#250851) (#269001)

https://github.com/elastic/kibanaMatthew KimeMay 18, 2026Fixed in 8.19.16via llm-release-walk
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()),
     });
     
    
79b7a1b14d0c
https://github.com/elastic/kibanaFixed in 9.3.5via llm-release-walk
ca34dedcfe61

[9.4] [Entity Store] Change defaults to conservative values (#270617) (#270915)

https://github.com/elastic/kibanaRômulo FariasMay 25, 2026Fixed in 9.4.2via release-tag
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)

https://github.com/elastic/kibanaKibana MachineMay 23, 2026Fixed in 8.19.16via release-tag
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)

https://github.com/elastic/kibanaKibana MachineMay 22, 2026Fixed in 9.3.5via release-tag
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

1

News mentions

0

No linked articles in our index yet.