[Security Solution][Connectors] Torq connector allow EU hooks hostname (#212563)

## Summary

From: https://github.com/elastic/kibana/issues/212511

Add support for EU domains

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Sergi Massaneda 2025-03-07 18:43:24 +01:00 committed by GitHub
parent 5041031b5d
commit 723a33b7de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 68 additions and 5 deletions

View file

@ -0,0 +1,12 @@
/*
* 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.
*/
const hostNameRegExp = /^hooks\.(eu\.)?torq\.io$/;
export const isValidTorqHostName = (hostName: string) => {
return hostNameRegExp.test(hostName);
};

View file

@ -93,6 +93,41 @@ describe('TorqActionConnectorFields renders', () => {
});
});
it('connector validation succeeds when using a EU torq webhook URL', async () => {
const connector = {
...actionConnector,
config: { webhookIntegrationUrl: 'https://hooks.eu.torq.io/v1/webhooks/fjdksla' },
};
const { getByTestId } = render(
<ConnectorFormTestProvider connector={connector} onSubmit={onSubmit}>
<TorqActionConnectorFields
readOnly={false}
isEdit={false}
registerPreSubmitValidator={EMPTY_FUNC}
/>
</ConnectorFormTestProvider>
);
await act(async () => {
await userEvent.click(getByTestId('form-test-provide-submit'));
});
expect(onSubmit).toBeCalledWith({
data: {
actionTypeId: '.torq',
name: 'torq',
config: {
webhookIntegrationUrl: 'https://hooks.eu.torq.io/v1/webhooks/fjdksla',
},
secrets: {
token: 'testtoken',
},
isDeprecated: false,
},
isValid: true,
});
});
it('connector validation fails when there is no token', async () => {
const connector = {
...actionConnector,
@ -153,7 +188,7 @@ describe('TorqActionConnectorFields renders', () => {
const connector = {
...actionConnector,
config: {
webhookIntegrationUrl: 'https://test.com',
webhookIntegrationUrl: 'https://hooks.not-torq.io/v1/webhooks/fjdksla',
},
};

View file

@ -17,6 +17,7 @@ import {
import { isUrl } from '@kbn/es-ui-shared-plugin/static/validators/string';
import { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public';
import React from 'react';
import { isValidTorqHostName } from '../../../common/torq';
import * as i18n from './translations';
const { urlField, emptyField } = fieldValidators;
@ -42,7 +43,7 @@ const torqWebhookEndpoint =
};
if (!isUrl(value)) return error;
const hostname = new URL(value).hostname;
return hostname === 'hooks.torq.io' ? undefined : error;
return isValidTorqHostName(hostname) ? undefined : error;
};
const TorqActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps> = ({

View file

@ -85,6 +85,15 @@ describe('config validation', () => {
...config,
});
});
test('config validation passes with the EU endpoint', () => {
const config: Record<string, string | boolean> = {
webhookIntegrationUrl: 'https://hooks.eu.torq.io/v1/test',
};
expect(validateConfig(actionType, config, { configurationUtilities })).toEqual({
...defaultValues,
...config,
});
});
const errorCases: Array<{ name: string; url: string; errorMsg: string }> = [
{
@ -100,7 +109,12 @@ describe('config validation', () => {
{
name: 'fails when URL is not a Torq webhook endpoint',
url: 'http://mylisteningserver:9200/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io or https://hooks.eu.torq.io"`,
},
{
name: 'fails when URL is an unsupported Torq webhook subdomain',
url: 'https://hooks.anothersubdomain.torq.io/v1/test',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io or https://hooks.eu.torq.io"`,
},
];
errorCases.forEach(({ name, url, errorMsg }) => {

View file

@ -21,6 +21,7 @@ import {
import { renderMustacheObject } from '@kbn/actions-plugin/server/lib/mustache_renderer';
import { request } from '@kbn/actions-plugin/server/lib/axios_utils';
import { ActionTypeExecutorResult, ValidatorServices } from '@kbn/actions-plugin/server/types';
import { isValidTorqHostName } from '../../../common/torq';
import { getRetryAfterIntervalFromHeaders } from '../lib/http_response_retry_header';
import { promiseResult, isOk, Result } from '../lib/result_type';
@ -127,11 +128,11 @@ function validateActionTypeConfig(
);
}
if (configureUrlObj.hostname !== 'hooks.torq.io' && configureUrlObj.hostname !== 'localhost') {
if (!isValidTorqHostName(configureUrlObj.hostname) && configureUrlObj.hostname !== 'localhost') {
throw new Error(
i18n.translate('xpack.stackConnectors.torq.torqConfigurationErrorInvalidHostname', {
defaultMessage:
'error configuring send to Torq action: url must begin with https://hooks.torq.io',
'error configuring send to Torq action: url must begin with https://hooks.torq.io or https://hooks.eu.torq.io',
})
);
}