[EDR Workflows] Fix small CrowdStrike connector issues (#190689)

This commit is contained in:
Tomasz Ciecierski 2024-08-19 23:20:36 +02:00 committed by GitHub
parent 5be73216a3
commit 9524bbcdc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 26 additions and 27 deletions

View file

@ -70,9 +70,8 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl {
LogsEndpointAction<TParameters, TOutputContent, TMeta & CrowdstrikeActionRequestCommonMeta>
> {
const agentId = actionRequest.endpoint_ids[0];
const eventDetails = await this.getEventDetailsById(agentId);
const hostname = await this.getHostNameByAgentId(agentId);
const hostname = eventDetails.host.name;
return super.writeActionRequestToEndpointIndex({
...actionRequest,
hosts: {
@ -124,13 +123,12 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl {
return actionSendResponse;
}
private async getEventDetailsById(agentId: string): Promise<{
host: { name: string };
}> {
private async getHostNameByAgentId(agentId: string): Promise<string> {
const search = {
index: ['logs-crowdstrike.fdr*', 'logs-crowdstrike.falcon*'],
// Multiple indexes: .falcon, .fdr, .host, .alert
index: ['logs-crowdstrike*'],
size: 1,
_source: ['host.name'],
_source: ['host.hostname', 'host.name'],
body: {
query: {
bool: {
@ -140,13 +138,14 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl {
},
};
try {
const result: SearchResponse<{ host: { name: string } }> =
await this.options.esClient.search<{ host: { name: string } }>(search, {
const result: SearchResponse<{ host: { name: string; hostname: string } }> =
await this.options.esClient.search<{ host: { name: string; hostname: string } }>(search, {
ignore: [404],
});
// Check if host name exists
const hostName = result.hits.hits?.[0]?._source?.host?.name;
const host = result.hits.hits?.[0]?._source?.host;
const hostName = host?.name || host?.hostname;
if (!hostName) {
throw new ResponseActionsClientError(
`Host name not found in the event document for agentId: ${agentId}`,
@ -154,7 +153,7 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl {
);
}
return result.hits.hits[0]._source as { host: { name: string } };
return hostName;
} catch (err) {
throw new ResponseActionsClientError(
`Failed to fetch event document: ${err.message}`,

View file

@ -41,7 +41,7 @@ const getMockSearchResponse = (
{
_id: '1',
_index: 'index',
fields: { 'crowdstrike.host.id': [agentName] },
fields: { 'device.id': [agentName] },
inner_hits: {
most_recent: {
hits: {
@ -52,11 +52,13 @@ const getMockSearchResponse = (
_source: {
crowdstrike: {
host: {
id: !wrongAgentName ? agentName : 'wrongAgentName',
last_seen: '2023-01-01',
status,
},
},
device: {
id: !wrongAgentName ? agentName : 'wrongAgentName',
},
},
},
],

View file

@ -72,7 +72,7 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
filter: [
{
terms: {
'crowdstrike.host.id': agentIds,
'device.id': agentIds,
},
},
],
@ -91,8 +91,7 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
size: DEFAULT_MAX_TABLE_QUERY_SIZE,
query,
collapse: {
// TODO: check if we should use crowdstrike.cid instead
field: 'crowdstrike.host.id',
field: 'device.id',
inner_hits: {
name: 'most_recent',
size: 1,
@ -123,10 +122,8 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
const mostRecentAgentInfosByAgentId = searchResponse?.hits?.hits?.reduce<
Record<string, RawCrowdstrikeInfo>
>((acc, hit) => {
// TODO TC: check if we should use crowdstrike.cid instead
if (hit.fields?.['crowdstrike.host.id'][0]) {
acc[hit.fields?.['crowdstrike.host.id'][0]] =
hit.inner_hits?.most_recent.hits.hits[0]._source;
if (hit.fields?.['device.id'][0]) {
acc[hit.fields?.['device.id'][0]] = hit.inner_hits?.most_recent.hits.hits[0]._source;
}
return acc;
@ -135,7 +132,7 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
const agentStatuses = await this.getAgentStatusFromConnectorAction(agentIds);
return agentIds.reduce<AgentStatusRecords>((acc, agentId) => {
const agentInfo = mostRecentAgentInfosByAgentId[agentId]?.crowdstrike;
const { device, crowdstrike } = mostRecentAgentInfosByAgentId[agentId];
const agentStatus = agentStatuses[agentId];
const pendingActions = allPendingActions.find(
@ -145,12 +142,11 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
acc[agentId] = {
agentId,
agentType: this.agentType,
// TODO: check if we should use crowdstrike.cid instead
found: agentInfo?.host.id === agentId,
found: device?.id === agentId,
isolated:
agentInfo?.host.status === CROWDSTRIKE_NETWORK_STATUS.CONTAINED ||
agentInfo?.host.status === CROWDSTRIKE_NETWORK_STATUS.LIFT_CONTAINMENT_PENDING,
lastSeen: agentInfo?.host.last_seen || '',
crowdstrike?.host.status === CROWDSTRIKE_NETWORK_STATUS.CONTAINED ||
crowdstrike?.host.status === CROWDSTRIKE_NETWORK_STATUS.LIFT_CONTAINMENT_PENDING,
lastSeen: crowdstrike?.host.last_seen || agentStatus?.last_seen || '',
status:
agentStatus?.state === CROWDSTRIKE_STATUS_RESPONSE.ONLINE
? HostStatus.HEALTHY
@ -162,6 +158,7 @@ export class CrowdstrikeAgentStatusClient extends AgentStatusClient {
pendingActions: pendingActions?.pending_actions ?? {},
};
// console.log({ acc });
return acc;
}, {});
} catch (err) {

View file

@ -41,6 +41,7 @@ export const CrowdstrikeGetAgentOnlineStatusResponseSchema = schema.object(
{
state: schema.maybe(schema.string()),
id: schema.maybe(schema.string()),
last_seen: schema.maybe(schema.string()),
},
{ unknowns: 'allow' }
)

View file

@ -154,7 +154,7 @@ export class CrowdstrikeConnector extends SubActionConnector<
headers: {
accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
authorization: 'Basic ' + this.base64encodedToken,
authorization: 'Basic ' + CrowdstrikeConnector.base64encodedToken,
},
responseSchema: CrowdstrikeGetTokenResponseSchema,
});