mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[SECURITY_SOLUTION][ENDPOINT] Generate Trusted Apps artifacts and Manifest entries (#74988)
* Generate Trusted Apps artifacts + manifest entries * Artifacts mocks support for generating trusted apps entries * Adjusted Manifest + Artifacts tests to account for trusted apps
This commit is contained in:
parent
bf7b4782e6
commit
97c8c941f3
11 changed files with 430 additions and 47 deletions
|
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
@ -76,7 +77,8 @@ 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;
|
||||
|
@ -84,7 +86,7 @@ export async function getFullEndpointExceptionList(
|
|||
|
||||
while (paging) {
|
||||
const response = await eClient.findExceptionListItem({
|
||||
listId: ENDPOINT_LIST_ID,
|
||||
listId,
|
||||
namespaceType: 'agnostic',
|
||||
filter: `exception-list-agnostic.attributes._tags:\"os:${os}\"`,
|
||||
perPage: 100,
|
||||
|
|
|
@ -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',
|
||||
|
@ -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',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -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 }) => {
|
||||
|
|
|
@ -4,7 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { buildArtifact, maybeCompressArtifact, isCompressed } from '../../lib/artifacts';
|
||||
import {
|
||||
buildArtifact,
|
||||
maybeCompressArtifact,
|
||||
isCompressed,
|
||||
ArtifactConstants,
|
||||
} from '../../lib/artifacts';
|
||||
import { getTranslatedExceptionListMock } from './lists.mock';
|
||||
import {
|
||||
InternalManifestSchema,
|
||||
|
@ -25,9 +30,15 @@ const compressArtifact = async (artifact: InternalArtifactCompleteSchema) => {
|
|||
export const getInternalArtifactMock = async (
|
||||
os: string,
|
||||
schemaVersion: string,
|
||||
opts?: { compress: boolean }
|
||||
opts?: { compress: boolean },
|
||||
artifactName: string = ArtifactConstants.GLOBAL_ALLOWLIST_NAME
|
||||
): Promise<InternalArtifactCompleteSchema> => {
|
||||
const artifact = await buildArtifact(getTranslatedExceptionListMock(), os, schemaVersion);
|
||||
const artifact = await buildArtifact(
|
||||
getTranslatedExceptionListMock(),
|
||||
os,
|
||||
schemaVersion,
|
||||
artifactName
|
||||
);
|
||||
return opts?.compress ? compressArtifact(artifact) : artifact;
|
||||
};
|
||||
|
||||
|
@ -36,7 +47,12 @@ export const getEmptyInternalArtifactMock = async (
|
|||
schemaVersion: string,
|
||||
opts?: { compress: boolean }
|
||||
): Promise<InternalArtifactCompleteSchema> => {
|
||||
const artifact = await buildArtifact({ entries: [] }, os, schemaVersion);
|
||||
const artifact = await buildArtifact(
|
||||
{ entries: [] },
|
||||
os,
|
||||
schemaVersion,
|
||||
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
|
||||
);
|
||||
return opts?.compress ? compressArtifact(artifact) : artifact;
|
||||
};
|
||||
|
||||
|
@ -47,7 +63,12 @@ export const getInternalArtifactMockWithDiffs = async (
|
|||
): Promise<InternalArtifactCompleteSchema> => {
|
||||
const mock = getTranslatedExceptionListMock();
|
||||
mock.entries.pop();
|
||||
const artifact = await buildArtifact(mock, os, schemaVersion);
|
||||
const artifact = await buildArtifact(
|
||||
mock,
|
||||
os,
|
||||
schemaVersion,
|
||||
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
|
||||
);
|
||||
return opts?.compress ? compressArtifact(artifact) : artifact;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,11 +24,41 @@ describe('manifest_manager', () => {
|
|||
'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',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-macos-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-windows-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-linux-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -44,16 +74,53 @@ describe('manifest_manager', () => {
|
|||
'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',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-macos-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-windows-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
{
|
||||
id:
|
||||
'endpoint-trustlist-linux-v1-1a8295e6ccb93022c6f5ceb8997b29f2912389b3b38f52a8f5a2ff7b0154b1bc',
|
||||
type: 'add',
|
||||
},
|
||||
]);
|
||||
|
||||
const newArtifactId = diffs[1].id;
|
||||
await newManifest.compressArtifact(newArtifactId);
|
||||
const artifact = newManifest.getArtifact(newArtifactId)!;
|
||||
const firstNewArtifactId = diffs.find((diff) => diff.type === 'add')!.id;
|
||||
|
||||
// Compress all `add` artifacts
|
||||
for (const artifactDiff of diffs) {
|
||||
if (artifactDiff.type === 'add') {
|
||||
await newManifest.compressArtifact(artifactDiff.id);
|
||||
}
|
||||
}
|
||||
|
||||
const artifact = newManifest.getArtifact(firstNewArtifactId)!;
|
||||
|
||||
if (isCompleteArtifact(artifact)) {
|
||||
await manifestManager.pushArtifacts([artifact]); // caches the artifact
|
||||
|
@ -61,7 +128,7 @@ describe('manifest_manager', () => {
|
|||
throw new Error('Artifact is missing a body.');
|
||||
}
|
||||
|
||||
const entry = JSON.parse(inflateSync(cache.get(newArtifactId)! as Buffer).toString());
|
||||
const entry = JSON.parse(inflateSync(cache.get(firstNewArtifactId)! as Buffer).toString());
|
||||
expect(entry).toEqual({
|
||||
entries: [
|
||||
{
|
||||
|
@ -107,8 +174,12 @@ describe('manifest_manager', () => {
|
|||
const oldManifest = await manifestManager.getLastComputedManifest();
|
||||
const newManifest = await manifestManager.buildNewManifest(oldManifest!);
|
||||
const diffs = newManifest.diff(oldManifest!);
|
||||
const newArtifactId = diffs[1].id;
|
||||
await newManifest.compressArtifact(newArtifactId);
|
||||
|
||||
for (const artifactDiff of diffs) {
|
||||
if (artifactDiff.type === 'add') {
|
||||
await newManifest.compressArtifact(artifactDiff.id);
|
||||
}
|
||||
}
|
||||
|
||||
newManifest.bumpSemanticVersion();
|
||||
|
||||
|
@ -145,6 +216,36 @@ describe('manifest_manager', () => {
|
|||
relative_url:
|
||||
'/api/endpoint/artifacts/download/endpoint-exceptionlist-windows-v1/96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3',
|
||||
},
|
||||
'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',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -155,8 +256,12 @@ describe('manifest_manager', () => {
|
|||
const oldManifest = await manifestManager.getLastComputedManifest();
|
||||
const newManifest = await manifestManager.buildNewManifest(oldManifest!);
|
||||
const diffs = newManifest.diff(oldManifest!);
|
||||
const newArtifactId = diffs[1].id;
|
||||
await newManifest.compressArtifact(newArtifactId);
|
||||
|
||||
for (const artifactDiff of diffs) {
|
||||
if (artifactDiff.type === 'add') {
|
||||
await newManifest.compressArtifact(artifactDiff.id);
|
||||
}
|
||||
}
|
||||
|
||||
newManifest.bumpSemanticVersion();
|
||||
|
||||
|
@ -174,11 +279,17 @@ describe('manifest_manager', () => {
|
|||
const oldManifest = await manifestManager.getLastComputedManifest();
|
||||
const newManifest = await manifestManager.buildNewManifest(oldManifest!);
|
||||
const diffs = newManifest.diff(oldManifest!);
|
||||
const oldArtifactId = diffs[0].id;
|
||||
const newArtifactId = diffs[1].id;
|
||||
await newManifest.compressArtifact(newArtifactId);
|
||||
const firstOldArtifactId = diffs.find((diff) => diff.type === 'delete')!.id;
|
||||
const FirstNewArtifactId = diffs.find((diff) => diff.type === 'add')!.id;
|
||||
|
||||
const artifact = newManifest.getArtifact(newArtifactId)!;
|
||||
// Compress all new artifacts
|
||||
for (const artifactDiff of diffs) {
|
||||
if (artifactDiff.type === 'add') {
|
||||
await newManifest.compressArtifact(artifactDiff.id);
|
||||
}
|
||||
}
|
||||
|
||||
const artifact = newManifest.getArtifact(FirstNewArtifactId)!;
|
||||
if (isCompleteArtifact(artifact)) {
|
||||
await manifestManager.pushArtifacts([artifact]);
|
||||
} else {
|
||||
|
@ -186,7 +297,7 @@ describe('manifest_manager', () => {
|
|||
}
|
||||
|
||||
await manifestManager.commit(newManifest);
|
||||
await manifestManager.deleteArtifacts([oldArtifactId]);
|
||||
await manifestManager.deleteArtifacts([firstOldArtifactId]);
|
||||
|
||||
// created new artifact
|
||||
expect(savedObjectsClient.create.mock.calls[0][0]).toEqual(
|
||||
|
@ -201,7 +312,7 @@ describe('manifest_manager', () => {
|
|||
// deleted old artifact
|
||||
expect(savedObjectsClient.delete).toHaveBeenCalledWith(
|
||||
ArtifactConstants.SAVED_OBJECT_TYPE,
|
||||
oldArtifactId
|
||||
firstOldArtifactId
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -13,11 +13,11 @@ import { manifestDispatchSchema } from '../../../../../common/endpoint/schema/ma
|
|||
|
||||
import {
|
||||
ArtifactConstants,
|
||||
Manifest,
|
||||
buildArtifact,
|
||||
getFullEndpointExceptionList,
|
||||
ManifestDiff,
|
||||
getArtifactId,
|
||||
getFullEndpointExceptionList,
|
||||
Manifest,
|
||||
ManifestDiff,
|
||||
} from '../../../lib/artifacts';
|
||||
import {
|
||||
InternalArtifactCompleteSchema,
|
||||
|
@ -25,6 +25,8 @@ import {
|
|||
} from '../../../schemas/artifacts';
|
||||
import { ArtifactClient } from '../artifact_client';
|
||||
import { ManifestClient } from '../manifest_client';
|
||||
import { ENDPOINT_LIST_ID } from '../../../../../../lists/common';
|
||||
import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../../../../../lists/common/constants';
|
||||
|
||||
export interface ManifestManagerContext {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
|
@ -87,9 +89,43 @@ export class ManifestManager {
|
|||
const exceptionList = await getFullEndpointExceptionList(
|
||||
this.exceptionListClient,
|
||||
os,
|
||||
artifactSchemaVersion ?? 'v1'
|
||||
artifactSchemaVersion ?? 'v1',
|
||||
ENDPOINT_LIST_ID
|
||||
);
|
||||
const artifact = await buildArtifact(
|
||||
exceptionList,
|
||||
os,
|
||||
artifactSchemaVersion ?? 'v1',
|
||||
ArtifactConstants.GLOBAL_ALLOWLIST_NAME
|
||||
);
|
||||
artifacts.push(artifact);
|
||||
}
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an array of artifacts (one per supported OS) based on the current state of the
|
||||
* Trusted Apps list (which uses the `exception-list-agnostic` SO type)
|
||||
* @param artifactSchemaVersion
|
||||
*/
|
||||
protected async buildTrustedAppsArtifacts(
|
||||
artifactSchemaVersion?: string
|
||||
): Promise<InternalArtifactCompleteSchema[]> {
|
||||
const artifacts: InternalArtifactCompleteSchema[] = [];
|
||||
|
||||
for (const os of ArtifactConstants.SUPPORTED_TRUSTED_APPS_OPERATING_SYSTEMS) {
|
||||
const trustedApps = await getFullEndpointExceptionList(
|
||||
this.exceptionListClient,
|
||||
os,
|
||||
artifactSchemaVersion ?? 'v1',
|
||||
ENDPOINT_TRUSTED_APPS_LIST_ID
|
||||
);
|
||||
const artifact = await buildArtifact(
|
||||
trustedApps,
|
||||
os,
|
||||
'v1',
|
||||
ArtifactConstants.GLOBAL_TRUSTED_APPS_NAME
|
||||
);
|
||||
const artifact = await buildArtifact(exceptionList, os, artifactSchemaVersion ?? 'v1');
|
||||
artifacts.push(artifact);
|
||||
}
|
||||
return artifacts;
|
||||
|
@ -205,7 +241,9 @@ export class ManifestManager {
|
|||
*/
|
||||
public async buildNewManifest(baselineManifest?: Manifest): Promise<Manifest> {
|
||||
// Build new exception list artifacts
|
||||
const artifacts = await this.buildExceptionListArtifacts();
|
||||
const artifacts = (
|
||||
await Promise.all([this.buildExceptionListArtifacts(), this.buildTrustedAppsArtifacts()])
|
||||
).flat();
|
||||
|
||||
// Build new manifest
|
||||
const manifest = Manifest.fromArtifacts(
|
||||
|
|
|
@ -142,6 +142,42 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
relative_url:
|
||||
'/api/endpoint/artifacts/download/endpoint-exceptionlist-windows-v1/d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
},
|
||||
'endpoint-trustlist-linux-v1': {
|
||||
compression_algorithm: 'zlib',
|
||||
decoded_sha256:
|
||||
'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
decoded_size: 14,
|
||||
encoded_sha256:
|
||||
'f8e6afa1d5662f5b37f83337af774b5785b5b7f1daee08b7b00c2d6813874cda',
|
||||
encoded_size: 22,
|
||||
encryption_algorithm: 'none',
|
||||
relative_url:
|
||||
'/api/endpoint/artifacts/download/endpoint-trustlist-linux-v1/d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
},
|
||||
'endpoint-trustlist-macos-v1': {
|
||||
compression_algorithm: 'zlib',
|
||||
decoded_sha256:
|
||||
'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
decoded_size: 14,
|
||||
encoded_sha256:
|
||||
'f8e6afa1d5662f5b37f83337af774b5785b5b7f1daee08b7b00c2d6813874cda',
|
||||
encoded_size: 22,
|
||||
encryption_algorithm: 'none',
|
||||
relative_url:
|
||||
'/api/endpoint/artifacts/download/endpoint-trustlist-macos-v1/d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
},
|
||||
'endpoint-trustlist-windows-v1': {
|
||||
compression_algorithm: 'zlib',
|
||||
decoded_sha256:
|
||||
'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
decoded_size: 14,
|
||||
encoded_sha256:
|
||||
'f8e6afa1d5662f5b37f83337af774b5785b5b7f1daee08b7b00c2d6813874cda',
|
||||
encoded_size: 22,
|
||||
encryption_algorithm: 'none',
|
||||
relative_url:
|
||||
'/api/endpoint/artifacts/download/endpoint-trustlist-windows-v1/d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
|
||||
},
|
||||
},
|
||||
// The manifest version could have changed when the Policy was updated because the
|
||||
// policy details page ensures that a save action applies the udpated policy on top
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue