Skip to content

Commit b8f7d62

Browse files
authored
[Security Solution][Cases] Fix bug with case connectors (#80642) (#80839)
* Fix bug with case connectors * Improve isCaseOwned function
1 parent edd8531 commit b8f7d62

File tree

5 files changed

+327
-37
lines changed

5 files changed

+327
-37
lines changed

x-pack/plugins/case/server/routes/api/__mocks__/request_responses.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,45 @@ export const getActions = (): FindActionResult[] => [
6262
isPreconfigured: false,
6363
referencedByCount: 0,
6464
},
65+
{
66+
id: '456',
67+
actionTypeId: '.jira',
68+
name: 'Connector without isCaseOwned',
69+
config: {
70+
incidentConfiguration: {
71+
mapping: [
72+
{
73+
source: 'title',
74+
target: 'short_description',
75+
actionType: 'overwrite',
76+
},
77+
{
78+
source: 'description',
79+
target: 'description',
80+
actionType: 'overwrite',
81+
},
82+
{
83+
source: 'comments',
84+
target: 'comments',
85+
actionType: 'append',
86+
},
87+
],
88+
},
89+
apiUrl: 'https://elastic.jira.com',
90+
},
91+
isPreconfigured: false,
92+
referencedByCount: 0,
93+
},
94+
{
95+
id: '789',
96+
actionTypeId: '.resilient',
97+
name: 'Connector without mapping',
98+
config: {
99+
apiUrl: 'https://elastic.resilient.com',
100+
},
101+
isPreconfigured: false,
102+
referencedByCount: 0,
103+
},
65104
];
66105

67106
export const newConfiguration: CasesConfigureRequest = {

x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515

1616
import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects';
1717
import { initCaseConfigureGetActionConnector } from './get_connectors';
18-
import { getActions } from '../../__mocks__/request_responses';
1918
import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants';
2019

2120
describe('GET connectors', () => {
@@ -24,7 +23,7 @@ describe('GET connectors', () => {
2423
routeHandler = await createRoute(initCaseConfigureGetActionConnector, 'get');
2524
});
2625

27-
it('returns the connectors', async () => {
26+
it('returns case owned connectors', async () => {
2827
const req = httpServerMock.createKibanaRequest({
2928
path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`,
3029
method: 'get',
@@ -38,9 +37,67 @@ describe('GET connectors', () => {
3837

3938
const res = await routeHandler(context, req, kibanaResponseFactory);
4039
expect(res.status).toEqual(200);
41-
expect(res.payload).toEqual(
42-
getActions().filter((action) => action.actionTypeId === '.servicenow')
43-
);
40+
expect(res.payload).toEqual([
41+
{
42+
id: '123',
43+
actionTypeId: '.servicenow',
44+
name: 'ServiceNow',
45+
config: {
46+
incidentConfiguration: {
47+
mapping: [
48+
{
49+
source: 'title',
50+
target: 'short_description',
51+
actionType: 'overwrite',
52+
},
53+
{
54+
source: 'description',
55+
target: 'description',
56+
actionType: 'overwrite',
57+
},
58+
{
59+
source: 'comments',
60+
target: 'comments',
61+
actionType: 'append',
62+
},
63+
],
64+
},
65+
apiUrl: 'https://dev102283.service-now.com',
66+
isCaseOwned: true,
67+
},
68+
isPreconfigured: false,
69+
referencedByCount: 0,
70+
},
71+
{
72+
id: '456',
73+
actionTypeId: '.jira',
74+
name: 'Connector without isCaseOwned',
75+
config: {
76+
incidentConfiguration: {
77+
mapping: [
78+
{
79+
source: 'title',
80+
target: 'short_description',
81+
actionType: 'overwrite',
82+
},
83+
{
84+
source: 'description',
85+
target: 'description',
86+
actionType: 'overwrite',
87+
},
88+
{
89+
source: 'comments',
90+
target: 'comments',
91+
actionType: 'append',
92+
},
93+
],
94+
},
95+
apiUrl: 'https://elastic.jira.com',
96+
},
97+
isPreconfigured: false,
98+
referencedByCount: 0,
99+
},
100+
]);
44101
});
45102

46103
it('it throws an error when actions client is null', async () => {

x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,44 @@
77
import Boom from 'boom';
88
import { RouteDeps } from '../../types';
99
import { wrapError } from '../../utils';
10+
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
11+
import { FindActionResult } from '../../../../../../actions/server/types';
1012

1113
import {
1214
CASE_CONFIGURE_CONNECTORS_URL,
13-
SUPPORTED_CONNECTORS,
1415
SERVICENOW_ACTION_TYPE_ID,
1516
JIRA_ACTION_TYPE_ID,
1617
RESILIENT_ACTION_TYPE_ID,
1718
} from '../../../../../common/constants';
1819

20+
/**
21+
* We need to take into account connectors that have been created within cases and
22+
* they do not have the isCaseOwned field. Checking for the existence of
23+
* the mapping attribute ensures that the connector is indeed a case connector.
24+
* Cases connector should always have a mapping.
25+
*/
26+
27+
interface CaseAction extends FindActionResult {
28+
config?: {
29+
isCaseOwned?: boolean;
30+
incidentConfiguration?: Record<string, unknown>;
31+
};
32+
}
33+
34+
const isCaseOwned = (action: CaseAction): boolean => {
35+
if (
36+
[SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, RESILIENT_ACTION_TYPE_ID].includes(
37+
action.actionTypeId
38+
)
39+
) {
40+
if (action.config?.isCaseOwned === true || action.config?.incidentConfiguration?.mapping) {
41+
return true;
42+
}
43+
}
44+
45+
return false;
46+
};
47+
1948
/*
2049
* Be aware that this api will only return 20 connectors
2150
*/
@@ -34,18 +63,7 @@ export function initCaseConfigureGetActionConnector({ caseService, router }: Rou
3463
throw Boom.notFound('Action client have not been found');
3564
}
3665

37-
const results = (await actionsClient.getAll()).filter(
38-
(action) =>
39-
SUPPORTED_CONNECTORS.includes(action.actionTypeId) &&
40-
// Need this filtering temporary to display only Case owned ServiceNow connectors
41-
(![SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, RESILIENT_ACTION_TYPE_ID].includes(
42-
action.actionTypeId
43-
) ||
44-
([SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, RESILIENT_ACTION_TYPE_ID].includes(
45-
action.actionTypeId
46-
) &&
47-
action.config?.isCaseOwned === true))
48-
);
66+
const results = (await actionsClient.getAll()).filter(isCaseOwned);
4967
return response.ok({ body: results });
5068
} catch (error) {
5169
return response.customError(wrapError(error));

0 commit comments

Comments
 (0)