From d521eb9dd9026a2f6585fb647890ed214692ba38 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Fri, 23 Apr 2021 15:50:24 -0400 Subject: [PATCH] [alerting] encode rule/connector ids in http requests made from alerting UI (#97854) resolves: https://github.com/elastic/kibana/issues/97852 Adds `encodeURIComponent()` wrappers around references to rule, alert, and connector ids. Without this fix, if an alert id (which can contain customer-generated data) contains a character that needs to be URL encoded, the resulting API call from the web UI will fail. --- .../builtin_action_types/jira/api.test.ts | 16 ++--- .../builtin_action_types/jira/api.ts | 60 +++++++++++-------- .../resilient/api.test.ts | 12 ++-- .../builtin_action_types/resilient/api.ts | 30 ++++++---- .../servicenow/api.test.ts | 4 +- .../builtin_action_types/servicenow/api.ts | 15 +++-- .../lib/action_connector_api/delete.test.ts | 4 +- .../lib/action_connector_api/delete.ts | 4 +- .../lib/action_connector_api/execute.test.ts | 4 +- .../lib/action_connector_api/execute.ts | 9 ++- .../lib/action_connector_api/update.test.ts | 8 +-- .../lib/action_connector_api/update.ts | 2 +- .../lib/alert_api/alert_summary.test.ts | 8 +-- .../lib/alert_api/alert_summary.ts | 4 +- .../application/lib/alert_api/delete.test.ts | 4 +- .../application/lib/alert_api/delete.ts | 4 +- .../application/lib/alert_api/disable.test.ts | 8 +-- .../application/lib/alert_api/disable.ts | 2 +- .../application/lib/alert_api/enable.test.ts | 8 +-- .../application/lib/alert_api/enable.ts | 2 +- .../lib/alert_api/get_rule.test.ts | 9 +-- .../application/lib/alert_api/get_rule.ts | 2 +- .../application/lib/alert_api/mute.test.ts | 8 +-- .../public/application/lib/alert_api/mute.ts | 2 +- .../lib/alert_api/mute_alert.test.ts | 4 +- .../application/lib/alert_api/mute_alert.ts | 6 +- .../application/lib/alert_api/unmute.test.ts | 8 +-- .../application/lib/alert_api/unmute.ts | 2 +- .../lib/alert_api/unmute_alert.test.ts | 4 +- .../application/lib/alert_api/unmute_alert.ts | 6 +- .../application/lib/alert_api/update.test.ts | 6 +- .../application/lib/alert_api/update.ts | 2 +- .../apps/triggers_actions_ui/details.ts | 26 ++++---- 33 files changed, 168 insertions(+), 125 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts index 679bc3d53c40da..38d65b923b3749 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts @@ -99,10 +99,10 @@ describe('Jira API', () => { test('should call get issue types API', async () => { const abortCtrl = new AbortController(); http.post.mockResolvedValueOnce(issueTypesResponse); - const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'test' }); + const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'te/st' }); expect(res).toEqual(issueTypesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"issueTypes","subActionParams":{}}}', signal: abortCtrl.signal, }); @@ -116,12 +116,12 @@ describe('Jira API', () => { const res = await getFieldsByIssueType({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', id: '10006', }); expect(res).toEqual(fieldsResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"fieldsByIssueType","subActionParams":{"id":"10006"}}}', signal: abortCtrl.signal, }); @@ -135,12 +135,12 @@ describe('Jira API', () => { const res = await getIssues({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', title: 'test issue', }); expect(res).toEqual(issuesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"issues","subActionParams":{"title":"test issue"}}}', signal: abortCtrl.signal, }); @@ -154,12 +154,12 @@ describe('Jira API', () => { const res = await getIssue({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', id: 'RJ-107', }); expect(res).toEqual(issuesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"issue","subActionParams":{"id":"RJ-107"}}}', signal: abortCtrl.signal, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts index 46ea9dea3aa56c..83e126ea9d2f6d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts @@ -17,12 +17,15 @@ export async function getIssueTypes({ signal: AbortSignal; connectorId: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'issueTypes', subActionParams: {} }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'issueTypes', subActionParams: {} }, + }), + signal, + } + ); } export async function getFieldsByIssueType({ @@ -36,12 +39,15 @@ export async function getFieldsByIssueType({ connectorId: string; id: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'fieldsByIssueType', subActionParams: { id } }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'fieldsByIssueType', subActionParams: { id } }, + }), + signal, + } + ); } export async function getIssues({ @@ -55,12 +61,15 @@ export async function getIssues({ connectorId: string; title: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'issues', subActionParams: { title } }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'issues', subActionParams: { title } }, + }), + signal, + } + ); } export async function getIssue({ @@ -74,10 +83,13 @@ export async function getIssue({ connectorId: string; id: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'issue', subActionParams: { id } }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'issue', subActionParams: { id } }, + }), + signal, + } + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts index 01208f93405d25..0d4bf9148a92ff 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts @@ -32,7 +32,7 @@ const incidentTypesResponse = { { id: 16, name: 'TBD / Unknown' }, { id: 15, name: 'Vendor / 3rd party error' }, ], - actionId: 'test', + actionId: 'te/st', }; const severityResponse = { @@ -42,7 +42,7 @@ const severityResponse = { { id: 5, name: 'Medium' }, { id: 6, name: 'High' }, ], - actionId: 'test', + actionId: 'te/st', }; describe('Resilient API', () => { @@ -57,11 +57,11 @@ describe('Resilient API', () => { const res = await getIncidentTypes({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', }); expect(res).toEqual(incidentTypesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"incidentTypes","subActionParams":{}}}', signal: abortCtrl.signal, }); @@ -75,11 +75,11 @@ describe('Resilient API', () => { const res = await getSeverity({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', }); expect(res).toEqual(severityResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"severity","subActionParams":{}}}', signal: abortCtrl.signal, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts index 8ea3c3c63e50f0..6bd9c43105cf0e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts @@ -17,12 +17,15 @@ export async function getIncidentTypes({ signal: AbortSignal; connectorId: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'incidentTypes', subActionParams: {} }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'incidentTypes', subActionParams: {} }, + }), + signal, + } + ); } export async function getSeverity({ @@ -34,10 +37,13 @@ export async function getSeverity({ signal: AbortSignal; connectorId: string; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'severity', subActionParams: {} }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'severity', subActionParams: {} }, + }), + signal, + } + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts index 5c814bbfd64505..ba820efc8111fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts @@ -56,12 +56,12 @@ describe('ServiceNow API', () => { const res = await getChoices({ http, signal: abortCtrl.signal, - connectorId: 'test', + connectorId: 'te/st', fields: ['priority'], }); expect(res).toEqual(choicesResponse); - expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', { + expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', { body: '{"params":{"subAction":"getChoices","subActionParams":{"fields":["priority"]}}}', signal: abortCtrl.signal, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts index bb909155912854..62347580e75ca1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts @@ -19,10 +19,13 @@ export async function getChoices({ connectorId: string; fields: string[]; }): Promise> { - return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, { - body: JSON.stringify({ - params: { subAction: 'getChoices', subActionParams: { fields } }, - }), - signal, - }); + return await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`, + { + body: JSON.stringify({ + params: { subAction: 'getChoices', subActionParams: { fields } }, + }), + signal, + } + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts index bb00c8c30e4ede..ba4c62471555b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts @@ -14,7 +14,7 @@ beforeEach(() => jest.resetAllMocks()); describe('deleteActions', () => { test('should call delete API per action', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await deleteActions({ ids, http }); expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] }); @@ -27,7 +27,7 @@ describe('deleteActions', () => { "/api/actions/connector/2", ], Array [ - "/api/actions/connector/3", + "/api/actions/connector/%2F", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts index c9c25db676a06d..868e5390045ccd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts @@ -16,7 +16,9 @@ export async function deleteActions({ }): Promise<{ successes: string[]; errors: string[] }> { const successes: string[] = []; const errors: string[] = []; - await Promise.all(ids.map((id) => http.delete(`${BASE_ACTION_API_PATH}/connector/${id}`))).then( + await Promise.all( + ids.map((id) => http.delete(`${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}`)) + ).then( function (fulfilled) { successes.push(...fulfilled); }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts index 60cd3132aa756b..2b0cdcb2ca69b4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts @@ -14,7 +14,7 @@ beforeEach(() => jest.resetAllMocks()); describe('executeAction', () => { test('should call execute API', async () => { - const id = '123'; + const id = '12/3'; const params = { stringParams: 'someString', numericParams: 123, @@ -32,7 +32,7 @@ describe('executeAction', () => { }); expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/actions/connector/123/_execute", + "/api/actions/connector/12%2F3/_execute", Object { "body": "{\\"params\\":{\\"stringParams\\":\\"someString\\",\\"numericParams\\":123}}", }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts index 638ceddb5652fb..d97ad7d5962b74 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts @@ -31,8 +31,11 @@ export async function executeAction({ http: HttpSetup; params: Record; }): Promise> { - const res = await http.post(`${BASE_ACTION_API_PATH}/connector/${id}/_execute`, { - body: JSON.stringify({ params }), - }); + const res = await http.post( + `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}/_execute`, + { + body: JSON.stringify({ params }), + } + ); return rewriteBodyRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts index 29e7a1e4bed3d0..3cee8d225b001e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts @@ -15,9 +15,9 @@ beforeEach(() => jest.resetAllMocks()); describe('updateActionConnector', () => { test('should call the update API', async () => { - const id = '123'; + const id = '12/3'; const apiResponse = { - connector_type_id: 'test', + connector_type_id: 'te/st', is_preconfigured: false, name: 'My test', config: {}, @@ -27,7 +27,7 @@ describe('updateActionConnector', () => { http.put.mockResolvedValueOnce(apiResponse); const connector: ActionConnectorWithoutId<{}, {}> = { - actionTypeId: 'test', + actionTypeId: 'te/st', isPreconfigured: false, name: 'My test', config: {}, @@ -39,7 +39,7 @@ describe('updateActionConnector', () => { expect(result).toEqual(resolvedValue); expect(http.put.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/actions/connector/123", + "/api/actions/connector/12%2F3", Object { "body": "{\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{}}", }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts index 18b8871ce25d1c..1bc0cefc2723b2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts @@ -30,7 +30,7 @@ export async function updateActionConnector({ connector: Pick; id: string; }): Promise { - const res = await http.put(`${BASE_ACTION_API_PATH}/connector/${id}`, { + const res = await http.put(`${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}`, { body: JSON.stringify({ name: connector.name, config: connector.config, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts index e94da81d0f5d51..c7b987f2b04bdf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts @@ -18,7 +18,7 @@ describe('loadAlertInstanceSummary', () => { consumer: 'alerts', enabled: true, errorMessages: [], - id: 'test', + id: 'te/st', lastRun: '2021-04-01T22:18:27.609Z', muteAll: false, name: 'test', @@ -35,7 +35,7 @@ describe('loadAlertInstanceSummary', () => { consumer: 'alerts', enabled: true, error_messages: [], - id: 'test', + id: 'te/st', last_run: '2021-04-01T22:18:27.609Z', mute_all: false, name: 'test', @@ -47,11 +47,11 @@ describe('loadAlertInstanceSummary', () => { throttle: null, }); - const result = await loadAlertInstanceSummary({ http, alertId: 'test' }); + const result = await loadAlertInstanceSummary({ http, alertId: 'te/st' }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/internal/alerting/rule/test/_alert_summary", + "/internal/alerting/rule/te%2Fst/_alert_summary", ] `); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts index e37c0640ec1c8f..cb924db74cea55 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts @@ -36,6 +36,8 @@ export async function loadAlertInstanceSummary({ http: HttpSetup; alertId: string; }): Promise { - const res = await http.get(`${INTERNAL_BASE_ALERTING_API_PATH}/rule/${alertId}/_alert_summary`); + const res = await http.get( + `${INTERNAL_BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(alertId)}/_alert_summary` + ); return rewriteBodyRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts index b279e4c0237d96..11e5f4763e775e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts @@ -12,7 +12,7 @@ const http = httpServiceMock.createStartContract(); describe('deleteAlerts', () => { test('should call delete API for each alert', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await deleteAlerts({ http, ids }); expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] }); expect(http.delete.mock.calls).toMatchInlineSnapshot(` @@ -24,7 +24,7 @@ describe('deleteAlerts', () => { "/api/alerting/rule/2", ], Array [ - "/api/alerting/rule/3", + "/api/alerting/rule/%2F", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts index 870d5a409c3dda..b853e722e6fc36 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts @@ -16,7 +16,9 @@ export async function deleteAlerts({ }): Promise<{ successes: string[]; errors: string[] }> { const successes: string[] = []; const errors: string[] = []; - await Promise.all(ids.map((id) => http.delete(`${BASE_ALERTING_API_PATH}/rule/${id}`))).then( + await Promise.all( + ids.map((id) => http.delete(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`)) + ).then( function (fulfilled) { successes.push(...fulfilled); }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts index 90d1cd13096e84..4323816221c6ed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts @@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks()); describe('disableAlert', () => { test('should call disable alert API', async () => { - const result = await disableAlert({ http, id: '1' }); + const result = await disableAlert({ http, id: '1/' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/_disable", + "/api/alerting/rule/1%2F/_disable", ], ] `); @@ -27,7 +27,7 @@ describe('disableAlert', () => { describe('disableAlerts', () => { test('should call disable alert API per alert', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await disableAlerts({ http, ids }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` @@ -39,7 +39,7 @@ describe('disableAlerts', () => { "/api/alerting/rule/2/_disable", ], Array [ - "/api/alerting/rule/3/_disable", + "/api/alerting/rule/%2F/_disable", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts index cc0939fbebfbde..758e66644b34e7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts @@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ALERTING_API_PATH } from '../../constants'; export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_disable`); + await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_disable`); } export async function disableAlerts({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts index ef65e8b605cba4..3a54a0772664b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts @@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks()); describe('enableAlert', () => { test('should call enable alert API', async () => { - const result = await enableAlert({ http, id: '1' }); + const result = await enableAlert({ http, id: '1/' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/_enable", + "/api/alerting/rule/1%2F/_enable", ], ] `); @@ -27,7 +27,7 @@ describe('enableAlert', () => { describe('enableAlerts', () => { test('should call enable alert API per alert', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await enableAlerts({ http, ids }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` @@ -39,7 +39,7 @@ describe('enableAlerts', () => { "/api/alerting/rule/2/_enable", ], Array [ - "/api/alerting/rule/3/_enable", + "/api/alerting/rule/%2F/_enable", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts index 3c16ffaec6223f..4bb3e3d45fcaea 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts @@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ALERTING_API_PATH } from '../../constants'; export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_enable`); + await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_enable`); } export async function enableAlerts({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts index f2d8337eb4091c..5c71f6433f2b9a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts @@ -13,9 +13,10 @@ const http = httpServiceMock.createStartContract(); describe('loadAlert', () => { test('should call get API with base parameters', async () => { - const alertId = uuid.v4(); + const alertId = `${uuid.v4()}/`; + const alertIdEncoded = encodeURIComponent(alertId); const resolvedValue = { - id: '1', + id: '1/', params: { aggType: 'count', termSize: 5, @@ -56,7 +57,7 @@ describe('loadAlert', () => { http.get.mockResolvedValueOnce(resolvedValue); expect(await loadAlert({ http, alertId })).toEqual({ - id: '1', + id: '1/', params: { aggType: 'count', termSize: 5, @@ -94,6 +95,6 @@ describe('loadAlert', () => { }, ], }); - expect(http.get).toHaveBeenCalledWith(`/api/alerting/rule/${alertId}`); + expect(http.get).toHaveBeenCalledWith(`/api/alerting/rule/${alertIdEncoded}`); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts index 2e4cbc9b50c51b..9fa882c02fa228 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts @@ -16,6 +16,6 @@ export async function loadAlert({ http: HttpSetup; alertId: string; }): Promise { - const res = await http.get(`${BASE_ALERTING_API_PATH}/rule/${alertId}`); + const res = await http.get(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(alertId)}`); return transformAlert(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts index 75143dd6b7f857..804096dbafac89 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts @@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks()); describe('muteAlert', () => { test('should call mute alert API', async () => { - const result = await muteAlert({ http, id: '1' }); + const result = await muteAlert({ http, id: '1/' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/_mute_all", + "/api/alerting/rule/1%2F/_mute_all", ], ] `); @@ -27,7 +27,7 @@ describe('muteAlert', () => { describe('muteAlerts', () => { test('should call mute alert API per alert', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await muteAlerts({ http, ids }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` @@ -39,7 +39,7 @@ describe('muteAlerts', () => { "/api/alerting/rule/2/_mute_all", ], Array [ - "/api/alerting/rule/3/_mute_all", + "/api/alerting/rule/%2F/_mute_all", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts index 22a96d7a11ff38..888cdfa92c8f5e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts @@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ALERTING_API_PATH } from '../../constants'; export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_mute_all`); + await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_mute_all`); } export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts index 4365cce42c8c3e..384bc65754b033 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts @@ -12,12 +12,12 @@ const http = httpServiceMock.createStartContract(); describe('muteAlertInstance', () => { test('should call mute instance alert API', async () => { - const result = await muteAlertInstance({ http, id: '1', instanceId: '123' }); + const result = await muteAlertInstance({ http, id: '1/', instanceId: '12/3' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/alert/123/_mute", + "/api/alerting/rule/1%2F/alert/12%2F3/_mute", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts index 0bb05010cfa3c5..05f2417db94722 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts @@ -16,5 +16,9 @@ export async function muteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_mute`); + await http.post( + `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( + instanceId + )}/_mute` + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts index 68a6feeb65e1e7..dfaceffcf8f00a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts @@ -13,7 +13,7 @@ beforeEach(() => jest.resetAllMocks()); describe('unmuteAlerts', () => { test('should call unmute alert API per alert', async () => { - const ids = ['1', '2', '3']; + const ids = ['1', '2', '/']; const result = await unmuteAlerts({ http, ids }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` @@ -25,7 +25,7 @@ describe('unmuteAlerts', () => { "/api/alerting/rule/2/_unmute_all", ], Array [ - "/api/alerting/rule/3/_unmute_all", + "/api/alerting/rule/%2F/_unmute_all", ], ] `); @@ -34,12 +34,12 @@ describe('unmuteAlerts', () => { describe('unmuteAlert', () => { test('should call unmute alert API', async () => { - const result = await unmuteAlert({ http, id: '1' }); + const result = await unmuteAlert({ http, id: '1/' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/_unmute_all", + "/api/alerting/rule/1%2F/_unmute_all", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts index c65be6a670a897..bd2139f0526451 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts @@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ALERTING_API_PATH } from '../../constants'; export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_unmute_all`); + await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_unmute_all`); } export async function unmuteAlerts({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts index c0131cbab0ebf1..d95c95158b0b7e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts @@ -12,12 +12,12 @@ const http = httpServiceMock.createStartContract(); describe('unmuteAlertInstance', () => { test('should call mute instance alert API', async () => { - const result = await unmuteAlertInstance({ http, id: '1', instanceId: '123' }); + const result = await unmuteAlertInstance({ http, id: '1/', instanceId: '12/3' }); expect(result).toEqual(undefined); expect(http.post.mock.calls).toMatchInlineSnapshot(` Array [ Array [ - "/api/alerting/rule/1/alert/123/_unmute", + "/api/alerting/rule/1%2F/alert/12%2F3/_unmute", ], ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts index 60d2cca72b85e6..2e37aa2c0ee295 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts @@ -16,5 +16,9 @@ export async function unmuteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_unmute`); + await http.post( + `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent( + instanceId + )}/_unmute` + ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts index 745a94b8d1134b..3a6059248a3b0b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts @@ -32,7 +32,7 @@ describe('updateAlert', () => { }; const resolvedValue: Alert = { ...alertToUpdate, - id: '123', + id: '12/3', enabled: true, alertTypeId: 'test', createdBy: null, @@ -46,11 +46,11 @@ describe('updateAlert', () => { }; http.put.mockResolvedValueOnce(resolvedValue); - const result = await updateAlert({ http, id: '123', alert: alertToUpdate }); + const result = await updateAlert({ http, id: '12/3', alert: alertToUpdate }); expect(result).toEqual(resolvedValue); expect(http.put.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/alerting/rule/123", + "/api/alerting/rule/12%2F3", Object { "body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"notify_when\\":\\"onThrottleInterval\\",\\"actions\\":[]}", }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts index 44b9306949f810..930c0c2fb21a08 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts @@ -41,7 +41,7 @@ export async function updateAlert({ >; id: string; }): Promise { - const res = await http.put(`${BASE_ALERTING_API_PATH}/rule/${id}`, { + const res = await http.put(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`, { body: JSON.stringify( rewriteBodyRequest( pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions', 'notifyWhen']) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 97f8b3f61dc892..b38b605bc1b678 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -93,14 +93,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function getAlertInstanceSummary(alertId: string) { const { body: summary } = await supertest - .get(`/internal/alerting/rule/${alertId}/_alert_summary`) + .get(`/internal/alerting/rule/${encodeURIComponent(alertId)}/_alert_summary`) .expect(200); return summary; } async function muteAlertInstance(alertId: string, alertInstanceId: string) { const { body: response } = await supertest - .post(`/api/alerting/rule/${alertId}/alert/${alertInstanceId}/_mute`) + .post( + `/api/alerting/rule/${encodeURIComponent(alertId)}/alert/${encodeURIComponent( + alertInstanceId + )}/_mute` + ) .set('kbn-xsrf', 'foo') .expect(204); @@ -640,17 +644,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the muted inactive alert instances', async () => { // mute an alert instance that doesn't exist - await muteAlertInstance(alert.id, 'eu-east'); + await muteAlertInstance(alert.id, 'eu/east'); // refresh to see alert await browser.refresh(); const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect( - instancesList.filter((alertInstance) => alertInstance.instance === 'eu-east') + instancesList.filter((alertInstance) => alertInstance.instance === 'eu/east') ).to.eql([ { - instance: 'eu-east', + instance: 'eu/east', status: 'OK', start: '', duration: '', @@ -693,14 +697,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('allows the user unmute an inactive instance', async () => { - log.debug(`Ensuring eu-east is muted`); - await pageObjects.alertDetailsUI.ensureAlertInstanceMute('eu-east', true); + log.debug(`Ensuring eu/east is muted`); + await pageObjects.alertDetailsUI.ensureAlertInstanceMute('eu/east', true); - log.debug(`Unmuting eu-east`); - await pageObjects.alertDetailsUI.clickAlertInstanceMuteButton('eu-east'); + log.debug(`Unmuting eu/east`); + await pageObjects.alertDetailsUI.clickAlertInstanceMuteButton('eu/east'); - log.debug(`Ensuring eu-east is removed from list`); - await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu-east', false); + log.debug(`Ensuring eu/east is removed from list`); + await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu/east', false); }); });