Skip to content

Commit

Permalink
[Defend Workflows][E2E] Isolate command e2e coverage (#154465)
Browse files Browse the repository at this point in the history
E2E coverage of TestRail "Isolate" suite. 
These tests are run against mocked documents. We intercept REST request
whenever `Isolate` button is used and mock `response action response`
with `success`.
  • Loading branch information
szwarckonrad authored Apr 24, 2023
1 parent d0642f7 commit 792c786
Show file tree
Hide file tree
Showing 18 changed files with 526 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const createActionAttachmentUserActionBuilder = ({
// TODO: Fix this manually. Issue #123375
// eslint-disable-next-line react/display-name
build: () => {
const actionIconName = comment.actions.type === 'isolate' ? 'lock' : 'lockOpen';
return [
{
username: (
Expand All @@ -52,7 +53,8 @@ export const createActionAttachmentUserActionBuilder = ({
),
'data-test-subj': 'endpoint-action',
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
timelineAvatar: comment.actions.type === 'isolate' ? 'lock' : 'lockOpen',
timelineAvatar: actionIconName,
timelineAvatarAriaLabel: actionIconName,
actions: <UserActionCopyLink id={comment.id} />,
children: comment.comment.trim().length > 0 && (
<ContentWrapper data-test-subj="user-action-markdown">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface GetCustomEndpointMetadataGeneratorOptions {
version: string;
/** OS type for the generated endpoint hosts */
os: 'macOS' | 'windows' | 'linux';
isolation: boolean;
}

/**
Expand All @@ -33,6 +34,7 @@ export class EndpointMetadataGenerator extends BaseDataGenerator {
static custom({
version,
os,
isolation,
}: Partial<GetCustomEndpointMetadataGeneratorOptions> = {}): typeof EndpointMetadataGenerator {
return class extends EndpointMetadataGenerator {
generate(overrides: DeepPartial<HostMetadataInterface> = {}): HostMetadataInterface {
Expand All @@ -54,6 +56,9 @@ export class EndpointMetadataGenerator extends BaseDataGenerator {
set(overrides, 'host.os', EndpointMetadataGenerator.windowsOSFields);
}
}
if (isolation !== undefined) {
set(overrides, 'Endpoint.state.isolation', isolation);
}

return super.generate(overrides);
}
Expand Down Expand Up @@ -104,10 +109,10 @@ export class EndpointMetadataGenerator extends BaseDataGenerator {
/** Generate an Endpoint host metadata document */
generate(overrides: DeepPartial<HostMetadataInterface> = {}): HostMetadataInterface {
const ts = overrides['@timestamp'] ?? new Date().getTime();
const hostName = this.randomHostname();
const hostName = overrides?.host?.hostname ?? this.randomHostname();
const agentVersion = overrides?.agent?.version ?? this.randomVersion();
const agentId = this.seededUUIDv4();
const isIsolated = this.randomBoolean(0.3);
const isIsolated = overrides?.Endpoint?.state?.isolation ?? this.randomBoolean(0.3);
const capabilities: EndpointCapabilities[] = ['isolation'];

// v8.4 introduced additional endpoint capabilities
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export class EndpointRuleAlertGenerator extends BaseDataGenerator {
const endpointMetadataGenerator = new EndpointMetadataGenerator();
const endpointMetadata = endpointMetadataGenerator.generate({
agent: { version: kibanaPackageJson.version },
host: { hostname: overrides?.host?.hostname },
Endpoint: { state: { isolation: overrides?.Endpoint?.state?.isolation } },
});
const now = overrides['@timestamp'] ?? new Date().toISOString();
const endpointAgentId = overrides?.agent?.id ?? this.seededUUIDv4();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface IndexedHostsResponse
* @param policyResponseIndex
* @param enrollFleet
* @param generator
* @param disableEndpointActionsForHost
*/
export async function indexEndpointHostDocs({
numDocs,
Expand All @@ -86,6 +87,7 @@ export async function indexEndpointHostDocs({
policyResponseIndex,
enrollFleet,
generator,
withResponseActions = true,
}: {
numDocs: number;
client: Client;
Expand All @@ -96,6 +98,7 @@ export async function indexEndpointHostDocs({
policyResponseIndex: string;
enrollFleet: boolean;
generator: EndpointDocGenerator;
withResponseActions?: boolean;
}): Promise<IndexedHostsResponse> {
const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents
const timestamp = new Date().getTime();
Expand Down Expand Up @@ -190,13 +193,15 @@ export async function indexEndpointHostDocs({
},
};

// Create some fleet endpoint actions and .logs-endpoint actions for this Host
const actionsResponse = await indexEndpointAndFleetActionsForHost(
client,
hostMetadata,
undefined
);
mergeAndAppendArrays(response, actionsResponse);
if (withResponseActions) {
// Create some fleet endpoint actions and .logs-endpoint actions for this Host
const actionsResponse = await indexEndpointAndFleetActionsForHost(
client,
hostMetadata,
undefined
);
mergeAndAppendArrays(response, actionsResponse);
}
}

await client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { EndpointRuleAlertGenerator } from '../data_generators/endpoint_rule_ale
export interface IndexEndpointRuleAlertsOptions {
esClient: Client;
endpointAgentId: string;
endpointHostname?: string;
endpointIsolated?: boolean;
count?: number;
log?: ToolingLog;
}
Expand All @@ -40,12 +42,16 @@ export interface DeletedIndexedEndpointRuleAlerts {
* written them to for a given endpoint
* @param esClient
* @param endpointAgentId
* @param endpointHostname
* @param endpointIsolated
* @param count
* @param log
*/
export const indexEndpointRuleAlerts = async ({
esClient,
endpointAgentId,
endpointHostname,
endpointIsolated,
count = 1,
log = new ToolingLog(),
}: IndexEndpointRuleAlertsOptions): Promise<IndexedEndpointRuleAlerts> => {
Expand All @@ -57,7 +63,11 @@ export const indexEndpointRuleAlerts = async ({
const indexedAlerts: estypes.IndexResponse[] = [];

for (let n = 0; n < count; n++) {
const alert = alertsGenerator.generate({ agent: { id: endpointAgentId } });
const alert = alertsGenerator.generate({
agent: { id: endpointAgentId },
host: { hostname: endpointHostname },
...(endpointIsolated ? { Endpoint: { state: { isolation: endpointIsolated } } } : {}),
});
const indexedAlert = await esClient.index({
index: `${DEFAULT_ALERTS_INDEX}-default`,
refresh: 'wait_for',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,9 @@ export class EndpointDocGenerator extends BaseDataGenerator {

private createHostData(): CommonHostInfo {
const { agent, elastic, host, Endpoint } = this.metadataGenerator.generate({
Endpoint: { policy: { applied: this.randomChoice(APPLIED_POLICIES) } },
Endpoint: {
policy: { applied: this.randomChoice(APPLIED_POLICIES) },
},
});

return { agent, elastic, host, Endpoint };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type IndexedHostsAndAlertsResponse = IndexedHostsResponse;
* @param fleet
* @param options
* @param DocGenerator
* @param withResponseActions
*/
export async function indexHostsAndAlerts(
client: Client,
Expand All @@ -62,7 +63,8 @@ export async function indexHostsAndAlerts(
alertsPerHost: number,
fleet: boolean,
options: TreeOptions = {},
DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator
DocGenerator: typeof EndpointDocGenerator = EndpointDocGenerator,
withResponseActions = true
): Promise<IndexedHostsAndAlertsResponse> {
const random = seedrandom(seed);
const epmEndpointPackage = await getEndpointPackageInfo(kbnClient);
Expand Down Expand Up @@ -114,6 +116,7 @@ export async function indexHostsAndAlerts(
policyResponseIndex,
enrollFleet: fleet,
generator,
withResponseActions,
});

mergeAndAppendArrays(response, indexedHosts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@

import type { CasePostRequest } from '@kbn/cases-plugin/common/api';
import type { IndexedEndpointPolicyResponse } from '../../../common/endpoint/data_loaders/index_endpoint_policy_response';
import type { HostPolicyResponse } from '../../../common/endpoint/types';
import type { IndexEndpointHostsCyTaskOptions } from './types';
import type {
HostPolicyResponse,
LogsEndpointActionResponse,
} from '../../../common/endpoint/types';
import type { IndexEndpointHostsCyTaskOptions, HostActionResponse } from './types';
import type {
DeleteIndexedFleetEndpointPoliciesResponse,
IndexedFleetEndpointPolicyResponse,
Expand Down Expand Up @@ -115,6 +118,12 @@ declare global {
arg: IndexedEndpointPolicyResponse,
options?: Partial<Loggable & Timeoutable>
): Chainable<null>;

task(
name: 'sendHostActionResponse',
arg: HostActionResponse,
options?: Partial<Loggable & Timeoutable>
): Chainable<LogsEndpointActionResponse>;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@
* 2.0.
*/

import type { ReturnTypeFromChainable } from '../../types';
import { indexEndpointHosts } from '../../tasks/index_endpoint_hosts';
import { login } from '../../tasks/login';
import { runEndpointLoaderScript } from '../../tasks/run_endpoint_loader';

describe('Endpoints page', () => {
let endpointData: ReturnTypeFromChainable<typeof indexEndpointHosts>;

before(() => {
runEndpointLoaderScript();
indexEndpointHosts().then((indexEndpoints) => {
endpointData = indexEndpoints;
});
});

beforeEach(() => {
login();
});

after(() => {
if (endpointData) {
endpointData.cleanup();
// @ts-expect-error ignore setting to undefined
endpointData = undefined;
}
});

it('Loads the endpoints page', () => {
cy.visit('/app/security/administration/endpoints');
cy.contains('Hosts running Elastic Defend').should('exist');
Expand Down
Loading

0 comments on commit 792c786

Please sign in to comment.