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

CVE-2026-49094

CVE-2026-49094

Description

Uncontrolled Resource Consumption (CWE-400) in Kibana can lead to denial of service via Excessive Allocation (CAPEC-130). An authenticated user with viewer-level access can submit a request containing an oversized input value to an analytics collections management endpoint. Kibana will consume excessive CPU and memory resources while processing the request. This results in Kibana becoming unavailable to all users until the service is manually recovered.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

An authenticated Kibana viewer can cause denial of service by submitting an oversized input to an analytics collections endpoint, exhausting CPU/memory until manual recovery.

Vulnerability

An uncontrolled resource consumption vulnerability (CWE-400) exists in Kibana's analytics collections management endpoint. An authenticated user with viewer-level access can submit a request containing an oversized input value, causing Kibana to allocate excessive CPU and memory resources while processing the request [1]. This affects all Kibana versions from 8.0.0 up to and including 8.19.15 in self-managed and Elastic Cloud Hosted deployments. Elastic Cloud Serverless is not affected.

Exploitation

An attacker needs only valid viewer-level authentication to Kibana and network access to the affected endpoint. No additional privileges or user interaction are required. The attacker sends a crafted request with an oversized input to the analytics collections management endpoint. Kibana's processing of this input triggers uncontrolled resource consumption, leading to service degradation [1].

Impact

Successful exploitation results in a denial of service condition. Kibana becomes unresponsive or unavailable to all users due to CPU and memory exhaustion. The service must be manually recovered to restore normal operation. There is no impact on confidentiality or integrity; the CVSS v3.1 score is 6.5 (Medium) with a vector of AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H [1].

Mitigation

