From 96885f44d9bb768a2f9777948d96060f2ac02b28 Mon Sep 17 00:00:00 2001
From: Yara Tercero <yctercero@users.noreply.github.com>
Date: Wed, 29 Jul 2020 01:15:15 -0400
Subject: [PATCH] [Security Solution][Exceptions] - Update rule.exceptions_list
 to include exception list list_id (#73349) (#73617)

## Summary

This PR addresses the following:
- Adds `list_id` to `rule.exceptions_list` - this is needed in a number of features
- Updated `getExceptions` in `x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts` to use the latest exception item find endpoint that accepts an array of lists (previously was looping through lists and conducting a `find` for each)
- Updated prepackaged rule that makes reference to global endpoint list to include `list_id`
- Updates `formatAboutStepData` in `x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts` to include exception list `list_id`
---
 .../hooks/use_exception_list.test.ts          | 28 +++++++---
 .../plugins/lists/public/exceptions/types.ts  |  1 +
 .../add_prepackged_rules_schema.test.ts       |  5 +-
 .../request/create_rules_schema.test.ts       |  5 +-
 .../request/import_rules_schema.test.ts       |  5 +-
 .../request/patch_rules_schema.test.ts        |  5 +-
 .../request/update_rules_schema.test.ts       |  5 +-
 .../schemas/types/lists.mock.ts               |  9 ++-
 .../schemas/types/lists.test.ts               | 10 ++--
 .../detection_engine/schemas/types/lists.ts   |  5 +-
 ...se_fetch_or_create_rule_exception_list.tsx |  1 +
 .../exceptions/viewer/index.test.tsx          |  2 +
 .../components/exceptions/viewer/index.tsx    |  2 -
 .../rules/all/__mocks__/mock.ts               |  7 ---
 .../rules/create/helpers.test.ts              | 31 ++++-------
 .../detection_engine/rules/create/helpers.ts  |  7 ++-
 .../detection_engine/rules/details/index.tsx  |  4 +-
 .../prepackaged_rules/elastic_endpoint.json   | 14 ++---
 .../prepackaged_rules/external_alerts.json    |  9 +--
 .../detection_engine/signals/utils.test.ts    | 55 ++++++++-----------
 .../lib/detection_engine/signals/utils.ts     | 49 +++++------------
 21 files changed, 122 insertions(+), 137 deletions(-)

diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.ts
index 918397d01ce2c..f678ed4faeeda 100644
--- a/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.ts
+++ b/x-pack/plugins/lists/public/exceptions/hooks/use_exception_list.test.ts
@@ -41,7 +41,9 @@ describe('useExceptionList', () => {
         useExceptionList({
           filterOptions: { filter: '', tags: [] },
           http: mockKibanaHttpService,
-          lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+          lists: [
+            { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+          ],
           onError: onErrorMock,
           pagination: {
             page: 1,
@@ -76,7 +78,9 @@ describe('useExceptionList', () => {
         useExceptionList({
           filterOptions: { filter: '', tags: [] },
           http: mockKibanaHttpService,
-          lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+          lists: [
+            { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+          ],
           onError: onErrorMock,
           onSuccess: onSuccessMock,
           pagination: {
@@ -131,7 +135,9 @@ describe('useExceptionList', () => {
           initialProps: {
             filterOptions: { filter: '', tags: [] },
             http: mockKibanaHttpService,
-            lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+            lists: [
+              { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+            ],
             onError: onErrorMock,
             onSuccess: onSuccessMock,
             pagination: {
@@ -146,7 +152,9 @@ describe('useExceptionList', () => {
       rerender({
         filterOptions: { filter: '', tags: [] },
         http: mockKibanaHttpService,
-        lists: [{ id: 'newListId', namespaceType: 'single', type: 'detection' }],
+        lists: [
+          { id: 'newListId', listId: 'new_list_id', namespaceType: 'single', type: 'detection' },
+        ],
         onError: onErrorMock,
         onSuccess: onSuccessMock,
         pagination: {
@@ -173,7 +181,9 @@ describe('useExceptionList', () => {
         useExceptionList({
           filterOptions: { filter: '', tags: [] },
           http: mockKibanaHttpService,
-          lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+          lists: [
+            { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+          ],
           onError: onErrorMock,
           pagination: {
             page: 1,
@@ -210,7 +220,9 @@ describe('useExceptionList', () => {
           useExceptionList({
             filterOptions: { filter: '', tags: [] },
             http: mockKibanaHttpService,
-            lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+            lists: [
+              { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+            ],
             onError: onErrorMock,
             pagination: {
               page: 1,
@@ -238,7 +250,9 @@ describe('useExceptionList', () => {
           useExceptionList({
             filterOptions: { filter: '', tags: [] },
             http: mockKibanaHttpService,
-            lists: [{ id: 'myListId', namespaceType: 'single', type: 'detection' }],
+            lists: [
+              { id: 'myListId', listId: 'list_id', namespaceType: 'single', type: 'detection' },
+            ],
             onError: onErrorMock,
             pagination: {
               page: 1,
diff --git a/x-pack/plugins/lists/public/exceptions/types.ts b/x-pack/plugins/lists/public/exceptions/types.ts
index f99323b384781..c0ec72e1c19eb 100644
--- a/x-pack/plugins/lists/public/exceptions/types.ts
+++ b/x-pack/plugins/lists/public/exceptions/types.ts
@@ -60,6 +60,7 @@ export interface UseExceptionListProps {
 
 export interface ExceptionIdentifiers {
   id: string;
+  listId: string;
   namespaceType: NamespaceType;
   type: ExceptionListType;
 }
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackged_rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackged_rules_schema.test.ts
index 5fd2c3dbbf894..3cad48ec18fc1 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackged_rules_schema.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackged_rules_schema.test.ts
@@ -1446,11 +1446,13 @@ describe('add prepackaged rules schema', () => {
         exceptions_list: [
           {
             id: 'some_uuid',
+            list_id: 'list_id_single',
             namespace_type: 'single',
             type: 'detection',
           },
           {
-            id: 'some_uuid',
+            id: 'endpoint_list',
+            list_id: 'endpoint_list',
             namespace_type: 'agnostic',
             type: 'endpoint',
           },
@@ -1535,6 +1537,7 @@ describe('add prepackaged rules schema', () => {
       const checked = exactCheck(payload, decoded);
       const message = pipe(checked, foldLeftRight);
       expect(getPaths(left(message.errors))).toEqual([
+        'Invalid value "undefined" supplied to "exceptions_list,list_id"',
         'Invalid value "undefined" supplied to "exceptions_list,type"',
         'Invalid value "not a namespace type" supplied to "exceptions_list,namespace_type"',
       ]);
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.test.ts
index 71f3964956249..c2c2f4784f2b5 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.test.ts
@@ -1513,11 +1513,13 @@ describe('create rules schema', () => {
         exceptions_list: [
           {
             id: 'some_uuid',
+            list_id: 'list_id_single',
             namespace_type: 'single',
             type: 'detection',
           },
           {
-            id: 'some_uuid',
+            id: 'endpoint_list',
+            list_id: 'endpoint_list',
             namespace_type: 'agnostic',
             type: 'endpoint',
           },
@@ -1600,6 +1602,7 @@ describe('create rules schema', () => {
       const checked = exactCheck(payload, decoded);
       const message = pipe(checked, foldLeftRight);
       expect(getPaths(left(message.errors))).toEqual([
+        'Invalid value "undefined" supplied to "exceptions_list,list_id"',
         'Invalid value "undefined" supplied to "exceptions_list,type"',
         'Invalid value "not a namespace type" supplied to "exceptions_list,namespace_type"',
       ]);
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.test.ts
index 828626ef26d6f..00a3f2126f44a 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.test.ts
@@ -1642,11 +1642,13 @@ describe('import rules schema', () => {
         exceptions_list: [
           {
             id: 'some_uuid',
+            list_id: 'list_id_single',
             namespace_type: 'single',
             type: 'detection',
           },
           {
-            id: 'some_uuid',
+            id: 'endpoint_list',
+            list_id: 'endpoint_list',
             namespace_type: 'agnostic',
             type: 'endpoint',
           },
@@ -1730,6 +1732,7 @@ describe('import rules schema', () => {
       const checked = exactCheck(payload, decoded);
       const message = pipe(checked, foldLeftRight);
       expect(getPaths(left(message.errors))).toEqual([
+        'Invalid value "undefined" supplied to "exceptions_list,list_id"',
         'Invalid value "undefined" supplied to "exceptions_list,type"',
         'Invalid value "not a namespace type" supplied to "exceptions_list,namespace_type"',
       ]);
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.test.ts
index e75aff1abe3e9..e4fc53b934f58 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.test.ts
@@ -1176,11 +1176,13 @@ describe('patch_rules_schema', () => {
         exceptions_list: [
           {
             id: 'some_uuid',
+            list_id: 'list_id_single',
             namespace_type: 'single',
             type: 'detection',
           },
           {
-            id: 'some_uuid',
+            id: 'endpoint_list',
+            list_id: 'endpoint_list',
             namespace_type: 'agnostic',
             type: 'endpoint',
           },
@@ -1251,6 +1253,7 @@ describe('patch_rules_schema', () => {
       const checked = exactCheck(payload, decoded);
       const message = pipe(checked, foldLeftRight);
       expect(getPaths(left(message.errors))).toEqual([
+        'Invalid value "undefined" supplied to "exceptions_list,list_id"',
         'Invalid value "undefined" supplied to "exceptions_list,type"',
         'Invalid value "not a namespace type" supplied to "exceptions_list,namespace_type"',
         'Invalid value "[{"id":"uuid_here","namespace_type":"not a namespace type"}]" supplied to "exceptions_list"',
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.test.ts
index d18d2d91b963c..024198d783048 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.test.ts
@@ -1448,11 +1448,13 @@ describe('update rules schema', () => {
         exceptions_list: [
           {
             id: 'some_uuid',
+            list_id: 'list_id_single',
             namespace_type: 'single',
             type: 'detection',
           },
           {
-            id: 'some_uuid',
+            id: 'endpoint_list',
+            list_id: 'endpoint_list',
             namespace_type: 'agnostic',
             type: 'endpoint',
           },
@@ -1534,6 +1536,7 @@ describe('update rules schema', () => {
       const checked = exactCheck(payload, decoded);
       const message = pipe(checked, foldLeftRight);
       expect(getPaths(left(message.errors))).toEqual([
+        'Invalid value "undefined" supplied to "exceptions_list,list_id"',
         'Invalid value "undefined" supplied to "exceptions_list,type"',
         'Invalid value "not a namespace type" supplied to "exceptions_list,namespace_type"',
       ]);
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.mock.ts
index 0c7853bc3c08a..fec7548811820 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.mock.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.mock.ts
@@ -4,17 +4,20 @@
  * you may not use this file except in compliance with the Elastic License.
  */
 import { List, ListArray } from './lists';
+import { ENDPOINT_LIST_ID } from '../../../shared_imports';
 
 export const getListMock = (): List => ({
   id: 'some_uuid',
+  list_id: 'list_id_single',
   namespace_type: 'single',
   type: 'detection',
 });
 
-export const getListAgnosticMock = (): List => ({
-  id: 'some_uuid',
+export const getEndpointListMock = (): List => ({
+  id: ENDPOINT_LIST_ID,
+  list_id: ENDPOINT_LIST_ID,
   namespace_type: 'agnostic',
   type: 'endpoint',
 });
 
-export const getListArrayMock = (): ListArray => [getListMock(), getListAgnosticMock()];
+export const getListArrayMock = (): ListArray => [getListMock(), getEndpointListMock()];
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.test.ts
index 56ee4630996fd..7a2c167bfd855 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.test.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.test.ts
@@ -9,7 +9,7 @@ import { left } from 'fp-ts/lib/Either';
 
 import { foldLeftRight, getPaths } from '../../../test_utils';
 
-import { getListAgnosticMock, getListMock, getListArrayMock } from './lists.mock';
+import { getEndpointListMock, getListMock, getListArrayMock } from './lists.mock';
 import {
   List,
   ListArray,
@@ -31,7 +31,7 @@ describe('Lists', () => {
     });
 
     test('it should validate a list with "namespace_type" of "agnostic"', () => {
-      const payload = getListAgnosticMock();
+      const payload = getEndpointListMock();
       const decoded = list.decode(payload);
       const message = pipe(decoded, foldLeftRight);
 
@@ -91,7 +91,7 @@ describe('Lists', () => {
       const message = pipe(decoded, foldLeftRight);
 
       expect(getPaths(left(message.errors))).toEqual([
-        'Invalid value "1" supplied to "Array<{| id: string, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}>"',
+        'Invalid value "1" supplied to "Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}>"',
       ]);
       expect(message.schema).toEqual({});
     });
@@ -122,8 +122,8 @@ describe('Lists', () => {
       const message = pipe(decoded, foldLeftRight);
 
       expect(getPaths(left(message.errors))).toEqual([
-        'Invalid value "1" supplied to "(Array<{| id: string, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}> | undefined)"',
-        'Invalid value "[1]" supplied to "(Array<{| id: string, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}> | undefined)"',
+        'Invalid value "1" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}> | undefined)"',
+        'Invalid value "[1]" supplied to "(Array<{| id: NonEmptyString, list_id: NonEmptyString, type: "detection" | "endpoint", namespace_type: "agnostic" | "single" |}> | undefined)"',
       ]);
       expect(message.schema).toEqual({});
     });
diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts
index e5aaee6d3ec74..fecdd0761aade 100644
--- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts
+++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/lists.ts
@@ -8,9 +8,12 @@ import * as t from 'io-ts';
 
 import { exceptionListType, namespaceType } from '../../../shared_imports';
 
+import { NonEmptyString } from './non_empty_string';
+
 export const list = t.exact(
   t.type({
-    id: t.string,
+    id: NonEmptyString,
+    list_id: NonEmptyString,
     type: exceptionListType,
     namespace_type: namespaceType,
   })
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx
index 2a5ef7b21b519..0d367e03a799f 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx
@@ -102,6 +102,7 @@ export const useFetchOrCreateRuleExceptionList = ({
 
       const newExceptionListReference = {
         id: newExceptionList.id,
+        list_id: newExceptionList.list_id,
         type: newExceptionList.type,
         namespace_type: newExceptionList.namespace_type,
       };
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx
index 986f27f6495ec..84613d1c73536 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.test.tsx
@@ -72,6 +72,7 @@ describe('ExceptionsViewer', () => {
           exceptionListsMeta={[
             {
               id: '5b543420',
+              listId: 'list_id',
               type: 'endpoint',
               namespaceType: 'single',
             },
@@ -124,6 +125,7 @@ describe('ExceptionsViewer', () => {
           exceptionListsMeta={[
             {
               id: '5b543420',
+              listId: 'list_id',
               type: 'endpoint',
               namespaceType: 'single',
             },
diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
index 16eaef4136983..7f4d825276406 100644
--- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx
@@ -176,8 +176,6 @@ const ExceptionsViewerComponent = ({
 
   const handleEditException = useCallback(
     (exception: ExceptionListItemSchema): void => {
-      // TODO: Added this just for testing. Update
-      // modal state logic as needed once ready
       dispatch({
         type: 'updateExceptionToEdit',
         exception,
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
index 7a6b61f0dfd89..8c6e91254314e 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts
@@ -6,7 +6,6 @@
 
 import { esFilters } from '../../../../../../../../../../src/plugins/data/public';
 import { Rule, RuleError } from '../../../../../containers/detection_engine/rules';
-import { List } from '../../../../../../../common/detection_engine/schemas/types';
 import { AboutStepRule, ActionsStepRule, DefineStepRule, ScheduleStepRule } from '../../types';
 import { FieldValueQueryBar } from '../../../../../components/rules/query_bar';
 import { fillEmptySeverityMappings } from '../../helpers';
@@ -242,9 +241,3 @@ export const mockRules: Rule[] = [
   mockRule('abe6c564-050d-45a5-aaf0-386c37dd1f61'),
   mockRule('63f06f34-c181-4b2d-af35-f2ace572a1ee'),
 ];
-
-export const mockExceptionsList: List = {
-  namespace_type: 'single',
-  id: '75cd4380-cc5e-11ea-9101-5b34f44aeb44',
-  type: 'detection',
-};
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts
index 6458d2faa2468..4f2055424ca61 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts
@@ -5,9 +5,11 @@
  */
 
 import { List } from '../../../../../../common/detection_engine/schemas/types';
-import { ENDPOINT_LIST_ID } from '../../../../../shared_imports';
 import { NewRule } from '../../../../containers/detection_engine/rules';
-
+import {
+  getListMock,
+  getEndpointListMock,
+} from '../../../../../../common/detection_engine/schemas/types/lists.mock';
 import {
   DefineStepRuleJson,
   ScheduleStepRuleJson,
@@ -29,19 +31,12 @@ import {
 } from './helpers';
 import {
   mockDefineStepRule,
-  mockExceptionsList,
   mockQueryBar,
   mockScheduleStepRule,
   mockAboutStepRule,
   mockActionsStepRule,
 } from '../all/__mocks__/mock';
 
-const ENDPOINT_LIST = {
-  id: ENDPOINT_LIST_ID,
-  namespace_type: 'agnostic',
-  type: 'endpoint',
-} as List;
-
 describe('helpers', () => {
   describe('getTimeTypeValue', () => {
     test('returns timeObj with value 0 if no time value found', () => {
@@ -391,14 +386,12 @@ describe('helpers', () => {
         },
         []
       );
-      expect(result.exceptions_list).toEqual([
-        { id: ENDPOINT_LIST_ID, namespace_type: 'agnostic', type: 'endpoint' },
-      ]);
+      expect(result.exceptions_list).toEqual([getEndpointListMock()]);
     });
 
     test('returns formatted object with detections exceptions_list', () => {
-      const result: AboutStepRuleJson = formatAboutStepData(mockData, [mockExceptionsList]);
-      expect(result.exceptions_list).toEqual([mockExceptionsList]);
+      const result: AboutStepRuleJson = formatAboutStepData(mockData, [getListMock()]);
+      expect(result.exceptions_list).toEqual([getListMock()]);
     });
 
     test('returns formatted object with both exceptions_lists', () => {
@@ -407,13 +400,13 @@ describe('helpers', () => {
           ...mockData,
           isAssociatedToEndpointList: true,
         },
-        [mockExceptionsList]
+        [getListMock()]
       );
-      expect(result.exceptions_list).toEqual([ENDPOINT_LIST, mockExceptionsList]);
+      expect(result.exceptions_list).toEqual([getEndpointListMock(), getListMock()]);
     });
 
     test('returns formatted object with pre-existing exceptions lists', () => {
-      const exceptionsLists: List[] = [ENDPOINT_LIST, mockExceptionsList];
+      const exceptionsLists: List[] = [getEndpointListMock(), getListMock()];
       const result: AboutStepRuleJson = formatAboutStepData(
         {
           ...mockData,
@@ -425,9 +418,9 @@ describe('helpers', () => {
     });
 
     test('returns formatted object with pre-existing endpoint exceptions list disabled', () => {
-      const exceptionsLists: List[] = [ENDPOINT_LIST, mockExceptionsList];
+      const exceptionsLists: List[] = [getEndpointListMock(), getListMock()];
       const result: AboutStepRuleJson = formatAboutStepData(mockData, exceptionsLists);
-      expect(result.exceptions_list).toEqual([mockExceptionsList]);
+      expect(result.exceptions_list).toEqual([getListMock()]);
     });
 
     test('returns formatted object with empty falsePositive and references filtered out', () => {
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
index 8b03f62fc82bd..434a33ac37722 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts
@@ -177,7 +177,12 @@ export const formatAboutStepData = (
     ...(isAssociatedToEndpointList
       ? {
           exceptions_list: [
-            { id: ENDPOINT_LIST_ID, namespace_type: 'agnostic', type: 'endpoint' },
+            {
+              id: ENDPOINT_LIST_ID,
+              list_id: ENDPOINT_LIST_ID,
+              namespace_type: 'agnostic',
+              type: 'endpoint',
+            },
             ...detectionExceptionLists,
           ] as AboutStepRuleJson['exceptions_list'],
         }
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
index 90424e1fb9dd0..789469e981fb4 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx
@@ -328,13 +328,13 @@ export const RuleDetailsPageComponent: FC<PropsFromRedux> = ({
         lists: ExceptionIdentifiers[];
         allowedExceptionListTypes: ExceptionListTypeEnum[];
       }>(
-        (acc, { id, namespace_type, type }) => {
+        (acc, { id, list_id, namespace_type, type }) => {
           const { allowedExceptionListTypes, lists } = acc;
           const shouldAddEndpoint =
             type === ExceptionListTypeEnum.ENDPOINT &&
             !allowedExceptionListTypes.includes(ExceptionListTypeEnum.ENDPOINT);
           return {
-            lists: [...lists, { id, namespaceType: namespace_type, type }],
+            lists: [...lists, { id, listId: list_id, namespaceType: namespace_type, type }],
             allowedExceptionListTypes: shouldAddEndpoint
               ? [...allowedExceptionListTypes, ExceptionListTypeEnum.ENDPOINT]
               : allowedExceptionListTypes,
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
index e6a517d85db81..05601ec8ffb4c 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json
@@ -1,20 +1,17 @@
 {
-  "author": [
-    "Elastic"
-  ],
+  "author": ["Elastic"],
   "description": "Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Elastic Endpoint alerts.",
   "enabled": true,
   "exceptions_list": [
     {
       "id": "endpoint_list",
+      "list_id": "endpoint_list",
       "namespace_type": "agnostic",
       "type": "endpoint"
     }
   ],
   "from": "now-10m",
-  "index": [
-    "logs-endpoint.alerts-*"
-  ],
+  "index": ["logs-endpoint.alerts-*"],
   "language": "kuery",
   "license": "Elastic License",
   "max_signals": 10000,
@@ -57,10 +54,7 @@
       "value": "99"
     }
   ],
-  "tags": [
-    "Elastic",
-    "Endpoint"
-  ],
+  "tags": ["Elastic", "Endpoint"],
   "timestamp_override": "event.ingested",
   "type": "query",
   "version": 1
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json
index 678ad9eb03b50..8b627c48d2904 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/external_alerts.json
@@ -1,12 +1,9 @@
 {
-  "author": [
-    "Elastic"
-  ],
+  "author": ["Elastic"],
   "description": "Generates a detection alert for each external alert written to the configured indices. Enabling this rule allows you to immediately begin investigating external alerts in the app.",
   "index": [
     "apm-*-transaction*",
     "auditbeat-*",
-    "endgame-*",
     "filebeat-*",
     "logs-*",
     "packetbeat-*",
@@ -54,9 +51,7 @@
       "value": "99"
     }
   ],
-  "tags": [
-    "Elastic"
-  ],
+  "tags": ["Elastic"],
   "timestamp_override": "event.ingested",
   "type": "query",
   "version": 1
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts
index a610970907bf8..3c41f29625a51 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts
@@ -716,26 +716,31 @@ describe('utils', () => {
 
   describe('#getExceptions', () => {
     test('it successfully returns array of exception list items', async () => {
+      listMock.getExceptionListClient = () =>
+        (({
+          findExceptionListsItem: jest.fn().mockResolvedValue({
+            data: [getExceptionListItemSchemaMock()],
+            page: 1,
+            per_page: 10000,
+            total: 1,
+          }),
+        } as unknown) as ExceptionListClient);
       const client = listMock.getExceptionListClient();
       const exceptions = await getExceptions({
         client,
         lists: getListArrayMock(),
       });
 
-      expect(client.getExceptionList).toHaveBeenNthCalledWith(1, {
-        id: 'some_uuid',
-        listId: undefined,
-        namespaceType: 'single',
-      });
-      expect(client.getExceptionList).toHaveBeenNthCalledWith(2, {
-        id: 'some_uuid',
-        listId: undefined,
-        namespaceType: 'agnostic',
+      expect(client.findExceptionListsItem).toHaveBeenCalledWith({
+        listId: ['list_id_single', 'endpoint_list'],
+        namespaceType: ['single', 'agnostic'],
+        page: 1,
+        perPage: 10000,
+        filter: [],
+        sortOrder: undefined,
+        sortField: undefined,
       });
-      expect(exceptions).toEqual([
-        getExceptionListItemSchemaMock(),
-        getExceptionListItemSchemaMock(),
-      ]);
+      expect(exceptions).toEqual([getExceptionListItemSchemaMock()]);
     });
 
     test('it throws if "client" is undefined', async () => {
@@ -747,7 +752,7 @@ describe('utils', () => {
       ).rejects.toThrowError('lists plugin unavailable during rule execution');
     });
 
-    test('it returns empty array if no "lists" is undefined', async () => {
+    test('it returns empty array if "lists" is undefined', async () => {
       const exceptions = await getExceptions({
         client: listMock.getExceptionListClient(),
         lists: undefined,
@@ -771,11 +776,11 @@ describe('utils', () => {
       ).rejects.toThrowError('unable to fetch exception list items');
     });
 
-    test('it throws if "findExceptionListItem" fails', async () => {
+    test('it throws if "findExceptionListsItem" fails', async () => {
       const err = new Error('error fetching list');
       listMock.getExceptionListClient = () =>
         (({
-          findExceptionListItem: jest.fn().mockRejectedValue(err),
+          findExceptionListsItem: jest.fn().mockRejectedValue(err),
         } as unknown) as ExceptionListClient);
 
       await expect(() =>
@@ -786,24 +791,10 @@ describe('utils', () => {
       ).rejects.toThrowError('unable to fetch exception list items');
     });
 
-    test('it returns empty array if "getExceptionList" returns null', async () => {
-      listMock.getExceptionListClient = () =>
-        (({
-          getExceptionList: jest.fn().mockResolvedValue(null),
-        } as unknown) as ExceptionListClient);
-
-      const exceptions = await getExceptions({
-        client: listMock.getExceptionListClient(),
-        lists: undefined,
-      });
-
-      expect(exceptions).toEqual([]);
-    });
-
-    test('it returns empty array if "findExceptionListItem" returns null', async () => {
+    test('it returns empty array if "findExceptionListsItem" returns null', async () => {
       listMock.getExceptionListClient = () =>
         (({
-          findExceptionListItem: jest.fn().mockResolvedValue(null),
+          findExceptionListsItem: jest.fn().mockResolvedValue(null),
         } as unknown) as ExceptionListClient);
 
       const exceptions = await getExceptions({
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
index ae4274f31e145..9519720d0bbec 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts
@@ -161,43 +161,20 @@ export const getExceptions = async ({
     throw new Error('lists plugin unavailable during rule execution');
   }
 
-  if (lists != null) {
+  if (lists != null && lists.length > 0) {
     try {
-      // Gather all exception items of all exception lists linked to rule
-      const exceptions = await Promise.all(
-        lists
-          .map(async (list) => {
-            const { id, namespace_type: namespaceType } = list;
-            try {
-              // TODO update once exceptions client `findExceptionListItem`
-              // accepts an array of list ids
-              const foundList = await client.getExceptionList({
-                id,
-                namespaceType,
-                listId: undefined,
-              });
-
-              if (foundList == null) {
-                return [];
-              } else {
-                const items = await client.findExceptionListItem({
-                  listId: foundList.list_id,
-                  namespaceType,
-                  page: 1,
-                  perPage: MAX_EXCEPTION_LIST_SIZE,
-                  filter: undefined,
-                  sortOrder: undefined,
-                  sortField: undefined,
-                });
-                return items != null ? items.data : [];
-              }
-            } catch {
-              throw new Error('unable to fetch exception list items');
-            }
-          })
-          .flat()
-      );
-      return exceptions.flat();
+      const listIds = lists.map(({ list_id: listId }) => listId);
+      const namespaceTypes = lists.map(({ namespace_type: namespaceType }) => namespaceType);
+      const items = await client.findExceptionListsItem({
+        listId: listIds,
+        namespaceType: namespaceTypes,
+        page: 1,
+        perPage: MAX_EXCEPTION_LIST_SIZE,
+        filter: [],
+        sortOrder: undefined,
+        sortField: undefined,
+      });
+      return items != null ? items.data : [];
     } catch {
       throw new Error('unable to fetch exception list items');
     }