/** * @vitest-environment node */ import { createMockRequest, hybridAuthMockFns } from '@sim/testing' import { beforeEach, describe, expect, it, vi } from 'vitest' const { fetchMock } = vi.hoisted(() => ({ fetchMock: vi.fn(), })) import { POST } from '@/app/api/tools/crowdstrike/query/route' function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' }, }) } const sensorResource = { agent_version: '6.1.0', cid: 'cid-1', device_id: 'sensor-1', heartbeat_time: 1700, hostname: 'host-1', idp_policy_id: 'policy-1', idp_policy_name: 'Default Policy', kerberos_config: 'configured', ldap_config: 'configured', ldaps_config: 'configured', local_ip: '10.0.0.1', machine_domain: 'corp.local', ntlm_config: 'configured', os_version: 'Windows Server 2022', rdp_to_dc_config: 'configured', smb_to_dc_config: 'configured', status: 'protected', status_causes: ['healthy'], ti_enabled: 'enabled', } const normalizedSensor = { agentVersion: '6.1.0', cid: 'cid-1', deviceId: 'sensor-1', heartbeatTime: 1700, hostname: 'host-1', idpPolicyId: 'policy-1', idpPolicyName: 'Default Policy', ipAddress: '10.0.0.1', kerberosConfig: 'configured', ldapConfig: 'configured', ldapsConfig: 'configured', machineDomain: 'corp.local', ntlmConfig: 'configured', osVersion: 'Windows Server 2022', rdpToDcConfig: 'configured', smbToDcConfig: 'configured', status: 'protected', statusCauses: ['healthy'], tiEnabled: 'enabled', } describe('CrowdStrike query route', () => { beforeEach(() => { vi.clearAllMocks() vi.stubGlobal('fetch', fetchMock) hybridAuthMockFns.mockCheckInternalAuth.mockResolvedValue({ success: true, userId: 'user-123', authType: 'internal_jwt', }) }) it('hydrates sensor details after querying sensor ids', async () => { fetchMock .mockResolvedValueOnce(jsonResponse({ access_token: 'token-123' })) .mockResolvedValueOnce( jsonResponse({ meta: { pagination: { expires_at: 111, limit: 1, offset: 0, total: 1 } }, resources: ['sensor-1'], }) ) .mockResolvedValueOnce( jsonResponse({ resources: [sensorResource], }) ) const request = createMockRequest('POST', { clientId: 'client-id', clientSecret: 'client-secret', cloud: 'us-1', limit: 1, operation: 'crowdstrike_query_sensors', }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(fetchMock).toHaveBeenCalledTimes(3) expect(fetchMock.mock.calls[1]?.[0]).toBe( 'https://api.crowdstrike.com/identity-protection/queries/devices/v1?limit=1' ) expect(fetchMock.mock.calls[2]?.[0]).toBe( 'https://api.crowdstrike.com/identity-protection/entities/devices/GET/v1' ) expect(fetchMock.mock.calls[2]?.[1]).toMatchObject({ body: JSON.stringify({ ids: ['sensor-1'] }), method: 'POST', }) expect(data.output).toEqual({ count: 1, pagination: { limit: 1, offset: 0, total: 1, }, sensors: [normalizedSensor], }) }) it('fetches sensor details directly from device ids', async () => { fetchMock .mockResolvedValueOnce(jsonResponse({ access_token: 'token-123' })) .mockResolvedValueOnce( jsonResponse({ resources: [sensorResource], }) ) const request = createMockRequest('POST', { clientId: 'client-id', clientSecret: 'client-secret', cloud: 'us-1', ids: ['sensor-1'], operation: 'crowdstrike_get_sensor_details', }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(fetchMock).toHaveBeenCalledTimes(2) expect(fetchMock.mock.calls[1]?.[0]).toBe( 'https://api.crowdstrike.com/identity-protection/entities/devices/GET/v1' ) expect(fetchMock.mock.calls[1]?.[1]).toMatchObject({ body: JSON.stringify({ ids: ['sensor-1'] }), method: 'POST', }) expect(data.output).toEqual({ count: 1, pagination: null, sensors: [normalizedSensor], }) }) it('normalizes sensor aggregate results', async () => { fetchMock .mockResolvedValueOnce(jsonResponse({ access_token: 'token-123' })) .mockResolvedValueOnce( jsonResponse({ resources: [ { buckets: [ { count: 2, key_as_string: 'protected', sub_aggregates: [ { buckets: [ { count: 2, key_as_string: 'corp.local', value: 2, value_as_string: '2', }, ], doc_count_error_upper_bound: 0, name: 'machine_domain_counts', sum_other_doc_count: 0, }, ], value: 2, value_as_string: '2', }, ], doc_count_error_upper_bound: 0, name: 'status_counts', sum_other_doc_count: 0, }, ], }) ) const aggregateQuery = { field: 'status', name: 'status_counts', size: 10, type: 'terms', } const request = createMockRequest('POST', { aggregateQuery, clientId: 'client-id', clientSecret: 'client-secret', cloud: 'us-1', operation: 'crowdstrike_get_sensor_aggregates', }) const response = await POST(request) const data = await response.json() expect(response.status).toBe(200) expect(fetchMock).toHaveBeenCalledTimes(2) expect(fetchMock.mock.calls[1]?.[0]).toBe( 'https://api.crowdstrike.com/identity-protection/aggregates/devices/GET/v1' ) expect(fetchMock.mock.calls[1]?.[1]).toMatchObject({ body: JSON.stringify(aggregateQuery), method: 'POST', }) expect(data.output).toEqual({ aggregates: [ { buckets: [ { count: 2, from: null, keyAsString: 'protected', label: null, stringFrom: null, stringTo: null, subAggregates: [ { buckets: [ { count: 2, from: null, keyAsString: 'corp.local', label: null, stringFrom: null, stringTo: null, subAggregates: [], to: null, value: 2, valueAsString: '2', }, ], docCountErrorUpperBound: 0, name: 'machine_domain_counts', sumOtherDocCount: 0, }, ], to: null, value: 2, valueAsString: '2', }, ], docCountErrorUpperBound: 0, name: 'status_counts', sumOtherDocCount: 0, }, ], count: 1, }) }) })