[Actions] Removed double parsing when passing action url for validation (#87928)
* Removed double parsing when passing action url for validation * Fixing functional test * PR fixes * PR fixes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
3fce1c6457
commit
60f8b24529
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { tryCatch, map, mapNullable, getOrElse } from 'fp-ts/lib/Option';
|
import { tryCatch, map, mapNullable, getOrElse } from 'fp-ts/lib/Option';
|
||||||
import { URL } from 'url';
|
import url from 'url';
|
||||||
import { curry } from 'lodash';
|
import { curry } from 'lodash';
|
||||||
import { pipe } from 'fp-ts/lib/pipeable';
|
import { pipe } from 'fp-ts/lib/pipeable';
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export enum EnabledActionTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AllowListingField {
|
enum AllowListingField {
|
||||||
url = 'url',
|
URL = 'url',
|
||||||
hostname = 'hostname',
|
hostname = 'hostname',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,17 +56,17 @@ function disabledActionTypeErrorMessage(actionType: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAllowed({ allowedHosts }: ActionsConfigType, hostname: string): boolean {
|
function isAllowed({ allowedHosts }: ActionsConfigType, hostname: string | null): boolean {
|
||||||
const allowed = new Set(allowedHosts);
|
const allowed = new Set(allowedHosts);
|
||||||
if (allowed.has(AllowedHosts.Any)) return true;
|
if (allowed.has(AllowedHosts.Any)) return true;
|
||||||
if (allowed.has(hostname)) return true;
|
if (hostname && allowed.has(hostname)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHostnameAllowedInUri(config: ActionsConfigType, uri: string): boolean {
|
function isHostnameAllowedInUri(config: ActionsConfigType, uri: string): boolean {
|
||||||
return pipe(
|
return pipe(
|
||||||
tryCatch(() => new URL(uri)),
|
tryCatch(() => url.parse(uri)),
|
||||||
map((url) => url.hostname),
|
map((parsedUrl) => parsedUrl.hostname),
|
||||||
mapNullable((hostname) => isAllowed(config, hostname)),
|
mapNullable((hostname) => isAllowed(config, hostname)),
|
||||||
getOrElse<boolean>(() => false)
|
getOrElse<boolean>(() => false)
|
||||||
);
|
);
|
||||||
|
@ -94,7 +94,7 @@ export function getActionsConfigurationUtilities(
|
||||||
isActionTypeEnabled,
|
isActionTypeEnabled,
|
||||||
ensureUriAllowed(uri: string) {
|
ensureUriAllowed(uri: string) {
|
||||||
if (!isUriAllowed(uri)) {
|
if (!isUriAllowed(uri)) {
|
||||||
throw new Error(allowListErrorMessage(AllowListingField.url, uri));
|
throw new Error(allowListErrorMessage(AllowListingField.URL, uri));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ensureHostnameAllowed(hostname: string) {
|
ensureHostnameAllowed(hostname: string) {
|
||||||
|
|
|
@ -117,7 +117,7 @@ describe('validateActionTypeSecrets()', () => {
|
||||||
logger: mockedLogger,
|
logger: mockedLogger,
|
||||||
configurationUtilities: {
|
configurationUtilities: {
|
||||||
...actionsConfigMock.create(),
|
...actionsConfigMock.create(),
|
||||||
ensureHostnameAllowed: () => {
|
ensureUriAllowed: () => {
|
||||||
throw new Error(`target hostname is not added to allowedHosts`);
|
throw new Error(`target hostname is not added to allowedHosts`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -70,7 +70,7 @@ export function getActionType({
|
||||||
}),
|
}),
|
||||||
validate: {
|
validate: {
|
||||||
secrets: schema.object(secretsSchemaProps, {
|
secrets: schema.object(secretsSchemaProps, {
|
||||||
validate: curry(valdiateActionTypeConfig)(configurationUtilities),
|
validate: curry(validateActionTypeConfig)(configurationUtilities),
|
||||||
}),
|
}),
|
||||||
params: ParamsSchema,
|
params: ParamsSchema,
|
||||||
},
|
},
|
||||||
|
@ -88,13 +88,13 @@ function renderParameterTemplates(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function valdiateActionTypeConfig(
|
function validateActionTypeConfig(
|
||||||
configurationUtilities: ActionsConfigurationUtilities,
|
configurationUtilities: ActionsConfigurationUtilities,
|
||||||
secretsObject: ActionTypeSecretsType
|
secretsObject: ActionTypeSecretsType
|
||||||
) {
|
) {
|
||||||
let url: URL;
|
const configuredUrl = secretsObject.webhookUrl;
|
||||||
try {
|
try {
|
||||||
url = new URL(secretsObject.webhookUrl);
|
new URL(configuredUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return i18n.translate('xpack.actions.builtin.slack.slackConfigurationErrorNoHostname', {
|
return i18n.translate('xpack.actions.builtin.slack.slackConfigurationErrorNoHostname', {
|
||||||
defaultMessage: 'error configuring slack action: unable to parse host name from webhookUrl',
|
defaultMessage: 'error configuring slack action: unable to parse host name from webhookUrl',
|
||||||
|
@ -102,7 +102,7 @@ function valdiateActionTypeConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configurationUtilities.ensureHostnameAllowed(url.hostname);
|
configurationUtilities.ensureUriAllowed(configuredUrl);
|
||||||
} catch (allowListError) {
|
} catch (allowListError) {
|
||||||
return i18n.translate('xpack.actions.builtin.slack.slackConfigurationError', {
|
return i18n.translate('xpack.actions.builtin.slack.slackConfigurationError', {
|
||||||
defaultMessage: 'error configuring slack action: {message}',
|
defaultMessage: 'error configuring slack action: {message}',
|
||||||
|
|
|
@ -117,7 +117,7 @@ describe('validateActionTypeSecrets()', () => {
|
||||||
logger: mockedLogger,
|
logger: mockedLogger,
|
||||||
configurationUtilities: {
|
configurationUtilities: {
|
||||||
...actionsConfigMock.create(),
|
...actionsConfigMock.create(),
|
||||||
ensureHostnameAllowed: () => {
|
ensureUriAllowed: () => {
|
||||||
throw new Error(`target hostname is not added to allowedHosts`);
|
throw new Error(`target hostname is not added to allowedHosts`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -71,9 +71,9 @@ function validateActionTypeConfig(
|
||||||
configurationUtilities: ActionsConfigurationUtilities,
|
configurationUtilities: ActionsConfigurationUtilities,
|
||||||
secretsObject: ActionTypeSecretsType
|
secretsObject: ActionTypeSecretsType
|
||||||
) {
|
) {
|
||||||
let url: URL;
|
const configuredUrl = secretsObject.webhookUrl;
|
||||||
try {
|
try {
|
||||||
url = new URL(secretsObject.webhookUrl);
|
new URL(configuredUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return i18n.translate('xpack.actions.builtin.teams.teamsConfigurationErrorNoHostname', {
|
return i18n.translate('xpack.actions.builtin.teams.teamsConfigurationErrorNoHostname', {
|
||||||
defaultMessage: 'error configuring teams action: unable to parse host name from webhookUrl',
|
defaultMessage: 'error configuring teams action: unable to parse host name from webhookUrl',
|
||||||
|
@ -81,7 +81,7 @@ function validateActionTypeConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configurationUtilities.ensureHostnameAllowed(url.hostname);
|
configurationUtilities.ensureUriAllowed(configuredUrl);
|
||||||
} catch (allowListError) {
|
} catch (allowListError) {
|
||||||
return i18n.translate('xpack.actions.builtin.teams.teamsConfigurationError', {
|
return i18n.translate('xpack.actions.builtin.teams.teamsConfigurationError', {
|
||||||
defaultMessage: 'error configuring teams action: {message}',
|
defaultMessage: 'error configuring teams action: {message}',
|
||||||
|
|
|
@ -112,9 +112,9 @@ function validateActionTypeConfig(
|
||||||
configurationUtilities: ActionsConfigurationUtilities,
|
configurationUtilities: ActionsConfigurationUtilities,
|
||||||
configObject: ActionTypeConfigType
|
configObject: ActionTypeConfigType
|
||||||
) {
|
) {
|
||||||
let url: URL;
|
const configuredUrl = configObject.url;
|
||||||
try {
|
try {
|
||||||
url = new URL(configObject.url);
|
new URL(configuredUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return i18n.translate('xpack.actions.builtin.webhook.webhookConfigurationErrorNoHostname', {
|
return i18n.translate('xpack.actions.builtin.webhook.webhookConfigurationErrorNoHostname', {
|
||||||
defaultMessage: 'error configuring webhook action: unable to parse url: {err}',
|
defaultMessage: 'error configuring webhook action: unable to parse url: {err}',
|
||||||
|
@ -125,7 +125,7 @@ function validateActionTypeConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configurationUtilities.ensureUriAllowed(url.toString());
|
configurationUtilities.ensureUriAllowed(configuredUrl);
|
||||||
} catch (allowListError) {
|
} catch (allowListError) {
|
||||||
return i18n.translate('xpack.actions.builtin.webhook.webhookConfigurationError', {
|
return i18n.translate('xpack.actions.builtin.webhook.webhookConfigurationError', {
|
||||||
defaultMessage: 'error configuring webhook action: {message}',
|
defaultMessage: 'error configuring webhook action: {message}',
|
||||||
|
|
|
@ -114,8 +114,7 @@ export default function slackTest({ getService }: FtrProviderContext) {
|
||||||
expect(resp.body).to.eql({
|
expect(resp.body).to.eql({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
message:
|
message: `error validating action type secrets: error configuring slack action: target url \"http://slack.mynonexistent.com/other/stuff/in/the/path\" is not added to the Kibana config xpack.actions.allowedHosts`,
|
||||||
'error validating action type secrets: error configuring slack action: target hostname "slack.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue