Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { Field, PasswordField } from '@kbn/es-ui-shared-plugin/static/forms/components';
import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import { ERROR_CODE } from '@kbn/es-ui-shared-plugin/static/forms/helpers/field_validators/types';
import {
UseField,
ValidationError,
ValidationFunc,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { isUrl } from '@kbn/es-ui-shared-plugin/static/validators/string';
import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { ActionConnectorFieldsProps } from '@kbn/triggers-actions-ui-plugin/public';
import React from 'react';
import TorqWebhookEndpoint from '@kbn/stack-connectors-plugin/public/connector_types/torq/torq_utils';
import * as i18n from './translations';

const { urlField, emptyField } = fieldValidators;
Expand All @@ -31,20 +26,6 @@ const Callout: React.FC<{ title: string; dataTestSubj: string }> = ({ title, dat
);
};

const torqWebhookEndpoint =
(message: string) =>
(...args: Parameters<ValidationFunc>): ReturnType<ValidationFunc<any, ERROR_CODE>> => {
const [{ value }] = args as Array<{ value: string }>;
const error: ValidationError<ERROR_CODE> = {
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message,
};
if (!isUrl(value)) return error;
const hostname = new URL(value).hostname;
return hostname === 'hooks.torq.io' ? undefined : error;
};

const TorqActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps> = ({
readOnly,
}) => {
Expand All @@ -66,7 +47,7 @@ const TorqActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsPr
validator: urlField(i18n.URL_INVALID),
},
{
validator: torqWebhookEndpoint(i18n.URL_NOT_TORQ_WEBHOOK),
validator: TorqWebhookEndpoint(i18n.URL_NOT_TORQ_WEBHOOK),
},
],
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 TorqWebhookEndpoint from './torq_utils';

describe('TorqWebhookEndpoint', () => {
it('should return undefined for valid Torq webhook URL', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://hooks.torq.io' });

expect(result).toBeUndefined();
});

it('should return undefined for valid Torq webhook subdomain URL', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://hooks.eu.torq.io' });

expect(result).toBeUndefined();
});

it('should return undefined for invalid Torq webhook hostname', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://hooks.eu.west.torq.io' });

expect(result).toBeUndefined();
});

it('should return error for non-Torq URL', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://example.com' });

expect(result).toEqual({
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message: 'Invalid URL provided',
});
});

it('should return error for badly ordered hostname', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://invalid.hooks.torq.io' });

expect(result).toEqual({
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message: 'Invalid URL provided',
});
});

it('should return error for hook.torq.io', () => {
const validation = TorqWebhookEndpoint('Invalid URL provided');
const result = validation({ value: 'https://hook.torq.io' });

expect(result).toEqual({
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message: 'Invalid URL provided',
});
});

it('should return error for an empty URL', () => {
const validation = TorqWebhookEndpoint('URL cannot be empty');
const result = validation({ value: '' });

expect(result).toEqual({
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message: 'URL cannot be empty',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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 {
ValidationError,
ValidationFunc,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { ERROR_CODE } from '@kbn/es-ui-shared-plugin/static/forms/helpers/field_validators/types';
import { isUrl } from '@kbn/es-ui-shared-plugin/static/validators/string';

const TorqWebhookEndpoint =
(message: string) =>
(...args: Parameters<ValidationFunc>): ReturnType<ValidationFunc<any, ERROR_CODE>> => {
const [{ value }] = args as Array<{ value: string }>;
const error: ValidationError<ERROR_CODE> = {
code: 'ERR_FIELD_FORMAT',
formatType: 'URL',
message,
};
if (!isUrl(value)) return error;
const hostname = new URL(value).hostname;
const isTorqHostname = /^hooks(\.[a-z0-9]+)*\.torq\.io$/.test(hostname);
return isTorqHostname ? undefined : error;
};

// eslint-disable-next-line import/no-default-export
export { TorqWebhookEndpoint as default };
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,22 @@ describe('secrets validation', () => {
describe('config validation', () => {
const defaultValues: Record<string, string | null> = {};

test('config validation passes with an appropriate endpoint', () => {
const config: Record<string, string | boolean> = {
webhookIntegrationUrl: 'https://hooks.torq.io/v1/test',
};
expect(validateConfig(actionType, config, { configurationUtilities })).toEqual({
...defaultValues,
...config,
const validHostnames = [
'https://hooks.torq.io/v1/test',
'https://hooks.eu.torq.io/v1/test',
'https://hooks.eu.west.torq.io/v1/test',
'https://localhost/v1/test',
];

validHostnames.forEach((hostname) => {
test('config validation passes with an appropriate endpoint', () => {
const config: Record<string, string | boolean> = {
webhookIntegrationUrl: hostname,
};
expect(validateConfig(actionType, config, { configurationUtilities })).toEqual({
...defaultValues,
...config,
});
});
});

Expand All @@ -102,6 +111,36 @@ describe('config validation', () => {
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"`,
},
{
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"`,
},
{
name: 'fails - ending in com and not io',
url: 'https://hooks.torq.com/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
},
{
name: 'fails - ending in io.com and not io',
url: 'https://hooks.torq.com.io/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
},
{
name: 'fails - should be hooks but is subdomain',
url: 'https://subdomain.torq.io/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
},
{
name: 'fails - should be hooks but is subdomain 2',
url: 'https://subdomain.eu.torq.io/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
},
{
name: 'fails - abruptly ends',
url: 'https://hooks.torq/endpoint',
errorMsg: `"error validating action type config: error configuring send to Torq action: url must begin with https://hooks.torq.io"`,
},
];
errorCases.forEach(({ name, url, errorMsg }) => {
test(name, () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ function validateActionTypeConfig(
);
}

if (configureUrlObj.hostname !== 'hooks.torq.io' && configureUrlObj.hostname !== 'localhost') {
const isValidHostname = /^hooks(\.[a-z0-9]+)*\.torq\.io$/.test(configureUrlObj.hostname) || configureUrlObj.hostname === 'localhost';
if (!isValidHostname) {
throw new Error(
i18n.translate('xpack.stackConnectors.torq.torqConfigurationErrorInvalidHostname', {
defaultMessage:
Expand Down