Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Security Solution] Generate host isolation exceptions artifact (#115160) #115512

Merged
merged 2 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const ArtifactConstants = {

SUPPORTED_EVENT_FILTERS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'],
GLOBAL_EVENT_FILTERS_NAME: 'endpoint-eventfilterlist',

SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'],
GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME: 'endpoint-hostisolationexceptionlist',
};

export const ManifestConstants = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { validate } from '@kbn/securitysolution-io-ts-utils';

import {
ENDPOINT_EVENT_FILTERS_LIST_ID,
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
ENDPOINT_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
Expand Down Expand Up @@ -65,6 +66,7 @@ export async function getFilteredEndpointExceptionList(
| typeof ENDPOINT_LIST_ID
| typeof ENDPOINT_TRUSTED_APPS_LIST_ID
| typeof ENDPOINT_EVENT_FILTERS_LIST_ID
| typeof ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID
): Promise<WrappedTranslatedExceptionList> {
const exceptions: WrappedTranslatedExceptionList = { entries: [] };
let page = 1;
Expand Down Expand Up @@ -148,6 +150,24 @@ export async function getEndpointEventFiltersList(
);
}

export async function getHostIsolationExceptionsList(
eClient: ExceptionListClient,
schemaVersion: string,
os: string,
policyId?: string
): Promise<WrappedTranslatedExceptionList> {
const osFilter = `exception-list-agnostic.attributes.os_types:\"${os}\"`;
const policyFilter = `(exception-list-agnostic.attributes.tags:\"policy:all\"${
policyId ? ` or exception-list-agnostic.attributes.tags:\"policy:${policyId}\"` : ''
})`;

return getFilteredEndpointExceptionList(
eClient,
schemaVersion,
`${osFilter} and ${policyFilter}`,
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID
);
}
/**
* Translates Exception list items to Exceptions the endpoint can understand
* @param exceptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { savedObjectsClientMock } from 'src/core/server/mocks';
import {
ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID,
ENDPOINT_LIST_ID,
ENDPOINT_TRUSTED_APPS_LIST_ID,
} from '@kbn/securitysolution-list-constants';
Expand Down Expand Up @@ -66,6 +67,12 @@ describe('ManifestManager', () => {
const ARTIFACT_NAME_EVENT_FILTERS_MACOS = 'endpoint-eventfilterlist-macos-v1';
const ARTIFACT_NAME_EVENT_FILTERS_WINDOWS = 'endpoint-eventfilterlist-windows-v1';
const ARTIFACT_NAME_EVENT_FILTERS_LINUX = 'endpoint-eventfilterlist-linux-v1';
const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS =
'endpoint-hostisolationexceptionlist-macos-v1';
const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS =
'endpoint-hostisolationexceptionlist-windows-v1';
const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX =
'endpoint-hostisolationexceptionlist-linux-v1';

let ARTIFACTS: InternalArtifactCompleteSchema[] = [];
let ARTIFACTS_BY_ID: { [K: string]: InternalArtifactCompleteSchema } = {};
Expand Down Expand Up @@ -157,31 +164,29 @@ describe('ManifestManager', () => {
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);

savedObjectsClient.get = jest
.fn()
.mockImplementation(async (objectType: string, id: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});
savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});

(
manifestManagerContext.artifactClient as jest.Mocked<EndpointArtifactClientInterface>
Expand Down Expand Up @@ -218,31 +223,29 @@ describe('ManifestManager', () => {
const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient });
const manifestManager = new ManifestManager(manifestManagerContext);

savedObjectsClient.get = jest
.fn()
.mockImplementation(async (objectType: string, id: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});
savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => {
if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) {
return {
attributes: {
created: '20-01-2020 10:00:00.000Z',
schemaVersion: 'v2',
semanticVersion: '1.0.0',
artifacts: [
{ artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined },
{ artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 },
{ artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 },
],
},
version: '2.0.0',
};
} else {
return null;
}
});

(
manifestManagerContext.artifactClient as jest.Mocked<EndpointArtifactClientInterface>
Expand Down Expand Up @@ -278,6 +281,9 @@ describe('ManifestManager', () => {
ARTIFACT_NAME_EVENT_FILTERS_MACOS,
ARTIFACT_NAME_EVENT_FILTERS_WINDOWS,
ARTIFACT_NAME_EVENT_FILTERS_LINUX,
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS,
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS,
ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX,
];

const getArtifactIds = (artifacts: InternalArtifactSchema[]) => [
Expand Down Expand Up @@ -310,7 +316,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const manifest = await manifestManager.buildNewManifest();
Expand All @@ -321,7 +327,7 @@ describe('ManifestManager', () => {

const artifacts = manifest.getAllArtifacts();

expect(artifacts.length).toBe(9);
expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);

for (const artifact of artifacts) {
Expand All @@ -336,16 +342,18 @@ describe('ManifestManager', () => {
test('Builds fully new manifest if no baseline parameter passed and present exception list items', async () => {
const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] });
const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'] });
const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({ os_types: ['linux'] });
const context = buildManifestManagerContextMock({});
const manifestManager = new ManifestManager(context);

context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({
[ENDPOINT_LIST_ID]: { macos: [exceptionListItem] },
[ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] },
[ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] },
});
context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
Expand All @@ -358,7 +366,7 @@ describe('ManifestManager', () => {

const artifacts = manifest.getAllArtifacts();

expect(artifacts.length).toBe(9);
expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);

expect(getArtifactObject(artifacts[0])).toStrictEqual({
Expand All @@ -374,6 +382,11 @@ describe('ManifestManager', () => {
expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] });
expect(getArtifactObject(artifacts[11])).toStrictEqual({
entries: translateToEndpointExceptions([hostIsolationExceptionsItem], 'v1'),
});

for (const artifact of artifacts) {
expect(manifest.isDefaultArtifact(artifact)).toBe(true);
Expand All @@ -395,7 +408,7 @@ describe('ManifestManager', () => {
context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]);
context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const oldManifest = await manifestManager.buildNewManifest();
Expand All @@ -413,7 +426,7 @@ describe('ManifestManager', () => {

const artifacts = manifest.getAllArtifacts();

expect(artifacts.length).toBe(9);
expect(artifacts.length).toBe(12);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);

expect(artifacts[0]).toStrictEqual(oldManifest.getAllArtifacts()[0]);
Expand Down Expand Up @@ -462,7 +475,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));

Expand All @@ -474,7 +487,7 @@ describe('ManifestManager', () => {

const artifacts = manifest.getAllArtifacts();

expect(artifacts.length).toBe(10);
expect(artifacts.length).toBe(13);
expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES);

expect(getArtifactObject(artifacts[0])).toStrictEqual({
Expand Down Expand Up @@ -653,7 +666,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => object);
.mockImplementation((_type: string, object: InternalManifestSchema) => object);

await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();

Expand Down Expand Up @@ -690,7 +703,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.update = jest
.fn()
.mockImplementation((type: string, id: string, object: InternalManifestSchema) => object);
.mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object);

await expect(manifestManager.commit(manifest)).resolves.toBeUndefined();

Expand Down Expand Up @@ -1023,7 +1036,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));
const manifest = await manifestManager.buildNewManifest();
Expand All @@ -1046,7 +1059,7 @@ describe('ManifestManager', () => {

context.savedObjectsClient.create = jest
.fn()
.mockImplementation((type: string, object: InternalManifestSchema) => ({
.mockImplementation((_type: string, object: InternalManifestSchema) => ({
attributes: object,
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
getEndpointEventFiltersList,
getEndpointExceptionList,
getEndpointTrustedAppsList,
getHostIsolationExceptionsList,
Manifest,
} from '../../../lib/artifacts';
import {
Expand Down Expand Up @@ -237,6 +238,46 @@ export class ManifestManager {
);
}

protected async buildHostIsolationExceptionsArtifacts(): Promise<ArtifactsBuildResult> {
const defaultArtifacts: InternalArtifactCompleteSchema[] = [];
const policySpecificArtifacts: Record<string, InternalArtifactCompleteSchema[]> = {};

for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) {
defaultArtifacts.push(await this.buildHostIsolationExceptionForOs(os));
}

await iterateAllListItems(
(page) => this.listEndpointPolicyIds(page),
async (policyId) => {
for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) {
policySpecificArtifacts[policyId] = policySpecificArtifacts[policyId] || [];
policySpecificArtifacts[policyId].push(
await this.buildHostIsolationExceptionForOs(os, policyId)
);
}
}
);

return { defaultArtifacts, policySpecificArtifacts };
}

protected async buildHostIsolationExceptionForOs(
os: string,
policyId?: string
): Promise<InternalArtifactCompleteSchema> {
return buildArtifact(
await getHostIsolationExceptionsList(
this.exceptionListClient,
this.schemaVersion,
os,
policyId
),
this.schemaVersion,
os,
ArtifactConstants.GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME
);
}

/**
* Writes new artifact SO.
*
Expand Down Expand Up @@ -381,6 +422,7 @@ export class ManifestManager {
this.buildExceptionListArtifacts(),
this.buildTrustedAppsArtifacts(),
this.buildEventFiltersArtifacts(),
this.buildHostIsolationExceptionsArtifacts(),
]);

const manifest = new Manifest({
Expand Down