Skip to content

Commit

Permalink
[SECURITY_SOLUTION][ENDPOINT] Generate Trusted Apps artifacts and Man…
Browse files Browse the repository at this point in the history
…ifest entries (#74988) (#76366)

* Generate Trusted Apps artifacts + manifest entries
* Artifacts mocks support for generating trusted apps entries
* Adjusted Manifest + Artifacts tests to account for trusted apps
  • Loading branch information
paul-tavares authored Sep 1, 2020
1 parent f29e079 commit 0a69e48
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"list_id": "endpoint_trusted_apps",
"item_id": "endpoint_trusted_apps_item",
"_tags": ["endpoint", "os:linux", "os:windows", "os:macos", "trusted-app"],
"tags": ["user added string for a tag", "malware"],
"type": "simple",
"description": "This is a sample agnostic endpoint trusted app entry",
"name": "Sample Endpoint Trusted App Entry",
"namespace_type": "agnostic",
"entries": [
{
"field": "actingProcess.file.signer",
"operator": "included",
"type": "match",
"value": "Elastic, N.V."
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,36 @@ describe('ingest_integration tests ', () => {
relative_url:
'/api/endpoint/artifacts/download/endpoint-exceptionlist-windows-v1/d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
},
'endpoint-trustlist-linux-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
decoded_size: 287,
encoded_sha256: 'c3dec543df1177561ab2aa74a37997ea3c1d748d532a597884f5a5c16670d56c',
encoded_size: 133,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-linux-v1/1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
},
'endpoint-trustlist-macos-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
decoded_size: 287,
encoded_sha256: 'c3dec543df1177561ab2aa74a37997ea3c1d748d532a597884f5a5c16670d56c',
encoded_size: 133,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-macos-v1/1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
},
'endpoint-trustlist-windows-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
decoded_size: 287,
encoded_sha256: 'c3dec543df1177561ab2aa74a37997ea3c1d748d532a597884f5a5c16670d56c',
encoded_size: 133,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-windows-v1/1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
},
},
manifest_version: '1.0.0',
schema_version: 'v1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export const ArtifactConstants = {
GLOBAL_ALLOWLIST_NAME: 'endpoint-exceptionlist',
SAVED_OBJECT_TYPE: 'endpoint:user-artifact',
SUPPORTED_OPERATING_SYSTEMS: ['macos', 'windows'],
SUPPORTED_TRUSTED_APPS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'],
GLOBAL_TRUSTED_APPS_NAME: 'endpoint-trustlist',
};

export const ManifestConstants = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { getExceptionListItemSchemaMock } from '../../../../../lists/common/sche
import { EntriesArray, EntryList } from '../../../../../lists/common/schemas/types';
import { buildArtifact, getFullEndpointExceptionList } from './lists';
import { TranslatedEntry, TranslatedExceptionListItem } from '../../schemas/artifacts';
import { ArtifactConstants } from './common';
import { ENDPOINT_LIST_ID } from '../../../../../lists/common';

