diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index abb0ccee8d90..0054c1f1abdd 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -718,7 +718,10 @@ export type SafeEndpointEvent = Partial<{ forwarded_ip: ECSField; }>; dns: Partial<{ - question: Partial<{ name: ECSField }>; + question: Partial<{ + name: ECSField; + type: ECSField; + }>; }>; process: Partial<{ entity_id: ECSField; diff --git a/x-pack/test/functional/es_archives/endpoint/pipeline/dns/data.json.gz b/x-pack/test/functional/es_archives/endpoint/pipeline/dns/data.json.gz new file mode 100644 index 000000000000..5caab4767dbe Binary files /dev/null and b/x-pack/test/functional/es_archives/endpoint/pipeline/dns/data.json.gz differ diff --git a/x-pack/test/ingest_manager_api_integration/config.ts b/x-pack/test/ingest_manager_api_integration/config.ts index d11884667c48..193ac0d5974e 100644 --- a/x-pack/test/ingest_manager_api_integration/config.ts +++ b/x-pack/test/ingest_manager_api_integration/config.ts @@ -12,7 +12,7 @@ import { defineDockerServersConfig } from '@kbn/test'; // Docker image to use for Ingest Manager API integration tests. // This hash comes from the commit hash here: https://github.com/elastic/package-storage/commit export const dockerImage = - 'docker.elastic.co/package-registry/distribution:a5132271ad37209d6978018bfe6e224546d719a8'; + 'docker.elastic.co/package-registry/distribution:fb58d670bafbac7e9e28baf6d6f99ba65cead548'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/package.ts b/x-pack/test/security_solution_endpoint_api_int/apis/package.ts index 3b5873d1fe0c..afbf0dcd7bd1 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/package.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/package.ts @@ -5,6 +5,10 @@ */ import expect from '@kbn/expect'; import { SearchResponse } from 'elasticsearch'; +import { + ResolverPaginatedEvents, + SafeEndpointEvent, +} from '../../../plugins/security_solution/common/endpoint/types'; import { eventsIndexPattern } from '../../../plugins/security_solution/common/endpoint/constants'; import { EndpointDocGenerator, @@ -12,6 +16,7 @@ import { } from '../../../plugins/security_solution/common/endpoint/generate_data'; import { FtrProviderContext } from '../ftr_provider_context'; import { InsertedEvents, processEventsIndex } from '../services/resolver'; +import { deleteEventsStream } from './data_stream_helper'; interface EventIngested { event: { @@ -35,6 +40,8 @@ interface NetworkEvent { const networkIndex = 'logs-endpoint.events.network-default'; export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); const resolver = getService('resolverGenerator'); const es = getService('es'); const generator = new EndpointDocGenerator('data'); @@ -59,6 +66,72 @@ export default function ({ getService }: FtrProviderContext) { }; describe('Endpoint package', () => { + describe('network processors', () => { + let networkIndexData: InsertedEvents; + + after(async () => { + await resolver.deleteData(networkIndexData); + }); + + it('handles events without the `network.protocol` field being defined', async () => { + const eventWithoutNetworkObject = generator.generateEvent(); + // ensure that `network.protocol` does not exist in the event to test that the pipeline handles those type of events + delete eventWithoutNetworkObject.network; + + // this call will fail if the pipeline fails + networkIndexData = await resolver.insertEvents([eventWithoutNetworkObject], networkIndex); + const eventWithBothIPs = await searchForID( + networkIndexData.eventsInfo[0]._id + ); + + // ensure that the event was inserted into ES + expect(eventWithBothIPs.body.hits.hits[0]._source.event?.id).to.be( + eventWithoutNetworkObject.event?.id + ); + }); + }); + + describe('dns processor', () => { + before(async () => { + await esArchiver.load('endpoint/pipeline/dns', { useCreate: true }); + }); + + after(async () => { + await deleteEventsStream(getService); + }); + + it('does not set dns.question.type if it is already populated', async () => { + // this id comes from the es archive file endpoint/pipeline/dns + const id = 'LrLSOVHVsFY94TAi++++++eF'; + const { body }: { body: ResolverPaginatedEvents } = await supertest + .post(`/api/endpoint/resolver/events?limit=1`) + .set('kbn-xsrf', 'xxx') + .send({ + filter: `event.id:"${id}"`, + }) + .expect(200); + expect(body.events.length).to.eql(1); + expect((body.events[0] as SafeEndpointEvent).dns?.question?.name).to.eql('www.google.com'); + expect((body.events[0] as SafeEndpointEvent).dns?.question?.type).to.eql('INVALID_VALUE'); + }); + + it('sets dns.question.type if it is not populated', async () => { + // this id comes from the es archive file endpoint/pipeline/dns + const id = 'LrLSOVHVsFY94TAi++++++eP'; + const { body }: { body: ResolverPaginatedEvents } = await supertest + .post(`/api/endpoint/resolver/events?limit=1`) + .set('kbn-xsrf', 'xxx') + .send({ + filter: `event.id:"${id}"`, + }) + .expect(200); + expect(body.events.length).to.eql(1); + expect((body.events[0] as SafeEndpointEvent).dns?.question?.name).to.eql('www.aol.com'); + // This value is parsed out of the message field in the event. type 28 = AAAA + expect((body.events[0] as SafeEndpointEvent).dns?.question?.type).to.eql('AAAA'); + }); + }); + describe('ingested processor', () => { let event: Event; let genData: InsertedEvents; @@ -92,6 +165,7 @@ export default function ({ getService }: FtrProviderContext) { const eventWithSourceOnly = generator.generateEvent({ extensions: { source: { ip: '8.8.8.8' } }, }); + networkIndexData = await resolver.insertEvents( [eventWithBothIPs, eventWithSourceOnly], networkIndex