The vulnerability is fixed in Kibana version 8.19.16 [1]. Users who cannot upgrade immediately can restrict access to the behavioral analytics collections feature by limiting the relevant Kibana feature privilege. Elastic Cloud Serverless customers were already protected before public disclosure due to continuous patching. No workaround other than privilege restriction is available for affected self-managed or Elastic Cloud Hosted deployments.

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

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(
    
c8dea77991d1

[8.19] [Search] limit analytics collection name size in route (#266225) (#266507)

https://github.com/elastic/kibanaKibana MachineApr 30, 2026Fixed in 8.19.16via llm-release-walk
1 file changed · +1 1
  • x-pack/solutions/search/plugins/enterprise_search/server/routes/enterprise_search/analytics.ts+1 1 modified
    @@ -154,7 +154,7 @@ export function registerAnalyticsRoutes({
           },
           validate: {
             body: schema.object({
    -          name: schema.string(),
    +          name: schema.string({ maxLength: 1024 }),
             }),
           },
         },
    
76216f26242a

[8.19] Validate email address format in email connector (#268496) (#269518)

https://github.com/elastic/kibanaAdam KasztennyMay 20, 2026Fixed in 8.19.16via llm-release-walk
8 files changed · +538 8
  • packages/kbn-optimizer/limits.yml+1 1 modified
    @@ -1,5 +1,5 @@
     pageLoadAssetSize:
    -  actions: 20000
    +  actions: 20500
       advancedSettings: 6196
       aiAssistantManagementSelection: 6917
       aiops: 19890
    
  • x-pack/platform/plugins/shared/actions/common/validate_email_addresses.test.ts+165 0 modified
    @@ -285,4 +285,169 @@ describe('validate_email_address', () => {
           );
         });
       });
    +
    +  describe('email format validation', () => {
    +    test('rejects addresses with leading hyphen in local part', () => {
    +      const result = validateEmailAddresses(null, ['-user@example.com']);
    +      expect(result).toEqual([
    +        { address: '-user@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects addresses with trailing hyphen in local part', () => {
    +      const result = validateEmailAddresses(null, ['user-@example.com']);
    +      expect(result).toEqual([
    +        { address: 'user-@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects addresses with leading hyphen in domain label', () => {
    +      const result = validateEmailAddresses(null, ['user@-example.com']);
    +      expect(result).toEqual([
    +        { address: 'user@-example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects addresses with trailing hyphen in domain label', () => {
    +      const result = validateEmailAddresses(null, ['user@example-.com']);
    +      expect(result).toEqual([
    +        { address: 'user@example-.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('accepts addresses with single-label domain (on-prem MTA)', () => {
    +      const result = validateEmailAddresses(null, ['user@localhost']);
    +      expect(result).toEqual([{ address: 'user@localhost', valid: true }]);
    +    });
    +
    +    test('allows quoted local part with leading hyphen', () => {
    +      const result = validateEmailAddresses(null, ['"-user"@example.com']);
    +      expect(result).toEqual([{ address: '"-user"@example.com', valid: true }]);
    +    });
    +
    +    test('allows addresses with hyphens in the middle of local part', () => {
    +      const result = validateEmailAddresses(null, ['first-last@example.com']);
    +      expect(result).toEqual([{ address: 'first-last@example.com', valid: true }]);
    +    });
    +
    +    test('allows addresses with hyphens in the middle of domain labels', () => {
    +      const result = validateEmailAddresses(null, ['user@my-domain.example.com']);
    +      expect(result).toEqual([{ address: 'user@my-domain.example.com', valid: true }]);
    +    });
    +
    +    test('allows standard valid email addresses', () => {
    +      const validEmails = [
    +        'user@example.com',
    +        'first.last@example.com',
    +        'user+tag@example.com',
    +        'user@sub.domain.example.com',
    +      ];
    +      const result = validateEmailAddresses(null, validEmails);
    +      result.forEach((r) => expect(r.valid).toBe(true));
    +    });
    +
    +    test('rejects address with leading dot in local part', () => {
    +      const result = validateEmailAddresses(null, ['.user@example.com']);
    +      expect(result).toEqual([
    +        { address: '.user@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with trailing dot in local part', () => {
    +      const result = validateEmailAddresses(null, ['user.@example.com']);
    +      expect(result).toEqual([
    +        { address: 'user.@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with leading dot in domain', () => {
    +      const result = validateEmailAddresses(null, ['user@.example.com']);
    +      expect(result).toEqual([
    +        { address: 'user@.example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with trailing dot in domain', () => {
    +      const result = validateEmailAddresses(null, ['user@example.com.']);
    +      expect(result).toEqual([
    +        { address: 'user@example.com.', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address starting with @ sign', () => {
    +      const result = validateEmailAddresses(null, ['@something@example.com']);
    +      expect(result).toEqual([
    +        { address: '@something@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with double @ sign', () => {
    +      const result = validateEmailAddresses(null, ['user@@example.com']);
    +      expect(result).toEqual([
    +        { address: 'user@@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with double dots in domain', () => {
    +      const result = validateEmailAddresses(null, ['user@example..com']);
    +      expect(result).toEqual([
    +        { address: 'user@example..com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with space in domain', () => {
    +      const result = validateEmailAddresses(null, ['user@exam ple.com']);
    +      expect(result).toEqual([
    +        { address: 'user@exam ple.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with angle brackets and script content', () => {
    +      const result = validateEmailAddresses(null, ['<script>@example.com']);
    +      expect(result).toEqual([
    +        { address: '<script>@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('rejects address with path traversal characters', () => {
    +      const result = validateEmailAddresses(null, ['../etc/passwd@example.com']);
    +      expect(result).toEqual([
    +        { address: '../etc/passwd@example.com', valid: false, reason: InvalidEmailReason.invalid },
    +      ]);
    +    });
    +
    +    test('accepts RFC 5322 quoted local part', () => {
    +      const result = validateEmailAddresses(null, ['"quoted"@example.com']);
    +      expect(result).toEqual([{ address: '"quoted"@example.com', valid: true }]);
    +    });
    +
    +    test('rejects group address with invalid local part in a member', () => {
    +      const result = validateEmailAddresses(null, ['Team: -alice@example.com, bob@example.com;']);
    +      expect(result).toEqual([
    +        {
    +          address: 'Team: -alice@example.com, bob@example.com;',
    +          valid: false,
    +          reason: InvalidEmailReason.invalid,
    +        },
    +      ]);
    +    });
    +
    +    test('rejects group address with invalid domain in a member', () => {
    +      const result = validateEmailAddresses(null, ['Team: alice@-example.com, bob@example.com;']);
    +      expect(result).toEqual([
    +        {
    +          address: 'Team: alice@-example.com, bob@example.com;',
    +          valid: false,
    +          reason: InvalidEmailReason.invalid,
    +        },
    +      ]);
    +    });
    +
    +    test('accepts group address when all members are valid', () => {
    +      const result = validateEmailAddresses(null, ['Team: alice@example.com, bob@example.com;']);
    +      expect(result).toEqual([
    +        { address: 'Team: alice@example.com, bob@example.com;', valid: true },
    +      ]);
    +    });
    +  });
     });
    
  • x-pack/platform/plugins/shared/actions/common/validate_email_addresses.ts+37 0 modified
    @@ -74,12 +74,49 @@ function validateEmailAddress(
       }
     }
     
    +function hasValidDomainLabels(domain: string): boolean {
    +  return domain
    +    .split('.')
    +    .every((label) => label.length > 0 && !label.startsWith('-') && !label.endsWith('-'));
    +}
    +
    +function hasValidLocalPart(local: string, tokens: string): boolean {
    +  if (local.length === 0) return false;
    +  // The parser strips quotes, so "-foo"@example.com yields local="-foo".
    +  // Check the per-mailbox tokens (not the full input) to avoid a quoted
    +  // member in a group falsely bypassing the check for an unquoted member.
    +  if (tokens.includes(`"${local}"@`)) return true;
    +  return !local.startsWith('-') && !local.endsWith('-');
    +}
    +
     function validateEmailAddress_(allowedDomains: string[] | null, address: string): ValidatedEmail {
       const emailAddresses = parseAddressList(address);
       if (emailAddresses == null) {
         return { address, valid: false, reason: InvalidEmailReason.invalid };
       }
     
    +  for (const emailAddress of emailAddresses) {
    +    if (emailAddress.type === 'mailbox') {
    +      const tokens = emailAddress.parts.address.tokens;
    +      if (
    +        !hasValidLocalPart(emailAddress.local, tokens) ||
    +        !hasValidDomainLabels(emailAddress.domain)
    +      ) {
    +        return { address, valid: false, reason: InvalidEmailReason.invalid };
    +      }
    +    } else if (emailAddress.type === 'group') {
    +      for (const groupAddress of emailAddress.addresses) {
    +        const tokens = groupAddress.parts.address.tokens;
    +        if (
    +          !hasValidLocalPart(groupAddress.local, tokens) ||
    +          !hasValidDomainLabels(groupAddress.domain)
    +        ) {
    +          return { address, valid: false, reason: InvalidEmailReason.invalid };
    +        }
    +      }
    +    }
    +  }
    +
       if (allowedDomains !== null) {
         const allowedDomainsSet = new Set(allowedDomains);
     
    
  • x-pack/platform/plugins/shared/actions/server/actions_config.test.ts+8 2 modified
    @@ -505,12 +505,18 @@ const testEmailsInvalid = ['invalid-email-address', '(garbage)'];
     const testEmailsAll = testEmailsOk.concat(testEmailsNotAllowed).concat(testEmailsInvalid);
     
     describe('validateEmailAddresses()', () => {
    -  test('all domains allowed if config not set', () => {
    +  test('all domains allowed if config not set, but format still validated', () => {
         const acu = getActionsConfigurationUtilities(defaultActionsConfig);
    -    const message = acu.validateEmailAddresses(testEmailsAll);
    +    const message = acu.validateEmailAddresses(testEmailsOk.concat(testEmailsNotAllowed));
         expect(message).toEqual(undefined);
       });
     
    +  test('invalid format rejected even without domain allowlist', () => {
    +    const acu = getActionsConfigurationUtilities(defaultActionsConfig);
    +    const message = acu.validateEmailAddresses(testEmailsInvalid);
    +    expect(message).toMatchInlineSnapshot(`"not valid emails: invalid-email-address, (garbage)"`);
    +  });
    +
       test('only filtered domains allowed if config set', () => {
         const acu = getActionsConfigurationUtilities({
           ...defaultActionsConfig,
    
  • x-pack/platform/plugins/shared/actions/server/actions_config.ts+5 5 modified
    @@ -186,11 +186,11 @@ function validateEmails(
       addresses: string[],
       options: ValidateEmailAddressesOptions
     ): string | undefined {
    -  if (config.email?.domain_allowlist == null) {
    -    return;
    -  }
    -
    -  const validated = validateEmailAddresses(config.email.domain_allowlist, addresses, options);
    +  const validated = validateEmailAddresses(
    +    config.email?.domain_allowlist ?? null,
    +    addresses,
    +    options
    +  );
       return invalidEmailsAsMessage(validated);
     }
     
    
  • x-pack/platform/plugins/shared/stack_connectors/server/connector_types/email/index.test.ts+170 0 modified
    @@ -509,6 +509,176 @@ describe('params validation', () => {
         );
       });
     
    +  test('params validation fails when email has invalid format (leading hyphen in local part)', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['-user@example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email has invalid format (leading hyphen in domain)', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@-example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email has invalid format (trailing hyphen in domain)', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@example-.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email starts with @ sign', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['@something@example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email has double dots in domain', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@example..com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email has double @ sign', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@@example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation fails when email has space in domain', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@exam ple.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation accepts email with single-label domain (on-prem MTA)', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['user@localhost'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).not.toThrowError();
    +  });
    +
    +  test('params validation fails when email has path traversal characters', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['../etc/passwd@example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).toThrowError(/not valid emails/);
    +  });
    +
    +  test('params validation succeeds for valid email with hyphens and subdomains', () => {
    +    const configUtils = getActionsConfigUtils({});
    +    expect(() => {
    +      validateParams(
    +        connectorType,
    +        {
    +          to: ['first-last@my-domain.example.com'],
    +          cc: [],
    +          bcc: [],
    +          subject: 'this is a test',
    +          message: 'this is the message',
    +        },
    +        { configurationUtilities: configUtils }
    +      );
    +    }).not.toThrow();
    +  });
    +
       test('params validation for emails calls validateEmailAddresses', async () => {
         const configUtils = actionsConfigMock.create();
         configUtils.validateEmailAddresses.mockImplementation(validateEmailAddressesImpl);
    
  • x-pack/platform/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/email.ts+56 0 modified
    @@ -112,6 +112,62 @@ export default function emailTest({ getService }: FtrProviderContext) {
           });
         });
     
    +    describe('rejects invalid email address formats', () => {
    +      it('rejects addresses with leading hyphen in local part', async () => {
    +        const from = `bob@${EmailDomainAllowed}`;
    +        const conn = await createConnector(from);
    +        expect(conn.status).to.be(200);
    +        const { id } = conn.body;
    +
    +        const { status, body } = await runConnector(id, ['-user@' + EmailDomainAllowed], [], []);
    +        expect(status).to.be(200);
    +        expect(body?.status).to.be('error');
    +        expect(body?.message).to.match(/not valid emails/);
    +      });
    +
    +      it('rejects addresses with leading hyphen in domain', async () => {
    +        const from = `bob@${EmailDomainAllowed}`;
    +        const conn = await createConnector(from);
    +        expect(conn.status).to.be(200);
    +        const { id } = conn.body;
    +
    +        const { status, body } = await runConnector(id, ['user@-example.com'], [], []);
    +        expect(status).to.be(200);
    +        expect(body?.status).to.be('error');
    +        expect(body?.message).to.match(/not valid emails/);
    +      });
    +
    +      it('rejects addresses with trailing hyphen in domain', async () => {
    +        const from = `bob@${EmailDomainAllowed}`;
    +        const conn = await createConnector(from);
    +        expect(conn.status).to.be(200);
    +        const { id } = conn.body;
    +
    +        const { status, body } = await runConnector(id, ['user@example-.com'], [], []);
    +        expect(status).to.be(200);
    +        expect(body?.status).to.be('error');
    +        expect(body?.message).to.match(/not valid emails/);
    +      });
    +
    +      it('rejects addresses with invalid format in cc and bcc', async () => {
    +        const from = `bob@${EmailDomainAllowed}`;
    +        const conn = await createConnector(from);
    +        expect(conn.status).to.be(200);
    +        const { id } = conn.body;
    +
    +        const validTo = [`jeb@${EmailDomainAllowed}`];
    +        const { status, body } = await runConnector(
    +          id,
    +          validTo,
    +          ['-cc@example.com'],
    +          ['user@bad-.org']
    +        );
    +        expect(status).to.be(200);
    +        expect(body?.status).to.be('error');
    +        expect(body?.message).to.match(/not valid emails/);
    +      });
    +    });
    +
         describe('export, import, then execute email connector', () => {
           afterEach(() => objectRemover.removeAll());
     
    
  • x-pack/platform/test/functional_with_es_ssl/apps/triggers_actions_ui/email.ts+96 0 modified
    @@ -11,12 +11,108 @@ import { FtrProviderContext } from '../../ftr_provider_context';
     export default ({ getPageObjects, getService }: FtrProviderContext) => {
       const testSubjects = getService('testSubjects');
       const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']);
    +  const supertest = getService('supertest');
    +  const find = getService('find');
    +  const retry = getService('retry');
     
       describe('Email', () => {
         beforeEach(async () => {
           await pageObjects.common.navigateToApp('triggersActionsConnectors');
         });
     
    +    describe('test form recipient validation', () => {
    +      let connectorId: string;
    +
    +      before(async () => {
    +        const { body } = await supertest
    +          .post('/api/actions/connector')
    +          .set('kbn-xsrf', 'foo')
    +          .send({
    +            name: 'email-format-validation-test',
    +            connector_type_id: '.email',
    +            config: {
    +              from: 'test@test.com',
    +              host: 'localhost',
    +              port: 1025,
    +              secure: false,
    +              hasAuth: false,
    +            },
    +            secrets: {},
    +          })
    +          .expect(200);
    +        connectorId = body.id;
    +      });
    +
    +      after(async () => {
    +        if (connectorId) {
    +          await supertest
    +            .delete(`/api/actions/connector/${connectorId}`)
    +            .set('kbn-xsrf', 'foo')
    +            .expect(204);
    +        }
    +      });
    +
    +      it('shows invalid email error for addresses with leading hyphen in local part', async () => {
    +        await pageObjects.common.navigateToApp('triggersActionsConnectors');
    +        await pageObjects.triggersActionsUI.searchConnectors('email-format-validation-test');
    +        await retry.try(async () => {
    +          const rows = await find.allByCssSelector(
    +            '[data-test-subj="connectorsTableCell-name"] button'
    +          );
    +          expect(rows.length).to.be(1);
    +        });
    +        await find.clickByCssSelector('[data-test-subj="connectorsTableCell-name"] button');
    +        await testSubjects.click('testConnectorTab');
    +        await testSubjects.existOrFail('test-connector-form');
    +
    +        const comboBox = await testSubjects.find('toEmailAddressInput');
    +        const input = await comboBox.findByCssSelector('input');
    +        await input.type('-user@example.com');
    +        await input.pressKeys('\uE007');
    +
    +        await retry.try(async () => {
    +          const errors = await find.allByCssSelector('.euiFormErrorText');
    +          const messages = await Promise.all(errors.map((el) => el.getVisibleText()));
    +          expect(messages.join(' ')).to.contain('is not valid');
    +        });
    +
    +        await testSubjects.click('edit-connector-flyout-close-btn');
    +        if (await testSubjects.exists('confirmModalConfirmButton', { timeout: 2000 })) {
    +          await testSubjects.click('confirmModalConfirmButton');
    +        }
    +      });
    +
    +      it('shows invalid email error for addresses with leading hyphen in domain', async () => {
    +        await pageObjects.common.navigateToApp('triggersActionsConnectors');
    +        await pageObjects.triggersActionsUI.searchConnectors('email-format-validation-test');
    +        await retry.try(async () => {
    +          const rows = await find.allByCssSelector(
    +            '[data-test-subj="connectorsTableCell-name"] button'
    +          );
    +          expect(rows.length).to.be(1);
    +        });
    +        await find.clickByCssSelector('[data-test-subj="connectorsTableCell-name"] button');
    +        await testSubjects.click('testConnectorTab');
    +        await testSubjects.existOrFail('test-connector-form');
    +
    +        const comboBox = await testSubjects.find('toEmailAddressInput');
    +        const input = await comboBox.findByCssSelector('input');
    +        await input.type('user@-example.com');
    +        await input.pressKeys('\uE007');
    +
    +        await retry.try(async () => {
    +          const errors = await find.allByCssSelector('.euiFormErrorText');
    +          const messages = await Promise.all(errors.map((el) => el.getVisibleText()));
    +          expect(messages.join(' ')).to.contain('is not valid');
    +        });
    +
    +        await testSubjects.click('edit-connector-flyout-close-btn');
    +        if (await testSubjects.exists('confirmModalConfirmButton', { timeout: 2000 })) {
    +          await testSubjects.click('confirmModalConfirmButton');
    +        }
    +      });
    +    });
    +
         it('should use the kibana config for aws ses defaults', async () => {
           await new Promise((resolve) => setTimeout(resolve, 10000));
           await pageObjects.triggersActionsUI.clickCreateConnectorButton();
    
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()),
     });
     
    
41f145b9e0bf

[8.19] [Security] Add maxLength to login fields (#266186) (#268544)

https://github.com/elastic/kibanaKibana MachineMay 8, 2026Fixed in 8.19.16via llm-release-walk
4 files changed · +105 8
  • x-pack/platform/plugins/shared/security/server/config.test.ts+28 0 modified
    @@ -666,6 +666,14 @@ describe('config schema', () => {
               }
             `);
           });
    +
    +      it('rejects provider names longer than 1024 characters', () => {
    +        expect(() =>
    +          ConfigSchema.validate({
    +            authc: { providers: { basic: { ['a'.repeat(1025)]: { order: 0 } } } },
    +          })
    +        ).toThrow(/maximum length of \[1024\]/);
    +      });
         });
     
         describe('`token` provider', () => {
    @@ -971,6 +979,16 @@ describe('config schema', () => {
               }
             `);
           });
    +
    +      it('rejects provider names longer than 1024 characters', () => {
    +        expect(() =>
    +          ConfigSchema.validate({
    +            authc: {
    +              providers: { oidc: { ['a'.repeat(1025)]: { order: 0, realm: 'oidc1' } } },
    +            },
    +          })
    +        ).toThrow(/maximum length of \[1024\]/);
    +      });
         });
     
         describe('`saml` provider', () => {
    @@ -1099,6 +1117,16 @@ describe('config schema', () => {
               }
             `);
           });
    +
    +      it('rejects provider names longer than 1024 characters', () => {
    +        expect(() =>
    +          ConfigSchema.validate({
    +            authc: {
    +              providers: { saml: { ['a'.repeat(1025)]: { order: 0, realm: 'saml1' } } },
    +            },
    +          })
    +        ).toThrow(/maximum length of \[1024\]/);
    +      });
         });
     
         describe('`anonymous` provider', () => {
    
  • x-pack/platform/plugins/shared/security/server/config.ts+3 3 modified
    @@ -65,7 +65,7 @@ function getUniqueProviderSchema<TProperties extends Record<string, Type<any>>>(
     ) {
       return schema.maybe(
         schema.recordOf(
    -      schema.string(),
    +      schema.string({ maxLength: 1024 }),
           schema.object(
             properties
               ? { ...getCommonProviderSchemaProperties(overrides), ...properties }
    @@ -122,7 +122,7 @@ const providersConfigSchema = schema.object(
         pki: getUniqueProviderSchema('pki'),
         saml: schema.maybe(
           schema.recordOf(
    -        schema.string(),
    +        schema.string({ maxLength: 1024 }),
             schema.object({
               ...getCommonProviderSchemaProperties(),
               realm: schema.string(),
    @@ -133,7 +133,7 @@ const providersConfigSchema = schema.object(
         ),
         oidc: schema.maybe(
           schema.recordOf(
    -        schema.string(),
    +        schema.string({ maxLength: 1024 }),
             schema.object({ ...getCommonProviderSchemaProperties(), realm: schema.string() })
           )
         ),
    
  • x-pack/platform/plugins/shared/security/server/routes/authentication/common.test.ts+69 0 modified
    @@ -411,6 +411,58 @@ describe('Common authentication routes', () => {
           ).toThrowErrorMatchingInlineSnapshot(
             `"[params.password]: value has length [0] but it must have a minimum length of [1]."`
           );
    +
    +      expect(() =>
    +        bodyValidator.validate({
    +          providerType: 'a'.repeat(1025),
    +          providerName: 'saml1',
    +          currentURL: '/some-url',
    +        })
    +      ).toThrowErrorMatchingInlineSnapshot(
    +        `"[providerType]: value has length [1025] but it must have a maximum length of [1024]."`
    +      );
    +
    +      expect(() =>
    +        bodyValidator.validate({
    +          providerType: 'saml',
    +          providerName: 'a'.repeat(1025),
    +          currentURL: '/some-url',
    +        })
    +      ).toThrowErrorMatchingInlineSnapshot(
    +        `"[providerName]: value has length [1025] but it must have a maximum length of [1024]."`
    +      );
    +
    +      expect(() =>
    +        bodyValidator.validate({
    +          providerType: 'saml',
    +          providerName: 'saml1',
    +          currentURL: 'a'.repeat(8193),
    +        })
    +      ).toThrowErrorMatchingInlineSnapshot(
    +        `"[currentURL]: value has length [8193] but it must have a maximum length of [8192]."`
    +      );
    +
    +      expect(() =>
    +        bodyValidator.validate({
    +          providerType: 'basic',
    +          providerName: 'basic1',
    +          currentURL: '/some-url',
    +          params: { username: 'a'.repeat(1025), password: 'some-password' },
    +        })
    +      ).toThrowErrorMatchingInlineSnapshot(
    +        `"[params.username]: value has length [1025] but it must have a maximum length of [1024]."`
    +      );
    +
    +      expect(() =>
    +        bodyValidator.validate({
    +          providerType: 'basic',
    +          providerName: 'basic1',
    +          currentURL: '/some-url',
    +          params: { username: 'some-user', password: 'a'.repeat(1025) },
    +        })
    +      ).toThrowErrorMatchingInlineSnapshot(
    +        `"[params.password]: value has length [1025] but it must have a maximum length of [1024]."`
    +      );
         });
     
         it('returns 500 if login throws unhandled exception.', async () => {
    @@ -459,6 +511,23 @@ describe('Common authentication routes', () => {
           });
         });
     
    +    it('returns 401 when providerType or providerName is an empty string.', async () => {
    +      authc.login.mockResolvedValue(AuthenticationResult.notHandled());
    +
    +      for (const body of [
    +        { providerType: '', providerName: 'saml1', currentURL: '/some-url' },
    +        { providerType: 'saml', providerName: '', currentURL: '/some-url' },
    +      ]) {
    +        const request = httpServerMock.createKibanaRequest({ body });
    +
    +        await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({
    +          status: 401,
    +          payload: 'Unauthorized',
    +          options: {},
    +        });
    +      }
    +    });
    +
         it('returns redirect location from authentication result if any.', async () => {
           authc.login.mockResolvedValue(AuthenticationResult.redirectTo('http://redirect-to/path'));
     
    
  • x-pack/platform/plugins/shared/security/server/routes/authentication/common.ts+5 5 modified
    @@ -160,8 +160,8 @@ export function defineCommonRoutes({
       }
     
       const basicParamsSchema = schema.object({
    -    username: schema.string({ minLength: 1 }),
    -    password: schema.string({ minLength: 1 }),
    +    username: schema.string({ minLength: 1, maxLength: 1024 }),
    +    password: schema.string({ minLength: 1, maxLength: 1024 }),
       });
     
       function getLoginAttemptForProviderType<T extends string>(
    @@ -205,9 +205,9 @@ export function defineCommonRoutes({
           },
           validate: {
             body: schema.object({
    -          providerType: schema.string(),
    -          providerName: schema.string(),
    -          currentURL: schema.string(),
    +          providerType: schema.string({ maxLength: 1024 }),
    +          providerName: schema.string({ maxLength: 1024 }),
    +          currentURL: schema.string({ maxLength: 8192 }),
               params: schema.conditional(
                 schema.siblingRef('providerType'),
                 schema.oneOf([
    
c30e82656f00
https://github.com/elastic/kibanaFixed in 8.19.16via llm-release-walk
108d5b8a6e2d
https://github.com/elastic/kibanaFixed in 8.19.16via llm-release-walk
209c12d77d1b
https://github.com/elastic/kibanaFixed in 8.19.16via release-tag

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.