describe('buildEventTypeSignal', () => {
let mockExceptionClient: ExceptionListClient;
Expand Down Expand Up @@ -47,7 +49,12 @@ describe('buildEventTypeSignal', () => {

const first = getFoundExceptionListItemSchemaMock();
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);
const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -88,7 +95,12 @@ describe('buildEventTypeSignal', () => {
first.data[0].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -134,7 +146,12 @@ describe('buildEventTypeSignal', () => {
first.data[0].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -182,7 +199,12 @@ describe('buildEventTypeSignal', () => {
first.data[0].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -229,7 +251,12 @@ describe('buildEventTypeSignal', () => {
first.data[0].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -267,7 +294,12 @@ describe('buildEventTypeSignal', () => {
first.data[1].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand Down Expand Up @@ -305,7 +337,12 @@ describe('buildEventTypeSignal', () => {
first.data[0].entries = testEntries;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(first);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp).toEqual({
entries: [expectedEndpointExceptions],
});
Expand All @@ -329,7 +366,12 @@ describe('buildEventTypeSignal', () => {
.mockReturnValueOnce(first)
.mockReturnValueOnce(second);

const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);

// Expect 2 exceptions, the first two calls returned the same exception list items
expect(resp.entries.length).toEqual(2);
Expand All @@ -340,7 +382,12 @@ describe('buildEventTypeSignal', () => {
exceptionsResponse.data = [];
exceptionsResponse.total = 0;
mockExceptionClient.findExceptionListItem = jest.fn().mockReturnValueOnce(exceptionsResponse);
const resp = await getFullEndpointExceptionList(mockExceptionClient, 'linux', 'v1');
const resp = await getFullEndpointExceptionList(
mockExceptionClient,
'linux',
'v1',
ENDPOINT_LIST_ID
);
expect(resp.entries.length).toEqual(0);
});

Expand Down Expand Up @@ -385,8 +432,18 @@ describe('buildEventTypeSignal', () => {
],
};

const artifact1 = await buildArtifact(translatedExceptionList, 'linux', 'v1');
const artifact2 = await buildArtifact(translatedExceptionListReversed, 'linux', 'v1');
const artifact1 = await buildArtifact(
translatedExceptionList,
'linux',
'v1',
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
);
const artifact2 = await buildArtifact(
translatedExceptionListReversed,
'linux',
'v1',
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
);
expect(artifact1.decodedSha256).toEqual(artifact2.decodedSha256);
});

Expand Down Expand Up @@ -430,8 +487,18 @@ describe('buildEventTypeSignal', () => {
entries: translatedItems.reverse(),
};

const artifact1 = await buildArtifact(translatedExceptionList, 'linux', 'v1');
const artifact2 = await buildArtifact(translatedExceptionListReversed, 'linux', 'v1');
const artifact1 = await buildArtifact(
translatedExceptionList,
'linux',
'v1',
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
);
const artifact2 = await buildArtifact(
translatedExceptionListReversed,
'linux',
'v1',
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
);
expect(artifact1.decodedSha256).toEqual(artifact2.decodedSha256);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,20 @@ import {
internalArtifactCompleteSchema,
InternalArtifactCompleteSchema,
} from '../../schemas';
import { ArtifactConstants } from './common';
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../../lists/common/constants';

export async function buildArtifact(
exceptions: WrappedTranslatedExceptionList,
os: string,
schemaVersion: string
schemaVersion: string,
name: string
): Promise<InternalArtifactCompleteSchema> {
const exceptionsBuffer = Buffer.from(JSON.stringify(exceptions));
const sha256 = createHash('sha256').update(exceptionsBuffer.toString()).digest('hex');

// Keep compression info empty in case its a duplicate. Lazily compress before committing if needed.
return {
identifier: `${ArtifactConstants.GLOBAL_ALLOWLIST_NAME}-${os}-${schemaVersion}`,
identifier: `${name}-${os}-${schemaVersion}`,
compressionAlgorithm: 'none',
encryptionAlgorithm: 'none',
decodedSha256: sha256,
Expand Down Expand Up @@ -76,15 +77,16 @@ export function isCompressed(artifact: InternalArtifactSchema) {
export async function getFullEndpointExceptionList(
eClient: ExceptionListClient,
os: string,
schemaVersion: string
schemaVersion: string,
listId: typeof ENDPOINT_LIST_ID | typeof ENDPOINT_TRUSTED_APPS_LIST_ID
): Promise<WrappedTranslatedExceptionList> {
const exceptions: WrappedTranslatedExceptionList = { entries: [] };
let page = 1;
let paging = true;

while (paging) {
const response = await eClient.findExceptionListItem({
listId: ENDPOINT_LIST_ID,
listId,
namespaceType: 'agnostic',
filter: `exception-list-agnostic.attributes._tags:\"os:${os}\"`,
perPage: 100,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,36 @@ describe('manifest', () => {
relative_url:
'/api/endpoint/artifacts/download/endpoint-exceptionlist-windows-v1/96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
},
'endpoint-trustlist-linux-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
decoded_size: 432,
encoded_sha256: '975382ab55d019cbab0bbac207a54e2a7d489fad6e8f6de34fc6402e5ef37b1e',
encoded_size: 147,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-linux-v1/96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
},
'endpoint-trustlist-macos-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
decoded_size: 432,
encoded_sha256: '975382ab55d019cbab0bbac207a54e2a7d489fad6e8f6de34fc6402e5ef37b1e',
encoded_size: 147,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-macos-v1/96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
},
'endpoint-trustlist-windows-v1': {
compression_algorithm: 'zlib',
decoded_sha256: '96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
decoded_size: 432,
encoded_sha256: '975382ab55d019cbab0bbac207a54e2a7d489fad6e8f6de34fc6402e5ef37b1e',
encoded_size: 147,
encryption_algorithm: 'none',
relative_url:
'/api/endpoint/artifacts/download/endpoint-trustlist-windows-v1/96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
},
},
manifest_version: '1.0.0',
schema_version: 'v1',
Expand All @@ -107,6 +137,9 @@ describe('manifest', () => {
ids: [
'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-exceptionlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-linux-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
],
});
});
Expand All @@ -119,6 +152,21 @@ describe('manifest', () => {
'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
type: 'delete',
},
{
id:
'endpoint-trustlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
type: 'delete',
},
{
id:
'endpoint-trustlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
type: 'delete',
},
{
id:
'endpoint-trustlist-linux-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
type: 'delete',
},
{
id:
'endpoint-exceptionlist-macos-v1-0a5a2013a79f9e60682472284a1be45ab1ff68b9b43426d00d665016612c15c8',
Expand All @@ -139,6 +187,9 @@ describe('manifest', () => {
expect(keys).toEqual([
'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-exceptionlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-windows-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
'endpoint-trustlist-linux-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,20 @@ import { ArtifactConstants } from './common';
import { Manifest } from './manifest';

export const getMockArtifacts = async (opts?: { compress: boolean }) => {
return Promise.all(
ArtifactConstants.SUPPORTED_OPERATING_SYSTEMS.map<Promise<InternalArtifactCompleteSchema>>(
return Promise.all([
// Exceptions items
...ArtifactConstants.SUPPORTED_OPERATING_SYSTEMS.map<Promise<InternalArtifactCompleteSchema>>(
async (os) => {
return getInternalArtifactMock(os, 'v1', opts);
}
)
);
),
// Trusted Apps items
...ArtifactConstants.SUPPORTED_TRUSTED_APPS_OPERATING_SYSTEMS.map<
Promise<InternalArtifactCompleteSchema>
>(async (os) => {
return getInternalArtifactMock(os, 'v1', opts, ArtifactConstants.GLOBAL_TRUSTED_APPS_NAME);
}),
]);
};

export const getMockArtifactsWithDiff = async (opts?: { compress: boolean }) => {
Expand Down
Loading

0 comments on commit 0a69e48

Please sign in to comment.