mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] remove query strategy v1 (#104196)
This commit is contained in:
parent
857dc9f2e1
commit
e4ba52928c
28 changed files with 125 additions and 1593 deletions
|
@ -178,8 +178,6 @@ export interface HostResultList {
|
|||
request_page_size: number;
|
||||
/* the page index requested */
|
||||
request_page_index: number;
|
||||
/* the version of the query strategy */
|
||||
query_strategy_version: MetadataQueryStrategyVersions;
|
||||
/* policy IDs and versions */
|
||||
policy_info?: HostInfo['policy_info'];
|
||||
}
|
||||
|
@ -404,21 +402,11 @@ export enum HostStatus {
|
|||
INACTIVE = 'inactive',
|
||||
}
|
||||
|
||||
export enum MetadataQueryStrategyVersions {
|
||||
VERSION_1 = 'v1',
|
||||
VERSION_2 = 'v2',
|
||||
}
|
||||
|
||||
export type PolicyInfo = Immutable<{
|
||||
revision: number;
|
||||
id: string;
|
||||
}>;
|
||||
|
||||
export interface HostMetadataInfo {
|
||||
metadata: HostMetadata;
|
||||
query_strategy_version: MetadataQueryStrategyVersions;
|
||||
}
|
||||
|
||||
export type HostInfo = Immutable<{
|
||||
metadata: HostMetadata;
|
||||
host_status: HostStatus;
|
||||
|
@ -438,8 +426,6 @@ export type HostInfo = Immutable<{
|
|||
*/
|
||||
endpoint: PolicyInfo;
|
||||
};
|
||||
/* the version of the query strategy */
|
||||
query_strategy_version: MetadataQueryStrategyVersions;
|
||||
}>;
|
||||
|
||||
// HostMetadataDetails is now just HostMetadata
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
HostPolicyResponse,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import {
|
||||
|
@ -54,7 +53,6 @@ export const endpointMetadataHttpMocks = httpHandlerMockFactory<EndpointMetadata
|
|||
const endpoint = {
|
||||
metadata: generator.generateHostMetadata(),
|
||||
host_status: HostStatus.UNHEALTHY,
|
||||
query_strategy_version: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
|
||||
generator.updateCommonInfo();
|
||||
|
@ -64,7 +62,6 @@ export const endpointMetadataHttpMocks = httpHandlerMockFactory<EndpointMetadata
|
|||
total: 10,
|
||||
request_page_size: 10,
|
||||
request_page_index: 0,
|
||||
query_strategy_version: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
@ -78,7 +75,6 @@ export const endpointMetadataHttpMocks = httpHandlerMockFactory<EndpointMetadata
|
|||
return {
|
||||
metadata: generator.generateHostMetadata(),
|
||||
host_status: HostStatus.UNHEALTHY,
|
||||
query_strategy_version: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -56,7 +56,6 @@ export const initialEndpointPageState = (): Immutable<EndpointState> => {
|
|||
agentsWithEndpointsTotalError: undefined,
|
||||
endpointsTotal: 0,
|
||||
endpointsTotalError: undefined,
|
||||
queryStrategyVersion: undefined,
|
||||
policyVersionInfo: undefined,
|
||||
hostStatus: undefined,
|
||||
isolationRequestState: createUninitialisedResourceState(),
|
||||
|
|
|
@ -28,7 +28,6 @@ import {
|
|||
nonExistingPolicies,
|
||||
patterns,
|
||||
searchBarQuery,
|
||||
isTransformEnabled,
|
||||
getIsIsolationRequestPending,
|
||||
getCurrentIsolationRequestState,
|
||||
getActivityLogData,
|
||||
|
@ -180,7 +179,7 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
|
|||
}
|
||||
|
||||
// get index pattern and fields for search bar
|
||||
if (patterns(getState()).length === 0 && isTransformEnabled(getState())) {
|
||||
if (patterns(getState()).length === 0) {
|
||||
try {
|
||||
const indexPatterns = await fetchIndexPatterns();
|
||||
if (indexPatterns !== undefined) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
HostPolicyResponse,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
PendingActionsResponse,
|
||||
} from '../../../../../common/endpoint/types';
|
||||
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
|
||||
|
@ -38,13 +37,11 @@ export const mockEndpointResultList: (options?: {
|
|||
total?: number;
|
||||
request_page_size?: number;
|
||||
request_page_index?: number;
|
||||
query_strategy_version?: MetadataQueryStrategyVersions;
|
||||
}) => HostResultList = (options = {}) => {
|
||||
const {
|
||||
total = 1,
|
||||
request_page_size: requestPageSize = 10,
|
||||
request_page_index: requestPageIndex = 0,
|
||||
query_strategy_version: queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
|
||||
} = options;
|
||||
|
||||
// Skip any that are before the page we're on
|
||||
|
@ -58,7 +55,6 @@ export const mockEndpointResultList: (options?: {
|
|||
hosts.push({
|
||||
metadata: generator.generateHostMetadata(),
|
||||
host_status: HostStatus.UNHEALTHY,
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
});
|
||||
}
|
||||
const mock: HostResultList = {
|
||||
|
@ -66,7 +62,6 @@ export const mockEndpointResultList: (options?: {
|
|||
total,
|
||||
request_page_size: requestPageSize,
|
||||
request_page_index: requestPageIndex,
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
};
|
||||
return mock;
|
||||
};
|
||||
|
@ -78,7 +73,6 @@ export const mockEndpointDetailsApiResult = (): HostInfo => {
|
|||
return {
|
||||
metadata: generator.generateHostMetadata(),
|
||||
host_status: HostStatus.UNHEALTHY,
|
||||
query_strategy_version: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -92,7 +86,6 @@ const endpointListApiPathHandlerMocks = ({
|
|||
endpointPackagePolicies = [],
|
||||
policyResponse = generator.generatePolicyResponse(),
|
||||
agentPolicy = generator.generateAgentPolicy(),
|
||||
queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
|
||||
totalAgentsUsingEndpoint = 0,
|
||||
}: {
|
||||
/** route handlers will be setup for each individual host in this array */
|
||||
|
@ -101,7 +94,6 @@ const endpointListApiPathHandlerMocks = ({
|
|||
endpointPackagePolicies?: GetPolicyListResponse['items'];
|
||||
policyResponse?: HostPolicyResponse;
|
||||
agentPolicy?: GetAgentPoliciesResponseItem;
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions;
|
||||
totalAgentsUsingEndpoint?: number;
|
||||
} = {}) => {
|
||||
const apiHandlers = {
|
||||
|
@ -119,7 +111,6 @@ const endpointListApiPathHandlerMocks = ({
|
|||
request_page_size: 10,
|
||||
request_page_index: 0,
|
||||
total: endpointsResults?.length || 0,
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -192,16 +183,11 @@ export const setEndpointListApiMockImplementation: (
|
|||
apiResponses?: Parameters<typeof endpointListApiPathHandlerMocks>[0]
|
||||
) => void = (
|
||||
mockedHttpService,
|
||||
{
|
||||
endpointsResults = mockEndpointResultList({ total: 3 }).hosts,
|
||||
queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2,
|
||||
...pathHandlersOptions
|
||||
} = {}
|
||||
{ endpointsResults = mockEndpointResultList({ total: 3 }).hosts, ...pathHandlersOptions } = {}
|
||||
) => {
|
||||
const apiHandlers = endpointListApiPathHandlerMocks({
|
||||
...pathHandlersOptions,
|
||||
endpointsResults,
|
||||
queryStrategyVersion,
|
||||
});
|
||||
|
||||
mockedHttpService.post
|
||||
|
|
|
@ -89,7 +89,6 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
|
|||
total,
|
||||
request_page_size: pageSize,
|
||||
request_page_index: pageIndex,
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
policy_info: policyVersionInfo,
|
||||
} = action.payload;
|
||||
return {
|
||||
|
@ -98,7 +97,6 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
|
|||
total,
|
||||
pageSize,
|
||||
pageIndex,
|
||||
queryStrategyVersion,
|
||||
policyVersionInfo,
|
||||
loading: false,
|
||||
error: undefined,
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
HostPolicyResponseAppliedAction,
|
||||
HostPolicyResponseConfiguration,
|
||||
HostPolicyResponseActionStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
HostStatus,
|
||||
ActivityLog,
|
||||
HostMetadata,
|
||||
|
@ -90,17 +89,11 @@ export const agentsWithEndpointsTotalError = (state: Immutable<EndpointState>) =
|
|||
state.agentsWithEndpointsTotalError;
|
||||
|
||||
export const endpointsTotalError = (state: Immutable<EndpointState>) => state.endpointsTotalError;
|
||||
const queryStrategyVersion = (state: Immutable<EndpointState>) => state.queryStrategyVersion;
|
||||
|
||||
export const endpointPackageVersion = createSelector(endpointPackageInfo, (info) =>
|
||||
isLoadedResourceState(info) ? info.data.version : undefined
|
||||
);
|
||||
|
||||
export const isTransformEnabled = createSelector(
|
||||
queryStrategyVersion,
|
||||
(version) => version !== MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the index patterns for the SearchBar to use for autosuggest
|
||||
*/
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
HostPolicyResponse,
|
||||
AppLocation,
|
||||
PolicyData,
|
||||
MetadataQueryStrategyVersions,
|
||||
HostStatus,
|
||||
HostIsolationResponse,
|
||||
EndpointPendingActions,
|
||||
|
@ -96,8 +95,6 @@ export interface EndpointState {
|
|||
endpointsTotal: number;
|
||||
/** api error for total, actual Endpoints */
|
||||
endpointsTotalError?: ServerApiError;
|
||||
/** The query strategy version that informs whether the transform for KQL is enabled or not */
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions;
|
||||
/** The policy IDs and revision number of the corresponding agent, and endpoint. May be more recent than what's running */
|
||||
policyVersionInfo?: HostInfo['policy_info'];
|
||||
/** The status of the host, which is mapped to the Elastic Agent status in Fleet */
|
||||
|
|
|
@ -23,7 +23,6 @@ import {
|
|||
HostPolicyResponseActionStatus,
|
||||
HostPolicyResponseAppliedAction,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
} from '../../../../../common/endpoint/types';
|
||||
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
|
||||
import { POLICY_STATUS_TO_TEXT } from './host_constants';
|
||||
|
@ -167,31 +166,6 @@ describe('when on the endpoint list page', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when loading data with the query_strategy_version is `v1`', () => {
|
||||
beforeEach(() => {
|
||||
reactTestingLibrary.act(() => {
|
||||
const mockedEndpointListData = mockEndpointResultList({
|
||||
total: 4,
|
||||
query_strategy_version: MetadataQueryStrategyVersions.VERSION_1,
|
||||
});
|
||||
setEndpointListApiMockImplementation(coreStart.http, {
|
||||
endpointsResults: mockedEndpointListData.hosts,
|
||||
queryStrategyVersion: mockedEndpointListData.query_strategy_version,
|
||||
});
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('should not display the KQL bar', async () => {
|
||||
const renderResult = render();
|
||||
await reactTestingLibrary.act(async () => {
|
||||
await middlewareSpy.waitForAction('serverReturnedEndpointList');
|
||||
});
|
||||
expect(renderResult.queryByTestId('adminSearchBar')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when determining when to show the enrolling message', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -268,7 +242,6 @@ describe('when on the endpoint list page', () => {
|
|||
reactTestingLibrary.act(() => {
|
||||
const mockedEndpointData = mockEndpointResultList({ total: 5 });
|
||||
const hostListData = mockedEndpointData.hosts;
|
||||
const queryStrategyVersion = mockedEndpointData.query_strategy_version;
|
||||
|
||||
firstPolicyID = hostListData[0].metadata.Endpoint.policy.applied.id;
|
||||
firstPolicyRev = hostListData[0].metadata.Endpoint.policy.applied.endpoint_policy_version;
|
||||
|
@ -329,7 +302,6 @@ describe('when on the endpoint list page', () => {
|
|||
hostListData[index].metadata.Endpoint.policy.applied,
|
||||
setup.policy
|
||||
),
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
};
|
||||
});
|
||||
hostListData.forEach((item, index) => {
|
||||
|
@ -535,8 +507,6 @@ describe('when on the endpoint list page', () => {
|
|||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
host_status,
|
||||
metadata: { agent, Endpoint, ...details },
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
query_strategy_version,
|
||||
} = mockEndpointDetailsApiResult();
|
||||
|
||||
hostDetails = {
|
||||
|
@ -555,7 +525,6 @@ describe('when on the endpoint list page', () => {
|
|||
id: '1',
|
||||
},
|
||||
},
|
||||
query_strategy_version,
|
||||
};
|
||||
|
||||
const policy = docGenerator.generatePolicyPackagePolicy();
|
||||
|
@ -1198,7 +1167,7 @@ describe('when on the endpoint list page', () => {
|
|||
let renderResult: ReturnType<AppContextTestRender['render']>;
|
||||
|
||||
const mockEndpointListApi = () => {
|
||||
const { hosts, query_strategy_version: queryStrategyVersion } = mockEndpointResultList();
|
||||
const { hosts } = mockEndpointResultList();
|
||||
hostInfo = {
|
||||
host_status: hosts[0].host_status,
|
||||
metadata: {
|
||||
|
@ -1222,7 +1191,6 @@ describe('when on the endpoint list page', () => {
|
|||
version: '7.14.0',
|
||||
},
|
||||
},
|
||||
query_strategy_version: queryStrategyVersion,
|
||||
};
|
||||
|
||||
const packagePolicy = docGenerator.generatePolicyPackagePolicy();
|
||||
|
|
|
@ -120,7 +120,6 @@ export const EndpointList = () => {
|
|||
areEndpointsEnrolling,
|
||||
agentsWithEndpointsTotalError,
|
||||
endpointsTotalError,
|
||||
isTransformEnabled,
|
||||
} = useEndpointSelector(selector);
|
||||
const { search } = useFormatUrl(SecurityPageName.administration);
|
||||
const { getAppUrl } = useAppUrl();
|
||||
|
@ -476,8 +475,8 @@ export const EndpointList = () => {
|
|||
const hasListData = listData && listData.length > 0;
|
||||
|
||||
const refreshStyle = useMemo(() => {
|
||||
return { display: endpointsExist && isTransformEnabled ? 'flex' : 'none', maxWidth: 200 };
|
||||
}, [endpointsExist, isTransformEnabled]);
|
||||
return { display: endpointsExist ? 'flex' : 'none', maxWidth: 200 };
|
||||
}, [endpointsExist]);
|
||||
|
||||
const refreshIsPaused = useMemo(() => {
|
||||
return !endpointsExist ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled;
|
||||
|
@ -492,8 +491,8 @@ export const EndpointList = () => {
|
|||
}, [endpointsTotalError, agentsWithEndpointsTotalError]);
|
||||
|
||||
const shouldShowKQLBar = useMemo(() => {
|
||||
return endpointsExist && !patternsError && isTransformEnabled;
|
||||
}, [endpointsExist, patternsError, isTransformEnabled]);
|
||||
return endpointsExist && !patternsError;
|
||||
}, [endpointsExist, patternsError]);
|
||||
|
||||
return (
|
||||
<AdministrationListPage
|
||||
|
|
|
@ -20,7 +20,6 @@ import { SecurityPluginStart } from '../../../security/server';
|
|||
import {
|
||||
AgentService,
|
||||
FleetStartContract,
|
||||
PackageService,
|
||||
AgentPolicyServiceInterface,
|
||||
PackagePolicyServiceInterface,
|
||||
} from '../../../fleet/server';
|
||||
|
@ -30,14 +29,6 @@ import {
|
|||
getPackagePolicyUpdateCallback,
|
||||
} from '../fleet_integration/fleet_integration';
|
||||
import { ManifestManager } from './services/artifacts';
|
||||
import { MetadataQueryStrategy } from './types';
|
||||
import { MetadataQueryStrategyVersions } from '../../common/endpoint/types';
|
||||
import {
|
||||
metadataQueryStrategyV1,
|
||||
metadataQueryStrategyV2,
|
||||
} from './routes/metadata/support/query_strategies';
|
||||
import { ElasticsearchAssetType } from '../../../fleet/common/types/models';
|
||||
import { metadataTransformPrefix } from '../../common/endpoint/constants';
|
||||
import { AppClientFactory } from '../client';
|
||||
import { ConfigType } from '../config';
|
||||
import { LicenseService } from '../../common/license';
|
||||
|
@ -46,45 +37,6 @@ import {
|
|||
parseExperimentalConfigValue,
|
||||
} from '../../common/experimental_features';
|
||||
|
||||
export interface MetadataService {
|
||||
queryStrategy(
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
version?: MetadataQueryStrategyVersions
|
||||
): Promise<MetadataQueryStrategy>;
|
||||
}
|
||||
|
||||
export const createMetadataService = (packageService: PackageService): MetadataService => {
|
||||
return {
|
||||
async queryStrategy(
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
version?: MetadataQueryStrategyVersions
|
||||
): Promise<MetadataQueryStrategy> {
|
||||
if (version === MetadataQueryStrategyVersions.VERSION_1) {
|
||||
return metadataQueryStrategyV1();
|
||||
}
|
||||
if (!packageService) {
|
||||
throw new Error('package service is uninitialized');
|
||||
}
|
||||
|
||||
if (version === MetadataQueryStrategyVersions.VERSION_2 || !version) {
|
||||
const assets =
|
||||
(await packageService.getInstallation({ savedObjectsClient, pkgName: 'endpoint' }))
|
||||
?.installed_es ?? [];
|
||||
const expectedTransformAssets = assets.filter(
|
||||
(ref) =>
|
||||
ref.type === ElasticsearchAssetType.transform &&
|
||||
ref.id.startsWith(metadataTransformPrefix)
|
||||
);
|
||||
if (expectedTransformAssets && expectedTransformAssets.length === 1) {
|
||||
return metadataQueryStrategyV2();
|
||||
}
|
||||
return metadataQueryStrategyV1();
|
||||
}
|
||||
return metadataQueryStrategyV1();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export type EndpointAppContextServiceStartContract = Partial<
|
||||
Pick<
|
||||
FleetStartContract,
|
||||
|
@ -114,7 +66,6 @@ export class EndpointAppContextService {
|
|||
private packagePolicyService: PackagePolicyServiceInterface | undefined;
|
||||
private agentPolicyService: AgentPolicyServiceInterface | undefined;
|
||||
private savedObjectsStart: SavedObjectsServiceStart | undefined;
|
||||
private metadataService: MetadataService | undefined;
|
||||
private config: ConfigType | undefined;
|
||||
private license: LicenseService | undefined;
|
||||
public security: SecurityPluginStart | undefined;
|
||||
|
@ -128,7 +79,6 @@ export class EndpointAppContextService {
|
|||
this.agentPolicyService = dependencies.agentPolicyService;
|
||||
this.manifestManager = dependencies.manifestManager;
|
||||
this.savedObjectsStart = dependencies.savedObjectsStart;
|
||||
this.metadataService = createMetadataService(dependencies.packageService!);
|
||||
this.config = dependencies.config;
|
||||
this.license = dependencies.licenseService;
|
||||
this.security = dependencies.security;
|
||||
|
@ -176,10 +126,6 @@ export class EndpointAppContextService {
|
|||
return this.agentPolicyService;
|
||||
}
|
||||
|
||||
public getMetadataService(): MetadataService | undefined {
|
||||
return this.metadataService;
|
||||
}
|
||||
|
||||
public getManifestManager(): ManifestManager | undefined {
|
||||
return this.manifestManager;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ export const isolationRequestHandler = function (
|
|||
|
||||
// fetch the Agent IDs to send the commands to
|
||||
const endpointIDs = [...new Set(req.body.endpoint_ids)]; // dedupe
|
||||
const endpointData = await getMetadataForEndpoints(endpointIDs, context, endpointContext);
|
||||
const endpointData = await getMetadataForEndpoints(endpointIDs, context);
|
||||
|
||||
const casesClient = await endpointContext.service.getCasesClient(req);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { HostStatus, MetadataQueryStrategyVersions } from '../../../../common/endpoint/types';
|
||||
import { HostStatus } from '../../../../common/endpoint/types';
|
||||
import { createMockMetadataRequestContext } from '../../mocks';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { enrichHostMetadata, MetadataRequestContext } from './handlers';
|
||||
|
@ -18,30 +18,6 @@ describe('test document enrichment', () => {
|
|||
metaReqCtx = createMockMetadataRequestContext();
|
||||
});
|
||||
|
||||
// verify query version passed through
|
||||
describe('metadata query strategy enrichment', () => {
|
||||
it('should match v1 strategy when directed', async () => {
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
expect(enrichedHostList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
});
|
||||
it('should match v2 strategy when directed', async () => {
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
expect(enrichedHostList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('host status enrichment', () => {
|
||||
let statusFn: jest.Mock;
|
||||
|
||||
|
@ -57,77 +33,49 @@ describe('test document enrichment', () => {
|
|||
it('should return host healthy for online agent', async () => {
|
||||
statusFn.mockImplementation(() => 'online');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.HEALTHY);
|
||||
});
|
||||
|
||||
it('should return host offline for offline agent', async () => {
|
||||
statusFn.mockImplementation(() => 'offline');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.OFFLINE);
|
||||
});
|
||||
|
||||
it('should return host updating for unenrolling agent', async () => {
|
||||
statusFn.mockImplementation(() => 'unenrolling');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.UPDATING);
|
||||
});
|
||||
|
||||
it('should return host unhealthy for degraded agent', async () => {
|
||||
statusFn.mockImplementation(() => 'degraded');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
|
||||
it('should return host unhealthy for erroring agent', async () => {
|
||||
statusFn.mockImplementation(() => 'error');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
|
||||
it('should return host unhealthy for warning agent', async () => {
|
||||
statusFn.mockImplementation(() => 'warning');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
|
||||
it('should return host unhealthy for invalid agent', async () => {
|
||||
statusFn.mockImplementation(() => 'asliduasofb');
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
});
|
||||
|
@ -164,11 +112,7 @@ describe('test document enrichment', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.policy_info).toBeDefined();
|
||||
expect(enrichedHostList.policy_info!.agent.applied.id).toEqual(policyID);
|
||||
expect(enrichedHostList.policy_info!.agent.applied.revision).toEqual(policyRev);
|
||||
|
@ -184,11 +128,7 @@ describe('test document enrichment', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.policy_info).toBeDefined();
|
||||
expect(enrichedHostList.policy_info!.agent.configured.id).toEqual(policyID);
|
||||
expect(enrichedHostList.policy_info!.agent.configured.revision).toEqual(policyRev);
|
||||
|
@ -209,11 +149,7 @@ describe('test document enrichment', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const enrichedHostList = await enrichHostMetadata(
|
||||
docGen.generateHostMetadata(),
|
||||
metaReqCtx,
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx);
|
||||
expect(enrichedHostList.policy_info).toBeDefined();
|
||||
expect(enrichedHostList.policy_info!.endpoint.id).toEqual(policyID);
|
||||
expect(enrichedHostList.policy_info!.endpoint.revision).toEqual(policyRev);
|
||||
|
|
|
@ -17,10 +17,8 @@ import {
|
|||
import {
|
||||
HostInfo,
|
||||
HostMetadata,
|
||||
HostMetadataInfo,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import type { SecuritySolutionRequestHandlerContext } from '../../../types';
|
||||
|
||||
|
@ -33,6 +31,10 @@ import { findAllUnenrolledAgentIds } from './support/unenroll';
|
|||
import { findAgentIDsByStatus } from './support/agent_status';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import { fleetAgentStatusToEndpointHostStatus } from '../../utils';
|
||||
import {
|
||||
queryResponseToHostListResult,
|
||||
queryResponseToHostResult,
|
||||
} from './support/query_strategies';
|
||||
|
||||
export interface MetadataRequestContext {
|
||||
esClient?: IScopedClusterClient;
|
||||
|
@ -58,8 +60,7 @@ export const getLogger = (endpointAppContext: EndpointAppContext): Logger => {
|
|||
|
||||
export const getMetadataListRequestHandler = function (
|
||||
endpointAppContext: EndpointAppContext,
|
||||
logger: Logger,
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions
|
||||
logger: Logger
|
||||
): RequestHandler<
|
||||
unknown,
|
||||
unknown,
|
||||
|
@ -96,24 +97,15 @@ export const getMetadataListRequestHandler = function (
|
|||
)
|
||||
: undefined;
|
||||
|
||||
const queryStrategy = await endpointAppContext.service
|
||||
?.getMetadataService()
|
||||
?.queryStrategy(context.core.savedObjects.client, queryStrategyVersion);
|
||||
|
||||
const queryParams = await kibanaRequestToMetadataListESQuery(
|
||||
request,
|
||||
endpointAppContext,
|
||||
queryStrategy!,
|
||||
{
|
||||
unenrolledAgentIds: unenrolledAgentIds.concat(IGNORED_ELASTIC_AGENT_IDS),
|
||||
statusAgentIDs: statusIDs,
|
||||
}
|
||||
);
|
||||
const queryParams = await kibanaRequestToMetadataListESQuery(request, endpointAppContext, {
|
||||
unenrolledAgentIds: unenrolledAgentIds.concat(IGNORED_ELASTIC_AGENT_IDS),
|
||||
statusAgentIDs: statusIDs,
|
||||
});
|
||||
|
||||
const result = await context.core.elasticsearch.client.asCurrentUser.search<HostMetadata>(
|
||||
queryParams
|
||||
);
|
||||
const hostListQueryResult = queryStrategy!.queryResponseToHostListResult(result.body);
|
||||
const hostListQueryResult = queryResponseToHostListResult(result.body);
|
||||
return response.ok({
|
||||
body: await mapToHostResultList(queryParams, hostListQueryResult, metadataRequestContext),
|
||||
});
|
||||
|
@ -122,8 +114,7 @@ export const getMetadataListRequestHandler = function (
|
|||
|
||||
export const getMetadataRequestHandler = function (
|
||||
endpointAppContext: EndpointAppContext,
|
||||
logger: Logger,
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions
|
||||
logger: Logger
|
||||
): RequestHandler<
|
||||
TypeOf<typeof GetMetadataRequestSchema.params>,
|
||||
unknown,
|
||||
|
@ -145,11 +136,7 @@ export const getMetadataRequestHandler = function (
|
|||
};
|
||||
|
||||
try {
|
||||
const doc = await getHostData(
|
||||
metadataRequestContext,
|
||||
request?.params?.id,
|
||||
queryStrategyVersion
|
||||
);
|
||||
const doc = await getHostData(metadataRequestContext, request?.params?.id);
|
||||
if (doc) {
|
||||
return response.ok({ body: doc });
|
||||
}
|
||||
|
@ -169,9 +156,8 @@ export const getMetadataRequestHandler = function (
|
|||
|
||||
export async function getHostMetaData(
|
||||
metadataRequestContext: MetadataRequestContext,
|
||||
id: string,
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions
|
||||
): Promise<HostMetadataInfo | undefined> {
|
||||
id: string
|
||||
): Promise<HostMetadata | undefined> {
|
||||
if (
|
||||
!metadataRequestContext.esClient &&
|
||||
!metadataRequestContext.requestHandlerContext?.core.elasticsearch.client
|
||||
|
@ -190,32 +176,23 @@ export async function getHostMetaData(
|
|||
metadataRequestContext.requestHandlerContext?.core.elasticsearch
|
||||
.client) as IScopedClusterClient;
|
||||
|
||||
const esSavedObjectClient =
|
||||
metadataRequestContext?.savedObjectsClient ??
|
||||
(metadataRequestContext.requestHandlerContext?.core.savedObjects
|
||||
.client as SavedObjectsClientContract);
|
||||
|
||||
const queryStrategy = await metadataRequestContext.endpointAppContextService
|
||||
?.getMetadataService()
|
||||
?.queryStrategy(esSavedObjectClient, queryStrategyVersion);
|
||||
const query = getESQueryHostMetadataByID(id, queryStrategy!);
|
||||
const query = getESQueryHostMetadataByID(id);
|
||||
|
||||
const response = await esClient.asCurrentUser.search<HostMetadata>(query);
|
||||
|
||||
const hostResult = queryStrategy!.queryResponseToHostResult(response.body);
|
||||
const hostResult = queryResponseToHostResult(response.body);
|
||||
|
||||
const hostMetadata = hostResult.result;
|
||||
if (!hostMetadata) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { metadata: hostMetadata, query_strategy_version: hostResult.queryStrategyVersion };
|
||||
return hostMetadata;
|
||||
}
|
||||
|
||||
export async function getHostData(
|
||||
metadataRequestContext: MetadataRequestContext,
|
||||
id: string,
|
||||
queryStrategyVersion?: MetadataQueryStrategyVersions
|
||||
id: string
|
||||
): Promise<HostInfo | undefined> {
|
||||
if (!metadataRequestContext.savedObjectsClient) {
|
||||
throw Boom.badRequest('savedObjectsClient not found');
|
||||
|
@ -228,25 +205,21 @@ export async function getHostData(
|
|||
throw Boom.badRequest('esClient not found');
|
||||
}
|
||||
|
||||
const hostResult = await getHostMetaData(metadataRequestContext, id, queryStrategyVersion);
|
||||
const hostMetadata = await getHostMetaData(metadataRequestContext, id);
|
||||
|
||||
if (!hostResult) {
|
||||
if (!hostMetadata) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const agent = await findAgent(metadataRequestContext, hostResult.metadata);
|
||||
const agent = await findAgent(metadataRequestContext, hostMetadata);
|
||||
|
||||
if (agent && !agent.active) {
|
||||
throw Boom.badRequest('the requested endpoint is unenrolled');
|
||||
}
|
||||
|
||||
const metadata = await enrichHostMetadata(
|
||||
hostResult.metadata,
|
||||
metadataRequestContext,
|
||||
hostResult.query_strategy_version
|
||||
);
|
||||
const metadata = await enrichHostMetadata(hostMetadata, metadataRequestContext);
|
||||
|
||||
return { ...metadata, query_strategy_version: hostResult.query_strategy_version };
|
||||
return metadata;
|
||||
}
|
||||
|
||||
async function findAgent(
|
||||
|
@ -293,15 +266,10 @@ export async function mapToHostResultList(
|
|||
request_page_index: queryParams.from,
|
||||
hosts: await Promise.all(
|
||||
hostListQueryResult.resultList.map(async (entry) =>
|
||||
enrichHostMetadata(
|
||||
entry,
|
||||
metadataRequestContext,
|
||||
hostListQueryResult.queryStrategyVersion
|
||||
)
|
||||
enrichHostMetadata(entry, metadataRequestContext)
|
||||
)
|
||||
),
|
||||
total: totalNumberOfHosts,
|
||||
query_strategy_version: hostListQueryResult.queryStrategyVersion,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
|
@ -309,15 +277,13 @@ export async function mapToHostResultList(
|
|||
request_page_index: queryParams.from,
|
||||
total: totalNumberOfHosts,
|
||||
hosts: [],
|
||||
query_strategy_version: hostListQueryResult.queryStrategyVersion,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function enrichHostMetadata(
|
||||
hostMetadata: HostMetadata,
|
||||
metadataRequestContext: MetadataRequestContext,
|
||||
metadataQueryStrategyVersion: MetadataQueryStrategyVersions
|
||||
metadataRequestContext: MetadataRequestContext
|
||||
): Promise<HostInfo> {
|
||||
let hostStatus = HostStatus.UNHEALTHY;
|
||||
let elasticAgentId = hostMetadata?.elastic?.agent?.id;
|
||||
|
@ -413,6 +379,5 @@ export async function enrichHostMetadata(
|
|||
metadata: hostMetadata,
|
||||
host_status: hostStatus,
|
||||
policy_info: policyInfo,
|
||||
query_strategy_version: metadataQueryStrategyVersion,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,19 +7,15 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import { HostStatus, MetadataQueryStrategyVersions } from '../../../../common/endpoint/types';
|
||||
import { HostStatus } from '../../../../common/endpoint/types';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
import { getLogger, getMetadataListRequestHandler, getMetadataRequestHandler } from './handlers';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../types';
|
||||
import {
|
||||
BASE_ENDPOINT_ROUTE,
|
||||
HOST_METADATA_GET_ROUTE,
|
||||
HOST_METADATA_LIST_ROUTE,
|
||||
} from '../../../../common/endpoint/constants';
|
||||
|
||||
export const METADATA_REQUEST_V1_ROUTE = `${BASE_ENDPOINT_ROUTE}/v1/metadata`;
|
||||
export const GET_METADATA_REQUEST_V1_ROUTE = `${METADATA_REQUEST_V1_ROUTE}/{id}`;
|
||||
|
||||
/* Filters that can be applied to the endpoint fetch route */
|
||||
export const endpointFilters = schema.object({
|
||||
kql: schema.nullable(schema.string()),
|
||||
|
@ -69,18 +65,6 @@ export function registerEndpointRoutes(
|
|||
endpointAppContext: EndpointAppContext
|
||||
) {
|
||||
const logger = getLogger(endpointAppContext);
|
||||
router.post(
|
||||
{
|
||||
path: `${METADATA_REQUEST_V1_ROUTE}`,
|
||||
validate: GetMetadataListRequestSchema,
|
||||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
getMetadataListRequestHandler(
|
||||
endpointAppContext,
|
||||
logger,
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
)
|
||||
);
|
||||
|
||||
router.post(
|
||||
{
|
||||
|
@ -91,15 +75,6 @@ export function registerEndpointRoutes(
|
|||
getMetadataListRequestHandler(endpointAppContext, logger)
|
||||
);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: `${GET_METADATA_REQUEST_V1_ROUTE}`,
|
||||
validate: GetMetadataRequestSchema,
|
||||
options: { authRequired: true, tags: ['access:securitySolution'] },
|
||||
},
|
||||
getMetadataRequestHandler(endpointAppContext, logger, MetadataQueryStrategyVersions.VERSION_1)
|
||||
);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: `${HOST_METADATA_GET_ROUTE}`,
|
||||
|
|
|
@ -19,12 +19,7 @@ import {
|
|||
loggingSystemMock,
|
||||
savedObjectsClientMock,
|
||||
} from '../../../../../../../src/core/server/mocks';
|
||||
import {
|
||||
HostInfo,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { HostInfo, HostResultList, HostStatus } from '../../../../common/endpoint/types';
|
||||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { registerEndpointRoutes } from './index';
|
||||
import {
|
||||
|
@ -39,7 +34,7 @@ import {
|
|||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { Agent, ElasticsearchAssetType } from '../../../../../fleet/common/types/models';
|
||||
import { createV1SearchResponse, createV2SearchResponse } from './support/test_support';
|
||||
import { createV2SearchResponse } from './support/test_support';
|
||||
import { PackageService } from '../../../../../fleet/server/services';
|
||||
import {
|
||||
HOST_METADATA_LIST_ROUTE,
|
||||
|
@ -98,94 +93,6 @@ describe('test endpoint route', () => {
|
|||
);
|
||||
});
|
||||
|
||||
describe('with no transform package', () => {
|
||||
beforeEach(() => {
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
mockPackageService = createMockPackageService();
|
||||
mockPackageService.getInstallation.mockReturnValue(Promise.resolve(undefined));
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: endpointAppContextService,
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => endpointAppContextService.stop());
|
||||
|
||||
it('test find the latest of all endpoints', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({});
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${HOST_METADATA_LIST_ROUTE}`)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
|
||||
expect(endpointResultList.hosts.length).toEqual(1);
|
||||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(0);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a single endpoint with status healthy', async () => {
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('online');
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(({
|
||||
active: true,
|
||||
} as unknown) as Agent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${HOST_METADATA_LIST_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
|
||||
expect(result).toHaveProperty('metadata.Endpoint');
|
||||
expect(result.host_status).toEqual(HostStatus.HEALTHY);
|
||||
expect(result.query_strategy_version).toEqual(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with new transform package', () => {
|
||||
beforeEach(() => {
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
|
@ -254,9 +161,6 @@ describe('test endpoint route', () => {
|
|||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(0);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
});
|
||||
|
||||
it('test find the latest of all endpoints with paging properties', async () => {
|
||||
|
@ -311,9 +215,6 @@ describe('test endpoint route', () => {
|
|||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(10);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
});
|
||||
|
||||
it('test find the latest of all endpoints with paging and filters properties', async () => {
|
||||
|
@ -405,9 +306,6 @@ describe('test endpoint route', () => {
|
|||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(10);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_2
|
||||
);
|
||||
});
|
||||
|
||||
describe('Endpoint Details route', () => {
|
||||
|
@ -475,7 +373,6 @@ describe('test endpoint route', () => {
|
|||
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
|
||||
expect(result).toHaveProperty('metadata.Endpoint');
|
||||
expect(result.host_status).toEqual(HostStatus.HEALTHY);
|
||||
expect(result.query_strategy_version).toEqual(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('should return a single endpoint with status unhealthy when AgentService throw 404', async () => {
|
||||
|
|
|
@ -1,456 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
KibanaResponseFactory,
|
||||
RequestHandler,
|
||||
RouteConfig,
|
||||
SavedObjectsClientContract,
|
||||
SavedObjectsErrorHelpers,
|
||||
} from '../../../../../../../src/core/server';
|
||||
import {
|
||||
ClusterClientMock,
|
||||
ScopedClusterClientMock,
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
} from '../../../../../../../src/core/server/elasticsearch/client/mocks';
|
||||
import {
|
||||
elasticsearchServiceMock,
|
||||
httpServerMock,
|
||||
httpServiceMock,
|
||||
loggingSystemMock,
|
||||
savedObjectsClientMock,
|
||||
} from '../../../../../../../src/core/server/mocks';
|
||||
import {
|
||||
HostInfo,
|
||||
HostResultList,
|
||||
HostStatus,
|
||||
MetadataQueryStrategyVersions,
|
||||
} from '../../../../common/endpoint/types';
|
||||
import { registerEndpointRoutes, METADATA_REQUEST_V1_ROUTE } from './index';
|
||||
import {
|
||||
createMockEndpointAppContextServiceStartContract,
|
||||
createMockPackageService,
|
||||
createRouteHandlerContext,
|
||||
} from '../../mocks';
|
||||
import {
|
||||
EndpointAppContextService,
|
||||
EndpointAppContextServiceStartContract,
|
||||
} from '../../endpoint_app_context_services';
|
||||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { Agent } from '../../../../../fleet/common/types/models';
|
||||
import { createV1SearchResponse } from './support/test_support';
|
||||
import { PackageService } from '../../../../../fleet/server/services';
|
||||
import type { SecuritySolutionPluginRouter } from '../../../types';
|
||||
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
|
||||
|
||||
describe('test endpoint route v1', () => {
|
||||
let routerMock: jest.Mocked<SecuritySolutionPluginRouter>;
|
||||
let mockResponse: jest.Mocked<KibanaResponseFactory>;
|
||||
let mockClusterClient: ClusterClientMock;
|
||||
let mockScopedClient: ScopedClusterClientMock;
|
||||
let mockSavedObjectClient: jest.Mocked<SavedObjectsClientContract>;
|
||||
let mockPackageService: jest.Mocked<PackageService>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let routeHandler: RequestHandler<any, any, any, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let routeConfig: RouteConfig<any, any, any, any>;
|
||||
// tests assume that fleet is enabled, and thus agentService is available
|
||||
let mockAgentService: Required<
|
||||
ReturnType<typeof createMockEndpointAppContextServiceStartContract>
|
||||
>['agentService'];
|
||||
let endpointAppContextService: EndpointAppContextService;
|
||||
let startContract: EndpointAppContextServiceStartContract;
|
||||
const noUnenrolledAgent = {
|
||||
agents: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 1,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockClusterClient = elasticsearchServiceMock.createClusterClient();
|
||||
mockScopedClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
mockSavedObjectClient = savedObjectsClientMock.create();
|
||||
mockClusterClient.asScoped.mockReturnValue(mockScopedClient);
|
||||
routerMock = httpServiceMock.createRouter();
|
||||
mockResponse = httpServerMock.createResponseFactory();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
mockPackageService = createMockPackageService();
|
||||
mockPackageService.getInstallation.mockReturnValue(Promise.resolve(undefined));
|
||||
startContract = createMockEndpointAppContextServiceStartContract();
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
|
||||
(startContract.packagePolicyService as jest.Mocked<PackagePolicyServiceInterface>).list.mockImplementation(
|
||||
() => {
|
||||
return Promise.resolve({
|
||||
items: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: endpointAppContextService,
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => endpointAppContextService.stop());
|
||||
|
||||
it('test find the latest of all endpoints', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({});
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
|
||||
expect(endpointResultList.hosts.length).toEqual(1);
|
||||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(0);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
});
|
||||
|
||||
it('test find the latest of all endpoints with paging properties', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
page_index: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
body: createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata()),
|
||||
})
|
||||
);
|
||||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool
|
||||
.must_not
|
||||
).toContainEqual({
|
||||
terms: {
|
||||
'elastic.agent.id': [
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
|
||||
expect(endpointResultList.hosts.length).toEqual(1);
|
||||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(10);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
});
|
||||
|
||||
it('test find the latest of all endpoints with paging and filters properties', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
page_index: 1,
|
||||
},
|
||||
],
|
||||
|
||||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
body: createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata()),
|
||||
})
|
||||
);
|
||||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toBeCalled();
|
||||
// needs to have the KQL filter passed through
|
||||
expect(
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool.must
|
||||
).toContainEqual({
|
||||
bool: {
|
||||
must_not: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'host.ip': '10.140.73.246',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// and unenrolled should be filtered out.
|
||||
expect(
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mock.calls[0][0]?.body?.query.bool.must
|
||||
).toContainEqual({
|
||||
bool: {
|
||||
must_not: [
|
||||
{
|
||||
terms: {
|
||||
'elastic.agent.id': [
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
terms: {
|
||||
// we actually don't care about HostDetails in v1 queries, but
|
||||
// harder to set up the expectation to ignore its inclusion succinctly
|
||||
'HostDetails.elastic.agent.id': [
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
'11111111-1111-1111-1111-111111111111',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'] });
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList;
|
||||
expect(endpointResultList.hosts.length).toEqual(1);
|
||||
expect(endpointResultList.total).toEqual(1);
|
||||
expect(endpointResultList.request_page_index).toEqual(10);
|
||||
expect(endpointResultList.request_page_size).toEqual(10);
|
||||
expect(endpointResultList.query_strategy_version).toEqual(
|
||||
MetadataQueryStrategyVersions.VERSION_1
|
||||
);
|
||||
});
|
||||
|
||||
describe('Endpoint Details route', () => {
|
||||
it('should return 404 on no results', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ params: { id: 'BADID' } });
|
||||
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: createV1SearchResponse() })
|
||||
);
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(({
|
||||
active: true,
|
||||
} as unknown) as Agent);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.notFound).toBeCalled();
|
||||
const message = mockResponse.notFound.mock.calls[0][0]?.body;
|
||||
expect(message).toEqual('Endpoint Not Found');
|
||||
});
|
||||
|
||||
it('should return a single endpoint with status healthy', async () => {
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('online');
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(({
|
||||
active: true,
|
||||
} as unknown) as Agent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
|
||||
expect(result).toHaveProperty('metadata.Endpoint');
|
||||
expect(result.host_status).toEqual(HostStatus.HEALTHY);
|
||||
});
|
||||
|
||||
it('should return a single endpoint with status unhealthy when AgentService throw 404', async () => {
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockImplementation(() => {
|
||||
SavedObjectsErrorHelpers.createGenericNotFoundError();
|
||||
});
|
||||
|
||||
mockAgentService.getAgent = jest.fn().mockImplementation(() => {
|
||||
SavedObjectsErrorHelpers.createGenericNotFoundError();
|
||||
});
|
||||
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
|
||||
expect(result.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
|
||||
it('should return a single endpoint with status unhealthy when status is not offline, online or enrolling', async () => {
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('warning');
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(({
|
||||
active: true,
|
||||
} as unknown) as Agent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(routeConfig.options).toEqual({
|
||||
authRequired: true,
|
||||
tags: ['access:securitySolution'],
|
||||
});
|
||||
expect(mockResponse.ok).toBeCalled();
|
||||
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
|
||||
expect(result.host_status).toEqual(HostStatus.UNHEALTHY);
|
||||
});
|
||||
|
||||
it('should throw error when endpoint agent is not active', async () => {
|
||||
const response = createV1SearchResponse(new EndpointDocGenerator().generateHostMetadata());
|
||||
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(({
|
||||
active: false,
|
||||
} as unknown) as Agent);
|
||||
|
||||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
)!;
|
||||
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
mockResponse
|
||||
);
|
||||
|
||||
expect(mockScopedClient.asCurrentUser.search).toHaveBeenCalledTimes(1);
|
||||
expect(mockResponse.customError).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,38 +11,29 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
|||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { metadataQueryStrategyV2 } from './support/query_strategies';
|
||||
import { get } from 'lodash';
|
||||
|
||||
describe('query builder', () => {
|
||||
describe('MetadataListESQuery', () => {
|
||||
it('queries the correct index', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV2()
|
||||
);
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
expect(query.index).toEqual(metadataCurrentIndexPattern);
|
||||
});
|
||||
|
||||
it('sorts using *event.created', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({ body: {} });
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV2()
|
||||
);
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
expect(query.body.sort).toContainEqual({
|
||||
'event.created': {
|
||||
order: 'desc',
|
||||
|
@ -61,16 +52,12 @@ describe('query builder', () => {
|
|||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV2()
|
||||
);
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
expect(query.body.query).toHaveProperty('match_all');
|
||||
});
|
||||
|
||||
|
@ -87,7 +74,6 @@ describe('query builder', () => {
|
|||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV2(),
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
|
@ -111,16 +97,12 @@ describe('query builder', () => {
|
|||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV2()
|
||||
);
|
||||
const query = await kibanaRequestToMetadataListESQuery(mockRequest, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
});
|
||||
|
||||
expect(query.body.query.bool.must).toContainEqual({
|
||||
bool: {
|
||||
|
@ -160,7 +142,6 @@ describe('query builder', () => {
|
|||
createMockConfig().enableExperimental
|
||||
),
|
||||
},
|
||||
metadataQueryStrategyV2(),
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
|
@ -197,13 +178,13 @@ describe('query builder', () => {
|
|||
|
||||
describe('MetadataGetQuery', () => {
|
||||
it('searches the correct index', () => {
|
||||
const query = getESQueryHostMetadataByID('nonsense-id', metadataQueryStrategyV2());
|
||||
const query = getESQueryHostMetadataByID('nonsense-id');
|
||||
expect(query.index).toEqual(metadataCurrentIndexPattern);
|
||||
});
|
||||
|
||||
it('searches for the correct ID', () => {
|
||||
const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
|
||||
const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV2());
|
||||
const query = getESQueryHostMetadataByID(mockID);
|
||||
|
||||
expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
|
||||
term: { 'agent.id': mockID },
|
||||
|
@ -212,7 +193,7 @@ describe('query builder', () => {
|
|||
|
||||
it('supports HostDetails in schema for backwards compat', () => {
|
||||
const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
|
||||
const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV2());
|
||||
const query = getESQueryHostMetadataByID(mockID);
|
||||
|
||||
expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
|
||||
term: { 'HostDetails.agent.id': mockID },
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { metadataCurrentIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { KibanaRequest } from '../../../../../../../src/core/server';
|
||||
import { esKuery } from '../../../../../../../src/plugins/data/server';
|
||||
import { EndpointAppContext, MetadataQueryStrategy } from '../../types';
|
||||
import { EndpointAppContext } from '../../types';
|
||||
|
||||
export interface QueryBuilderOptions {
|
||||
unenrolledAgentIds?: string[];
|
||||
|
@ -39,7 +40,6 @@ export async function kibanaRequestToMetadataListESQuery(
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: KibanaRequest<any, any, any>,
|
||||
endpointAppContext: EndpointAppContext,
|
||||
metadataQueryStrategy: MetadataQueryStrategy,
|
||||
queryBuilderOptions?: QueryBuilderOptions
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): Promise<Record<string, any>> {
|
||||
|
@ -49,16 +49,15 @@ export async function kibanaRequestToMetadataListESQuery(
|
|||
body: {
|
||||
query: buildQueryBody(
|
||||
request,
|
||||
metadataQueryStrategy,
|
||||
queryBuilderOptions?.unenrolledAgentIds!,
|
||||
queryBuilderOptions?.statusAgentIDs!
|
||||
),
|
||||
...metadataQueryStrategy.extraBodyProperties,
|
||||
track_total_hits: true,
|
||||
sort: MetadataSortMethod,
|
||||
},
|
||||
from: pagingProperties.pageIndex * pagingProperties.pageSize,
|
||||
size: pagingProperties.pageSize,
|
||||
index: metadataQueryStrategy.index,
|
||||
index: metadataCurrentIndexPattern,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -86,7 +85,6 @@ async function getPagingProperties(
|
|||
function buildQueryBody(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: KibanaRequest<any, any, any>,
|
||||
metadataQueryStrategy: MetadataQueryStrategy,
|
||||
unerolledAgentIds: string[] | undefined,
|
||||
statusAgentIDs: string[] | undefined
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -144,10 +142,7 @@ function buildQueryBody(
|
|||
};
|
||||
}
|
||||
|
||||
export function getESQueryHostMetadataByID(
|
||||
agentID: string,
|
||||
metadataQueryStrategy: MetadataQueryStrategy
|
||||
): estypes.SearchRequest {
|
||||
export function getESQueryHostMetadataByID(agentID: string): estypes.SearchRequest {
|
||||
return {
|
||||
body: {
|
||||
query: {
|
||||
|
@ -167,14 +162,11 @@ export function getESQueryHostMetadataByID(
|
|||
sort: MetadataSortMethod,
|
||||
size: 1,
|
||||
},
|
||||
index: metadataQueryStrategy.index,
|
||||
index: metadataCurrentIndexPattern,
|
||||
};
|
||||
}
|
||||
|
||||
export function getESQueryHostMetadataByIDs(
|
||||
agentIDs: string[],
|
||||
metadataQueryStrategy: MetadataQueryStrategy
|
||||
) {
|
||||
export function getESQueryHostMetadataByIDs(agentIDs: string[]) {
|
||||
return {
|
||||
body: {
|
||||
query: {
|
||||
|
@ -193,6 +185,6 @@ export function getESQueryHostMetadataByIDs(
|
|||
},
|
||||
sort: MetadataSortMethod,
|
||||
},
|
||||
index: metadataQueryStrategy.index,
|
||||
index: metadataCurrentIndexPattern,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { httpServerMock, loggingSystemMock } from '../../../../../../../src/core/server/mocks';
|
||||
import { kibanaRequestToMetadataListESQuery, getESQueryHostMetadataByID } from './query_builders';
|
||||
import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
||||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { metadataIndexPattern } from '../../../../common/endpoint/constants';
|
||||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { metadataQueryStrategyV1 } from './support/query_strategies';
|
||||
import { get } from 'lodash';
|
||||
|
||||
describe('query builder v1', () => {
|
||||
describe('MetadataListESQuery', () => {
|
||||
it('test default query params for all endpoints metadata when no params or body is provided', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV1()
|
||||
);
|
||||
|
||||
expect(query.body.query).toHaveProperty('match_all'); // no filtering
|
||||
expect(query.body.collapse).toEqual({
|
||||
field: 'agent.id',
|
||||
inner_hits: {
|
||||
name: 'most_recent',
|
||||
size: 1,
|
||||
sort: [{ 'event.created': 'desc' }],
|
||||
},
|
||||
});
|
||||
expect(query.body.aggs).toEqual({
|
||||
total: {
|
||||
cardinality: {
|
||||
field: 'agent.id',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(query.index).toEqual(metadataIndexPattern);
|
||||
});
|
||||
|
||||
it(
|
||||
'test default query params for all endpoints metadata when no params or body is provided ' +
|
||||
'with unenrolled host ids excluded',
|
||||
async () => {
|
||||
const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(
|
||||
createMockConfig().enableExperimental
|
||||
),
|
||||
},
|
||||
metadataQueryStrategyV1(),
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
);
|
||||
expect(Object.keys(query.body.query.bool)).toEqual(['must_not']); // only filtering out unenrolled
|
||||
expect(query.body.query.bool.must_not).toContainEqual({
|
||||
terms: { 'elastic.agent.id': [unenrolledElasticAgentId] },
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('test query builder with kql filter', () => {
|
||||
it('test default query params for all endpoints metadata when body filter is provided', async () => {
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(createMockConfig().enableExperimental),
|
||||
},
|
||||
metadataQueryStrategyV1()
|
||||
);
|
||||
expect(query.body.query.bool.must).toHaveLength(1); // should not be any other filtering happening
|
||||
expect(query.body.query.bool.must).toContainEqual({
|
||||
bool: {
|
||||
must_not: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'host.ip': '10.140.73.246',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(
|
||||
'test default query params for all endpoints endpoint metadata excluding unerolled endpoint ' +
|
||||
'and when body filter is provided',
|
||||
async () => {
|
||||
const unenrolledElasticAgentId = '1fdca33f-799f-49f4-939c-ea4383c77672';
|
||||
const mockRequest = httpServerMock.createKibanaRequest({
|
||||
body: {
|
||||
filters: { kql: 'not host.ip:10.140.73.246' },
|
||||
},
|
||||
});
|
||||
const query = await kibanaRequestToMetadataListESQuery(
|
||||
mockRequest,
|
||||
{
|
||||
logFactory: loggingSystemMock.create(),
|
||||
service: new EndpointAppContextService(),
|
||||
config: () => Promise.resolve(createMockConfig()),
|
||||
experimentalFeatures: parseExperimentalConfigValue(
|
||||
createMockConfig().enableExperimental
|
||||
),
|
||||
},
|
||||
metadataQueryStrategyV1(),
|
||||
{
|
||||
unenrolledAgentIds: [unenrolledElasticAgentId],
|
||||
}
|
||||
);
|
||||
|
||||
expect(query.body.query.bool.must.length).toBeGreaterThan(1);
|
||||
// unenrollment filter should be there
|
||||
expect(query.body.query.bool.must).toContainEqual({
|
||||
bool: {
|
||||
must_not: [
|
||||
{ terms: { 'elastic.agent.id': [unenrolledElasticAgentId] } },
|
||||
// below is not actually necessary behavior for v1, but hard to structure the test to ignore it
|
||||
{ terms: { 'HostDetails.elastic.agent.id': [unenrolledElasticAgentId] } },
|
||||
],
|
||||
},
|
||||
});
|
||||
// and KQL should also be there
|
||||
expect(query.body.query.bool.must).toContainEqual({
|
||||
bool: {
|
||||
must_not: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'host.ip': '10.140.73.246',
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('MetadataGetQuery', () => {
|
||||
it('searches for the correct ID', () => {
|
||||
const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899';
|
||||
const query = getESQueryHostMetadataByID(mockID, metadataQueryStrategyV1());
|
||||
|
||||
expect(get(query, 'body.query.bool.filter.0.bool.should')).toContainEqual({
|
||||
term: { 'agent.id': mockID },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -6,102 +6,39 @@
|
|||
*/
|
||||
|
||||
import { SearchResponse } from '@elastic/elasticsearch/api/types';
|
||||
import {
|
||||
metadataCurrentIndexPattern,
|
||||
metadataIndexPattern,
|
||||
} from '../../../../../common/endpoint/constants';
|
||||
import { HostMetadata, MetadataQueryStrategyVersions } from '../../../../../common/endpoint/types';
|
||||
import { HostListQueryResult, HostQueryResult, MetadataQueryStrategy } from '../../../types';
|
||||
|
||||
export function metadataQueryStrategyV1(): MetadataQueryStrategy {
|
||||
return {
|
||||
index: metadataIndexPattern,
|
||||
extraBodyProperties: {
|
||||
collapse: {
|
||||
field: 'agent.id',
|
||||
inner_hits: {
|
||||
name: 'most_recent',
|
||||
size: 1,
|
||||
sort: [{ 'event.created': 'desc' }],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
total: {
|
||||
cardinality: {
|
||||
field: 'agent.id',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
queryResponseToHostListResult: (
|
||||
searchResponse: SearchResponse<HostMetadata>
|
||||
): HostListQueryResult => {
|
||||
const response = searchResponse as SearchResponse<HostMetadata>;
|
||||
return {
|
||||
resultLength:
|
||||
((response?.aggregations?.total as unknown) as { value?: number; relation: string })
|
||||
?.value || 0,
|
||||
resultList: response.hits.hits
|
||||
.map((hit) => hit.inner_hits?.most_recent.hits.hits)
|
||||
.flatMap((data) => data)
|
||||
.map((entry) => (entry?._source ?? {}) as HostMetadata),
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_1,
|
||||
};
|
||||
},
|
||||
queryResponseToHostResult: (searchResponse: SearchResponse<HostMetadata>): HostQueryResult => {
|
||||
const response = searchResponse as SearchResponse<HostMetadata>;
|
||||
return {
|
||||
resultLength: response.hits.hits.length,
|
||||
result: response.hits.hits.length > 0 ? response.hits.hits[0]._source : undefined,
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_1,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function metadataQueryStrategyV2(): MetadataQueryStrategy {
|
||||
return {
|
||||
index: metadataCurrentIndexPattern,
|
||||
extraBodyProperties: {
|
||||
track_total_hits: true,
|
||||
},
|
||||
queryResponseToHostListResult: (
|
||||
searchResponse: SearchResponse<HostMetadata | { HostDetails: HostMetadata }>
|
||||
): HostListQueryResult => {
|
||||
const response = searchResponse as SearchResponse<
|
||||
HostMetadata | { HostDetails: HostMetadata }
|
||||
>;
|
||||
const list =
|
||||
response.hits.hits.length > 0
|
||||
? response.hits.hits.map((entry) => stripHostDetails(entry?._source as HostMetadata))
|
||||
: [];
|
||||
|
||||
return {
|
||||
resultLength:
|
||||
((response.hits?.total as unknown) as { value: number; relation: string }).value || 0,
|
||||
resultList: list,
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
},
|
||||
queryResponseToHostResult: (
|
||||
searchResponse: SearchResponse<HostMetadata | { HostDetails: HostMetadata }>
|
||||
): HostQueryResult => {
|
||||
const response = searchResponse as SearchResponse<
|
||||
HostMetadata | { HostDetails: HostMetadata }
|
||||
>;
|
||||
return {
|
||||
resultLength: response.hits.hits.length,
|
||||
result:
|
||||
response.hits.hits.length > 0
|
||||
? stripHostDetails(response.hits.hits[0]._source as HostMetadata)
|
||||
: undefined,
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions.VERSION_2,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
import { HostMetadata } from '../../../../../common/endpoint/types';
|
||||
import { HostListQueryResult, HostQueryResult } from '../../../types';
|
||||
|
||||
// remove the top-level 'HostDetails' property if found, from previous schemas
|
||||
function stripHostDetails(host: HostMetadata | { HostDetails: HostMetadata }): HostMetadata {
|
||||
return 'HostDetails' in host ? host.HostDetails : host;
|
||||
}
|
||||
|
||||
export const queryResponseToHostResult = (
|
||||
searchResponse: SearchResponse<HostMetadata | { HostDetails: HostMetadata }>
|
||||
): HostQueryResult => {
|
||||
const response = searchResponse as SearchResponse<HostMetadata | { HostDetails: HostMetadata }>;
|
||||
return {
|
||||
resultLength: response.hits.hits.length,
|
||||
result:
|
||||
response.hits.hits.length > 0
|
||||
? stripHostDetails(response.hits.hits[0]._source as HostMetadata)
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const queryResponseToHostListResult = (
|
||||
searchResponse: SearchResponse<HostMetadata | { HostDetails: HostMetadata }>
|
||||
): HostListQueryResult => {
|
||||
const response = searchResponse as SearchResponse<HostMetadata | { HostDetails: HostMetadata }>;
|
||||
const list =
|
||||
response.hits.hits.length > 0
|
||||
? response.hits.hits.map((entry) => stripHostDetails(entry?._source as HostMetadata))
|
||||
: [];
|
||||
|
||||
return {
|
||||
resultLength:
|
||||
((response.hits?.total as unknown) as { value: number; relation: string }).value || 0,
|
||||
resultList: list,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,62 +8,6 @@
|
|||
import { SearchResponse } from 'elasticsearch';
|
||||
import { HostMetadata } from '../../../../../common/endpoint/types';
|
||||
|
||||
export function createV1SearchResponse(hostMetadata?: HostMetadata): SearchResponse<HostMetadata> {
|
||||
return ({
|
||||
took: 15,
|
||||
timed_out: false,
|
||||
_shards: {
|
||||
total: 1,
|
||||
successful: 1,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 5,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: hostMetadata
|
||||
? [
|
||||
{
|
||||
_index: 'metrics-endpoint.metadata-default',
|
||||
_id: '8FhM0HEBYyRTvb6lOQnw',
|
||||
_score: null,
|
||||
_source: hostMetadata,
|
||||
sort: [1588337587997],
|
||||
inner_hits: {
|
||||
most_recent: {
|
||||
hits: {
|
||||
total: {
|
||||
value: 2,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [
|
||||
{
|
||||
_index: 'metrics-endpoint.metadata-default',
|
||||
_id: 'W6Vo1G8BYQH1gtPUgYkC',
|
||||
_score: null,
|
||||
_source: hostMetadata,
|
||||
sort: [1579816615336],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
},
|
||||
aggregations: {
|
||||
total: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
} as unknown) as SearchResponse<HostMetadata>;
|
||||
}
|
||||
|
||||
export function createV2SearchResponse(hostMetadata?: HostMetadata): SearchResponse<HostMetadata> {
|
||||
return ({
|
||||
took: 15,
|
||||
|
|
|
@ -10,20 +10,15 @@ import { SearchResponse } from 'elasticsearch';
|
|||
import { HostMetadata } from '../../../common/endpoint/types';
|
||||
import { SecuritySolutionRequestHandlerContext } from '../../types';
|
||||
import { getESQueryHostMetadataByIDs } from '../routes/metadata/query_builders';
|
||||
import { EndpointAppContext } from '../types';
|
||||
import { queryResponseToHostListResult } from '../routes/metadata/support/query_strategies';
|
||||
|
||||
export async function getMetadataForEndpoints(
|
||||
endpointIDs: string[],
|
||||
requestHandlerContext: SecuritySolutionRequestHandlerContext,
|
||||
endpointAppContext: EndpointAppContext
|
||||
requestHandlerContext: SecuritySolutionRequestHandlerContext
|
||||
): Promise<HostMetadata[]> {
|
||||
const queryStrategy = await endpointAppContext.service
|
||||
?.getMetadataService()
|
||||
?.queryStrategy(requestHandlerContext.core.savedObjects.client);
|
||||
|
||||
const query = getESQueryHostMetadataByIDs(endpointIDs, queryStrategy!);
|
||||
const query = getESQueryHostMetadataByIDs(endpointIDs);
|
||||
const esClient = requestHandlerContext.core.elasticsearch.client.asCurrentUser;
|
||||
const { body } = await esClient.search<HostMetadata>(query as SearchRequest);
|
||||
const hosts = queryStrategy!.queryResponseToHostListResult(body as SearchResponse<HostMetadata>);
|
||||
const hosts = queryResponseToHostListResult(body as SearchResponse<HostMetadata>);
|
||||
return hosts.resultList;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,9 @@
|
|||
|
||||
import { LoggerFactory } from 'kibana/server';
|
||||
|
||||
import { SearchResponse } from '@elastic/elasticsearch/api/types';
|
||||
import { JsonObject } from '@kbn/common-utils';
|
||||
import { ConfigType } from '../config';
|
||||
import { EndpointAppContextService } from './endpoint_app_context_services';
|
||||
import { HostMetadata, MetadataQueryStrategyVersions } from '../../common/endpoint/types';
|
||||
import { HostMetadata } from '../../common/endpoint/types';
|
||||
import { ExperimentalFeatures } from '../../common/experimental_features';
|
||||
|
||||
/**
|
||||
|
@ -31,20 +29,9 @@ export interface EndpointAppContext {
|
|||
export interface HostListQueryResult {
|
||||
resultLength: number;
|
||||
resultList: HostMetadata[];
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions;
|
||||
}
|
||||
|
||||
export interface HostQueryResult {
|
||||
resultLength: number;
|
||||
result: HostMetadata | undefined;
|
||||
queryStrategyVersion: MetadataQueryStrategyVersions;
|
||||
}
|
||||
|
||||
export interface MetadataQueryStrategy {
|
||||
index: string;
|
||||
extraBodyProperties?: JsonObject;
|
||||
queryResponseToHostListResult: (
|
||||
searchResponse: SearchResponse<HostMetadata>
|
||||
) => HostListQueryResult;
|
||||
queryResponseToHostResult: (searchResponse: SearchResponse<HostMetadata>) => HostQueryResult;
|
||||
}
|
||||
|
|
|
@ -199,10 +199,10 @@ export const getHostEndpoint = async (
|
|||
};
|
||||
const endpointData =
|
||||
id != null && metadataRequestContext.endpointAppContextService.getAgentService() != null
|
||||
? await getHostMetaData(metadataRequestContext, id, undefined)
|
||||
? await getHostMetaData(metadataRequestContext, id)
|
||||
: null;
|
||||
|
||||
const fleetAgentId = endpointData?.metadata.elastic.agent.id;
|
||||
const fleetAgentId = endpointData?.elastic.agent.id;
|
||||
const [fleetAgentStatus, pendingActions] = !fleetAgentId
|
||||
? [undefined, {}]
|
||||
: await Promise.all([
|
||||
|
@ -214,13 +214,13 @@ export const getHostEndpoint = async (
|
|||
}),
|
||||
]);
|
||||
|
||||
return endpointData != null && endpointData.metadata
|
||||
return endpointData != null && endpointData
|
||||
? {
|
||||
endpointPolicy: endpointData.metadata.Endpoint.policy.applied.name,
|
||||
policyStatus: endpointData.metadata.Endpoint.policy.applied.status,
|
||||
sensorVersion: endpointData.metadata.agent.version,
|
||||
endpointPolicy: endpointData.Endpoint.policy.applied.name,
|
||||
policyStatus: endpointData.Endpoint.policy.applied.status,
|
||||
sensorVersion: endpointData.agent.version,
|
||||
elasticAgentStatus: fleetAgentStatusToEndpointHostStatus(fleetAgentStatus!),
|
||||
isolation: endpointData.metadata.Endpoint.state?.isolation ?? false,
|
||||
isolation: endpointData.Endpoint.state?.isolation ?? false,
|
||||
pendingActions,
|
||||
}
|
||||
: null;
|
||||
|
|
|
@ -29,7 +29,6 @@ export default function endpointAPIIntegrationTests(providerContext: FtrProvider
|
|||
});
|
||||
loadTestFile(require.resolve('./resolver/index'));
|
||||
loadTestFile(require.resolve('./metadata'));
|
||||
loadTestFile(require.resolve('./metadata_v1'));
|
||||
loadTestFile(require.resolve('./policy'));
|
||||
loadTestFile(require.resolve('./package'));
|
||||
});
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
deleteAllDocsFromMetadataIndex,
|
||||
deleteMetadataStream,
|
||||
} from './data_stream_helper';
|
||||
import { MetadataQueryStrategyVersions } from '../../../plugins/security_solution/common/endpoint/types';
|
||||
import { HOST_METADATA_LIST_ROUTE } from '../../../plugins/security_solution/common/endpoint/constants';
|
||||
|
||||
/**
|
||||
|
@ -88,7 +87,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(1);
|
||||
expect(body.request_page_index).to.eql(1);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
/* test that when paging properties produces no result, the total should reflect the actual number of metadata
|
||||
|
@ -113,7 +111,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(0);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(30);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return 400 when pagingProperties is below boundaries.', async () => {
|
||||
|
@ -148,7 +145,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return page based on filters and paging passed.', async () => {
|
||||
|
@ -186,7 +182,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return page based on host.os.Ext.variant filter.', async () => {
|
||||
|
@ -208,7 +203,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return the latest event for all the events for an endpoint', async () => {
|
||||
|
@ -231,7 +225,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return the latest event for all the events where policy status is not success', async () => {
|
||||
|
@ -275,7 +268,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
|
||||
it('metadata api should return all hosts when filter is empty string', async () => {
|
||||
|
@ -292,7 +284,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(body.hosts.length).to.eql(numberOfHostsInFixture);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
import { deleteMetadataStream } from './data_stream_helper';
|
||||
import { METADATA_REQUEST_V1_ROUTE } from '../../../plugins/security_solution/server/endpoint/routes/metadata';
|
||||
import { MetadataQueryStrategyVersions } from '../../../plugins/security_solution/common/endpoint/types';
|
||||
|
||||
/**
|
||||
* The number of host documents in the es archive.
|
||||
*/
|
||||
const numberOfHostsInFixture = 3;
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertest = getService('supertest');
|
||||
describe('test metadata api v1', () => {
|
||||
describe(`POST ${METADATA_REQUEST_V1_ROUTE} when index is empty`, () => {
|
||||
it('metadata api should return empty result when index is empty', async () => {
|
||||
// the endpoint uses data streams and es archiver does not support deleting them at the moment so we need
|
||||
// to do it manually
|
||||
await deleteMetadataStream(getService);
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send()
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(0);
|
||||
expect(body.hosts.length).to.eql(0);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`POST ${METADATA_REQUEST_V1_ROUTE} when index is not empty`, () => {
|
||||
before(
|
||||
async () =>
|
||||
await esArchiver.load(
|
||||
'x-pack/test/functional/es_archives/endpoint/metadata/api_feature',
|
||||
{ useCreate: true }
|
||||
)
|
||||
);
|
||||
// the endpoint uses data streams and es archiver does not support deleting them at the moment so we need
|
||||
// to do it manually
|
||||
after(async () => await deleteMetadataStream(getService));
|
||||
it('metadata api should return one entry for each host with default paging', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send()
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(numberOfHostsInFixture);
|
||||
expect(body.hosts.length).to.eql(numberOfHostsInFixture);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return page based on paging properties passed.', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 1,
|
||||
},
|
||||
{
|
||||
page_index: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(numberOfHostsInFixture);
|
||||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(1);
|
||||
expect(body.request_page_index).to.eql(1);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
/* test that when paging properties produces no result, the total should reflect the actual number of metadata
|
||||
in the index.
|
||||
*/
|
||||
it('metadata api should return accurate total metadata if page index produces no result', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
page_index: 3,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(numberOfHostsInFixture);
|
||||
expect(body.hosts.length).to.eql(0);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(30);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return 400 when pagingProperties is below boundaries.', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 0,
|
||||
},
|
||||
{
|
||||
page_index: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(400);
|
||||
expect(body.message).to.contain('Value must be equal to or greater than [1]');
|
||||
});
|
||||
|
||||
it('metadata api should return page based on filters passed.', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: 'not host.ip:10.46.229.234',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(2);
|
||||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return page based on filters and paging passed.', async () => {
|
||||
const notIncludedIp = '10.46.229.234';
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
paging_properties: [
|
||||
{
|
||||
page_size: 10,
|
||||
},
|
||||
{
|
||||
page_index: 0,
|
||||
},
|
||||
],
|
||||
filters: {
|
||||
kql: `not host.ip:${notIncludedIp}`,
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(2);
|
||||
const resultIps: string[] = [].concat(
|
||||
...body.hosts.map((hostInfo: Record<string, any>) => hostInfo.metadata.host.ip)
|
||||
);
|
||||
expect(resultIps).to.eql([
|
||||
'10.192.213.130',
|
||||
'10.70.28.129',
|
||||
'10.101.149.26',
|
||||
'2606:a000:ffc0:39:11ef:37b9:3371:578c',
|
||||
]);
|
||||
expect(resultIps).not.include.eql(notIncludedIp);
|
||||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return page based on host.os.Ext.variant filter.', async () => {
|
||||
const variantValue = 'Windows Pro';
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: `host.os.Ext.variant:${variantValue}`,
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(2);
|
||||
const resultOsVariantValue: Set<string> = new Set(
|
||||
body.hosts.map((hostInfo: Record<string, any>) => hostInfo.metadata.host.os.Ext.variant)
|
||||
);
|
||||
expect(Array.from(resultOsVariantValue)).to.eql([variantValue]);
|
||||
expect(body.hosts.length).to.eql(2);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return the latest event for all the events for an endpoint', async () => {
|
||||
const targetEndpointIp = '10.46.229.234';
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: `host.ip:${targetEndpointIp}`,
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(1);
|
||||
const resultIp: string = body.hosts[0].metadata.host.ip.filter(
|
||||
(ip: string) => ip === targetEndpointIp
|
||||
);
|
||||
expect(resultIp).to.eql([targetEndpointIp]);
|
||||
expect(body.hosts[0].metadata.event.created).to.eql(1618841405309);
|
||||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return the latest event for all the events where policy status is not success', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: `not Endpoint.policy.applied.status:success`,
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
const statuses: Set<string> = new Set(
|
||||
body.hosts.map(
|
||||
(hostInfo: Record<string, any>) => hostInfo.metadata.Endpoint.policy.applied.status
|
||||
)
|
||||
);
|
||||
expect(statuses.size).to.eql(1);
|
||||
expect(Array.from(statuses)).to.eql(['failure']);
|
||||
});
|
||||
|
||||
it('metadata api should return the endpoint based on the elastic agent id, and status should be unhealthy', async () => {
|
||||
const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf';
|
||||
const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095';
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: `elastic.agent.id:${targetElasticAgentId}`,
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(1);
|
||||
const resultHostId: string = body.hosts[0].metadata.host.id;
|
||||
const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id;
|
||||
expect(resultHostId).to.eql(targetEndpointId);
|
||||
expect(resultElasticAgentId).to.eql(targetElasticAgentId);
|
||||
expect(body.hosts[0].metadata.event.created).to.eql(1618841405309);
|
||||
expect(body.hosts[0].host_status).to.eql('unhealthy');
|
||||
expect(body.hosts.length).to.eql(1);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
|
||||
it('metadata api should return all hosts when filter is empty string', async () => {
|
||||
const { body } = await supertest
|
||||
.post(`${METADATA_REQUEST_V1_ROUTE}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
filters: {
|
||||
kql: '',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
expect(body.total).to.eql(numberOfHostsInFixture);
|
||||
expect(body.hosts.length).to.eql(numberOfHostsInFixture);
|
||||
expect(body.request_page_size).to.eql(10);
|
||||
expect(body.request_page_index).to.eql(0);
|
||||
expect(body.query_strategy_version).to.eql(MetadataQueryStrategyVersions.VERSION_1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue