diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 9629b8ccf28e1..75a15cc16db2e 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -39,6 +39,14 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo const defaultTryTimeout = config.get('timeouts.try'); const defaultFindTimeout = config.get('timeouts.find'); + interface NavigateProps { + appConfig: {}; + ensureCurrentUrl: boolean; + shouldLoginIfPrompted: boolean; + shouldAcceptAlert: boolean; + useActualUrl: boolean; + } + class CommonPage { /** * Navigates the browser window to provided URL @@ -115,6 +123,34 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo return currentUrl; } + private async navigate(navigateProps: NavigateProps) { + const { + appConfig, + ensureCurrentUrl, + shouldLoginIfPrompted, + shouldAcceptAlert, + useActualUrl, + } = navigateProps; + const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig); + + await retry.try(async () => { + if (useActualUrl) { + log.debug(`navigateToActualUrl ${appUrl}`); + await browser.get(appUrl); + } else { + await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert); + } + + const currentUrl = shouldLoginIfPrompted + ? await this.loginIfPrompted(appUrl) + : await browser.getCurrentUrl(); + + if (ensureCurrentUrl && !currentUrl.includes(appUrl)) { + throw new Error(`expected ${currentUrl}.includes(${appUrl})`); + } + }); + } + /** * Navigates browser using the pathname from the appConfig and subUrl as the hash * @param appName As defined in the apps config, e.g. 'home' @@ -137,23 +173,44 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo hash: useActualUrl ? subUrl : `/${appName}/${subUrl}`, }; - const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig); - - await retry.try(async () => { - if (useActualUrl) { - log.debug(`navigateToActualUrl ${appUrl}`); - await browser.get(appUrl); - } else { - await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert); - } + await this.navigate({ + appConfig, + ensureCurrentUrl, + shouldLoginIfPrompted, + shouldAcceptAlert, + useActualUrl, + }); + } - const currentUrl = shouldLoginIfPrompted - ? await this.loginIfPrompted(appUrl) - : await browser.getCurrentUrl(); + /** + * Navigates browser using the pathname from the appConfig and subUrl as the extended path. + * This was added to be able to test an application that uses browser history over hash history. + * @param appName As defined in the apps config, e.g. 'home' + * @param subUrl The route after the appUrl, e.g. 'tutorial_directory/sampleData' + * @param args additional arguments + */ + public async navigateToUrlWithBrowserHistory( + appName: string, + subUrl?: string, + { + basePath = '', + ensureCurrentUrl = true, + shouldLoginIfPrompted = true, + shouldAcceptAlert = true, + useActualUrl = true, + } = {} + ) { + const appConfig = { + // subUrl following the basePath, assumes no hashes. Ex: 'app/endpoint/management' + pathname: `${basePath}${config.get(['apps', appName]).pathname}${subUrl}`, + }; - if (ensureCurrentUrl && !currentUrl.includes(appUrl)) { - throw new Error(`expected ${currentUrl}.includes(${appUrl})`); - } + await this.navigate({ + appConfig, + ensureCurrentUrl, + shouldLoginIfPrompted, + shouldAcceptAlert, + useActualUrl, }); } diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 17faebbe94e6d..310cda5a97dc9 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -39,7 +39,8 @@ "xpack.transform": "legacy/plugins/transform", "xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant", "xpack.uptime": "legacy/plugins/uptime", - "xpack.watcher": "plugins/watcher" + "xpack.watcher": "plugins/watcher", + "xpack.endpoint": "plugins/endpoint" }, "translations": [ "plugins/translations/translations/zh-CN.json", diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts new file mode 100644 index 0000000000000..5ef9d22e4dd7b --- /dev/null +++ b/x-pack/plugins/endpoint/common/types.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * A deep readonly type that will make all children of a given object readonly recursively + */ +export type Immutable = T extends undefined | null | boolean | string | number + ? T + : T extends Array + ? ImmutableArray + : T extends Map + ? ImmutableMap + : T extends Set + ? ImmutableSet + : ImmutableObject; + +export type ImmutableArray = ReadonlyArray>; +export type ImmutableMap = ReadonlyMap, Immutable>; +export type ImmutableSet = ReadonlySet>; +export type ImmutableObject = { readonly [K in keyof T]: Immutable }; + +export class EndpointAppConstants { + static ALERT_INDEX_NAME = 'my-index'; + static ENDPOINT_INDEX_NAME = 'endpoint-agent*'; +} + +export interface AlertResultList { + /** + * The alerts restricted by page size. + */ + alerts: AlertData[]; + + /** + * The total number of alerts on the page. + */ + total: number; + + /** + * The size of the requested page. + */ + request_page_size: number; + + /** + * The index of the requested page, starting at 0. + */ + request_page_index: number; + + /** + * The offset of the requested page, starting at 0. + */ + result_from_index: number; +} + +export interface EndpointResultList { + /* the endpoints restricted by the page size */ + endpoints: EndpointMetadata[]; + /* the total number of unique endpoints in the index */ + total: number; + /* the page size requested */ + request_page_size: number; + /* the page index requested */ + request_page_index: number; +} + +export interface AlertData { + '@timestamp': Date; + agent: { + id: string; + version: string; + }; + event: { + action: string; + }; + file_classification: { + malware_classification: { + score: number; + }; + }; + host: { + hostname: string; + ip: string; + os: { + name: string; + }; + }; + thread: {}; +} + +export interface EndpointMetadata { + event: { + created: Date; + }; + endpoint: { + policy: { + id: string; + }; + }; + agent: { + version: string; + id: string; + }; + host: { + id: string; + hostname: string; + ip: string[]; + mac: string[]; + os: { + name: string; + full: string; + version: string; + }; + }; +} + +/** + * The PageId type is used for the payload when firing userNavigatedToPage actions + */ +export type PageId = 'alertsPage' | 'managementPage' | 'policyListPage'; diff --git a/x-pack/plugins/endpoint/kibana.json b/x-pack/plugins/endpoint/kibana.json new file mode 100644 index 0000000000000..f7a4acd629324 --- /dev/null +++ b/x-pack/plugins/endpoint/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "endpoint", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "endpoint"], + "requiredPlugins": ["features", "embeddable"], + "server": true, + "ui": true +} diff --git a/x-pack/plugins/endpoint/package.json b/x-pack/plugins/endpoint/package.json new file mode 100644 index 0000000000000..25afe2c8442ba --- /dev/null +++ b/x-pack/plugins/endpoint/package.json @@ -0,0 +1,15 @@ +{ + "author": "Elastic", + "name": "endpoint", + "version": "0.0.0", + "private": true, + "license": "Elastic-License", + "scripts": {}, + "dependencies": { + "react-redux": "^7.1.0" + }, + "devDependencies": { + "@types/react-redux": "^7.1.0", + "redux-devtools-extension": "^2.13.8" + } +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts b/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts new file mode 100644 index 0000000000000..83f4bc1e79317 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import styled from 'styled-components'; + +export const TruncateText = styled.div` + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx new file mode 100644 index 0000000000000..7bb3b13525914 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import ReactDOM from 'react-dom'; +import { CoreStart, AppMountParameters } from 'kibana/public'; +import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; +import { Route, BrowserRouter, Switch } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { Store } from 'redux'; +import { appStoreFactory } from './store'; +import { AlertIndex } from './view/alerts'; +import { ManagementList } from './view/managing'; +import { PolicyList } from './view/policy'; + +/** + * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. + */ +export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMountParameters) { + coreStart.http.get('/api/endpoint/hello-world'); + + const store = appStoreFactory(coreStart); + + ReactDOM.render(, element); + + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +} + +interface RouterProps { + basename: string; + store: Store; +} + +const AppRoot: React.FunctionComponent = React.memo(({ basename, store }) => ( + + + + + ( +

+ +

+ )} + /> + + + + ( + + )} + /> +
+
+
+
+)); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts new file mode 100644 index 0000000000000..ba2e1ce8f9fe6 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './saga'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.ts new file mode 100644 index 0000000000000..7c06681184085 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSagaMiddleware, SagaContext, SagaMiddleware } from './index'; +import { applyMiddleware, createStore, Reducer, Store } from 'redux'; + +describe('saga', () => { + const INCREMENT_COUNTER = 'INCREMENT'; + const DELAYED_INCREMENT_COUNTER = 'DELAYED INCREMENT COUNTER'; + const STOP_SAGA_PROCESSING = 'BREAK ASYNC ITERATOR'; + + const sleep = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms)); + let store: Store; + let reducerA: Reducer; + let sideAffect: (a: unknown, s: unknown) => void; + let sagaExe: (sagaContext: SagaContext) => Promise; + let sagaExeReduxMiddleware: SagaMiddleware; + + beforeEach(() => { + reducerA = jest.fn((prevState = { count: 0 }, { type }) => { + switch (type) { + case INCREMENT_COUNTER: + return { ...prevState, count: prevState.count + 1 }; + default: + return prevState; + } + }); + + sideAffect = jest.fn(); + + sagaExe = jest.fn(async ({ actionsAndState, dispatch }: SagaContext) => { + for await (const { action, state } of actionsAndState()) { + expect(action).toBeDefined(); + expect(state).toBeDefined(); + + if (action.type === STOP_SAGA_PROCESSING) { + break; + } + + sideAffect(action, state); + + if (action.type === DELAYED_INCREMENT_COUNTER) { + await sleep(1); + dispatch({ + type: INCREMENT_COUNTER, + }); + } + } + }); + + sagaExeReduxMiddleware = createSagaMiddleware(sagaExe); + store = createStore(reducerA, applyMiddleware(sagaExeReduxMiddleware)); + }); + + afterEach(() => { + sagaExeReduxMiddleware.stop(); + }); + + test('it does nothing if saga is not started', () => { + expect(sagaExe).not.toHaveBeenCalled(); + }); + + test('it can dispatch store actions once running', async () => { + sagaExeReduxMiddleware.start(); + expect(store.getState()).toEqual({ count: 0 }); + expect(sagaExe).toHaveBeenCalled(); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + expect(store.getState()).toEqual({ count: 0 }); + + await sleep(); + + expect(sideAffect).toHaveBeenCalled(); + expect(store.getState()).toEqual({ count: 1 }); + }); + + test('it stops processing if break out of loop', async () => { + sagaExeReduxMiddleware.start(); + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + + store.dispatch({ type: STOP_SAGA_PROCESSING }); + await sleep(); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + }); + + test('it stops saga middleware when stop() is called', async () => { + sagaExeReduxMiddleware.start(); + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + + sagaExeReduxMiddleware.stop(); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts new file mode 100644 index 0000000000000..2a79827847f2e --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AnyAction, Dispatch, Middleware, MiddlewareAPI } from 'redux'; +import { GlobalState } from '../types'; + +interface QueuedAction { + /** + * The Redux action that was dispatched + */ + action: TAction; + /** + * The Global state at the time the action was dispatched + */ + state: GlobalState; +} + +interface IteratorInstance { + queue: QueuedAction[]; + nextResolve: null | ((inst: QueuedAction) => void); +} + +type Saga = (storeContext: SagaContext) => Promise; + +type StoreActionsAndState = AsyncIterableIterator>; + +export interface SagaContext { + /** + * A generator function that will `yield` `Promise`s that resolve with a `QueuedAction` + */ + actionsAndState: () => StoreActionsAndState; + dispatch: Dispatch; +} + +export interface SagaMiddleware extends Middleware { + /** + * Start the saga. Should be called after the `store` has been created + */ + start: () => void; + + /** + * Stop the saga by exiting the internal generator `for await...of` loop. + */ + stop: () => void; +} + +const noop = () => {}; +const STOP = Symbol('STOP'); + +/** + * Creates Saga Middleware for use with Redux. + * + * @param {Saga} saga The `saga` should initialize a long-running `for await...of` loop against + * the return value of the `actionsAndState()` method provided by the `SagaContext`. + * + * @return {SagaMiddleware} + * + * @example + * + * type TPossibleActions = { type: 'add', payload: any[] }; + * //... + * const endpointsSaga = async ({ actionsAndState, dispatch }: SagaContext) => { + * for await (const { action, state } of actionsAndState()) { + * if (action.type === "userRequestedResource") { + * const resourceData = await doApiFetch('of/some/resource'); + * dispatch({ + * type: 'add', + * payload: [ resourceData ] + * }); + * } + * } + * } + * const endpointsSagaMiddleware = createSagaMiddleware(endpointsSaga); + * //.... + * const store = createStore(reducers, [ endpointsSagaMiddleware ]); + */ +export function createSagaMiddleware(saga: Saga): SagaMiddleware { + const iteratorInstances = new Set(); + let runSaga: () => void = noop; + let stopSaga: () => void = noop; + let runningPromise: Promise; + + async function* getActionsAndStateIterator(): StoreActionsAndState { + const instance: IteratorInstance = { queue: [], nextResolve: null }; + iteratorInstances.add(instance); + + try { + while (true) { + const actionAndState = await Promise.race([nextActionAndState(), runningPromise]); + + if (actionAndState === STOP) { + break; + } + + yield actionAndState as QueuedAction; + } + } finally { + // If the consumer stops consuming this (e.g. `break` or `return` is called in the `for await` + // then this `finally` block will run and unregister this instance and reset `runSaga` + iteratorInstances.delete(instance); + runSaga = stopSaga = noop; + } + + function nextActionAndState() { + if (instance.queue.length) { + return Promise.resolve(instance.queue.shift() as QueuedAction); + } else { + return new Promise(function(resolve) { + instance.nextResolve = resolve; + }); + } + } + } + + function enqueue(value: QueuedAction) { + for (const iteratorInstance of iteratorInstances) { + iteratorInstance.queue.push(value); + if (iteratorInstance.nextResolve !== null) { + iteratorInstance.nextResolve(iteratorInstance.queue.shift() as QueuedAction); + iteratorInstance.nextResolve = null; + } + } + } + + function middleware({ getState, dispatch }: MiddlewareAPI) { + if (runSaga === noop) { + runSaga = saga.bind>(null, { + actionsAndState: getActionsAndStateIterator, + dispatch, + }); + } + return (next: Dispatch) => (action: AnyAction) => { + // Call the next dispatch method in the middleware chain. + const returnValue = next(action); + + enqueue({ + action, + state: getState(), + }); + + // This will likely be the action itself, unless a middleware further in chain changed it. + return returnValue; + }; + } + + middleware.start = () => { + runningPromise = new Promise(resolve => (stopSaga = () => resolve(STOP))); + runSaga(); + }; + + middleware.stop = () => { + stopSaga(); + }; + + return middleware; +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts new file mode 100644 index 0000000000000..d099c81317090 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementAction } from './managing'; +import { AlertAction } from './alerts'; +import { RoutingAction } from './routing'; +import { PolicyListAction } from './policy_list'; + +export type AppAction = ManagementAction | AlertAction | RoutingAction | PolicyListAction; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts new file mode 100644 index 0000000000000..464a04eff5ebd --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/action.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertListData } from '../../types'; + +interface ServerReturnedAlertsData { + type: 'serverReturnedAlertsData'; + payload: AlertListData; +} + +export type AlertAction = ServerReturnedAlertsData; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/index.ts new file mode 100644 index 0000000000000..f63910a1c305e --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { alertListReducer } from './reducer'; +export { AlertAction } from './action'; +export * from '../../types'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts new file mode 100644 index 0000000000000..4a7fac147852b --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { parse } from 'query-string'; +import { HttpFetchQuery } from 'src/core/public'; +import { AppAction } from '../action'; +import { MiddlewareFactory, AlertListData } from '../../types'; + +export const alertMiddlewareFactory: MiddlewareFactory = coreStart => { + const qp = parse(window.location.search.slice(1), { sort: false }); + + return api => next => async (action: AppAction) => { + next(action); + if (action.type === 'userNavigatedToPage' && action.payload === 'alertsPage') { + const response: AlertListData = await coreStart.http.get('/api/endpoint/alerts', { + query: qp as HttpFetchQuery, + }); + api.dispatch({ type: 'serverReturnedAlertsData', payload: response }); + } + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts new file mode 100644 index 0000000000000..de79476245d29 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/reducer.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { AlertListState } from '../../types'; +import { AppAction } from '../action'; + +const initialState = (): AlertListState => { + return { + alerts: [], + request_page_size: 10, + request_page_index: 0, + result_from_index: 0, + total: 0, + }; +}; + +export const alertListReducer: Reducer = ( + state = initialState(), + action +) => { + if (action.type === 'serverReturnedAlertsData') { + return { + ...state, + ...action.payload, + }; + } + + return state; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts new file mode 100644 index 0000000000000..51903a0a641e8 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertListState } from '../../types'; + +export const alertListData = (state: AlertListState) => state.alerts; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts new file mode 100644 index 0000000000000..8fe61ae01d319 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + createStore, + compose, + applyMiddleware, + Store, + MiddlewareAPI, + Dispatch, + Middleware, +} from 'redux'; +import { CoreStart } from 'kibana/public'; +import { appReducer } from './reducer'; +import { alertMiddlewareFactory } from './alerts/middleware'; +import { managementMiddlewareFactory } from './managing'; +import { policyListMiddlewareFactory } from './policy_list'; +import { GlobalState } from '../types'; +import { AppAction } from './action'; + +const composeWithReduxDevTools = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ + ? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ name: 'EndpointApp' }) + : compose; + +export type Selector = (state: S) => R; + +/** + * Wrap Redux Middleware and adjust 'getState()' to return the namespace from 'GlobalState that applies to the given Middleware concern. + * + * @param selector + * @param middleware + */ +export const substateMiddlewareFactory = ( + selector: Selector, + middleware: Middleware<{}, Substate, Dispatch> +): Middleware<{}, GlobalState, Dispatch> => { + return api => { + const substateAPI: MiddlewareAPI, Substate> = { + ...api, + getState() { + return selector(api.getState()); + }, + }; + return middleware(substateAPI); + }; +}; + +export const appStoreFactory = (coreStart: CoreStart): Store => { + const store = createStore( + appReducer, + composeWithReduxDevTools( + applyMiddleware( + alertMiddlewareFactory(coreStart), + substateMiddlewareFactory( + globalState => globalState.managementList, + managementMiddlewareFactory(coreStart) + ), + substateMiddlewareFactory( + globalState => globalState.policyList, + policyListMiddlewareFactory(coreStart) + ) + ) + ) + ); + + return store; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts new file mode 100644 index 0000000000000..e916dc66c59f0 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementListPagination } from '../../types'; +import { EndpointResultList } from '../../../../../common/types'; + +interface ServerReturnedManagementList { + type: 'serverReturnedManagementList'; + payload: EndpointResultList; +} + +interface UserExitedManagementList { + type: 'userExitedManagementList'; +} + +interface UserPaginatedManagementList { + type: 'userPaginatedManagementList'; + payload: ManagementListPagination; +} + +export type ManagementAction = + | ServerReturnedManagementList + | UserExitedManagementList + | UserPaginatedManagementList; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts new file mode 100644 index 0000000000000..dde0ba1e96a8a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createStore, Dispatch, Store } from 'redux'; +import { ManagementAction, managementListReducer } from './index'; +import { EndpointMetadata } from '../../../../../common/types'; +import { ManagementListState } from '../../types'; +import { listData } from './selectors'; + +describe('endpoint_list store concerns', () => { + let store: Store; + let dispatch: Dispatch; + const createTestStore = () => { + store = createStore(managementListReducer); + dispatch = store.dispatch; + }; + const generateEndpoint = (): EndpointMetadata => { + return { + event: { + created: new Date(0), + }, + endpoint: { + policy: { + id: '', + }, + }, + agent: { + version: '', + id: '', + }, + host: { + id: '', + hostname: '', + ip: [''], + mac: [''], + os: { + name: '', + full: '', + version: '', + }, + }, + }; + }; + const loadDataToStore = () => { + dispatch({ + type: 'serverReturnedManagementList', + payload: { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_page_index: 1, + total: 10, + }, + }); + }; + + describe('# Reducers', () => { + beforeEach(() => { + createTestStore(); + }); + + test('it creates default state', () => { + expect(store.getState()).toEqual({ + endpoints: [], + pageSize: 10, + pageIndex: 0, + total: 0, + loading: false, + }); + }); + + test('it handles `serverReturnedManagementList', () => { + const payload = { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_page_index: 1, + total: 10, + }; + dispatch({ + type: 'serverReturnedManagementList', + payload, + }); + + const currentState = store.getState(); + expect(currentState.endpoints).toEqual(payload.endpoints); + expect(currentState.pageSize).toEqual(payload.request_page_size); + expect(currentState.pageIndex).toEqual(payload.request_page_index); + expect(currentState.total).toEqual(payload.total); + }); + + test('it handles `userExitedManagementListPage`', () => { + loadDataToStore(); + + expect(store.getState().total).toEqual(10); + + dispatch({ type: 'userExitedManagementList' }); + expect(store.getState().endpoints.length).toEqual(0); + expect(store.getState().pageIndex).toEqual(0); + }); + }); + + describe('# Selectors', () => { + beforeEach(() => { + createTestStore(); + loadDataToStore(); + }); + + test('it selects `managementListData`', () => { + const currentState = store.getState(); + expect(listData(currentState)).toEqual(currentState.endpoints); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts new file mode 100644 index 0000000000000..f0bfe27c9e30f --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { managementListReducer } from './reducer'; +export { ManagementAction } from './action'; +export { managementMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts new file mode 100644 index 0000000000000..095e49a6c4306 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { CoreStart, HttpSetup } from 'kibana/public'; +import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { managementListReducer, managementMiddlewareFactory } from './index'; +import { EndpointMetadata, EndpointResultList } from '../../../../../common/types'; +import { ManagementListState } from '../../types'; +import { AppAction } from '../action'; +import { listData } from './selectors'; +describe('endpoint list saga', () => { + const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms)); + let fakeCoreStart: jest.Mocked; + let fakeHttpServices: jest.Mocked; + let store: Store; + let getState: typeof store['getState']; + let dispatch: Dispatch; + // https://github.com/elastic/endpoint-app-team/issues/131 + const generateEndpoint = (): EndpointMetadata => { + return { + event: { + created: new Date(0), + }, + endpoint: { + policy: { + id: '', + }, + }, + agent: { + version: '', + id: '', + }, + host: { + id: '', + hostname: '', + ip: [''], + mac: [''], + os: { + name: '', + full: '', + version: '', + }, + }, + }; + }; + const getEndpointListApiResponse = (): EndpointResultList => { + return { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_page_index: 1, + total: 10, + }; + }; + beforeEach(() => { + fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); + fakeHttpServices = fakeCoreStart.http as jest.Mocked; + store = createStore( + managementListReducer, + applyMiddleware(managementMiddlewareFactory(fakeCoreStart)) + ); + getState = store.getState; + dispatch = store.dispatch; + }); + test('it handles `userNavigatedToPage`', async () => { + const apiResponse = getEndpointListApiResponse(); + fakeHttpServices.post.mockResolvedValue(apiResponse); + expect(fakeHttpServices.post).not.toHaveBeenCalled(); + dispatch({ type: 'userNavigatedToPage', payload: 'managementPage' }); + await sleep(); + expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/endpoints', { + body: JSON.stringify({ + paging_properties: [{ page_index: 0 }, { page_size: 10 }], + }), + }); + expect(listData(getState())).toEqual(apiResponse.endpoints); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts new file mode 100644 index 0000000000000..ae756caf5aa35 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MiddlewareFactory } from '../../types'; +import { pageIndex, pageSize } from './selectors'; +import { ManagementListState } from '../../types'; +import { AppAction } from '../action'; + +export const managementMiddlewareFactory: MiddlewareFactory = coreStart => { + return ({ getState, dispatch }) => next => async (action: AppAction) => { + next(action); + if ( + (action.type === 'userNavigatedToPage' && action.payload === 'managementPage') || + action.type === 'userPaginatedManagementList' + ) { + const managementPageIndex = pageIndex(getState()); + const managementPageSize = pageSize(getState()); + const response = await coreStart.http.post('/api/endpoint/endpoints', { + body: JSON.stringify({ + paging_properties: [ + { page_index: managementPageIndex }, + { page_size: managementPageSize }, + ], + }), + }); + response.request_page_index = managementPageIndex; + dispatch({ + type: 'serverReturnedManagementList', + payload: response, + }); + } + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts new file mode 100644 index 0000000000000..bbbbdc4d17ce6 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { ManagementListState } from '../../types'; +import { AppAction } from '../action'; + +const initialState = (): ManagementListState => { + return { + endpoints: [], + pageSize: 10, + pageIndex: 0, + total: 0, + loading: false, + }; +}; + +export const managementListReducer: Reducer = ( + state = initialState(), + action +) => { + if (action.type === 'serverReturnedManagementList') { + const { + endpoints, + total, + request_page_size: pageSize, + request_page_index: pageIndex, + } = action.payload; + return { + ...state, + endpoints, + total, + pageSize, + pageIndex, + loading: false, + }; + } + + if (action.type === 'userExitedManagementList') { + return initialState(); + } + + if (action.type === 'userPaginatedManagementList') { + return { + ...state, + ...action.payload, + loading: true, + }; + } + + return state; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts new file mode 100644 index 0000000000000..3dcb144c2bade --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementListState } from '../../types'; + +export const listData = (state: ManagementListState) => state.endpoints; + +export const pageIndex = (state: ManagementListState) => state.pageIndex; + +export const pageSize = (state: ManagementListState) => state.pageSize; + +export const totalHits = (state: ManagementListState) => state.total; + +export const isLoading = (state: ManagementListState) => state.loading; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts new file mode 100644 index 0000000000000..5ac2a4328b00a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyData } from '../../types'; + +interface ServerReturnedPolicyListData { + type: 'serverReturnedPolicyListData'; + payload: { + policyItems: PolicyData[]; + total: number; + pageSize: number; + pageIndex: number; + }; +} + +interface UserPaginatedPolicyListTable { + type: 'userPaginatedPolicyListTable'; + payload: { + pageSize: number; + pageIndex: number; + }; +} + +export type PolicyListAction = ServerReturnedPolicyListData | UserPaginatedPolicyListTable; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts new file mode 100644 index 0000000000000..62bdd28f30be1 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// !!!! Should be deleted when https://github.com/elastic/endpoint-app-team/issues/150 +// is implemented + +const dateOffsets = [ + 0, + 1000, + 300000, // 5 minutes + 3.6e6, // 1 hour + 86340000, // 23h, 59m + 9e7, // 25h + 9e7 * 5, // 5d +]; + +const randomNumbers = [5, 50, 500, 5000, 50000]; + +const getRandomDateIsoString = () => { + const randomIndex = Math.floor(Math.random() * Math.floor(dateOffsets.length)); + return new Date(Date.now() - dateOffsets[randomIndex]).toISOString(); +}; + +const getRandomNumber = () => { + const randomIndex = Math.floor(Math.random() * Math.floor(randomNumbers.length)); + return randomNumbers[randomIndex]; +}; + +export const getFakeDatasourceApiResponse = async (page: number, pageSize: number) => { + await new Promise(resolve => setTimeout(resolve, 500)); + + // Emulates the API response - see PR: + // https://github.com/elastic/kibana/pull/56567/files#diff-431549a8739efe0c56763f164c32caeeR25 + return { + items: Array.from({ length: pageSize }, (x, i) => ({ + name: `policy with some protections ${i + 1}`, + total: getRandomNumber(), + pending: getRandomNumber(), + failed: getRandomNumber(), + created_by: `admin ABC`, + created: getRandomDateIsoString(), + updated_by: 'admin 123', + updated: getRandomDateIsoString(), + })), + success: true, + total: pageSize * 10, + page, + perPage: pageSize, + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts new file mode 100644 index 0000000000000..ae4a0868a68fe --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyListState } from '../../types'; +import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; +import { AppAction } from '../action'; +import { policyListReducer } from './reducer'; +import { policyListMiddlewareFactory } from './middleware'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { CoreStart } from 'kibana/public'; +import { selectIsLoading } from './selectors'; + +describe('policy list store concerns', () => { + const sleep = () => new Promise(resolve => setTimeout(resolve, 1000)); + let fakeCoreStart: jest.Mocked; + let store: Store; + let getState: typeof store['getState']; + let dispatch: Dispatch; + + beforeEach(() => { + fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); + store = createStore( + policyListReducer, + applyMiddleware(policyListMiddlewareFactory(fakeCoreStart)) + ); + getState = store.getState; + dispatch = store.dispatch; + }); + + test('it sets `isLoading` when `userNavigatedToPage`', async () => { + expect(selectIsLoading(getState())).toBe(false); + dispatch({ type: 'userNavigatedToPage', payload: 'policyListPage' }); + expect(selectIsLoading(getState())).toBe(true); + await sleep(); + expect(selectIsLoading(getState())).toBe(false); + }); + + test('it sets `isLoading` when `userPaginatedPolicyListTable`', async () => { + expect(selectIsLoading(getState())).toBe(false); + dispatch({ + type: 'userPaginatedPolicyListTable', + payload: { + pageSize: 10, + pageIndex: 1, + }, + }); + expect(selectIsLoading(getState())).toBe(true); + await sleep(); + expect(selectIsLoading(getState())).toBe(false); + }); + + test('it resets state on `userNavigatedFromPage` action', async () => { + dispatch({ + type: 'serverReturnedPolicyListData', + payload: { + policyItems: [], + pageIndex: 20, + pageSize: 50, + total: 200, + }, + }); + dispatch({ type: 'userNavigatedFromPage', payload: 'policyListPage' }); + expect(getState()).toEqual({ + policyItems: [], + isLoading: false, + pageIndex: 0, + pageSize: 10, + total: 0, + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts new file mode 100644 index 0000000000000..8086acc41d2bd --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { policyListReducer } from './reducer'; +export { PolicyListAction } from './action'; +export { policyListMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts new file mode 100644 index 0000000000000..f8e2b7d07c389 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MiddlewareFactory, PolicyListState } from '../../types'; + +export const policyListMiddlewareFactory: MiddlewareFactory = coreStart => { + return ({ getState, dispatch }) => next => async action => { + next(action); + + if ( + (action.type === 'userNavigatedToPage' && action.payload === 'policyListPage') || + action.type === 'userPaginatedPolicyListTable' + ) { + const state = getState(); + let pageSize: number; + let pageIndex: number; + + if (action.type === 'userPaginatedPolicyListTable') { + pageSize = action.payload.pageSize; + pageIndex = action.payload.pageIndex; + } else { + pageSize = state.pageSize; + pageIndex = state.pageIndex; + } + + // Need load data from API and remove fake data below + // Refactor tracked via: https://github.com/elastic/endpoint-app-team/issues/150 + const { getFakeDatasourceApiResponse } = await import('./fake_data'); + const { items: policyItems, total } = await getFakeDatasourceApiResponse(pageIndex, pageSize); + + dispatch({ + type: 'serverReturnedPolicyListData', + payload: { + policyItems, + pageIndex, + pageSize, + total, + }, + }); + } + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts new file mode 100644 index 0000000000000..77f536d413ae3 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { PolicyListState } from '../../types'; +import { AppAction } from '../action'; + +const initialPolicyListState = (): PolicyListState => { + return { + policyItems: [], + isLoading: false, + pageIndex: 0, + pageSize: 10, + total: 0, + }; +}; + +export const policyListReducer: Reducer = ( + state = initialPolicyListState(), + action +) => { + if (action.type === 'serverReturnedPolicyListData') { + return { + ...state, + ...action.payload, + isLoading: false, + }; + } + + if ( + action.type === 'userPaginatedPolicyListTable' || + (action.type === 'userNavigatedToPage' && action.payload === 'policyListPage') + ) { + return { + ...state, + isLoading: true, + }; + } + + if (action.type === 'userNavigatedFromPage' && action.payload === 'policyListPage') { + return initialPolicyListState(); + } + + return state; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts new file mode 100644 index 0000000000000..b9c2edbf5d55b --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyListState } from '../../types'; + +export const selectPolicyItems = (state: PolicyListState) => state.policyItems; + +export const selectPageIndex = (state: PolicyListState) => state.pageIndex; + +export const selectPageSize = (state: PolicyListState) => state.pageSize; + +export const selectTotal = (state: PolicyListState) => state.total; + +export const selectIsLoading = (state: PolicyListState) => state.isLoading; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts new file mode 100644 index 0000000000000..3d9d21c0da9c3 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { combineReducers, Reducer } from 'redux'; +import { managementListReducer } from './managing'; +import { AppAction } from './action'; +import { alertListReducer } from './alerts'; +import { GlobalState } from '../types'; +import { policyListReducer } from './policy_list'; + +export const appReducer: Reducer = combineReducers({ + managementList: managementListReducer, + alertList: alertListReducer, + policyList: policyListReducer, +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts new file mode 100644 index 0000000000000..9080af8c91817 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PageId } from '../../../../../common/types'; + +interface UserNavigatedToPage { + readonly type: 'userNavigatedToPage'; + readonly payload: PageId; +} + +interface UserNavigatedFromPage { + readonly type: 'userNavigatedFromPage'; + readonly payload: PageId; +} + +export type RoutingAction = UserNavigatedToPage | UserNavigatedFromPage; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/index.ts new file mode 100644 index 0000000000000..68fd04d6a8355 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { RoutingAction } from './action'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts new file mode 100644 index 0000000000000..2766707271cde --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/selectors.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GlobalState } from '../types'; +import * as alertListSelectors from './alerts/selectors'; + +export const alertListData = composeSelectors( + alertListStateSelector, + alertListSelectors.alertListData +); + +/** + * Returns the alert list state from within Global State + */ +function alertListStateSelector(state: GlobalState) { + return state.alertList; +} + +/** + * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a + * concern-specific selector. `selector` should return the concern-specific state. + */ +function composeSelectors( + selector: (state: OuterState) => InnerState, + secondSelector: (state: InnerState) => ReturnValue +): (state: OuterState) => ReturnValue { + return state => secondSelector(selector(state)); +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts new file mode 100644 index 0000000000000..6b20012592fd9 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Dispatch, MiddlewareAPI } from 'redux'; +import { CoreStart } from 'kibana/public'; +import { EndpointMetadata } from '../../../common/types'; +import { AppAction } from './store/action'; +import { AlertResultList } from '../../../common/types'; + +export type MiddlewareFactory = ( + coreStart: CoreStart +) => ( + api: MiddlewareAPI, S> +) => (next: Dispatch) => (action: AppAction) => unknown; + +export interface ManagementListState { + endpoints: EndpointMetadata[]; + total: number; + pageSize: number; + pageIndex: number; + loading: boolean; +} + +export interface ManagementListPagination { + pageIndex: number; + pageSize: number; +} + +// REFACTOR to use Types from Ingest Manager - see: https://github.com/elastic/endpoint-app-team/issues/150 +export interface PolicyData { + name: string; + total: number; + pending: number; + failed: number; + created_by: string; + created: string; + updated_by: string; + updated: string; +} + +/** + * Policy list store state + */ +export interface PolicyListState { + /** Array of policy items */ + policyItems: PolicyData[]; + /** total number of policies */ + total: number; + /** Number of policies per page */ + pageSize: number; + /** page number (zero based) */ + pageIndex: number; + /** data is being retrieved from server */ + isLoading: boolean; +} + +export interface GlobalState { + readonly managementList: ManagementListState; + readonly alertList: AlertListState; + readonly policyList: PolicyListState; +} + +export type AlertListData = AlertResultList; +export type AlertListState = AlertResultList; +export type CreateStructuredSelector = < + SelectorMap extends { [key: string]: (...args: never[]) => unknown } +>( + selectorMap: SelectorMap +) => ( + state: SelectorMap[keyof SelectorMap] extends (state: infer State) => unknown ? State : never +) => { + [Key in keyof SelectorMap]: ReturnType; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx new file mode 100644 index 0000000000000..8c32426dcc868 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/index.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { memo, useState, useMemo } from 'react'; +import React from 'react'; +import { EuiDataGrid } from '@elastic/eui'; +import { useSelector } from 'react-redux'; +import { i18n } from '@kbn/i18n'; +import * as selectors from '../../store/selectors'; +import { usePageId } from '../use_page_id'; + +export const AlertIndex = memo(() => { + usePageId('alertsPage'); + + const columns: Array<{ id: string }> = useMemo(() => { + return [ + { id: 'alert_type' }, + { id: 'event_type' }, + { id: 'os' }, + { id: 'ip_address' }, + { id: 'host_name' }, + { id: 'timestamp' }, + { id: 'archived' }, + { id: 'malware_score' }, + ]; + }, []); + + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + + const json = useSelector(selectors.alertListData); + + const renderCellValue = useMemo(() => { + return ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => { + if (rowIndex > json.length) { + return null; + } + + const row = json[rowIndex]; + + if (columnId === 'alert_type') { + return i18n.translate( + 'xpack.endpoint.application.endpoint.alerts.alertType.maliciousFileDescription', + { + defaultMessage: 'Malicious File', + } + ); + } else if (columnId === 'event_type') { + return row.event.action; + } else if (columnId === 'os') { + return row.host.os.name; + } else if (columnId === 'ip_address') { + return row.host.ip; + } else if (columnId === 'host_name') { + return row.host.hostname; + } else if (columnId === 'timestamp') { + return row['@timestamp']; + } else if (columnId === 'archived') { + return null; + } else if (columnId === 'malware_score') { + return row.file_classification.malware_classification.score; + } + return null; + }; + }, [json]); + + return ( + + ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts new file mode 100644 index 0000000000000..a0720fbd8aeeb --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useSelector } from 'react-redux'; +import { GlobalState, ManagementListState } from '../../types'; + +export function useManagementListSelector( + selector: (state: ManagementListState) => TSelected +) { + return useSelector(function(state: GlobalState) { + return selector(state.managementList); + }); +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx new file mode 100644 index 0000000000000..44b08f25c7653 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx @@ -0,0 +1,167 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiTitle, + EuiBasicTable, + EuiTextColor, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { createStructuredSelector } from 'reselect'; +import * as selectors from '../../store/managing/selectors'; +import { ManagementAction } from '../../store/managing/action'; +import { useManagementListSelector } from './hooks'; +import { usePageId } from '../use_page_id'; +import { CreateStructuredSelector } from '../../types'; + +const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); +export const ManagementList = () => { + usePageId('managementPage'); + const dispatch = useDispatch<(a: ManagementAction) => void>(); + const { + listData, + pageIndex, + pageSize, + totalHits: totalItemCount, + isLoading, + } = useManagementListSelector(selector); + + const paginationSetup = useMemo(() => { + return { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: [10, 20, 50], + hidePerPageOptions: false, + }; + }, [pageIndex, pageSize, totalItemCount]); + + const onTableChange = useCallback( + ({ page }: { page: { index: number; size: number } }) => { + const { index, size } = page; + dispatch({ + type: 'userPaginatedManagementList', + payload: { pageIndex: index, pageSize: size }, + }); + }, + [dispatch] + ); + + const columns = [ + { + field: 'host.hostname', + name: i18n.translate('xpack.endpoint.management.list.host', { + defaultMessage: 'Hostname', + }), + }, + { + field: '', + name: i18n.translate('xpack.endpoint.management.list.policy', { + defaultMessage: 'Policy', + }), + render: () => { + return 'Policy Name'; + }, + }, + { + field: '', + name: i18n.translate('xpack.endpoint.management.list.policyStatus', { + defaultMessage: 'Policy Status', + }), + render: () => { + return 'Policy Status'; + }, + }, + { + field: '', + name: i18n.translate('xpack.endpoint.management.list.alerts', { + defaultMessage: 'Alerts', + }), + render: () => { + return '0'; + }, + }, + { + field: 'host.os.name', + name: i18n.translate('xpack.endpoint.management.list.os', { + defaultMessage: 'Operating System', + }), + }, + { + field: 'host.ip', + name: i18n.translate('xpack.endpoint.management.list.ip', { + defaultMessage: 'IP Address', + }), + }, + { + field: '', + name: i18n.translate('xpack.endpoint.management.list.sensorVersion', { + defaultMessage: 'Sensor Version', + }), + render: () => { + return 'version'; + }, + }, + { + field: '', + name: i18n.translate('xpack.endpoint.management.list.lastActive', { + defaultMessage: 'Last Active', + }), + render: () => { + return 'xxxx'; + }, + }, + ]; + + return ( + + + + + + +

+ +

+
+

+ + + +

+
+
+ + + +
+
+
+ ); +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts new file mode 100644 index 0000000000000..d561da7574de0 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './policy_list'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts new file mode 100644 index 0000000000000..14558fb6504bb --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useSelector } from 'react-redux'; +import { GlobalState, PolicyListState } from '../../types'; + +export function usePolicyListSelector(selector: (state: PolicyListState) => TSelected) { + return useSelector((state: GlobalState) => selector(state.policyList)); +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx new file mode 100644 index 0000000000000..75ffa5e8806e9 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useMemo } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiTitle, + EuiBasicTable, + EuiText, + EuiTableFieldDataColumnType, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + FormattedMessage, + FormattedDate, + FormattedTime, + FormattedNumber, + FormattedRelative, +} from '@kbn/i18n/react'; +import { useDispatch } from 'react-redux'; +import styled from 'styled-components'; +import { usePageId } from '../use_page_id'; +import { + selectIsLoading, + selectPageIndex, + selectPageSize, + selectPolicyItems, + selectTotal, +} from '../../store/policy_list/selectors'; +import { usePolicyListSelector } from './policy_hooks'; +import { PolicyListAction } from '../../store/policy_list'; +import { PolicyData } from '../../types'; +import { TruncateText } from '../../components/truncate_text'; + +interface TableChangeCallbackArguments { + page: { index: number; size: number }; +} + +const TruncateTooltipText = styled(TruncateText)` + .euiToolTipAnchor { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } +`; + +const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { + // If date is greater than or equal to 24h (ago), then show it as a date + // else, show it as relative to "now" + return Date.now() - date.getTime() >= 8.64e7 ? ( + <> + + {' @'} + + + ) : ( + <> + + + ); +}; + +const renderDate = (date: string, _item: PolicyData) => ( + + + + + +); + +const renderFormattedNumber = (value: number, _item: PolicyData) => ( + + + +); + +export const PolicyList = React.memo(() => { + usePageId('policyListPage'); + + const dispatch = useDispatch<(action: PolicyListAction) => void>(); + const policyItems = usePolicyListSelector(selectPolicyItems); + const pageIndex = usePolicyListSelector(selectPageIndex); + const pageSize = usePolicyListSelector(selectPageSize); + const totalItemCount = usePolicyListSelector(selectTotal); + const loading = usePolicyListSelector(selectIsLoading); + + const paginationSetup = useMemo(() => { + return { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: [10, 20, 50], + hidePerPageOptions: false, + }; + }, [pageIndex, pageSize, totalItemCount]); + + const handleTableChange = useCallback( + ({ page: { index, size } }: TableChangeCallbackArguments) => { + dispatch({ + type: 'userPaginatedPolicyListTable', + payload: { + pageIndex: index, + pageSize: size, + }, + }); + }, + [dispatch] + ); + + const columns: Array> = useMemo( + () => [ + { + field: 'name', + name: i18n.translate('xpack.endpoint.policyList.nameField', { + defaultMessage: 'Policy Name', + }), + truncateText: true, + }, + { + field: 'total', + name: i18n.translate('xpack.endpoint.policyList.totalField', { + defaultMessage: 'Total', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'pending', + name: i18n.translate('xpack.endpoint.policyList.pendingField', { + defaultMessage: 'Pending', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'failed', + name: i18n.translate('xpack.endpoint.policyList.failedField', { + defaultMessage: 'Failed', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'created_by', + name: i18n.translate('xpack.endpoint.policyList.createdByField', { + defaultMessage: 'Created By', + }), + truncateText: true, + }, + { + field: 'created', + name: i18n.translate('xpack.endpoint.policyList.createdField', { + defaultMessage: 'Created', + }), + render: renderDate, + truncateText: true, + }, + { + field: 'updated_by', + name: i18n.translate('xpack.endpoint.policyList.updatedByField', { + defaultMessage: 'Last Updated By', + }), + truncateText: true, + }, + { + field: 'updated', + name: i18n.translate('xpack.endpoint.policyList.updatedField', { + defaultMessage: 'Last Updated', + }), + render: renderDate, + truncateText: true, + }, + ], + [] + ); + + return ( + + + + + + +

+ +

+
+

+ + + +

+
+
+ + + +
+
+
+ ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts new file mode 100644 index 0000000000000..49c39064c8d9a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { PageId } from '../../../../common/types'; +import { RoutingAction } from '../store/routing'; + +/** + * Dispatches a 'userNavigatedToPage' action with the given 'pageId' as the action payload. + * When the component is un-mounted, a `userNavigatedFromPage` action will be dispatched + * with the given `pageId`. + * + * @param pageId A page id + */ +export function usePageId(pageId: PageId) { + const dispatch: (action: RoutingAction) => unknown = useDispatch(); + useEffect(() => { + dispatch({ type: 'userNavigatedToPage', payload: pageId }); + + return () => { + dispatch({ type: 'userNavigatedFromPage', payload: pageId }); + }; + }, [dispatch, pageId]); +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/documentation/camera.md b/x-pack/plugins/endpoint/public/embeddables/resolver/documentation/camera.md new file mode 100644 index 0000000000000..aeca76fad916f --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/documentation/camera.md @@ -0,0 +1,26 @@ +# Introduction + +Resolver renders a map in a DOM element. Items on the map are placed in 2 dimensions using arbitrary units. Like other mapping software, the map can show things at different scales. The 'camera' determines what is shown on the map. + +The camera is positioned. When the user clicks-and-drags the map, the camera's position is changed. This allows the user to pan around the map and see things that would otherwise be out of view, at a given scale. + +The camera determines the scale. If the scale is smaller, the viewport of the map is larger and more is visible. This allows the user to zoom in an out. On screen controls and gestures (trackpad-pinch, or CTRL-mousewheel) change the scale. + +# Concepts + +## Scaling +The camera scale is controlled both by the user and programatically by Resolver. There is a maximum and minimum scale value (at the time of this writing they are 0.5 and 6.) This means that the map, and things on the map, will be rendered at between 0.5 and 6 times their instrinsic dimensions. + +A range control is provided so that the user can change the scale. The user can also pinch-to-zoom on Mac OS X (or use ctrl-mousewheel otherwise) to change the scale. These interactions change the `scalingFactor`. This number is between 0 and 1. It represents how zoomed-in things should be. When the `scalingFactor` is 1, the scale will be the maximum scale value. When `scalingFactor` is 0, the scale will be the minimum scale value. Otherwise we interpolate between the minimum and maximum scale factor. The rate that the scale increases between the two is controlled by `scalingFactor**zoomCurveRate` The zoom curve rate is 4 at the time of this writing. This makes it so that the change in scale is more pronounced when the user is zoomed in. + +``` +renderScale = minimumScale * (1 - scalingFactor**curveRate) + maximumScale * scalingFactor**curveRate; +``` + +## Panning +When the user clicks and drags the map, the camera is 'moved' around. This allows the user to see different things on the map. The on-screen controls provide 4 directional buttons which nudge the camera, as well as a reset button. The reset button brings the camera back where it started (0, 0). + +Resolver may programatically change the position of the camera in order to bring some interesting elements into view. + +## Animation +The camera can animate changes to its position. Animations usually have a short, fixed duration, such as 1 second. If the camera is moving a great deal during the animation, then things could end up moving across the screen too quickly. In this case, looking at Resolver might be disorienting. In order to combat this, Resolver may temporarily decrease the scale. By decreasing the scale, objects look futher away. Far away objects appear to move slower. diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/embeddable.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/embeddable.tsx new file mode 100644 index 0000000000000..6680ba615e353 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/embeddable.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import ReactDOM from 'react-dom'; +import React from 'react'; +import { Provider } from 'react-redux'; +import { Resolver } from './view'; +import { storeFactory } from './store'; +import { Embeddable } from '../../../../../../src/plugins/embeddable/public'; + +export class ResolverEmbeddable extends Embeddable { + public readonly type = 'resolver'; + private lastRenderTarget?: Element; + + public render(node: HTMLElement) { + if (this.lastRenderTarget !== undefined) { + ReactDOM.unmountComponentAtNode(this.lastRenderTarget); + } + this.lastRenderTarget = node; + const { store } = storeFactory(); + ReactDOM.render( + + + , + node + ); + } + + public reload(): void { + throw new Error('Method not implemented.'); + } + + public destroy(): void { + if (this.lastRenderTarget !== undefined) { + ReactDOM.unmountComponentAtNode(this.lastRenderTarget); + } + } +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts new file mode 100644 index 0000000000000..f5d1aad93ed57 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/factory.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { + EmbeddableFactory, + IContainer, + EmbeddableInput, +} from '../../../../../../src/plugins/embeddable/public'; +import { ResolverEmbeddable } from './embeddable'; + +export class ResolverEmbeddableFactory extends EmbeddableFactory { + public readonly type = 'resolver'; + + public isEditable() { + return true; + } + + public async create(initialInput: EmbeddableInput, parent?: IContainer) { + return new ResolverEmbeddable(initialInput, {}, parent); + } + + public getDisplayName() { + return i18n.translate('xpack.endpoint.resolver.displayNameTitle', { + defaultMessage: 'Resolver', + }); + } +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/index.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/index.ts new file mode 100644 index 0000000000000..e4f3cc90ae30a --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ResolverEmbeddableFactory } from './factory'; +export { ResolverEmbeddable } from './embeddable'; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/math.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/math.ts new file mode 100644 index 0000000000000..6bf0fedc84dfe --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/math.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * Return `value` unless it is less than `minimum`, in which case return `minimum` or unless it is greater than `maximum`, in which case return `maximum`. + */ +export function clamp(value: number, minimum: number, maximum: number) { + return Math.max(Math.min(value, maximum), minimum); +} + +/** + * linearly interpolate between `a` and `b` at a ratio of `ratio`. If `ratio` is `0`, return `a`, if ratio is `1`, return `b`. + */ +export function lerp(a: number, b: number, ratio: number): number { + return a * (1 - ratio) + b * ratio; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.test.ts new file mode 100644 index 0000000000000..c0e9f982e23ac --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.test.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { multiply } from './matrix3'; +describe('matrix3', () => { + it('can multiply two matrix3s', () => { + expect(multiply([1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18])).toEqual([ + 84, + 90, + 96, + 201, + 216, + 231, + 318, + 342, + 366, + ]); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.ts new file mode 100644 index 0000000000000..0b4a72b9d79a6 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/matrix3.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Matrix3 } from '../types'; + +/** + * Return a new matrix which is the product of the first and second matrix. + */ +export function multiply( + [a11, a12, a13, a21, a22, a23, a31, a32, a33]: Matrix3, + [b11, b12, b13, b21, b22, b23, b31, b32, b33]: Matrix3 +): Matrix3 { + const s11 = a11 * b11 + a12 * b21 + a13 * b31; + const s12 = a11 * b12 + a12 * b22 + a13 * b32; + const s13 = a11 * b13 + a12 * b23 + a13 * b33; + + const s21 = a21 * b11 + a22 * b21 + a23 * b31; + const s22 = a21 * b12 + a22 * b22 + a23 * b32; + const s23 = a21 * b13 + a22 * b23 + a23 * b33; + + const s31 = a31 * b11 + a32 * b21 + a33 * b31; + const s32 = a31 * b12 + a32 * b22 + a33 * b32; + const s33 = a31 * b13 + a32 * b23 + a33 * b33; + + // prettier-ignore + return [ + s11, s12, s13, + s21, s22, s23, + s31, s32, s33, + ]; +} + +/** + * Return a new matrix which is the sum of the two passed in. + */ +export function add( + [a11, a12, a13, a21, a22, a23, a31, a32, a33]: Matrix3, + [b11, b12, b13, b21, b22, b23, b31, b32, b33]: Matrix3 +): Matrix3 { + return [ + a11 + b11, + a12 + b12, + a13 + b13, + + a21 + b21, + a22 + b22, + a23 + b23, + + a31 + b31, + a32 + b32, + a33 + b33, + ]; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.test.ts new file mode 100644 index 0000000000000..3fe6941279bc5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.test.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { applyMatrix3 } from './vector2'; +import { scalingTransformation } from './transformation'; + +describe('transforms', () => { + it('applying a scale matrix to a vector2 can invert the y value', () => { + expect(applyMatrix3([1, 2], scalingTransformation([1, -1]))).toEqual([1, -2]); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.ts new file mode 100644 index 0000000000000..bd7d1bf743df8 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/transformation.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Matrix3, Vector2 } from '../types'; + +/** + * The inverse of `orthographicProjection`. + */ +export function inverseOrthographicProjection( + top: number, + right: number, + bottom: number, + left: number +): Matrix3 { + let m11: number; + let m13: number; + let m22: number; + let m23: number; + + /** + * If `right - left` is 0, the width is 0, so scale everything to 0 + */ + if (right - left === 0) { + m11 = 0; + m13 = 0; + } else { + m11 = (right - left) / 2; + m13 = (right + left) / (right - left); + } + + /** + * If `top - bottom` is 0, the height is 0, so scale everything to 0 + */ + if (top - bottom === 0) { + m22 = 0; + m23 = 0; + } else { + m22 = (top - bottom) / 2; + m23 = (top + bottom) / (top - bottom); + } + + return [m11, 0, m13, 0, m22, m23, 0, 0, 0]; +} + +/** + * Adjust x, y to be bounded, in scale, of a clipping plane defined by top, right, bottom, left. + * + * See explanation: + * https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix + * https://en.wikipedia.org/wiki/Orthographic_projection + */ +export function orthographicProjection( + top: number, + right: number, + bottom: number, + left: number +): Matrix3 { + let m11: number; + let m13: number; + let m22: number; + let m23: number; + + /** + * If `right - left` is 0, the width is 0, so scale everything to 0 + */ + if (right - left === 0) { + m11 = 0; + m13 = 0; + } else { + m11 = 2 / (right - left); // adjust x scale to match ndc (-1, 1) bounds + m13 = -((right + left) / (right - left)); + } + + /** + * If `top - bottom` is 0, the height is 0, so scale everything to 0 + */ + if (top - bottom === 0) { + m22 = 0; + m23 = 0; + } else { + m22 = top - bottom === 0 ? 0 : 2 / (top - bottom); // adjust y scale to match ndc (-1, 1) bounds + m23 = top - bottom === 0 ? 0 : -((top + bottom) / (top - bottom)); + } + + return [m11, 0, m13, 0, m22, m23, 0, 0, 0]; +} + +/** + * Returns a 2D transformation matrix that when applied to a vector will scale the vector by `x` and `y` in their respective axises. + * See https://en.wikipedia.org/wiki/Scaling_(geometry)#Matrix_representation + */ +export function scalingTransformation([x, y]: Vector2): Matrix3 { + // prettier-ignore + return [ + x, 0, 0, + 0, y, 0, + 0, 0, 0 + ] +} + +/** + * Returns a 2D transformation matrix that when applied to a vector will translate by `x` and `y` in their respective axises. + * See https://en.wikipedia.org/wiki/Translation_(geometry)#Matrix_representation + */ +export function translationTransformation([x, y]: Vector2): Matrix3 { + // prettier-ignore + return [ + 1, 0, x, + 0, 1, y, + 0, 0, 0 + ] +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/tree_sequencers.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/tree_sequencers.ts new file mode 100644 index 0000000000000..219c58d6efc9c --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/tree_sequencers.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * Sequences a tree, yielding children returned by the `children` function. Sequencing is done in 'depth first preorder' fashion. See https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR) + */ +export function* depthFirstPreorder(root: T, children: (parent: T) => T[]): Iterable { + const nodesToVisit = [root]; + while (nodesToVisit.length !== 0) { + const currentNode = nodesToVisit.shift(); + if (currentNode !== undefined) { + nodesToVisit.unshift(...(children(currentNode) || [])); + yield currentNode; + } + } +} + +/** + * Sequences a tree, yielding children returned by the `children` function. Sequencing is done in 'level order' fashion. + */ +export function* levelOrder(root: T, children: (parent: T) => T[]): Iterable { + let level = [root]; + while (level.length !== 0) { + let nextLevel = []; + for (const node of level) { + yield node; + nextLevel.push(...(children(node) || [])); + } + level = nextLevel; + nextLevel = []; + } +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/lib/vector2.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/vector2.ts new file mode 100644 index 0000000000000..898ce6f6bacd2 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/lib/vector2.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Vector2, Matrix3 } from '../types'; + +/** + * Returns a vector which is the sum of `a` and `b`. + */ +export function add(a: Vector2, b: Vector2): Vector2 { + return [a[0] + b[0], a[1] + b[1]]; +} + +/** + * Returns a vector which is the difference of `a` and `b`. + */ +export function subtract(a: Vector2, b: Vector2): Vector2 { + return [a[0] - b[0], a[1] - b[1]]; +} + +/** + * Returns a vector which is the quotient of `a` and `b`. + */ +export function divide(a: Vector2, b: Vector2): Vector2 { + return [a[0] / b[0], a[1] / b[1]]; +} + +/** + * Return `[ a[0] * b[0], a[1] * b[1] ]` + */ +export function multiply(a: Vector2, b: Vector2): Vector2 { + return [a[0] * b[0], a[1] * b[1]]; +} + +/** + * Returns a vector which is the result of applying a 2D transformation matrix to the provided vector. + */ +export function applyMatrix3([x, y]: Vector2, [m11, m12, m13, m21, m22, m23]: Matrix3): Vector2 { + return [x * m11 + y * m12 + m13, x * m21 + y * m22 + m23]; +} + +/** + * Returns the distance between two vectors + */ +export function distance(a: Vector2, b: Vector2) { + const [x1, y1] = a; + const [x2, y2] = b; + return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); +} + +/** + * Returns the angle between two vectors + */ +export function angle(a: Vector2, b: Vector2) { + const deltaX = b[0] - a[0]; + const deltaY = b[1] - a[1]; + return Math.atan2(deltaY, deltaX); +} + +/** + * Clamp `vector`'s components. + */ +export function clamp([x, y]: Vector2, [minX, minY]: Vector2, [maxX, maxY]: Vector2): Vector2 { + return [Math.max(minX, Math.min(maxX, x)), Math.max(minY, Math.min(maxY, y))]; +} + +/** + * Scale vector by number + */ +export function scale(a: Vector2, n: number): Vector2 { + return [a[0] * n, a[1] * n]; +} + +/** + * Linearly interpolate between `a` and `b`. + * `t` represents progress and: + * 0 <= `t` <= 1 + */ +export function lerp(a: Vector2, b: Vector2, t: number): Vector2 { + return add(scale(a, 1 - t), scale(b, t)); +} + +/** + * The length of the vector + */ +export function length([x, y]: Vector2): number { + return Math.sqrt(x * x + y * y); +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts new file mode 100644 index 0000000000000..0eb3505096b4a --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event'; +import { IndexedProcessTree, ProcessEvent } from '../types'; +import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers'; + +/** + * Create a new IndexedProcessTree from an array of ProcessEvents + */ +export function factory(processes: ProcessEvent[]): IndexedProcessTree { + const idToChildren = new Map(); + const idToValue = new Map(); + + for (const process of processes) { + idToValue.set(uniquePidForProcess(process), process); + const uniqueParentPid = uniqueParentPidForProcess(process); + const processChildren = idToChildren.get(uniqueParentPid); + if (processChildren) { + processChildren.push(process); + } else { + idToChildren.set(uniqueParentPid, [process]); + } + } + + return { + idToChildren, + idToProcess: idToValue, + }; +} + +/** + * Returns an array with any children `ProcessEvent`s of the passed in `process` + */ +export function children(tree: IndexedProcessTree, process: ProcessEvent): ProcessEvent[] { + const id = uniquePidForProcess(process); + const processChildren = tree.idToChildren.get(id); + return processChildren === undefined ? [] : processChildren; +} + +/** + * Returns the parent ProcessEvent, if any, for the passed in `childProcess` + */ +export function parent( + tree: IndexedProcessTree, + childProcess: ProcessEvent +): ProcessEvent | undefined { + const uniqueParentPid = uniqueParentPidForProcess(childProcess); + if (uniqueParentPid === undefined) { + return undefined; + } else { + return tree.idToProcess.get(uniqueParentPid); + } +} + +/** + * Number of processes in the tree + */ +export function size(tree: IndexedProcessTree) { + return tree.idToProcess.size; +} + +/** + * Return the root process + */ +export function root(tree: IndexedProcessTree) { + if (size(tree) === 0) { + return null; + } + let current: ProcessEvent = tree.idToProcess.values().next().value; + while (parent(tree, current) !== undefined) { + current = parent(tree, current)!; + } + return current; +} + +/** + * Yield processes in level order + */ +export function* levelOrder(tree: IndexedProcessTree) { + const rootNode = root(tree); + if (rootNode !== null) { + yield* baseLevelOrder(rootNode, children.bind(null, tree)); + } +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.test.ts new file mode 100644 index 0000000000000..3177671a30001 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.test.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { eventType } from './process_event'; +import { ProcessEvent } from '../types'; +import { mockProcessEvent } from './process_event_test_helpers'; + +describe('process event', () => { + describe('eventType', () => { + let event: ProcessEvent; + beforeEach(() => { + event = mockProcessEvent({ + data_buffer: { + node_id: 1, + event_type_full: 'process_event', + }, + }); + }); + it("returns the right value when the subType is 'creation_event'", () => { + event.data_buffer.event_subtype_full = 'creation_event'; + expect(eventType(event)).toEqual('processCreated'); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts new file mode 100644 index 0000000000000..c8496b8e6e7a5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessEvent } from '../types'; + +/** + * Returns true if the process's eventType is either 'processCreated' or 'processRan'. + * Resolver will only render 'graphable' process events. + */ +export function isGraphableProcess(event: ProcessEvent) { + return eventType(event) === 'processCreated' || eventType(event) === 'processRan'; +} + +/** + * Returns a custom event type for a process event based on the event's metadata. + */ +export function eventType(event: ProcessEvent) { + const { + data_buffer: { event_type_full: type, event_subtype_full: subType }, + } = event; + + if (type === 'process_event') { + if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') { + return 'processCreated'; + } else if (subType === 'already_running') { + return 'processRan'; + } else if (subType === 'termination_event') { + return 'processTerminated'; + } else { + return 'unknownProcessEvent'; + } + } else if (type === 'alert_event') { + return 'processCausedAlert'; + } + return 'unknownEvent'; +} + +/** + * Returns the process event's pid + */ +export function uniquePidForProcess(event: ProcessEvent) { + return event.data_buffer.node_id; +} + +/** + * Returns the process event's parent pid + */ +export function uniqueParentPidForProcess(event: ProcessEvent) { + return event.data_buffer.source_id; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event_test_helpers.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event_test_helpers.ts new file mode 100644 index 0000000000000..9a6f19adcc101 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event_test_helpers.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessEvent } from '../types'; +type DeepPartial = { [K in keyof T]?: DeepPartial }; + +/** + * Creates a mock process event given the 'parts' argument, which can + * include all or some process event fields as determined by the ProcessEvent type. + * The only field that must be provided is the event's 'node_id' field. + * The other fields are populated by the function unless provided in 'parts' + */ +export function mockProcessEvent( + parts: { + data_buffer: { node_id: ProcessEvent['data_buffer']['node_id'] }; + } & DeepPartial +): ProcessEvent { + const { data_buffer: dataBuffer } = parts; + return { + event_timestamp: 1, + event_type: 1, + machine_id: '', + ...parts, + data_buffer: { + timestamp_utc: '2019-09-24 01:47:47Z', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + process_name: '', + process_path: '', + ...dataBuffer, + }, + }; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts new file mode 100644 index 0000000000000..25f196c76a290 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { ProcessEvent } from '../types'; +import { CameraAction } from './camera'; +import { DataAction } from './data'; + +/** + * When the user wants to bring a process node front-and-center on the map. + */ +interface UserBroughtProcessIntoView { + readonly type: 'userBroughtProcessIntoView'; + readonly payload: { + /** + * Used to identify the process node that should be brought into view. + */ + readonly process: ProcessEvent; + /** + * The time (since epoch in milliseconds) when the action was dispatched. + */ + readonly time: number; + }; +} + +export type ResolverAction = CameraAction | DataAction | UserBroughtProcessIntoView; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/action.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/action.ts new file mode 100644 index 0000000000000..dcc6c2c9c9411 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/action.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Vector2 } from '../../types'; + +interface TimestampedPayload { + /** + * Time (since epoch in milliseconds) when this action was dispatched. + */ + readonly time: number; +} + +interface UserSetZoomLevel { + readonly type: 'userSetZoomLevel'; + /** + * A number whose value is always between 0 and 1 and will be the new scaling factor for the projection. + */ + readonly payload: number; +} + +interface UserClickedZoomOut { + readonly type: 'userClickedZoomOut'; +} + +interface UserClickedZoomIn { + readonly type: 'userClickedZoomIn'; +} + +interface UserZoomed { + readonly type: 'userZoomed'; + readonly payload: { + /** + * A value to zoom in by. Should be a fraction of `1`. For a `'wheel'` event when `event.deltaMode` is `'pixel'`, + * pass `event.deltaY / -renderHeight` where `renderHeight` is the height of the Resolver element in pixels. + */ + readonly zoomChange: number; + } & TimestampedPayload; +} + +interface UserSetRasterSize { + readonly type: 'userSetRasterSize'; + /** + * The dimensions of the Resolver component in pixels. The Resolver component should not be scrollable itself. + */ + readonly payload: Vector2; +} + +/** + * When the user warps the camera to an exact point instantly. + */ +interface UserSetPositionOfCamera { + readonly type: 'userSetPositionOfCamera'; + /** + * The world transform of the camera + */ + readonly payload: Vector2; +} + +interface UserStartedPanning { + readonly type: 'userStartedPanning'; + + readonly payload: { + /** + * A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen) + * relative to the Resolver component. + * Represents a starting position during panning for a pointing device. + */ + readonly screenCoordinates: Vector2; + } & TimestampedPayload; +} + +interface UserStoppedPanning { + readonly type: 'userStoppedPanning'; + + readonly payload: TimestampedPayload; +} + +interface UserNudgedCamera { + readonly type: 'userNudgedCamera'; + /** + * String that represents the direction in which Resolver can be panned + */ + readonly payload: { + /** + * A cardinal direction to move the users perspective in. + */ + readonly direction: Vector2; + } & TimestampedPayload; +} + +interface UserMovedPointer { + readonly type: 'userMovedPointer'; + readonly payload: { + /** + * A vector in screen coordinates relative to the Resolver component. + * The payload should be contain clientX and clientY minus the client position of the Resolver component. + */ + screenCoordinates: Vector2; + } & TimestampedPayload; +} + +export type CameraAction = + | UserSetZoomLevel + | UserSetRasterSize + | UserSetPositionOfCamera + | UserStartedPanning + | UserStoppedPanning + | UserZoomed + | UserMovedPointer + | UserClickedZoomOut + | UserClickedZoomIn + | UserNudgedCamera; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/animation.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/animation.test.ts new file mode 100644 index 0000000000000..795344d8af092 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/animation.test.ts @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createStore, Store, Reducer } from 'redux'; +import { cameraReducer, cameraInitialState } from './reducer'; +import { CameraState, Vector2, ResolverAction } from '../../types'; +import * as selectors from './selectors'; +import { animatePanning } from './methods'; +import { lerp } from '../../lib/math'; + +type TestAction = + | ResolverAction + | { + readonly type: 'animatePanning'; + readonly payload: { + /** + * The start time of the animation. + */ + readonly time: number; + /** + * The duration of the animation. + */ + readonly duration: number; + /** + * The target translation the camera will animate towards. + */ + readonly targetTranslation: Vector2; + }; + }; + +describe('when the camera is created', () => { + let store: Store; + beforeEach(() => { + const testReducer: Reducer = ( + state = cameraInitialState(), + action + ): CameraState => { + // If the test action is fired, call the animatePanning method + if (action.type === 'animatePanning') { + const { + payload: { time, targetTranslation, duration }, + } = action; + return animatePanning(state, time, targetTranslation, duration); + } + return cameraReducer(state, action); + }; + store = createStore(testReducer); + }); + it('should be at 0,0', () => { + expect(selectors.translation(store.getState())(0)).toEqual([0, 0]); + }); + it('should have scale of [1,1]', () => { + expect(selectors.scale(store.getState())(0)).toEqual([1, 1]); + }); + describe('when animation begins', () => { + const duration = 1000; + let targetTranslation: Vector2; + const startTime = 0; + beforeEach(() => { + // The distance the camera moves must be nontrivial in order to trigger a scale animation + targetTranslation = [1000, 1000]; + const action: TestAction = { + type: 'animatePanning', + payload: { + time: startTime, + duration, + targetTranslation, + }, + }; + store.dispatch(action); + }); + describe('when the animation is in progress', () => { + let translationAtIntervals: Vector2[]; + let scaleAtIntervals: Vector2[]; + beforeEach(() => { + translationAtIntervals = []; + scaleAtIntervals = []; + const state = store.getState(); + for (let progress = 0; progress <= 1; progress += 0.1) { + translationAtIntervals.push( + selectors.translation(state)(lerp(startTime, startTime + duration, progress)) + ); + scaleAtIntervals.push( + selectors.scale(state)(lerp(startTime, startTime + duration, progress)) + ); + } + }); + it('should gradually translate to the target', () => { + expect(translationAtIntervals).toMatchInlineSnapshot(` + Array [ + Array [ + 0, + 0, + ], + Array [ + 4.000000000000001, + 4.000000000000001, + ], + Array [ + 32.00000000000001, + 32.00000000000001, + ], + Array [ + 108.00000000000004, + 108.00000000000004, + ], + Array [ + 256.00000000000006, + 256.00000000000006, + ], + Array [ + 500, + 500, + ], + Array [ + 744, + 744, + ], + Array [ + 891.9999999999999, + 891.9999999999999, + ], + Array [ + 968, + 968, + ], + Array [ + 996, + 996, + ], + Array [ + 1000, + 1000, + ], + ] + `); + }); + it('should gradually zoom in and out to the target', () => { + expect(scaleAtIntervals).toMatchInlineSnapshot(` + Array [ + Array [ + 1, + 1, + ], + Array [ + 0.9873589660765236, + 0.9873589660765236, + ], + Array [ + 0.8988717286121894, + 0.8988717286121894, + ], + Array [ + 0.7060959612791753, + 0.7060959612791753, + ], + Array [ + 0.6176087238148411, + 0.6176087238148411, + ], + Array [ + 0.6049676898913647, + 0.6049676898913647, + ], + Array [ + 0.6176087238148411, + 0.6176087238148411, + ], + Array [ + 0.7060959612791753, + 0.7060959612791753, + ], + Array [ + 0.8988717286121893, + 0.8988717286121893, + ], + Array [ + 0.9873589660765237, + 0.9873589660765237, + ], + Array [ + 1, + 1, + ], + ] + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/index.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/index.ts new file mode 100644 index 0000000000000..f5e7d73aa10d5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * The 'camera' in Resolver models the visible area of the Resolver map. Resolver users + * can click and drag the Resolver element to pan the map. They can pinch the trackpad + * or use Ctrl-MouseWheel to _zoom_, which changes the scale. + * + * The camera considers the size of Resolver in pixels, and it considers any panning that + * has been done, and it considers the scale. With this information it maps points on + * the screen to points in Resolver's 'world'. Entities that are mapped in Resolver + * are positioned in these unitless 'world' coordinates, and where they show up (if at all) + * on the screen is determined by the camera. + * + * In the future, we may cull entities before rendering them to the DOM. Entities that + * would not be in the camera's viewport would be ignored. + */ +export { cameraReducer } from './reducer'; +export { CameraAction } from './action'; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/inverse_projection_matrix.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/inverse_projection_matrix.test.ts new file mode 100644 index 0000000000000..000dbb8d52841 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/inverse_projection_matrix.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store, createStore } from 'redux'; +import { CameraAction } from './action'; +import { CameraState } from '../../types'; +import { cameraReducer } from './reducer'; +import { inverseProjectionMatrix } from './selectors'; +import { applyMatrix3 } from '../../lib/vector2'; +import { scaleToZoom } from './scale_to_zoom'; + +describe('inverseProjectionMatrix', () => { + let store: Store; + let compare: (worldPosition: [number, number], expectedRasterPosition: [number, number]) => void; + beforeEach(() => { + store = createStore(cameraReducer, undefined); + compare = (rasterPosition: [number, number], expectedWorldPosition: [number, number]) => { + // time isn't really relevant as we aren't testing animation + const time = 0; + const [worldX, worldY] = applyMatrix3( + rasterPosition, + inverseProjectionMatrix(store.getState())(time) + ); + expect(worldX).toBeCloseTo(expectedWorldPosition[0]); + expect(worldY).toBeCloseTo(expectedWorldPosition[1]); + }; + }); + + describe('when the raster size is 0x0 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetRasterSize', payload: [0, 0] }; + store.dispatch(action); + }); + it('should convert 0,0 in raster space to 0,0 (center) in world space', () => { + compare([10, 0], [0, 0]); + }); + }); + + describe('when the raster size is 300 x 200 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetRasterSize', payload: [300, 200] }; + store.dispatch(action); + }); + it('should convert 150,100 in raster space to 0,0 (center) in world space', () => { + compare([150, 100], [0, 0]); + }); + it('should convert 150,0 in raster space to 0,100 (top) in world space', () => { + compare([150, 0], [0, 100]); + }); + it('should convert 300,0 in raster space to 150,100 (top right) in world space', () => { + compare([300, 0], [150, 100]); + }); + it('should convert 300,100 in raster space to 150,0 (right) in world space', () => { + compare([300, 100], [150, 0]); + }); + it('should convert 300,200 in raster space to 150,-100 (right bottom) in world space', () => { + compare([300, 200], [150, -100]); + }); + it('should convert 150,200 in raster space to 0,-100 (bottom) in world space', () => { + compare([150, 200], [0, -100]); + }); + it('should convert 0,200 in raster space to -150,-100 (bottom left) in world space', () => { + compare([0, 200], [-150, -100]); + }); + it('should convert 0,100 in raster space to -150,0 (left) in world space', () => { + compare([0, 100], [-150, 0]); + }); + it('should convert 0,0 in raster space to -150,100 (top left) in world space', () => { + compare([0, 0], [-150, 100]); + }); + describe('when the user has zoomed to 0.5', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(0.5) }; + store.dispatch(action); + }); + it('should convert 150, 100 (center) to 0, 0 (center) in world space', () => { + compare([150, 100], [0, 0]); + }); + }); + describe('when the user has panned to the right and up by 50', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetPositionOfCamera', payload: [50, 50] }; + store.dispatch(action); + }); + it('should convert 100,150 in raster space to 0,0 (center) in world space', () => { + compare([100, 150], [0, 0]); + }); + it('should convert 150,100 (center) in raster space to 50,50 (right and up a bit) in world space', () => { + compare([150, 100], [50, 50]); + }); + it('should convert 160,210 (center) in raster space to 60,-60 (right and down a bit) in world space', () => { + compare([160, 210], [60, -60]); + }); + }); + describe('when the user has panned to the right by 350 and up by 250', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetPositionOfCamera', payload: [350, 250] }; + store.dispatch(action); + }); + describe('when the user has scaled to 2', () => { + // the viewport will only cover half, or 150x100 instead of 300x200 + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(2) }; + store.dispatch(action); + }); + // we expect the viewport to be + // minX = 350 - (150/2) = 275 + // maxX = 350 + (150/2) = 425 + // minY = 250 - (100/2) = 200 + // maxY = 250 + (100/2) = 300 + it('should convert 150,100 (center) in raster space to 350,250 in world space', () => { + compare([150, 100], [350, 250]); + }); + it('should convert 0,0 (top left) in raster space to 275,300 in world space', () => { + compare([0, 0], [275, 300]); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/methods.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/methods.ts new file mode 100644 index 0000000000000..4afbacb819b1a --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/methods.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { translation } from './selectors'; +import { CameraState, Vector2 } from '../../types'; + +/** + * Return a new `CameraState` with the `animation` property + * set. The camera will animate to `targetTranslation` over `duration`. + */ +export function animatePanning( + state: CameraState, + startTime: number, + targetTranslation: Vector2, + duration: number +): CameraState { + const nextState: CameraState = { + ...state, + /** + * This cancels panning if any was taking place. + */ + panning: undefined, + translationNotCountingCurrentPanning: targetTranslation, + animation: { + startTime, + targetTranslation, + initialTranslation: translation(state)(startTime), + duration, + }, + }; + + return nextState; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/panning.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/panning.test.ts new file mode 100644 index 0000000000000..9a9a5ea1c0cfc --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/panning.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store, createStore } from 'redux'; +import { cameraReducer } from './reducer'; +import { CameraState, Vector2 } from '../../types'; +import { CameraAction } from './action'; +import { translation } from './selectors'; + +describe('panning interaction', () => { + let store: Store; + let translationShouldBeCloseTo: (expectedTranslation: Vector2) => void; + let time: number; + + beforeEach(() => { + // The time isn't relevant as we don't use animations in this suite. + time = 0; + store = createStore(cameraReducer, undefined); + translationShouldBeCloseTo = expectedTranslation => { + const actualTranslation = translation(store.getState())(time); + expect(expectedTranslation[0]).toBeCloseTo(actualTranslation[0]); + expect(expectedTranslation[1]).toBeCloseTo(actualTranslation[1]); + }; + }); + describe('when the raster size is 300 x 200 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetRasterSize', payload: [300, 200] }; + store.dispatch(action); + }); + it('should have a translation of 0,0', () => { + translationShouldBeCloseTo([0, 0]); + }); + describe('when the user has started panning at (100, 100)', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userStartedPanning', + payload: { screenCoordinates: [100, 100], time }, + }; + store.dispatch(action); + }); + it('should have a translation of 0,0', () => { + translationShouldBeCloseTo([0, 0]); + }); + describe('when the user moves their pointer 50px up and right (towards the top right of the screen)', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userMovedPointer', + payload: { screenCoordinates: [150, 50], time }, + }; + store.dispatch(action); + }); + it('should have a translation of [-50, -50] as the camera is now focused on things lower and to the left.', () => { + translationShouldBeCloseTo([-50, -50]); + }); + describe('when the user then stops panning', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userStoppedPanning', + payload: { time }, + }; + store.dispatch(action); + }); + it('should still have a translation of [-50, -50]', () => { + translationShouldBeCloseTo([-50, -50]); + }); + }); + }); + }); + }); + describe('when the user nudges the camera up', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userNudgedCamera', + payload: { direction: [0, 1], time }, + }; + store.dispatch(action); + }); + it('the camera eventually moves up so that objects appear closer to the bottom of the screen', () => { + const aBitIntoTheFuture = time + 100; + + /** + * Check the position once the animation has advanced 100ms + */ + const actual: Vector2 = translation(store.getState())(aBitIntoTheFuture); + expect(actual).toMatchInlineSnapshot(` + Array [ + 0, + 7.4074074074074066, + ] + `); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/projection_matrix.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/projection_matrix.test.ts new file mode 100644 index 0000000000000..e868424d06c94 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/projection_matrix.test.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store, createStore } from 'redux'; +import { CameraAction } from './action'; +import { CameraState } from '../../types'; +import { cameraReducer } from './reducer'; +import { projectionMatrix } from './selectors'; +import { applyMatrix3 } from '../../lib/vector2'; +import { scaleToZoom } from './scale_to_zoom'; + +describe('projectionMatrix', () => { + let store: Store; + let compare: (worldPosition: [number, number], expectedRasterPosition: [number, number]) => void; + beforeEach(() => { + store = createStore(cameraReducer, undefined); + compare = (worldPosition: [number, number], expectedRasterPosition: [number, number]) => { + // time isn't really relevant as we aren't testing animation + const time = 0; + const [rasterX, rasterY] = applyMatrix3( + worldPosition, + projectionMatrix(store.getState())(time) + ); + expect(rasterX).toBeCloseTo(expectedRasterPosition[0]); + expect(rasterY).toBeCloseTo(expectedRasterPosition[1]); + }; + }); + describe('when the raster size is 0 x 0 pixels (unpainted)', () => { + it('should convert 0,0 (center) in world space to 0,0 in raster space', () => { + compare([0, 0], [0, 0]); + }); + }); + describe('when the raster size is 300 x 200 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetRasterSize', payload: [300, 200] }; + store.dispatch(action); + }); + it('should convert 0,0 (center) in world space to 150,100 in raster space', () => { + compare([0, 0], [150, 100]); + }); + it('should convert 0,100 (top) in world space to 150,0 in raster space', () => { + compare([0, 100], [150, 0]); + }); + it('should convert 150,100 (top right) in world space to 300,0 in raster space', () => { + compare([150, 100], [300, 0]); + }); + it('should convert 150,0 (right) in world space to 300,100 in raster space', () => { + compare([150, 0], [300, 100]); + }); + it('should convert 150,-100 (right bottom) in world space to 300,200 in raster space', () => { + compare([150, -100], [300, 200]); + }); + it('should convert 0,-100 (bottom) in world space to 150,200 in raster space', () => { + compare([0, -100], [150, 200]); + }); + it('should convert -150,-100 (bottom left) in world space to 0,200 in raster space', () => { + compare([-150, -100], [0, 200]); + }); + it('should convert -150,0 (left) in world space to 0,100 in raster space', () => { + compare([-150, 0], [0, 100]); + }); + it('should convert -150,100 (top left) in world space to 0,0 in raster space', () => { + compare([-150, 100], [0, 0]); + }); + describe('when the user has zoomed to 0.5', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(0.5) }; + store.dispatch(action); + }); + it('should convert 0, 0 (center) in world space to 150, 100 (center)', () => { + compare([0, 0], [150, 100]); + }); + }); + describe('when the user has panned to the right and up by 50', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetPositionOfCamera', payload: [50, 50] }; + store.dispatch(action); + }); + it('should convert 0,0 (center) in world space to 100,150 in raster space', () => { + compare([0, 0], [100, 150]); + }); + it('should convert 50,50 (right and up a bit) in world space to 150,100 (center) in raster space', () => { + compare([50, 50], [150, 100]); + }); + it('should convert 60,-60 (right and down a bit) in world space to 160,210 (center) in raster space', () => { + compare([60, -60], [160, 210]); + }); + }); + describe('when the user has panned to the right by 350 and up by 250', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userSetPositionOfCamera', + payload: [350, 250], + }; + store.dispatch(action); + }); + it('should convert 350,250 in world space to 150,100 (center) in raster space', () => { + compare([350, 250], [150, 100]); + }); + describe('when the user has scaled to 2', () => { + // the viewport will only cover half, or 150x100 instead of 300x200 + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(2) }; + store.dispatch(action); + }); + // we expect the viewport to be + // minX = 350 - (150/2) = 275 + // maxX = 350 + (150/2) = 425 + // minY = 250 - (100/2) = 200 + // maxY = 250 + (100/2) = 300 + it('should convert 350,250 in world space to 150,100 (center) in raster space', () => { + compare([350, 250], [150, 100]); + }); + it('should convert 275,300 in world space to 0,0 (top left) in raster space', () => { + compare([275, 300], [0, 0]); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/reducer.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/reducer.ts new file mode 100644 index 0000000000000..0f6ae1b7d904a --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/reducer.ts @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { unitsPerNudge, nudgeAnimationDuration } from './scaling_constants'; +import { animatePanning } from './methods'; +import * as vector2 from '../../lib/vector2'; +import * as selectors from './selectors'; +import { clamp } from '../../lib/math'; + +import { CameraState, ResolverAction, Vector2 } from '../../types'; +import { scaleToZoom } from './scale_to_zoom'; + +/** + * Used in tests. + */ +export function cameraInitialState(): CameraState { + const state: CameraState = { + scalingFactor: scaleToZoom(1), // Defaulted to 1 to 1 scale + rasterSize: [0, 0] as const, + translationNotCountingCurrentPanning: [0, 0] as const, + latestFocusedWorldCoordinates: null, + animation: undefined, + panning: undefined, + }; + return state; +} + +export const cameraReducer: Reducer = ( + state = cameraInitialState(), + action +) => { + if (action.type === 'userSetZoomLevel') { + /** + * Handle the scale being explicitly set, for example by a 'reset zoom' feature, or by a range slider with exact scale values + */ + + const nextState: CameraState = { + ...state, + scalingFactor: clamp(action.payload, 0, 1), + }; + return nextState; + } else if (action.type === 'userClickedZoomIn') { + return { + ...state, + scalingFactor: clamp(state.scalingFactor + 0.1, 0, 1), + }; + } else if (action.type === 'userClickedZoomOut') { + return { + ...state, + scalingFactor: clamp(state.scalingFactor - 0.1, 0, 1), + }; + } else if (action.type === 'userZoomed') { + const stateWithNewScaling: CameraState = { + ...state, + scalingFactor: clamp(state.scalingFactor + action.payload.zoomChange, 0, 1), + }; + + /** + * Zooming fundamentally just changes the scale, but that would always zoom in (or out) around the center of the map. The user might be interested in + * something else, like a node. If the user has moved their pointer on to the map, we can keep the pointer over the same point in the map by adjusting the + * panning when we zoom. + * + * You can see this in action by moving your pointer over a node that isn't directly in the center of the map and then changing the zoom level. Do it by + * using CTRL and the mousewheel, or by pinching the trackpad on a Mac. The node will stay under your mouse cursor and other things in the map will get + * nearer or further from the mouse cursor. This lets you keep your context when changing zoom levels. + */ + if ( + state.latestFocusedWorldCoordinates !== null && + !selectors.isAnimating(state)(action.payload.time) + ) { + const rasterOfLastFocusedWorldCoordinates = vector2.applyMatrix3( + state.latestFocusedWorldCoordinates, + selectors.projectionMatrix(state)(action.payload.time) + ); + const newWorldCoordinatesAtLastFocusedPosition = vector2.applyMatrix3( + rasterOfLastFocusedWorldCoordinates, + selectors.inverseProjectionMatrix(stateWithNewScaling)(action.payload.time) + ); + + /** + * The change in world position incurred by changing scale. + */ + const delta = vector2.subtract( + newWorldCoordinatesAtLastFocusedPosition, + state.latestFocusedWorldCoordinates + ); + + /** + * Adjust for the change in position due to scale. + */ + const translationNotCountingCurrentPanning: Vector2 = vector2.subtract( + stateWithNewScaling.translationNotCountingCurrentPanning, + delta + ); + + const nextState: CameraState = { + ...stateWithNewScaling, + translationNotCountingCurrentPanning, + }; + + return nextState; + } else { + return stateWithNewScaling; + } + } else if (action.type === 'userSetPositionOfCamera') { + /** + * Handle the case where the position of the camera is explicitly set, for example by a 'back to center' feature. + */ + const nextState: CameraState = { + ...state, + animation: undefined, + translationNotCountingCurrentPanning: action.payload, + }; + return nextState; + } else if (action.type === 'userStartedPanning') { + if (selectors.isAnimating(state)(action.payload.time)) { + return state; + } + /** + * When the user begins panning with a mousedown event we mark the starting position for later comparisons. + */ + const nextState: CameraState = { + ...state, + animation: undefined, + panning: { + origin: action.payload.screenCoordinates, + currentOffset: action.payload.screenCoordinates, + }, + }; + return nextState; + } else if (action.type === 'userStoppedPanning') { + /** + * When the user stops panning (by letting up on the mouse) we calculate the new translation of the camera. + */ + const nextState: CameraState = { + ...state, + translationNotCountingCurrentPanning: selectors.translation(state)(action.payload.time), + panning: undefined, + }; + return nextState; + } else if (action.type === 'userNudgedCamera') { + const { direction, time } = action.payload; + /** + * Nudge less when zoomed in. + */ + const nudge = vector2.multiply( + vector2.divide([unitsPerNudge, unitsPerNudge], selectors.scale(state)(time)), + direction + ); + + return animatePanning( + state, + time, + vector2.add(state.translationNotCountingCurrentPanning, nudge), + nudgeAnimationDuration + ); + } else if (action.type === 'userSetRasterSize') { + /** + * Handle resizes of the Resolver component. We need to know the size in order to convert between screen + * and world coordinates. + */ + const nextState: CameraState = { + ...state, + rasterSize: action.payload, + }; + return nextState; + } else if (action.type === 'userMovedPointer') { + let stateWithUpdatedPanning: CameraState = state; + if (state.panning) { + stateWithUpdatedPanning = { + ...state, + panning: { + origin: state.panning.origin, + currentOffset: action.payload.screenCoordinates, + }, + }; + } + const nextState: CameraState = { + ...stateWithUpdatedPanning, + /** + * keep track of the last world coordinates the user moved over. + * When the scale of the projection matrix changes, we adjust the camera's world transform in order + * to keep the same point under the pointer. + * In order to do this, we need to know the position of the mouse when changing the scale. + */ + latestFocusedWorldCoordinates: vector2.applyMatrix3( + action.payload.screenCoordinates, + selectors.inverseProjectionMatrix(stateWithUpdatedPanning)(action.payload.time) + ), + }; + return nextState; + } else { + return state; + } +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scale_to_zoom.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scale_to_zoom.ts new file mode 100644 index 0000000000000..534e20e9ed3c4 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scale_to_zoom.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { maximum, minimum, zoomCurveRate } from './scaling_constants'; + +/** + * Calculates the zoom factor (between 0 and 1) for a given scale value. + */ +export const scaleToZoom = (scale: number): number => { + const delta = maximum - minimum; + return Math.pow((scale - minimum) / delta, 1 / zoomCurveRate); +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scaling_constants.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scaling_constants.ts new file mode 100644 index 0000000000000..243d8877a8b0d --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/scaling_constants.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * The minimum allowed value for the camera scale. This is the least scale that we will ever render something at. + */ +export const minimum = 0.5; + +/** + * The maximum allowed value for the camera scale. This is greatest scale that we will ever render something at. + */ +export const maximum = 6; + +/** + * The curve of the zoom function growth rate. The higher the scale factor is, the higher the zoom rate will be. + */ +export const zoomCurveRate = 4; + +/** + * The size, in world units, of a 'nudge' as caused by clicking the up, right, down, or left panning buttons. + */ +export const unitsPerNudge = 50; + +/** + * The duration a nudge animation lasts. + */ +export const nudgeAnimationDuration = 300; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/selectors.ts new file mode 100644 index 0000000000000..226e36f63d788 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/selectors.ts @@ -0,0 +1,520 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSelector, defaultMemoize } from 'reselect'; +import { easing } from 'ts-easing'; +import { clamp, lerp } from '../../lib/math'; +import * as vector2 from '../../lib/vector2'; +import { multiply, add as addMatrix } from '../../lib/matrix3'; +import { + inverseOrthographicProjection, + scalingTransformation, + orthographicProjection, + translationTransformation, +} from '../../lib/transformation'; +import * as scalingConstants from './scaling_constants'; +import { Vector2, CameraState, AABB, Matrix3, CameraAnimationState } from '../../types'; + +interface ClippingPlanes { + renderWidth: number; + renderHeight: number; + clippingPlaneRight: number; + clippingPlaneTop: number; + clippingPlaneLeft: number; + clippingPlaneBottom: number; +} + +function animationIsActive(animation: CameraAnimationState, time: number): boolean { + return animation.startTime + animation.duration >= time; +} + +/** + * The scale by which world values are scaled when rendered. + * + * When the camera position (translation) is changed programatically, it may be animated. + * The duration of the animation is generally fixed for a given type of interaction. This way + * the user won't have to wait for a variable amount of time to complete their interaction. + * + * Since the duration is fixed and the amount that the camera position changes is variable, + * the speed at which the camera changes is also variable. If the distance the camera will move + * is very far, the camera will move very fast. + * + * When the camera moves fast, elements will move across the screen quickly. These + * quick moving elements can be distracting to the user. They may also hinder the quality of + * animation. + * + * The speed at which objects move across the screen is dependent on the speed of the camera + * as well as the scale. If the scale is high, the camera is zoomed in, and so objects move + * across the screen faster at a given camera speed. Think of looking into a telephoto lense + * and moving around only a few degrees: many things might pass through your sight. + * + * If the scale is low, the camera is zoomed out, objects look further away, and so they move + * across the screen slower at a given camera speed. Therefore we can control the speed at + * which objects move across the screen without changing the camera speed. We do this by changing scale. + * + * Changing the scale abruptly isn't acceptable because it would be visually jarring. Also, the + * change in scale should be temporary, and the original scale should be resumed after the animation. + * + * In order to change the scale to lower value, and then back, without being jarring to the user, + * we calculate a temporary target scale and animate to it. + * + */ +export const scale: (state: CameraState) => (time: number) => Vector2 = createSelector( + state => state.scalingFactor, + state => state.animation, + (scalingFactor, animation) => { + const scaleNotCountingAnimation = scaleFromScalingFactor(scalingFactor); + /** + * If `animation` is defined, an animation may be in progress when the returned function is called + */ + if (animation !== undefined) { + /** + * The distance the camera will move during the animation is used to determine the camera speed. + */ + const panningDistance = vector2.distance( + animation.targetTranslation, + animation.initialTranslation + ); + + const panningDistanceInPixels = panningDistance * scaleNotCountingAnimation; + + /** + * The speed at which pixels move across the screen during animation in pixels per millisecond. + */ + const speed = panningDistanceInPixels / animation.duration; + + /** + * The speed (in pixels per millisecond) at which an animation is triggered is a constant. + * If the camera isn't moving very fast, no change in scale is necessary. + */ + const speedThreshold = 0.4; + + /** + * Growth in speed beyond the threshold is taken to the power of a constant. This limits the + * rate of growth of speed. + */ + const speedGrowthFactor = 0.4; + + /* + * Limit the rate of growth of speed. If the speed is too great, the animation will be + * unpleasant and have poor performance. + * + * gnuplot> plot [x=0:10][y=0:3] threshold=0.4, growthFactor=0.4, x < threshold ? x : x ** growthFactor - (threshold ** growthFactor - threshold) + * + * + * 3 +----------------------------------------------------------------------------+ + * | target speed + + + | + * | | + * | ******* | + * | | + * | | + * 2.5 |-+ +-| + * | | + * | | + * | **| + * | ******* | + * | ****** | + * 2 |-+ ****** +-| + * | ***** | + * | ***** | + * | ***** | + * | ***** | + * 1.5 |-+ ***** +-| + * | **** | + * | **** | + * | **** | + * | *** | + * | *** | + * 1 |-+ ** +-| + * | *** | + * | *** | + * | * | + * | ** | + * | ** | + * 0.5 |-+ * +-| + * | ** | + * | * | + * | * | + * | * | + * |* + + + + | + * 0 +----------------------------------------------------------------------------+ + * 0 2 4 6 8 10 + * camera speed (pixels per ms) + * + **/ + const limitedSpeed = + speed < speedThreshold + ? speed + : speed ** speedGrowthFactor - (speedThreshold ** speedGrowthFactor - speedThreshold); + + /** + * The distance and duration of the animation are independent variables. If the speed was + * limited, only the scale can change. The lower the scale, the further the camera is + * away from things, and therefore the slower things move across the screen. Adjust the + * scale (within its own limits) to match the limited speed. + * + * This will cause the camera to zoom out if it would otherwise move too fast. + */ + const adjustedScale = clamp( + (limitedSpeed * animation.duration) / panningDistance, + scalingConstants.minimum, + scalingConstants.maximum + ); + + return time => { + /** + * If the animation has completed, return the `scaleNotCountingAnimation`, as + * the animation always completes with the scale set back at starting value. + */ + if (animationIsActive(animation, time) === false) { + return [scaleNotCountingAnimation, scaleNotCountingAnimation]; + } else { + /** + * + * Animation is defined by a starting time, duration, starting position, and ending position. The amount of time + * which has passed since the start time, compared to the duration, defines the progress of the animation. + * We represent this process with a number between 0 and 1. As the animation progresses, the value changes from 0 + * to 1, linearly. + */ + const x = animationProgress(animation, time); + /** + * The change in scale over the duration of the animation should not be linear. It should grow to the target value, + * then shrink back down to the original value. We adjust the animation progress so that it reaches its peak + * halfway through the animation and then returns to the beginning value by the end of the animation. + * + * We ease the value so that the change from not-animating-at-all to animating-at-full-speed isn't abrupt. + * See the graph: + * + * gnuplot> plot [x=-0:1][x=0:1.2] eased(t)=t<.5? 4*t**3 : (t-1)*(2*t-2)**2+1, progress(t)=-abs(2*t-1)+1, eased(progress(x)) + * + * + * 1.2 +--------------------------------------------------------------------------------------+ + * | + + + + | + * | e(t)=t<.5? 4*t**3 : (t-1)*(2*t-2)**2+1, t(x)=-abs(2*x-1)+1, e(t(x)) ******* | + * | | + * | | + * | | + * 1 |-+ **************** +-| + * | *** *** | + * | ** ** | + * | ** ** | + * | * * | + * | * * | + * 0.8 |-+ * * +-| + * | * * | + * | * * | + * | * * | + * | * * | + * 0.6 |-+ * * +-| + * | * * | + * | * * | + * | * * | + * | * * | + * | * * | + * 0.4 |-+ * * +-| + * | * * | + * | * * | + * | * * | + * | * * | + * | * * | + * 0.2 |-+ * * +-| + * | * * | + * | * * | + * | ** ** | + * | * * | + * | *** + + + + *** | + * 0 +--------------------------------------------------------------------------------------+ + * 0 0.2 0.4 0.6 0.8 1 + * animation progress + * + */ + const easedInOutAnimationProgress = easing.inOutCubic(-Math.abs(2 * x - 1) + 1); + + /** + * Linearly interpolate between these, using the bell-shaped easing value + */ + const lerpedScale = lerp( + scaleNotCountingAnimation, + adjustedScale, + easedInOutAnimationProgress + ); + + /** + * The scale should be the same in both axes. + */ + return [lerpedScale, lerpedScale]; + } + }; + } else { + /** + * The scale should be the same in both axes. + */ + return () => [scaleNotCountingAnimation, scaleNotCountingAnimation]; + } + + /** + * Interpolate between the minimum and maximum scale, + * using a curved ratio based on `factor`. + */ + function scaleFromScalingFactor(factor: number): number { + return lerp( + scalingConstants.minimum, + scalingConstants.maximum, + Math.pow(factor, scalingConstants.zoomCurveRate) + ); + } + } +); + +/** + * The 2D clipping planes used for the orthographic projection. See https://en.wikipedia.org/wiki/Orthographic_projection + */ +export const clippingPlanes: ( + state: CameraState +) => (time: number) => ClippingPlanes = createSelector( + state => state.rasterSize, + scale, + (rasterSize, scaleAtTime) => (time: number) => { + const [scaleX, scaleY] = scaleAtTime(time); + const renderWidth = rasterSize[0]; + const renderHeight = rasterSize[1]; + const clippingPlaneRight = renderWidth / 2 / scaleX; + const clippingPlaneTop = renderHeight / 2 / scaleY; + + return { + renderWidth, + renderHeight, + clippingPlaneRight, + clippingPlaneTop, + clippingPlaneLeft: -clippingPlaneRight, + clippingPlaneBottom: -clippingPlaneTop, + }; + } +); + +/** + * Whether or not the camera is animating, at a given time. + */ +export const isAnimating: (state: CameraState) => (time: number) => boolean = createSelector( + state => state.animation, + animation => time => { + return animation !== undefined && animationIsActive(animation, time); + } +); + +/** + * The camera has a translation value (not counting any current panning.) This is initialized to (0, 0) and + * updating any time panning ends. + * + * When the user is panning, we keep the initial position of the pointer and the current position of the + * pointer. The difference between these values equals the panning vector. + * + * When the user is panning, the translation of the camera is found by adding the panning vector to the + * translationNotCountingCurrentPanning. + * + * We could update the translation as the user moved the mouse but floating point drift (round-off error) could occur. + */ +export const translation: (state: CameraState) => (time: number) => Vector2 = createSelector( + state => state.panning, + state => state.translationNotCountingCurrentPanning, + scale, + state => state.animation, + (panning, translationNotCountingCurrentPanning, scaleAtTime, animation) => { + return (time: number) => { + const [scaleX, scaleY] = scaleAtTime(time); + if (animation !== undefined && animationIsActive(animation, time)) { + return vector2.lerp( + animation.initialTranslation, + animation.targetTranslation, + easing.inOutCubic(animationProgress(animation, time)) + ); + } else if (panning) { + const changeInPanningOffset = vector2.subtract(panning.currentOffset, panning.origin); + /** + * invert the vector since panning moves the perception of the screen, which is inverse of the + * translation of the camera. Inverse the `y` axis again, since `y` is inverted between + * world and screen coordinates. + */ + const changeInTranslation = vector2.divide(changeInPanningOffset, [-scaleX, scaleY]); + return vector2.add(translationNotCountingCurrentPanning, changeInTranslation); + } else { + return translationNotCountingCurrentPanning; + } + }; + } +); + +/** + * A matrix that when applied to a Vector2 converts it from screen coordinates to world coordinates. + * See https://en.wikipedia.org/wiki/Orthographic_projection + */ +export const inverseProjectionMatrix: ( + state: CameraState +) => (time: number) => Matrix3 = createSelector( + clippingPlanes, + translation, + (clippingPlanesAtTime, translationAtTime) => { + return (time: number) => { + const { + renderWidth, + renderHeight, + clippingPlaneRight, + clippingPlaneTop, + clippingPlaneLeft, + clippingPlaneBottom, + } = clippingPlanesAtTime(time); + + /** + * 1. Convert from 0<=n<=screenDimension to -1<=n<=1 + * e.g. for x-axis, divide by renderWidth then multiply by 2 and subtract by one so the value is in range of -1->1 + */ + // prettier-ignore + const screenToNDC: Matrix3 = [ + renderWidth === 0 ? 0 : 2 / renderWidth, 0, -1, + 0, renderHeight === 0 ? 0 : 2 / renderHeight, -1, + 0, 0, 0 + ]; + + /** + * 2. Invert Y since DOM positioning has inverted Y axis + */ + const invertY = scalingTransformation([1, -1]); + + const [translationX, translationY] = translationAtTime(time); + + /** + * 3. Scale values to the clipping plane dimensions. + */ + const scaleToClippingPlaneDimensions = inverseOrthographicProjection( + clippingPlaneTop, + clippingPlaneRight, + clippingPlaneBottom, + clippingPlaneLeft + ); + + /** + * Move the values to accomodate for the perspective of the camera (based on the camera's transform) + */ + const translateForCamera: Matrix3 = [0, 0, translationX, 0, 0, translationY, 0, 0, 0]; + + return addMatrix( + translateForCamera, + multiply(scaleToClippingPlaneDimensions, multiply(invertY, screenToNDC)) + ); + }; + } +); + +/** + * The viewable area in the Resolver map, in world coordinates. + */ +export const viewableBoundingBox: (state: CameraState) => (time: number) => AABB = createSelector( + clippingPlanes, + inverseProjectionMatrix, + (clippingPlanesAtTime, matrixAtTime) => { + return (time: number) => { + const { renderWidth, renderHeight } = clippingPlanesAtTime(time); + const matrix = matrixAtTime(time); + const bottomLeftCorner: Vector2 = [0, renderHeight]; + const topRightCorner: Vector2 = [renderWidth, 0]; + return { + minimum: vector2.applyMatrix3(bottomLeftCorner, matrix), + maximum: vector2.applyMatrix3(topRightCorner, matrix), + }; + }; + } +); + +/** + * A matrix that when applied to a Vector2 will convert it from world coordinates to screen coordinates. + * See https://en.wikipedia.org/wiki/Orthographic_projection + */ +export const projectionMatrix: (state: CameraState) => (time: number) => Matrix3 = createSelector( + clippingPlanes, + translation, + (clippingPlanesAtTime, translationAtTime) => { + return defaultMemoize((time: number) => { + const { + renderWidth, + renderHeight, + clippingPlaneRight, + clippingPlaneTop, + clippingPlaneLeft, + clippingPlaneBottom, + } = clippingPlanesAtTime(time); + + /** + * 1. adjust for camera by subtracting its translation. The closer the camera is to a point, the closer that point + * should be to the center of the screen. + */ + const adjustForCameraPosition = translationTransformation( + vector2.scale(translationAtTime(time), -1) + ); + + /** + * 2. Scale the values based on the dimsension of Resolver on the screen. + */ + const screenToNDC = orthographicProjection( + clippingPlaneTop, + clippingPlaneRight, + clippingPlaneBottom, + clippingPlaneLeft + ); + + /** + * 3. invert y since CSS has inverted y + */ + const invertY = scalingTransformation([1, -1]); + + /** + * 3. Convert values from the scale of -1<=n<=1 to 0<=n<=2 + */ + // prettier-ignore + const fromNDCtoZeroToTwo: Matrix3 = [ + 0, 0, 1, + 0, 0, 1, + 0, 0, 0 + ] + + /** + * 4. convert from 0->2 to 0->rasterDimension by multiplying by rasterDimension/2 + */ + const fromZeroToTwoToRasterDimensions = scalingTransformation([ + renderWidth / 2, + renderHeight / 2, + ]); + + return multiply( + fromZeroToTwoToRasterDimensions, + addMatrix( + fromNDCtoZeroToTwo, + multiply(invertY, multiply(screenToNDC, adjustForCameraPosition)) + ) + ); + }); + } +); + +/** + * Scales the coordinate system, used for zooming. Should always be between 0 and 1 + */ +export const scalingFactor = (state: CameraState): CameraState['scalingFactor'] => { + return state.scalingFactor; +}; + +/** + * Whether or not the user is current panning the map. + */ +export const userIsPanning = (state: CameraState): boolean => state.panning !== undefined; + +/** + * Returns a number 0<=n<=1 where: + * 0 meaning it just started, + * 1 meaning it is done. + */ +function animationProgress(animation: CameraAnimationState, time: number): number { + return clamp((time - animation.startTime) / animation.duration, 0, 1); +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/test_helpers.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/test_helpers.ts new file mode 100644 index 0000000000000..25e0ec642086f --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/test_helpers.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Vector2 } from '../../types'; + +/** + * Used to assert that two Vector2s are close to each other (accounting for round-off errors.) + */ +export function expectVectorsToBeClose(first: Vector2, second: Vector2): void { + expect(first[0]).toBeCloseTo(second[0]); + expect(first[1]).toBeCloseTo(second[1]); +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/zooming.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/zooming.test.ts new file mode 100644 index 0000000000000..fb38c2f526e0b --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/camera/zooming.test.ts @@ -0,0 +1,182 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CameraAction } from './action'; +import { cameraReducer } from './reducer'; +import { createStore, Store } from 'redux'; +import { CameraState, AABB } from '../../types'; +import { viewableBoundingBox, inverseProjectionMatrix, scalingFactor } from './selectors'; +import { expectVectorsToBeClose } from './test_helpers'; +import { scaleToZoom } from './scale_to_zoom'; +import { applyMatrix3 } from '../../lib/vector2'; + +describe('zooming', () => { + let store: Store; + let time: number; + + const cameraShouldBeBoundBy = (expectedViewableBoundingBox: AABB): [string, () => void] => { + return [ + `the camera view should be bound by an AABB with a minimum point of ${expectedViewableBoundingBox.minimum} and a maximum point of ${expectedViewableBoundingBox.maximum}`, + () => { + const actual = viewableBoundingBox(store.getState())(time); + expect(actual.minimum[0]).toBeCloseTo(expectedViewableBoundingBox.minimum[0]); + expect(actual.minimum[1]).toBeCloseTo(expectedViewableBoundingBox.minimum[1]); + expect(actual.maximum[0]).toBeCloseTo(expectedViewableBoundingBox.maximum[0]); + expect(actual.maximum[1]).toBeCloseTo(expectedViewableBoundingBox.maximum[1]); + }, + ]; + }; + beforeEach(() => { + // Time isn't relevant as we aren't testing animation + time = 0; + store = createStore(cameraReducer, undefined); + }); + describe('when the raster size is 300 x 200 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetRasterSize', payload: [300, 200] }; + store.dispatch(action); + }); + it( + ...cameraShouldBeBoundBy({ + minimum: [-150, -100], + maximum: [150, 100], + }) + ); + describe('when the user has scaled in to 2x', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(2) }; + store.dispatch(action); + }); + it( + ...cameraShouldBeBoundBy({ + minimum: [-75, -50], + maximum: [75, 50], + }) + ); + }); + describe('when the user zooms in all the way', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userZoomed', + payload: { zoomChange: 1, time }, + }; + store.dispatch(action); + }); + it('should zoom to maximum scale factor', () => { + const actual = viewableBoundingBox(store.getState())(time); + expect(actual).toMatchInlineSnapshot(` + Object { + "maximum": Array [ + 25.000000000000007, + 16.666666666666668, + ], + "minimum": Array [ + -25, + -16.666666666666668, + ], + } + `); + }); + }); + it('the raster position 200, 50 should map to the world position 50, 50', () => { + expectVectorsToBeClose( + applyMatrix3([200, 50], inverseProjectionMatrix(store.getState())(time)), + [50, 50] + ); + }); + describe('when the user has moved their mouse to the raster position 200, 50', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userMovedPointer', + payload: { screenCoordinates: [200, 50], time }, + }; + store.dispatch(action); + }); + it('should have focused the world position 50, 50', () => { + const coords = store.getState().latestFocusedWorldCoordinates; + if (coords !== null) { + expectVectorsToBeClose(coords, [50, 50]); + } else { + throw new Error('coords should not have been null'); + } + }); + describe('when the user zooms in by 0.5 zoom units', () => { + beforeEach(() => { + const action: CameraAction = { + type: 'userZoomed', + payload: { zoomChange: 0.5, time }, + }; + store.dispatch(action); + }); + it('the raster position 200, 50 should map to the world position 50, 50', () => { + expectVectorsToBeClose( + applyMatrix3([200, 50], inverseProjectionMatrix(store.getState())(time)), + [50, 50] + ); + }); + }); + }); + describe('when the user pans right by 100 pixels', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetPositionOfCamera', payload: [100, 0] }; + store.dispatch(action); + }); + it( + ...cameraShouldBeBoundBy({ + minimum: [-50, -100], + maximum: [250, 100], + }) + ); + it('should be centered on 100, 0', () => { + const worldCenterPoint = applyMatrix3( + [150, 100], + inverseProjectionMatrix(store.getState())(time) + ); + expect(worldCenterPoint[0]).toBeCloseTo(100); + expect(worldCenterPoint[1]).toBeCloseTo(0); + }); + describe('when the user scales to 2x', () => { + beforeEach(() => { + const action: CameraAction = { type: 'userSetZoomLevel', payload: scaleToZoom(2) }; + store.dispatch(action); + }); + it('should be centered on 100, 0', () => { + const worldCenterPoint = applyMatrix3( + [150, 100], + inverseProjectionMatrix(store.getState())(time) + ); + expect(worldCenterPoint[0]).toBeCloseTo(100); + expect(worldCenterPoint[1]).toBeCloseTo(0); + }); + }); + }); + }); + describe('zoom controls', () => { + let previousScalingFactor: CameraState['scalingFactor']; + describe('when user clicks on zoom in button', () => { + beforeEach(() => { + previousScalingFactor = scalingFactor(store.getState()); + const action: CameraAction = { type: 'userClickedZoomIn' }; + store.dispatch(action); + }); + it('the scaling factor should increase by 0.1 units', () => { + const actual = scalingFactor(store.getState()); + expect(actual).toEqual(previousScalingFactor + 0.1); + }); + }); + describe('when user clicks on zoom out button', () => { + beforeEach(() => { + previousScalingFactor = scalingFactor(store.getState()); + const action: CameraAction = { type: 'userClickedZoomOut' }; + store.dispatch(action); + }); + it('the scaling factor should decrease by 0.1 units', () => { + const actual = scalingFactor(store.getState()); + expect(actual).toEqual(previousScalingFactor - 0.1); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/__snapshots__/graphing.test.ts.snap b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/__snapshots__/graphing.test.ts.snap new file mode 100644 index 0000000000000..1dc17054b9f47 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/__snapshots__/graphing.test.ts.snap @@ -0,0 +1,358 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`resolver graph layout when rendering no nodes renders right 1`] = ` +Object { + "edgeLineSegments": Array [], + "processNodePositions": Map {}, +} +`; + +exports[`resolver graph layout when rendering one node renders right 1`] = ` +Object { + "edgeLineSegments": Array [], + "processNodePositions": Map { + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 0, + "process_name": "", + "process_path": "", + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 0, + -0.8164965809277259, + ], + }, +} +`; + +exports[`resolver graph layout when rendering two forks, and one fork has an extra long tine renders right 1`] = ` +Object { + "edgeLineSegments": Array [ + Array [ + Array [ + 0, + -0.8164965809277259, + ], + Array [ + 35.35533905932738, + -21.228911104120876, + ], + ], + Array [ + Array [ + -35.35533905932738, + -62.053740150507174, + ], + Array [ + 106.06601717798213, + 19.595917942265423, + ], + ], + Array [ + Array [ + -35.35533905932738, + -62.053740150507174, + ], + Array [ + 0, + -82.46615467370032, + ], + ], + Array [ + Array [ + 106.06601717798213, + 19.595917942265423, + ], + Array [ + 141.4213562373095, + -0.8164965809277259, + ], + ], + Array [ + Array [ + 0, + -82.46615467370032, + ], + Array [ + 35.35533905932738, + -102.87856919689347, + ], + ], + Array [ + Array [ + 0, + -123.2909837200866, + ], + Array [ + 70.71067811865476, + -82.46615467370032, + ], + ], + Array [ + Array [ + 0, + -123.2909837200866, + ], + Array [ + 35.35533905932738, + -143.70339824327976, + ], + ], + Array [ + Array [ + 70.71067811865476, + -82.46615467370032, + ], + Array [ + 106.06601717798213, + -102.87856919689347, + ], + ], + Array [ + Array [ + 141.4213562373095, + -0.8164965809277259, + ], + Array [ + 176.7766952966369, + -21.22891110412087, + ], + ], + Array [ + Array [ + 141.4213562373095, + -41.64132562731402, + ], + Array [ + 212.13203435596427, + -0.8164965809277259, + ], + ], + Array [ + Array [ + 141.4213562373095, + -41.64132562731402, + ], + Array [ + 176.7766952966369, + -62.053740150507174, + ], + ], + Array [ + Array [ + 212.13203435596427, + -0.8164965809277259, + ], + Array [ + 247.48737341529164, + -21.228911104120883, + ], + ], + Array [ + Array [ + 247.48737341529164, + -21.228911104120883, + ], + Array [ + 318.1980515339464, + -62.05374015050717, + ], + ], + ], + "processNodePositions": Map { + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 0, + "process_name": "", + "process_path": "", + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 0, + -0.8164965809277259, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "already_running", + "event_type_full": "process_event", + "node_id": 1, + "process_name": "", + "process_path": "", + "source_id": 0, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 0, + -82.46615467370032, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 2, + "process_name": "", + "process_path": "", + "source_id": 0, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 141.4213562373095, + -0.8164965809277259, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 3, + "process_name": "", + "process_path": "", + "source_id": 1, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 35.35533905932738, + -143.70339824327976, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 4, + "process_name": "", + "process_path": "", + "source_id": 1, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 106.06601717798213, + -102.87856919689347, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 5, + "process_name": "", + "process_path": "", + "source_id": 2, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 176.7766952966369, + -62.053740150507174, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 6, + "process_name": "", + "process_path": "", + "source_id": 2, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 247.48737341529164, + -21.228911104120883, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 7, + "process_name": "", + "process_path": "", + "source_id": 6, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 318.1980515339464, + -62.05374015050717, + ], + }, +} +`; + +exports[`resolver graph layout when rendering two nodes, one being the parent of the other renders right 1`] = ` +Object { + "edgeLineSegments": Array [ + Array [ + Array [ + 0, + -0.8164965809277259, + ], + Array [ + 70.71067811865476, + -41.641325627314025, + ], + ], + ], + "processNodePositions": Map { + Object { + "data_buffer": Object { + "event_subtype_full": "creation_event", + "event_type_full": "process_event", + "node_id": 0, + "process_name": "", + "process_path": "", + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 0, + -0.8164965809277259, + ], + Object { + "data_buffer": Object { + "event_subtype_full": "already_running", + "event_type_full": "process_event", + "node_id": 1, + "process_name": "", + "process_path": "", + "source_id": 0, + "timestamp_utc": "2019-09-24 01:47:47Z", + }, + "event_timestamp": 1, + "event_type": 1, + "machine_id": "", + } => Array [ + 70.71067811865476, + -41.641325627314025, + ], + }, +} +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts new file mode 100644 index 0000000000000..900b9bda571da --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessEvent } from '../../types'; + +interface ServerReturnedResolverData { + readonly type: 'serverReturnedResolverData'; + readonly payload: { + readonly data: { + readonly result: { + readonly search_results: readonly ProcessEvent[]; + }; + }; + }; +} + +export type DataAction = ServerReturnedResolverData; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts new file mode 100644 index 0000000000000..fac70433f14b2 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts @@ -0,0 +1,212 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store, createStore } from 'redux'; +import { DataAction } from './action'; +import { dataReducer } from './reducer'; +import { DataState, ProcessEvent } from '../../types'; +import { graphableProcesses, processNodePositionsAndEdgeLineSegments } from './selectors'; +import { mockProcessEvent } from '../../models/process_event_test_helpers'; + +describe('resolver graph layout', () => { + let processA: ProcessEvent; + let processB: ProcessEvent; + let processC: ProcessEvent; + let processD: ProcessEvent; + let processE: ProcessEvent; + let processF: ProcessEvent; + let processG: ProcessEvent; + let processH: ProcessEvent; + let processI: ProcessEvent; + let store: Store; + + beforeEach(() => { + /* + * A + * ____|____ + * | | + * B C + * ___|___ ___|___ + * | | | | + * D E F G + * | + * H + * + */ + processA = mockProcessEvent({ + data_buffer: { + process_name: '', + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 0, + }, + }); + processB = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'already_running', + node_id: 1, + source_id: 0, + }, + }); + processC = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 2, + source_id: 0, + }, + }); + processD = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 3, + source_id: 1, + }, + }); + processE = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 4, + source_id: 1, + }, + }); + processF = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 5, + source_id: 2, + }, + }); + processG = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 6, + source_id: 2, + }, + }); + processH = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'creation_event', + node_id: 7, + source_id: 6, + }, + }); + processI = mockProcessEvent({ + data_buffer: { + event_type_full: 'process_event', + event_subtype_full: 'termination_event', + node_id: 8, + source_id: 0, + }, + }); + store = createStore(dataReducer, undefined); + }); + describe('when rendering no nodes', () => { + beforeEach(() => { + const payload = { + data: { + result: { + search_results: [], + }, + }, + }; + const action: DataAction = { type: 'serverReturnedResolverData', payload }; + store.dispatch(action); + }); + it('the graphableProcesses list should only include nothing', () => { + const actual = graphableProcesses(store.getState()); + expect(actual).toEqual([]); + }); + it('renders right', () => { + expect(processNodePositionsAndEdgeLineSegments(store.getState())).toMatchSnapshot(); + }); + }); + describe('when rendering one node', () => { + beforeEach(() => { + const payload = { + data: { + result: { + search_results: [processA], + }, + }, + }; + const action: DataAction = { type: 'serverReturnedResolverData', payload }; + store.dispatch(action); + }); + it('the graphableProcesses list should only include nothing', () => { + const actual = graphableProcesses(store.getState()); + expect(actual).toEqual([processA]); + }); + it('renders right', () => { + expect(processNodePositionsAndEdgeLineSegments(store.getState())).toMatchSnapshot(); + }); + }); + describe('when rendering two nodes, one being the parent of the other', () => { + beforeEach(() => { + const payload = { + data: { + result: { + search_results: [processA, processB], + }, + }, + }; + const action: DataAction = { type: 'serverReturnedResolverData', payload }; + store.dispatch(action); + }); + it('the graphableProcesses list should only include nothing', () => { + const actual = graphableProcesses(store.getState()); + expect(actual).toEqual([processA, processB]); + }); + it('renders right', () => { + expect(processNodePositionsAndEdgeLineSegments(store.getState())).toMatchSnapshot(); + }); + }); + describe('when rendering two forks, and one fork has an extra long tine', () => { + beforeEach(() => { + const payload = { + data: { + result: { + search_results: [ + processA, + processB, + processC, + processD, + processE, + processF, + processG, + processH, + processI, + ], + }, + }, + }; + const action: DataAction = { type: 'serverReturnedResolverData', payload }; + store.dispatch(action); + }); + it("the graphableProcesses list should only include events with 'processCreated' an 'processRan' eventType", () => { + const actual = graphableProcesses(store.getState()); + expect(actual).toEqual([ + processA, + processB, + processC, + processD, + processE, + processF, + processG, + processH, + ]); + }); + it('renders right', () => { + expect(processNodePositionsAndEdgeLineSegments(store.getState())).toMatchSnapshot(); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/index.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/index.ts new file mode 100644 index 0000000000000..8db57c5d9681f --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { dataReducer } from './reducer'; +export { DataAction } from './action'; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts new file mode 100644 index 0000000000000..848d814808bac --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { DataState, ResolverAction } from '../../types'; +import { sampleData } from './sample'; + +function initialState(): DataState { + return { + results: sampleData.data.result.search_results, + }; +} + +export const dataReducer: Reducer = (state = initialState(), action) => { + if (action.type === 'serverReturnedResolverData') { + const { + data: { + result: { search_results }, + }, + } = action.payload; + return { + ...state, + results: search_results, + }; + } else { + return state; + } +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/sample.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/sample.ts new file mode 100644 index 0000000000000..b0ed9f3554c9b --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/sample.ts @@ -0,0 +1,1608 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ProcessEvent } from '../../types'; + +interface ProcessEventSampleData { + data: { + result: { + search_results: ProcessEvent[]; + }; + }; +} + +const rawData = { + data: { + code: 200, + result: { + alert_id: 'a9834bf5-42c1-4039-83be-08c3ad3232b3', + bulk_task_id: null, + correlation_id: '7022e509-087e-493d-b02c-d88a206cd993', + created_at: '2019-09-24T03:17:36Z', + endpoint: { + ad_distinguished_name: + 'CN=ENDPOINT-W-1-07,OU=Desktops,OU=Workstations,OU=Computers_DEMO,DC=demo,DC=endgamelabs,DC=net', + ad_hostname: 'demo.endgamelabs.net', + display_operating_system: 'Windows 7 (SP1)', + hostname: 'ENDPOINT-W-1-07', + id: '39153006-0064-424b-99e9-4e21dcc00c2e', + ip_address: '172.31.27.17', + mac_address: '00:50:56:b1:b7:7b', + name: 'ENDPOINT-W-1-07', + operating_system: 'Windows 6.1 Service Pack 1', + status: 'monitored', + updated_at: '2019-09-24T01:48:47.960649+00:00', + }, + event_logging_search_request_count: 3, + family: 'collection', + investigation_id: null, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + message_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + metadata: { + chunk_id: 0, + correlation_id: '7022e509-087e-493d-b02c-d88a206cd993', + final: true, + message_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + origination_task_id: '2bed7d8b-72b1-4650-882c-5167a2fe1735', + os_type: 'windows', + priority: 50, + result: { + local_code: 0, + local_msg: 'Success', + }, + semantic_version: '3.52.8', + task_id: '2bed7d8b-72b1-4650-882c-5167a2fe1735', + type: 'collection', + }, + origination_task_id: '2bed7d8b-72b1-4650-882c-5167a2fe1735', + pagination: { + backwards: false, + eof: false, + page_number: 3, + page_offset: 31666, + params: + 'eyJhbGVydF9pZCI6ICJhOTgzNGJmNS00MmMxLTQwMzktODNiZS0wOGMzYWQzMjMyYjMiLCAidGVtcGxhdGVfZmlsZSI6ICJwcm9jZXNzLWNvbnRleHQubHVhIiwgImNyaXRlcmlhIjogeyJwaWQiOiAxODA4LCAidW5pcXVlX3BpZCI6IDE4OTQzfX0=', + remaining_events: 0, + }, + pending_event_logging_search_request: false, + results_count: 807, + search_results: [ + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 6, + command_line: '', + depth: -5, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'already_running', + event_type_full: 'process_event', + integrity_level: 'system', + node_id: 1002, + opcode: 3, + pid: 4, + ppid: 0, + process_name: '', + process_path: '', + serial_event_id: 1002, + timestamp: 132137632670000000, + timestamp_utc: '2019-09-24 01:47:47Z', + unique_pid: 1002, + unique_ppid: 1001, + user_domain: 'NT AUTHORITY', + user_name: 'SYSTEM', + user_sid: 'S-1-5-18', + }, + event_timestamp: 132137632670000000, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 5, + command_line: '\\SystemRoot\\System32\\smss.exe', + depth: -4, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'already_running', + event_type_full: 'process_event', + integrity_level: 'system', + md5: '1911a3356fa3f77ccc825ccbac038c2a', + node_id: 1003, + opcode: 3, + original_file_name: 'smss.exe', + pid: 244, + ppid: 4, + process_name: 'smss.exe', + process_path: 'C:\\Windows\\System32\\smss.exe', + serial_event_id: 1003, + sha1: '706473ad489e5365af1e3431c4f8fe80a9139bc2', + sha256: '6ed135b792c81d78b33a57f0f4770db6105c9ed3e2193629cb3ec38bfd5b7e1b', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 1002, + timestamp: 132137632670000000, + timestamp_utc: '2019-09-24 01:47:47Z', + unique_pid: 1003, + unique_ppid: 1002, + user_domain: 'NT AUTHORITY', + user_name: 'SYSTEM', + user_sid: 'S-1-5-18', + }, + event_timestamp: 132137632670000000, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 4, + authentication_id: 999, + command_line: '\\SystemRoot\\System32\\smss.exe 00000000 00000048 ', + depth: -3, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'system', + md5: '1911a3356fa3f77ccc825ccbac038c2a', + node_id: 18643, + opcode: 1, + original_file_name: 'smss.exe', + parent_process_name: 'smss.exe', + parent_process_path: 'C:\\Windows\\System32\\smss.exe', + pid: 2364, + ppid: 244, + process_name: 'smss.exe', + process_path: 'C:\\Windows\\System32\\smss.exe', + serial_event_id: 18643, + sha1: '706473ad489e5365af1e3431c4f8fe80a9139bc2', + sha256: '6ed135b792c81d78b33a57f0f4770db6105c9ed3e2193629cb3ec38bfd5b7e1b', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 1003, + timestamp: 132137681960227504, + timestamp_utc: '2019-09-24 03:09:56Z', + unique_pid: 18643, + unique_ppid: 1003, + user_domain: 'NT AUTHORITY', + user_name: 'SYSTEM', + user_sid: 'S-1-5-18', + }, + event_timestamp: 132137681960227504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 3, + authentication_id: 999, + command_line: 'winlogon.exe', + depth: -2, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'system', + md5: '1151b1baa6f350b1db6598e0fea7c457', + node_id: 18645, + opcode: 1, + original_file_name: 'WINLOGON.EXE', + parent_process_name: 'smss.exe', + parent_process_path: 'C:\\Windows\\System32\\smss.exe', + pid: 3108, + ppid: 2364, + process_name: 'winlogon.exe', + process_path: 'C:\\Windows\\System32\\winlogon.exe', + serial_event_id: 18645, + sha1: '434856b834baf163c5ea4d26434eeae775a507fb', + sha256: 'b1506e0a7e826eff0f5252ef5026070c46e2235438403a9a24d73ee69c0b8a49', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18643, + timestamp: 132137681961163504, + timestamp_utc: '2019-09-24 03:09:56Z', + unique_pid: 18645, + unique_ppid: 18643, + user_domain: 'NT AUTHORITY', + user_name: 'SYSTEM', + user_sid: 'S-1-5-18', + }, + event_timestamp: 132137681961163504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + depth: -2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: '1911a3356fa3f77ccc825ccbac038c2a', + node_id: 18646, + opcode: 2, + original_file_name: 'smss.exe', + parent_process_name: 'smss.exe', + parent_process_path: 'C:\\Windows\\System32\\smss.exe', + pid: 2364, + ppid: 244, + process_name: 'smss.exe', + process_path: 'C:\\Windows\\System32\\smss.exe', + serial_event_id: 18646, + sha1: '706473ad489e5365af1e3431c4f8fe80a9139bc2', + sha256: '6ed135b792c81d78b33a57f0f4770db6105c9ed3e2193629cb3ec38bfd5b7e1b', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18643, + timestamp: 132137681961787504, + timestamp_utc: '2019-09-24 03:09:56Z', + unique_pid: 18643, + unique_ppid: 1003, + user_domain: 'NT AUTHORITY', + user_name: 'SYSTEM', + user_sid: 'S-1-5-18', + }, + event_timestamp: 132137681961787504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 1, + authentication_id: 4904488, + command_line: 'C:\\Windows\\system32\\userinit.exe', + depth: -1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'bafe84e637bf7388c96ef48d4d3fdd53', + node_id: 18833, + opcode: 1, + original_file_name: 'USERINIT.EXE', + parent_process_name: 'winlogon.exe', + parent_process_path: 'C:\\Windows\\System32\\winlogon.exe', + pid: 3560, + ppid: 3108, + process_name: 'userinit.exe', + process_path: 'C:\\Windows\\System32\\userinit.exe', + serial_event_id: 18833, + sha1: '47267f943f060e36604d56c8895a6eece063d9a1', + sha256: '11c194d9adce90027272c627d7fbf3ba5025ff0f7b26a8333f764e11e1382cf9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18645, + timestamp: 132137681981287504, + timestamp_utc: '2019-09-24 03:09:58Z', + unique_pid: 18833, + unique_ppid: 18645, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137681981287504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + _descendant_count: 0, + authentication_id: 4904488, + command_line: 'C:\\Windows\\Explorer.EXE', + depth: 0, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 18943, + opcode: 1, + origin: true, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'userinit.exe', + parent_process_path: 'C:\\Windows\\System32\\userinit.exe', + pid: 1808, + ppid: 3560, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 18943, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18833, + timestamp: 132137681985655504, + timestamp_utc: '2019-09-24 03:09:58Z', + unique_pid: 18943, + unique_ppid: 18833, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137681985655504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe" -n vmusr', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '8dc5ad50587b936f7f616738112bfd2a', + node_id: 19545, + opcode: 1, + original_file_name: 'vmtoolsd.exe', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3596, + ppid: 1808, + process_name: 'vmtoolsd.exe', + process_path: 'C:\\Program Files\\VMware\\VMware Tools\\vmtoolsd.exe', + serial_event_id: 19545, + sha1: '04479ea30943ec471a6a5ca4c0dc74b5ff496e9f', + sha256: 'd6d9f041da6f724bf69f48bbee3bf41295a0ed4dca715b1908c5f35bc8034d53', + signature_signer: 'VMware, Inc.', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137681999539504, + timestamp_utc: '2019-09-24 03:09:59Z', + unique_pid: 19545, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137681999539504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + depth: 0, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'bafe84e637bf7388c96ef48d4d3fdd53', + node_id: 20261, + opcode: 2, + original_file_name: 'USERINIT.EXE', + parent_process_name: 'winlogon.exe', + parent_process_path: 'C:\\Windows\\System32\\winlogon.exe', + pid: 3560, + ppid: 3108, + process_name: 'userinit.exe', + process_path: 'C:\\Windows\\System32\\userinit.exe', + serial_event_id: 20261, + sha1: '47267f943f060e36604d56c8895a6eece063d9a1', + sha256: '11c194d9adce90027272c627d7fbf3ba5025ff0f7b26a8333f764e11e1382cf9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18833, + timestamp: 132137682277819504, + timestamp_utc: '2019-09-24 03:10:27Z', + unique_pid: 18833, + unique_ppid: 18645, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682277819504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\explorer.exe" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 20303, + opcode: 1, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3124, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 20303, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137682603979504, + timestamp_utc: '2019-09-24 03:11:00Z', + unique_pid: 20303, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682603979504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: '310624aa-bb2b-442b-a6c9-3284148b0ae3', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 1, + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 20310, + opcode: 2, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3124, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 20310, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 20303, + timestamp: 132137682604229504, + timestamp_utc: '2019-09-24 03:11:00Z', + unique_pid: 20303, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682604229504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\explorer.exe" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 20455, + opcode: 1, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3084, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 20455, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137682773669504, + timestamp_utc: '2019-09-24 03:11:17Z', + unique_pid: 20455, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682773669504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 1, + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 20462, + opcode: 2, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3084, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 20462, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 20455, + timestamp: 132137682774259504, + timestamp_utc: '2019-09-24 03:11:17Z', + unique_pid: 20455, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682774259504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\System32\\cmd.exe" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '5746bd7e255dd6a8afa06f7c42c1ba41', + node_id: 21120, + opcode: 1, + original_file_name: 'Cmd.Exe', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3280, + ppid: 1808, + process_name: 'cmd.exe', + process_path: 'C:\\Windows\\System32\\cmd.exe', + serial_event_id: 21120, + sha1: '0f3c4ff28f354aede202d54e9d1c5529a3bf87d8', + sha256: 'db06c3534964e3fc79d2763144ba53742d7fa250ca336f4a0fe724b75aaff386', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137682997939504, + timestamp_utc: '2019-09-24 03:11:39Z', + unique_pid: 21120, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137682997939504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\explorer.exe" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 21166, + opcode: 1, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3548, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 21166, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137683166079504, + timestamp_utc: '2019-09-24 03:11:56Z', + unique_pid: 21166, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683166079504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 1, + md5: 'ac4c51eb24aa95b77f705ab159189e24', + node_id: 21173, + opcode: 2, + original_file_name: 'EXPLORER.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3548, + ppid: 1808, + process_name: 'explorer.exe', + process_path: 'C:\\Windows\\explorer.exe', + serial_event_id: 21173, + sha1: '4583daf9442880204730fb2c8a060430640494b1', + sha256: '6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 21166, + timestamp: 132137683166729504, + timestamp_utc: '2019-09-24 03:11:56Z', + unique_pid: 21166, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683166729504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Python27\\python.exe" "C:\\tmp\\dns.py" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '743b91619fbfee3c3e173ba5a17b1290', + node_id: 21480, + opcode: 1, + original_file_name: '', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 4060, + ppid: 1808, + process_name: 'python.exe', + process_path: 'C:\\Python27\\python.exe', + serial_event_id: 21480, + sha1: 'edabcf58d55a5e462f7a368d99616e3ac051c620', + sha256: '45b9384b852d850327e194ac86d84aed8916a3c13fc8f49ca54fddcbca4f7e32', + signature_signer: '', + signature_status: 'noSignature', + source_id: 18943, + timestamp: 132137683493349504, + timestamp_utc: '2019-09-24 03:12:29Z', + unique_pid: 21480, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683493349504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: '743b91619fbfee3c3e173ba5a17b1290', + node_id: 21500, + opcode: 2, + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 4060, + ppid: 1808, + process_name: 'python.exe', + process_path: 'C:\\Python27\\python.exe', + serial_event_id: 21500, + sha1: 'edabcf58d55a5e462f7a368d99616e3ac051c620', + sha256: '45b9384b852d850327e194ac86d84aed8916a3c13fc8f49ca54fddcbca4f7e32', + signature_status: 'noSignature', + source_id: 21480, + timestamp: 132137683493889504, + timestamp_utc: '2019-09-24 03:12:29Z', + unique_pid: 21480, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683493889504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Python27\\python.exe" "C:\\tmp\\dns.py" ', + depth: 2, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '743b91619fbfee3c3e173ba5a17b1290', + node_id: 21539, + opcode: 1, + original_file_name: '', + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 2888, + ppid: 3280, + process_name: 'python.exe', + process_path: 'C:\\Python27\\python.exe', + serial_event_id: 21539, + sha1: 'edabcf58d55a5e462f7a368d99616e3ac051c620', + sha256: '45b9384b852d850327e194ac86d84aed8916a3c13fc8f49ca54fddcbca4f7e32', + signature_signer: '', + signature_status: 'noSignature', + source_id: 21120, + timestamp: 132137683555889504, + timestamp_utc: '2019-09-24 03:12:35Z', + unique_pid: 21539, + unique_ppid: 21120, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683555889504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 3, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: '743b91619fbfee3c3e173ba5a17b1290', + node_id: 21540, + opcode: 2, + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 2888, + ppid: 3280, + process_name: 'python.exe', + process_path: 'C:\\Python27\\python.exe', + serial_event_id: 21540, + sha1: 'edabcf58d55a5e462f7a368d99616e3ac051c620', + sha256: '45b9384b852d850327e194ac86d84aed8916a3c13fc8f49ca54fddcbca4f7e32', + signature_status: 'noSignature', + source_id: 21539, + timestamp: 132137683556159504, + timestamp_utc: '2019-09-24 03:12:35Z', + unique_pid: 21539, + unique_ppid: 21120, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683556159504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + depth: 2, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21634, + opcode: 1, + original_file_name: '', + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 3996, + ppid: 3280, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21634, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_signer: '', + signature_status: 'noSignature', + source_id: 21120, + timestamp: 132137683921669504, + timestamp_utc: '2019-09-24 03:13:12Z', + unique_pid: 21634, + unique_ppid: 21120, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683921669504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + depth: 3, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21669, + opcode: 1, + original_file_name: '', + parent_process_name: 'fakenet.exe', + parent_process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + pid: 184, + ppid: 3996, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21669, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_signer: '', + signature_status: 'noSignature', + source_id: 21634, + timestamp: 132137683923819504, + timestamp_utc: '2019-09-24 03:13:12Z', + unique_pid: 21669, + unique_ppid: 21634, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683923819504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 4, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 1, + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21679, + opcode: 2, + parent_process_name: 'fakenet.exe', + parent_process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + pid: 184, + ppid: 3996, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21679, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_status: 'noSignature', + source_id: 21669, + timestamp: 132137683931089504, + timestamp_utc: '2019-09-24 03:13:13Z', + unique_pid: 21669, + unique_ppid: 21634, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683931089504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 3, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 1, + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21694, + opcode: 2, + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 3996, + ppid: 3280, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21694, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_status: 'noSignature', + source_id: 21634, + timestamp: 132137683931569504, + timestamp_utc: '2019-09-24 03:13:13Z', + unique_pid: 21634, + unique_ppid: 21120, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137683931569504, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: + '"C:\\Windows\\system32\\NOTEPAD.EXE" C:\\tmp\\fakenet1.4.3\\configs\\default.ini', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'f2c7bb8acc97f92e987a2d4087d021b1', + node_id: 21769, + opcode: 1, + original_file_name: 'NOTEPAD.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 2492, + ppid: 1808, + process_name: 'notepad.exe', + process_path: 'C:\\Windows\\System32\\notepad.exe', + serial_event_id: 21769, + sha1: '7eb0139d2175739b3ccb0d1110067820be6abd29', + sha256: '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137684112851830, + timestamp_utc: '2019-09-24 03:13:31Z', + unique_pid: 21769, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137684112851830, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'f2c7bb8acc97f92e987a2d4087d021b1', + node_id: 21794, + opcode: 2, + original_file_name: 'NOTEPAD.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 2492, + ppid: 1808, + process_name: 'notepad.exe', + process_path: 'C:\\Windows\\System32\\notepad.exe', + serial_event_id: 21794, + sha1: '7eb0139d2175739b3ccb0d1110067820be6abd29', + sha256: '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 21769, + timestamp: 132137684131573702, + timestamp_utc: '2019-09-24 03:13:33Z', + unique_pid: 21769, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137684131573702, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: 'fakenet.exe', + depth: 2, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21890, + opcode: 1, + original_file_name: '', + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 1060, + ppid: 3280, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21890, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_signer: '', + signature_status: 'noSignature', + source_id: 21120, + timestamp: 132137684579848525, + timestamp_utc: '2019-09-24 03:14:17Z', + unique_pid: 21890, + unique_ppid: 21120, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137684579848525, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: 'fakenet.exe', + depth: 3, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'c29675ce0750f73225bf05d03080dfb2', + node_id: 21924, + opcode: 1, + original_file_name: '', + parent_process_name: 'fakenet.exe', + parent_process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + pid: 4024, + ppid: 1060, + process_name: 'fakenet.exe', + process_path: 'C:\\tmp\\fakenet1.4.3\\fakenet.exe', + serial_event_id: 21924, + sha1: 'b14763ef982450551bcb09f6e0ecc75d2b9684fb', + sha256: '948f1c024118e434b6867ea593bb180212d35f9d2a9401892903ef22841fb303', + signature_signer: '', + signature_status: 'noSignature', + source_id: 21890, + timestamp: 132137684580468587, + timestamp_utc: '2019-09-24 03:14:18Z', + unique_pid: 21924, + unique_ppid: 21890, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137684580468587, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\System32\\cmd.exe" ', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '5746bd7e255dd6a8afa06f7c42c1ba41', + node_id: 22238, + opcode: 1, + original_file_name: 'Cmd.Exe', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 3328, + ppid: 1808, + process_name: 'cmd.exe', + process_path: 'C:\\Windows\\System32\\cmd.exe', + serial_event_id: 22238, + sha1: '0f3c4ff28f354aede202d54e9d1c5529a3bf87d8', + sha256: 'db06c3534964e3fc79d2763144ba53742d7fa250ca336f4a0fe724b75aaff386', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137684944024939, + timestamp_utc: '2019-09-24 03:14:54Z', + unique_pid: 22238, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137684944024939, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + attack_references: [ + { + tactics: ['Privilege Escalation', 'Execution', 'Persistence'], + technique_id: 'T1053', + technique_name: 'Scheduled Task', + }, + ], + authentication_id: 4904488, + command_line: 'SCHTASKS /CREATE /SC MINUTE /TN "Windiws" /TR "C:\\tmp\\scheduler.bat"', + depth: 2, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: '97e0ec3d6d99e8cc2b17ef2d3760e8fc', + node_id: 22376, + opcode: 1, + original_file_name: 'sctasks.exe', + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 2864, + ppid: 3328, + process_name: 'schtasks.exe', + process_path: 'C:\\Windows\\System32\\schtasks.exe', + serial_event_id: 22376, + sha1: 'bd9dceffbcbbc82bee5f2109bd73a57477fe1f92', + sha256: '6dce7d58ebb0d705fcb4179349c441b45e160c94e43934c5ed8fa1964e2cd031', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 22238, + timestamp: 132137685249385472, + timestamp_utc: '2019-09-24 03:15:24Z', + unique_pid: 22376, + unique_ppid: 22238, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137685249385472, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 3, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: '97e0ec3d6d99e8cc2b17ef2d3760e8fc', + node_id: 22384, + opcode: 2, + original_file_name: 'sctasks.exe', + parent_process_name: 'cmd.exe', + parent_process_path: 'C:\\Windows\\System32\\cmd.exe', + pid: 2864, + ppid: 3328, + process_name: 'schtasks.exe', + process_path: 'C:\\Windows\\System32\\schtasks.exe', + serial_event_id: 22384, + sha1: 'bd9dceffbcbbc82bee5f2109bd73a57477fe1f92', + sha256: '6dce7d58ebb0d705fcb4179349c441b45e160c94e43934c5ed8fa1964e2cd031', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 22376, + timestamp: 132137685251515685, + timestamp_utc: '2019-09-24 03:15:25Z', + unique_pid: 22376, + unique_ppid: 22238, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137685251515685, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + authentication_id: 4904488, + command_line: '"C:\\Windows\\System32\\NOTEPAD.EXE" C:\\tmp\\scheduler.bat', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'f2c7bb8acc97f92e987a2d4087d021b1', + node_id: 22448, + opcode: 1, + original_file_name: 'NOTEPAD.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 4048, + ppid: 1808, + process_name: 'notepad.exe', + process_path: 'C:\\Windows\\System32\\notepad.exe', + serial_event_id: 22448, + sha1: '7eb0139d2175739b3ccb0d1110067820be6abd29', + sha256: '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137685448755407, + timestamp_utc: '2019-09-24 03:15:44Z', + unique_pid: 22448, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137685448755407, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'f2c7bb8acc97f92e987a2d4087d021b1', + node_id: 22464, + opcode: 2, + original_file_name: 'NOTEPAD.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 4048, + ppid: 1808, + process_name: 'notepad.exe', + process_path: 'C:\\Windows\\System32\\notepad.exe', + serial_event_id: 22464, + sha1: '7eb0139d2175739b3ccb0d1110067820be6abd29', + sha256: '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 22448, + timestamp: 132137685516752206, + timestamp_utc: '2019-09-24 03:15:51Z', + unique_pid: 22448, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137685516752206, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + attack_references: [ + { + tactics: ['Execution'], + technique_id: 'T1085', + technique_name: 'Rundll32', + }, + ], + authentication_id: 4904488, + command_line: + '"C:\\Windows\\system32\\rundll32.exe" C:\\Windows\\system32\\shell32.dll,OpenAs_RunDLL C:\\tmp\\XLS_no_email_Upcoming Events February 2018.xls\\cb85072e6ca66a29cb0b73659a0fe5ba2456d9ba0b52e3a4c89e86549bc6e2c7.xls', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 22799, + opcode: 1, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 2864, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 22799, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137686572217742, + timestamp_utc: '2019-09-24 03:17:37Z', + unique_pid: 22799, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686572217742, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 22805, + opcode: 2, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 2864, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 22805, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 22799, + timestamp: 132137686585839104, + timestamp_utc: '2019-09-24 03:17:38Z', + unique_pid: 22799, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686585839104, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + attack_references: [ + { + tactics: ['Execution'], + technique_id: 'T1085', + technique_name: 'Rundll32', + }, + ], + authentication_id: 4904488, + command_line: + '"C:\\Windows\\system32\\rundll32.exe" C:\\Windows\\system32\\shell32.dll,OpenAs_RunDLL C:\\tmp\\Upcoming Defense events February 2018.eml', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 22933, + opcode: 1, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 1864, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 22933, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137686702740793, + timestamp_utc: '2019-09-24 03:17:50Z', + unique_pid: 22933, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686702740793, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 22945, + opcode: 2, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 1864, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 22945, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 22933, + timestamp: 132137686718432362, + timestamp_utc: '2019-09-24 03:17:51Z', + unique_pid: 22933, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686718432362, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + attack_references: [ + { + tactics: ['Execution'], + technique_id: 'T1085', + technique_name: 'Rundll32', + }, + ], + authentication_id: 4904488, + command_line: + '"C:\\Windows\\system32\\rundll32.exe" C:\\Windows\\system32\\shell32.dll,OpenAs_RunDLL C:\\Users\\Administrator\\AppData\\Roaming\\Microsoft\\Windows\\SendTo\\Mail Recipient.MAPIMail', + depth: 1, + elevated: true, + elevation_type: 'default', + event_subtype_full: 'creation_event', + event_type_full: 'process_event', + integrity_level: 'high', + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 27050, + opcode: 1, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 568, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 27050, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 18943, + timestamp: 132137686926723189, + timestamp_utc: '2019-09-24 03:18:12Z', + unique_pid: 27050, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686926723189, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + { + collection_id: 'f287067d-3ec7-4d5a-b6de-ea1415bc5e56', + data_buffer: { + depth: 2, + event_subtype_full: 'termination_event', + event_type_full: 'process_event', + exit_code: 0, + md5: 'dd81d91ff3b0763c392422865c9ac12e', + node_id: 27053, + opcode: 2, + original_file_name: 'RUNDLL32.EXE', + parent_process_name: 'explorer.exe', + parent_process_path: 'C:\\Windows\\explorer.exe', + pid: 568, + ppid: 1808, + process_name: 'rundll32.exe', + process_path: 'C:\\Windows\\System32\\rundll32.exe', + serial_event_id: 27053, + sha1: '963b55acc8c566876364716d5aafa353995812a8', + sha256: 'f5691b8f200e3196e6808e932630e862f8f26f31cd949981373f23c9d87db8b9', + signature_signer: 'Microsoft Windows', + signature_status: 'trusted', + source_id: 27050, + timestamp: 132137686939784495, + timestamp_utc: '2019-09-24 03:18:13Z', + unique_pid: 27050, + unique_ppid: 18943, + user_domain: 'ENDPOINT-W-1-07', + user_name: 'vagrant', + user_sid: 'S-1-5-21-3883902650-1642591343-2485142877-1001', + }, + event_timestamp: 132137686939784495, + event_type: 4, + machine_id: '7f1660dc-2c12-ce99-71b8-1ef862aeec34', + serial_event_id: 0, + }, + ], + status: 'success', + task_id: '2bed7d8b-72b1-4650-882c-5167a2fe1735', + total_events_searched: 7730, + type: 'eventLoggingSearchResponse', + }, + }, + metadata: { + count: 39, + next: null, + next_url: null, + per_page: '4000', + previous_url: null, + timestamp: '2019-12-18T19:31:27.565110', + }, +}; + +export const sampleData: ProcessEventSampleData = rawData as ProcessEventSampleData; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts new file mode 100644 index 0000000000000..75b477dd7c7fc --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts @@ -0,0 +1,442 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSelector } from 'reselect'; +import { + DataState, + ProcessEvent, + IndexedProcessTree, + ProcessWidths, + ProcessPositions, + EdgeLineSegment, + ProcessWithWidthMetadata, + Matrix3, +} from '../../types'; +import { Vector2 } from '../../types'; +import { add as vector2Add, applyMatrix3 } from '../../lib/vector2'; +import { isGraphableProcess } from '../../models/process_event'; +import { + factory as indexedProcessTreeFactory, + children as indexedProcessTreeChildren, + parent as indexedProcessTreeParent, + size, + levelOrder, +} from '../../models/indexed_process_tree'; + +const unit = 100; +const distanceBetweenNodesInUnits = 1; + +/** + * An isometric projection is a method for representing three dimensional objects in 2 dimensions. + * More information about isometric projections can be found here https://en.wikipedia.org/wiki/Isometric_projection. + * In our case, we obtain the isometric projection by rotating the objects 45 degrees in the plane of the screen + * and arctan(1/sqrt(2)) (~35.3 degrees) through the horizontal axis. + * + * A rotation by 45 degrees in the plane of the screen is given by: + * [ sqrt(2)/2 -sqrt(2)/2 0 + * sqrt(2)/2 sqrt(2)/2 0 + * 0 0 1] + * + * A rotation by arctan(1/sqrt(2)) through the horizantal axis is given by: + * [ 1 0 0 + * 0 sqrt(3)/3 -sqrt(6)/3 + * 0 sqrt(6)/3 sqrt(3)/3] + * + * We can multiply both of these matrices to get the final transformation below. + */ +/* prettier-ignore */ +const isometricTransformMatrix: Matrix3 = [ + Math.sqrt(2) / 2, -(Math.sqrt(2) / 2), 0, + Math.sqrt(6) / 6, Math.sqrt(6) / 6, -(Math.sqrt(6) / 3), + 0, 0, 1, +] + +/** + * The distance in pixels (at scale 1) between nodes. Change this to space out nodes more + */ +const distanceBetweenNodes = distanceBetweenNodesInUnits * unit; + +/** + * Process events that will be graphed. + */ +export const graphableProcesses = createSelector( + ({ results }: DataState) => results, + function(results: DataState['results']) { + return results.filter(isGraphableProcess); + } +); + +/** + * In laying out the graph, we precalculate the 'width' of each subtree. The 'width' of the subtree is determined by its + * descedants and the rule that each process node must be at least 1 unit apart. Enforcing that all nodes are at least + * 1 unit apart on the x axis makes it easy to prevent the UI components from overlapping. There will always be space. + * + * Example widths: + * + * A and B each have a width of 0 + * + * A + * | + * B + * + * A has a width of 1. B and C have a width of 0. + * B and C must be 1 unit apart, so the A subtree has a width of 1. + * + * A + * ____|____ + * | | + * B C + * + * + * D, E, F, G, H all have a width of 0. + * B has a width of 1 since D->E must be 1 unit apart. + * Similarly, C has a width of 1 since F->G must be 1 unit apart. + * A has width of 3, since B has a width of 1, and C has a width of 1, and E->F must be at least + * 1 unit apart. + * A + * ____|____ + * | | + * B C + * ___|___ ___|___ + * | | | | + * D E F G + * | + * H + * + */ +function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): ProcessWidths { + const widths = new Map(); + + if (size(indexedProcessTree) === 0) { + return widths; + } + + const processesInReverseLevelOrder = [...levelOrder(indexedProcessTree)].reverse(); + + for (const process of processesInReverseLevelOrder) { + const children = indexedProcessTreeChildren(indexedProcessTree, process); + + const sumOfWidthOfChildren = function sumOfWidthOfChildren() { + return children.reduce(function sum(currentValue, child) { + /** + * `widths.get` will always return a number in this case. + * This loop sequences a tree in reverse level order. Width values are set for each node. + * Therefore a parent can always find a width for its children, since all of its children + * will have been handled already. + */ + return currentValue + widths.get(child)!; + }, 0); + }; + + const width = sumOfWidthOfChildren() + Math.max(0, children.length - 1) * distanceBetweenNodes; + widths.set(process, width); + } + + return widths; +} + +function processEdgeLineSegments( + indexedProcessTree: IndexedProcessTree, + widths: ProcessWidths, + positions: ProcessPositions +): EdgeLineSegment[] { + const edgeLineSegments: EdgeLineSegment[] = []; + for (const metadata of levelOrderWithWidths(indexedProcessTree, widths)) { + /** + * We only handle children, drawing lines back to their parents. The root has no parent, so we skip it + */ + if (metadata.parent === null) { + continue; + } + const { process, parent, parentWidth } = metadata; + const position = positions.get(process); + const parentPosition = positions.get(parent); + + if (position === undefined || parentPosition === undefined) { + /** + * All positions have been precalculated, so if any are missing, it's an error. This will never happen. + */ + throw new Error(); + } + + /** + * The point halfway between the parent and child on the y axis, we sometimes have a hard angle here in the edge line + */ + const midwayY = parentPosition[1] + (position[1] - parentPosition[1]) / 2; + + /** + * When drawing edge lines between a parent and children (when there are multiple children) we draw a pitchfork type + * design. The 'midway' line, runs along the x axis and joins all the children with a single descendant line from the parent. + * See the ascii diagram below. The underscore characters would be the midway line. + * + * A + * ____|____ + * | | + * B C + */ + const lineFromProcessToMidwayLine: EdgeLineSegment = [[position[0], midwayY], position]; + + const siblings = indexedProcessTreeChildren(indexedProcessTree, parent); + const isFirstChild = process === siblings[0]; + + if (metadata.isOnlyChild) { + // add a single line segment directly from parent to child. We don't do the 'pitchfork' in this case. + edgeLineSegments.push([parentPosition, position]); + } else if (isFirstChild) { + /** + * If the parent has multiple children, we draw the 'midway' line, and the line from the + * parent to the midway line, while handling the first child. + * + * Consider A the parent, and B the first child. We would draw somemthing like what's in the below diagram. The line from the + * midway line to C would be drawn when we handle C. + * + * A + * ____|____ + * | + * B C + */ + const { firstChildWidth, lastChildWidth } = metadata; + + const lineFromParentToMidwayLine: EdgeLineSegment = [ + parentPosition, + [parentPosition[0], midwayY], + ]; + + const widthOfMidline = parentWidth - firstChildWidth / 2 - lastChildWidth / 2; + + const minX = parentWidth / -2 + firstChildWidth / 2; + const maxX = minX + widthOfMidline; + + const midwayLine: EdgeLineSegment = [ + [ + // Position line relative to the parent's x component + parentPosition[0] + minX, + midwayY, + ], + [ + // Position line relative to the parent's x component + parentPosition[0] + maxX, + midwayY, + ], + ]; + + edgeLineSegments.push( + /* line from parent to midway line */ + lineFromParentToMidwayLine, + midwayLine, + lineFromProcessToMidwayLine + ); + } else { + // If this isn't the first child, it must have siblings (the first of which drew the midway line and line + // from the parent to the midway line + edgeLineSegments.push(lineFromProcessToMidwayLine); + } + } + return edgeLineSegments; +} + +function* levelOrderWithWidths( + tree: IndexedProcessTree, + widths: ProcessWidths +): Iterable { + for (const process of levelOrder(tree)) { + const parent = indexedProcessTreeParent(tree, process); + const width = widths.get(process); + + if (width === undefined) { + /** + * All widths have been precalcluated, so this will not happen. + */ + throw new Error(); + } + + /** If the parent is undefined, we are processing the root. */ + if (parent === undefined) { + yield { + process, + width, + parent: null, + parentWidth: null, + isOnlyChild: null, + firstChildWidth: null, + lastChildWidth: null, + }; + } else { + const parentWidth = widths.get(parent); + + if (parentWidth === undefined) { + /** + * All widths have been precalcluated, so this will not happen. + */ + throw new Error(); + } + + const metadata: Partial = { + process, + width, + parent, + parentWidth, + }; + + const siblings = indexedProcessTreeChildren(tree, parent); + if (siblings.length === 1) { + metadata.isOnlyChild = true; + metadata.lastChildWidth = width; + metadata.firstChildWidth = width; + } else { + const firstChildWidth = widths.get(siblings[0]); + const lastChildWidth = widths.get(siblings[0]); + if (firstChildWidth === undefined || lastChildWidth === undefined) { + /** + * All widths have been precalcluated, so this will not happen. + */ + throw new Error(); + } + metadata.isOnlyChild = false; + metadata.firstChildWidth = firstChildWidth; + metadata.lastChildWidth = lastChildWidth; + } + + yield metadata as ProcessWithWidthMetadata; + } + } +} + +function processPositions( + indexedProcessTree: IndexedProcessTree, + widths: ProcessWidths +): ProcessPositions { + const positions = new Map(); + /** + * This algorithm iterates the tree in level order. It keeps counters that are reset for each parent. + * By keeping track of the last parent node, we can know when we are dealing with a new set of siblings and + * reset the counters. + */ + let lastProcessedParentNode: ProcessEvent | undefined; + /** + * Nodes are positioned relative to their siblings. We walk this in level order, so we handle + * children left -> right. + * + * The width of preceding siblings is used to left align the node. + * The number of preceding siblings is important because each sibling must be 1 unit apart + * on the x axis. + */ + let numberOfPrecedingSiblings = 0; + let runningWidthOfPrecedingSiblings = 0; + + for (const metadata of levelOrderWithWidths(indexedProcessTree, widths)) { + // Handle root node + if (metadata.parent === null) { + const { process } = metadata; + /** + * Place the root node at (0, 0) for now. + */ + positions.set(process, [0, 0]); + } else { + const { process, parent, width, parentWidth } = metadata; + + // Reinit counters when parent changes + if (lastProcessedParentNode !== parent) { + numberOfPrecedingSiblings = 0; + runningWidthOfPrecedingSiblings = 0; + + // keep track of this so we know when to reinitialize + lastProcessedParentNode = parent; + } + + const parentPosition = positions.get(parent); + + if (parentPosition === undefined) { + /** + * Since this algorithm populates the `positions` map in level order, + * the parent node will have been processed already and the parent position + * will always be available. + * + * This will never happen. + */ + throw new Error(); + } + + /** + * The x 'offset' is added to the x value of the parent to determine the position of the node. + * We add `parentWidth / -2` in order to align the left side of this node with the left side of its parent. + * We add `numberOfPrecedingSiblings * distanceBetweenNodes` in order to keep each node 1 apart on the x axis. + * We add `runningWidthOfPrecedingSiblings` so that we don't overlap with our preceding siblings. We stack em up. + * We add `width / 2` so that we center the node horizontally (in case it has non-0 width.) + */ + const xOffset = + parentWidth / -2 + + numberOfPrecedingSiblings * distanceBetweenNodes + + runningWidthOfPrecedingSiblings + + width / 2; + + /** + * The y axis gains `-distanceBetweenNodes` as we move down the screen 1 unit at a time. + */ + const position = vector2Add([xOffset, -distanceBetweenNodes], parentPosition); + + positions.set(process, position); + + numberOfPrecedingSiblings += 1; + runningWidthOfPrecedingSiblings += width; + } + } + + return positions; +} + +export const processNodePositionsAndEdgeLineSegments = createSelector( + graphableProcesses, + function processNodePositionsAndEdgeLineSegments( + /* eslint-disable no-shadow */ + graphableProcesses + /* eslint-enable no-shadow */ + ) { + /** + * Index the tree, creating maps from id -> node and id -> children + */ + const indexedProcessTree = indexedProcessTreeFactory(graphableProcesses); + /** + * Walk the tree in reverse level order, calculating the 'width' of subtrees. + */ + const widths = widthsOfProcessSubtrees(indexedProcessTree); + + /** + * Walk the tree in level order. Using the precalculated widths, calculate the position of nodes. + * Nodes are positioned relative to their parents and preceding siblings. + */ + const positions = processPositions(indexedProcessTree, widths); + + /** + * With the widths and positions precalculated, we calculate edge line segments (arrays of vector2s) + * which connect them in a 'pitchfork' design. + */ + const edgeLineSegments = processEdgeLineSegments(indexedProcessTree, widths, positions); + + /** + * Transform the positions of nodes and edges so they seem like they are on an isometric grid. + */ + const transformedEdgeLineSegments: EdgeLineSegment[] = []; + const transformedPositions = new Map(); + + for (const [processEvent, position] of positions) { + transformedPositions.set(processEvent, applyMatrix3(position, isometricTransformMatrix)); + } + + for (const edgeLineSegment of edgeLineSegments) { + const transformedSegment = []; + for (const point of edgeLineSegment) { + transformedSegment.push(applyMatrix3(point, isometricTransformMatrix)); + } + transformedEdgeLineSegments.push(transformedSegment); + } + + return { + processNodePositions: transformedPositions, + edgeLineSegments: transformedEdgeLineSegments, + }; + } +); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/index.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/index.ts new file mode 100644 index 0000000000000..b17572bbc4ab4 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createStore, applyMiddleware, Store } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly'; +import { ResolverAction, ResolverState } from '../types'; +import { resolverReducer } from './reducer'; + +export const storeFactory = (): { store: Store } => { + const actionsBlacklist: Array = ['userMovedPointer']; + const composeEnhancers = composeWithDevTools({ + name: 'Resolver', + actionsBlacklist, + }); + + const middlewareEnhancer = applyMiddleware(); + + const store = createStore(resolverReducer, composeEnhancers(middlewareEnhancer)); + return { + store, + }; +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts new file mode 100644 index 0000000000000..8808160c9c631 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { animatePanning } from './camera/methods'; +import { processNodePositionsAndEdgeLineSegments } from './selectors'; +import { ResolverState, ProcessEvent } from '../types'; + +const animationDuration = 1000; + +/** + * Return new `ResolverState` with the camera animating to focus on `process`. + */ +export function animateProcessIntoView( + state: ResolverState, + startTime: number, + process: ProcessEvent +): ResolverState { + const { processNodePositions } = processNodePositionsAndEdgeLineSegments(state); + const position = processNodePositions.get(process); + if (position) { + return { + ...state, + camera: animatePanning(state.camera, startTime, position, animationDuration), + }; + } + return state; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/reducer.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/reducer.ts new file mode 100644 index 0000000000000..20c490b8998f9 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/reducer.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Reducer, combineReducers } from 'redux'; +import { animateProcessIntoView } from './methods'; +import { cameraReducer } from './camera/reducer'; +import { dataReducer } from './data/reducer'; +import { ResolverState, ResolverAction } from '../types'; + +const concernReducers = combineReducers({ + camera: cameraReducer, + data: dataReducer, +}); + +export const resolverReducer: Reducer = (state, action) => { + const nextState = concernReducers(state, action); + if (action.type === 'userBroughtProcessIntoView') { + return animateProcessIntoView(nextState, action.payload.time, action.payload.process); + } else { + return nextState; + } +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts new file mode 100644 index 0000000000000..4d12e656205fa --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as cameraSelectors from './camera/selectors'; +import * as dataSelectors from './data/selectors'; +import { ResolverState } from '../types'; + +/** + * A matrix that when applied to a Vector2 will convert it from world coordinates to screen coordinates. + * See https://en.wikipedia.org/wiki/Orthographic_projection + */ +export const projectionMatrix = composeSelectors( + cameraStateSelector, + cameraSelectors.projectionMatrix +); + +export const clippingPlanes = composeSelectors(cameraStateSelector, cameraSelectors.clippingPlanes); +export const translation = composeSelectors(cameraStateSelector, cameraSelectors.translation); + +/** + * A matrix that when applied to a Vector2 converts it from screen coordinates to world coordinates. + * See https://en.wikipedia.org/wiki/Orthographic_projection + */ +export const inverseProjectionMatrix = composeSelectors( + cameraStateSelector, + cameraSelectors.inverseProjectionMatrix +); + +/** + * The scale by which world values are scaled when rendered. + * TODO make it a number + */ +export const scale = composeSelectors(cameraStateSelector, cameraSelectors.scale); + +/** + * Scales the coordinate system, used for zooming. Should always be between 0 and 1 + */ +export const scalingFactor = composeSelectors(cameraStateSelector, cameraSelectors.scalingFactor); + +/** + * Whether or not the user is current panning the map. + */ +export const userIsPanning = composeSelectors(cameraStateSelector, cameraSelectors.userIsPanning); + +/** + * Whether or not the camera is animating, at a given time. + */ +export const isAnimating = composeSelectors(cameraStateSelector, cameraSelectors.isAnimating); + +export const processNodePositionsAndEdgeLineSegments = composeSelectors( + dataStateSelector, + dataSelectors.processNodePositionsAndEdgeLineSegments +); + +/** + * Returns the camera state from within ResolverState + */ +function cameraStateSelector(state: ResolverState) { + return state.camera; +} + +/** + * Returns the data state from within ResolverState + */ +function dataStateSelector(state: ResolverState) { + return state.data; +} + +/** + * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a + * concern-specific selector. `selector` should return the concern-specific state. + */ +function composeSelectors( + selector: (state: OuterState) => InnerState, + secondSelector: (state: InnerState) => ReturnValue +): (state: OuterState) => ReturnValue { + return state => secondSelector(selector(state)); +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts new file mode 100644 index 0000000000000..6c6936d377dea --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts @@ -0,0 +1,276 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Store } from 'redux'; + +import { ResolverAction } from './store/actions'; +export { ResolverAction } from './store/actions'; + +/** + * Redux state for the Resolver feature. Properties on this interface are populated via multiple reducers using redux's `combineReducers`. + */ +export interface ResolverState { + /** + * Contains the state of the camera. This includes panning interactions, transform, and projection. + */ + readonly camera: CameraState; + + /** + * Contains the state associated with event data (process events and possibly other event types). + */ + readonly data: DataState; +} + +/** + * Piece of redux state that models an animation for the camera. + */ +export interface CameraAnimationState { + /** + * The time when the animation began. + */ + readonly startTime: number; + /** + * The final translation when the animation is complete. + */ + readonly targetTranslation: Vector2; + /** + * The effective camera position (including an in-progress user panning) at the time + * when the animation began. + */ + readonly initialTranslation: Vector2; + + /** + * The duration, in milliseconds, that the animation should last. Should be > 0 + */ + readonly duration: number; +} + +/** + * The redux state for the `useCamera` hook. + */ +export type CameraState = { + /** + * Scales the coordinate system, used for zooming. Should always be between 0 and 1 + */ + readonly scalingFactor: number; + + /** + * The size (in pixels) of the Resolver component. + */ + readonly rasterSize: Vector2; + + /** + * The camera world transform not counting any change from panning. When panning finishes, this value is updated to account for it. + * Use the `translation` selector to get the effective translation adjusted for panning. + */ + readonly translationNotCountingCurrentPanning: Vector2; + + /** + * The world coordinates that the pointing device was last over. This is used during mousewheel zoom. + */ + readonly latestFocusedWorldCoordinates: Vector2 | null; +} & ( + | { + /** + * Contains the animation start time and target translation. This doesn't model the instantaneous + * progress of an animation. Instead, animation is model as functions-of-time. + */ + readonly animation: CameraAnimationState; + /** + * If the camera is animating, it must not be panning. + */ + readonly panning: undefined; + } + | { + /** + * If the camera is panning, it must not be animating. + */ + readonly animation: undefined; + /** + * Contains the starting and current position of the pointer when the user is panning the map. + */ + readonly panning: { + /** + * Screen coordinate vector representing the starting point when panning. + */ + readonly origin: Vector2; + + /** + * Screen coordinate vector representing the current point when panning. + */ + readonly currentOffset: Vector2; + }; + } + | { + readonly animation: undefined; + readonly panning: undefined; + } +); + +/** + * State for `data` reducer which handles receiving Resolver data from the backend. + */ +export interface DataState { + readonly results: readonly ProcessEvent[]; +} + +export type Vector2 = readonly [number, number]; + +/** + * A rectangle with sides that align with the `x` and `y` axises. + */ +export interface AABB { + /** + * Vector who's `x` component is the _left_ side of the AABB and who's `y` component is the _bottom_ side of the AABB. + **/ + readonly minimum: Vector2; + /** + * Vector who's `x` component is the _right_ side of the AABB and who's `y` component is the _bottom_ side of the AABB. + **/ + readonly maximum: Vector2; +} + +/** + * A 2D transformation matrix in row-major order. + */ +export type Matrix3 = readonly [ + number, + number, + number, + number, + number, + number, + number, + number, + number +]; + +type eventSubtypeFull = + | 'creation_event' + | 'fork_event' + | 'exec_event' + | 'already_running' + | 'termination_event'; + +type eventTypeFull = 'process_event'; + +/** + * The 'events' which contain process data and are used to model Resolver. + */ +export interface ProcessEvent { + readonly event_timestamp: number; + readonly event_type: number; + readonly machine_id: string; + readonly data_buffer: { + timestamp_utc: string; + event_subtype_full: eventSubtypeFull; + event_type_full: eventTypeFull; + node_id: number; + source_id?: number; + process_name: string; + process_path: string; + }; +} + +/** + * A represention of a process tree with indices for O(1) access to children and values by id. + */ +export interface IndexedProcessTree { + /** + * Map of ID to a process's children + */ + idToChildren: Map; + /** + * Map of ID to process + */ + idToProcess: Map; +} + +/** + * A map of ProcessEvents (representing process nodes) to the 'width' of their subtrees as calculated by `widthsOfProcessSubtrees` + */ +export type ProcessWidths = Map; +/** + * Map of ProcessEvents (representing process nodes) to their positions. Calculated by `processPositions` + */ +export type ProcessPositions = Map; +/** + * An array of vectors2 forming an polyline. Used to connect process nodes in the graph. + */ +export type EdgeLineSegment = Vector2[]; + +/** + * Used to provide precalculated info from `widthsOfProcessSubtrees`. These 'width' values are used in the layout of the graph. + */ +export type ProcessWithWidthMetadata = { + process: ProcessEvent; + width: number; +} & ( + | { + parent: ProcessEvent; + parentWidth: number; + isOnlyChild: boolean; + firstChildWidth: number; + lastChildWidth: number; + } + | { + parent: null; + /* Without a parent, there is no parent width */ + parentWidth: null; + /* Without a parent, we can't be an only child */ + isOnlyChild: null; + /** If there is no parent, there are no siblings */ + lastChildWidth: null; + firstChildWidth: null; + } +); + +/** + * The constructor for a ResizeObserver + */ +interface ResizeObserverConstructor { + prototype: ResizeObserver; + new (callback: ResizeObserverCallback): ResizeObserver; +} + +/** + * Functions that introduce side effects. A React context provides these, and they may be mocked in tests. + */ +export interface SideEffectors { + /** + * A function which returns the time since epoch in milliseconds. Injected because mocking Date is tedious. + */ + timestamp: () => number; + requestAnimationFrame: typeof window.requestAnimationFrame; + cancelAnimationFrame: typeof window.cancelAnimationFrame; + ResizeObserver: ResizeObserverConstructor; +} + +export interface SideEffectSimulator { + /** + * Control the mock `SideEffectors`. + */ + controls: { + /** + * Set or get the `time` number used for `timestamp` and `requestAnimationFrame` callbacks. + */ + time: number; + /** + * Call any pending `requestAnimationFrame` callbacks. + */ + provideAnimationFrame: () => void; + /** + * Trigger `ResizeObserver` callbacks for `element` and update the mocked value for `getBoundingClientRect`. + */ + simulateElementResize: (element: Element, contentRect: DOMRect) => void; + }; + /** + * Mocked `SideEffectors`. + */ + mock: jest.Mocked> & Pick; +} + +export type ResolverStore = Store; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/edge_line.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/edge_line.tsx new file mode 100644 index 0000000000000..3386ed4a448d5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/edge_line.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { applyMatrix3, distance, angle } from '../lib/vector2'; +import { Vector2, Matrix3 } from '../types'; + +/** + * A placeholder line segment view that connects process nodes. + */ +export const EdgeLine = styled( + React.memo( + ({ + className, + startPosition, + endPosition, + projectionMatrix, + }: { + /** + * A className string provided by `styled` + */ + className?: string; + /** + * The postion of first point in the line segment. In 'world' coordinates. + */ + startPosition: Vector2; + /** + * The postion of second point in the line segment. In 'world' coordinates. + */ + endPosition: Vector2; + /** + * projectionMatrix which can be used to convert `startPosition` and `endPosition` to screen coordinates. + */ + projectionMatrix: Matrix3; + }) => { + /** + * Convert the start and end positions, which are in 'world' coordinates, + * to `left` and `top` css values. + */ + const screenStart = applyMatrix3(startPosition, projectionMatrix); + const screenEnd = applyMatrix3(endPosition, projectionMatrix); + + /** + * We render the line using a short, long, `div` element. The length of this `div` + * should be the same as the distance between the start and end points. + */ + const length = distance(screenStart, screenEnd); + + const style = { + left: screenStart[0] + 'px', + top: screenStart[1] + 'px', + width: length + 'px', + /** + * Transform from the left of the div, as the left side of the `div` is positioned + * at the start point of the line segment. + */ + transformOrigin: 'top left', + /** + * Translate the `div` in the y axis to accomodate for the height of the `div`. + * Also rotate the `div` in the z axis so that it's angle matches the angle + * between the start and end points. + */ + transform: `translateY(-50%) rotateZ(${angle(screenStart, screenEnd)}rad)`, + }; + return
; + } + ) +)` + position: absolute; + height: 3px; + background-color: #d4d4d4; + color: #333333; +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/graph_controls.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/graph_controls.tsx new file mode 100644 index 0000000000000..a1cd003949a22 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/graph_controls.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useMemo, useContext } from 'react'; +import styled from 'styled-components'; +import { EuiRange, EuiPanel, EuiIcon } from '@elastic/eui'; +import { useSelector, useDispatch } from 'react-redux'; +import { SideEffectContext } from './side_effect_context'; +import { ResolverAction, Vector2 } from '../types'; +import * as selectors from '../store/selectors'; + +/** + * Controls for zooming, panning, and centering in Resolver + */ +export const GraphControls = styled( + React.memo( + ({ + className, + }: { + /** + * A className string provided by `styled` + */ + className?: string; + }) => { + const dispatch: (action: ResolverAction) => unknown = useDispatch(); + const scalingFactor = useSelector(selectors.scalingFactor); + const { timestamp } = useContext(SideEffectContext); + + const handleZoomAmountChange = useCallback( + (event: React.ChangeEvent | React.MouseEvent) => { + const valueAsNumber = parseFloat( + (event as React.ChangeEvent).target.value + ); + if (isNaN(valueAsNumber) === false) { + dispatch({ + type: 'userSetZoomLevel', + payload: valueAsNumber, + }); + } + }, + [dispatch] + ); + + const handleCenterClick = useCallback(() => { + dispatch({ + type: 'userSetPositionOfCamera', + payload: [0, 0], + }); + }, [dispatch]); + + const handleZoomOutClick = useCallback(() => { + dispatch({ + type: 'userClickedZoomOut', + }); + }, [dispatch]); + + const handleZoomInClick = useCallback(() => { + dispatch({ + type: 'userClickedZoomIn', + }); + }, [dispatch]); + + const [handleNorth, handleEast, handleSouth, handleWest] = useMemo(() => { + const directionVectors: readonly Vector2[] = [ + [0, 1], + [1, 0], + [0, -1], + [-1, 0], + ]; + return directionVectors.map(direction => { + return () => { + const action: ResolverAction = { + type: 'userNudgedCamera', + payload: { direction, time: timestamp() }, + }; + dispatch(action); + }; + }); + }, [dispatch, timestamp]); + + return ( +
+ +
+ +
+
+ + + +
+
+ +
+
+ + + + + +
+ ); + } + ) +)` + background-color: #d4d4d4; + color: #333333; + + .zoom-controls { + display: flex; + flex-direction: column; + align-items: center; + padding: 5px 0px; + + .zoom-slider { + width: 20px; + height: 150px; + margin: 5px 0px 2px 0px; + + input[type='range'] { + width: 150px; + height: 20px; + transform-origin: 75px 75px; + transform: rotate(-90deg); + } + } + } + .panning-controls { + text-align: center; + } +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx new file mode 100644 index 0000000000000..d71a4d87b7eab --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useSelector } from 'react-redux'; +import styled from 'styled-components'; +import * as selectors from '../store/selectors'; +import { EdgeLine } from './edge_line'; +import { Panel } from './panel'; +import { GraphControls } from './graph_controls'; +import { ProcessEventDot } from './process_event_dot'; +import { useCamera } from './use_camera'; + +const StyledPanel = styled(Panel)` + position: absolute; + left: 1em; + top: 1em; + max-height: calc(100% - 2em); + overflow: auto; + width: 25em; + max-width: 50%; +`; + +const StyledGraphControls = styled(GraphControls)` + position: absolute; + top: 5px; + right: 5px; +`; + +export const Resolver = styled( + React.memo(function Resolver({ className }: { className?: string }) { + const { processNodePositions, edgeLineSegments } = useSelector( + selectors.processNodePositionsAndEdgeLineSegments + ); + + const { projectionMatrix, ref, onMouseDown } = useCamera(); + + return ( +
+
+ {Array.from(processNodePositions).map(([processEvent, position], index) => ( + + ))} + {edgeLineSegments.map(([startPosition, endPosition], index) => ( + + ))} +
+ + +
+ ); + }) +)` + /** + * Take up all availble space + */ + &, + .resolver-graph { + display: flex; + flex-grow: 1; + } + /** + * The placeholder components use absolute positioning. + */ + position: relative; + /** + * Prevent partially visible components from showing up outside the bounds of Resolver. + */ + overflow: hidden; +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx new file mode 100644 index 0000000000000..c75b73b4bceaf --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { memo, useCallback, useMemo, useContext } from 'react'; +import { EuiPanel, EuiBadge, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiTitle } from '@elastic/eui'; +import { EuiHorizontalRule, EuiInMemoryTable } from '@elastic/eui'; +import euiVars from '@elastic/eui/dist/eui_theme_light.json'; +import { useSelector } from 'react-redux'; +import { i18n } from '@kbn/i18n'; +import { SideEffectContext } from './side_effect_context'; +import { ProcessEvent } from '../types'; +import { useResolverDispatch } from './use_resolver_dispatch'; +import * as selectors from '../store/selectors'; + +const HorizontalRule = memo(function HorizontalRule() { + return ( + + ); +}); + +export const Panel = memo(function Event({ className }: { className?: string }) { + interface ProcessTableView { + name: string; + timestamp?: Date; + event: ProcessEvent; + } + + const { processNodePositions } = useSelector(selectors.processNodePositionsAndEdgeLineSegments); + const { timestamp } = useContext(SideEffectContext); + + const processTableView: ProcessTableView[] = useMemo( + () => + [...processNodePositions.keys()].map(processEvent => { + const { data_buffer } = processEvent; + const date = new Date(data_buffer.timestamp_utc); + return { + name: data_buffer.process_name, + timestamp: isFinite(date.getTime()) ? date : undefined, + event: processEvent, + }; + }), + [processNodePositions] + ); + + const formatter = new Intl.DateTimeFormat(i18n.getLocale(), { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + + const dispatch = useResolverDispatch(); + + const handleBringIntoViewClick = useCallback( + processTableViewItem => { + dispatch({ + type: 'userBroughtProcessIntoView', + payload: { + time: timestamp(), + process: processTableViewItem.event, + }, + }); + }, + [dispatch, timestamp] + ); + + const columns = useMemo>>( + () => [ + { + field: 'name', + name: i18n.translate('xpack.endpoint.resolver.panel.tabel.row.processNameTitle', { + defaultMessage: 'Process Name', + }), + sortable: true, + truncateText: true, + render(name: string) { + return name === '' ? ( + + {i18n.translate('xpack.endpoint.resolver.panel.table.row.valueMissingDescription', { + defaultMessage: 'Value is missing', + })} + + ) : ( + name + ); + }, + }, + { + field: 'timestamp', + name: i18n.translate('xpack.endpoint.resolver.panel.tabel.row.timestampTitle', { + defaultMessage: 'Timestamp', + }), + dataType: 'date', + sortable: true, + render(eventTimestamp?: Date) { + return eventTimestamp ? ( + formatter.format(eventTimestamp) + ) : ( + + {i18n.translate('xpack.endpoint.resolver.panel.tabel.row.timestampInvalidLabel', { + defaultMessage: 'invalid', + })} + + ); + }, + }, + { + name: i18n.translate('xpack.endpoint.resolver.panel.tabel.row.actionsTitle', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: i18n.translate( + 'xpack.endpoint.resolver.panel.tabel.row.actions.bringIntoViewButtonLabel', + { + defaultMessage: 'Bring into view', + } + ), + description: i18n.translate( + 'xpack.endpoint.resolver.panel.tabel.row.bringIntoViewLabel', + { + defaultMessage: 'Bring the process into view on the map.', + } + ), + type: 'icon', + icon: 'flag', + onClick: handleBringIntoViewClick, + }, + ], + }, + ], + [formatter, handleBringIntoViewClick] + ); + return ( + + +

+ {i18n.translate('xpack.endpoint.resolver.panel.title', { + defaultMessage: 'Processes', + })} +

+
+ + items={processTableView} columns={columns} sorting /> +
+ ); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx new file mode 100644 index 0000000000000..384fbf90ed984 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { applyMatrix3 } from '../lib/vector2'; +import { Vector2, ProcessEvent, Matrix3 } from '../types'; + +/** + * A placeholder view for a process node. + */ +export const ProcessEventDot = styled( + React.memo( + ({ + className, + position, + event, + projectionMatrix, + }: { + /** + * A `className` string provided by `styled` + */ + className?: string; + /** + * The positon of the process node, in 'world' coordinates. + */ + position: Vector2; + /** + * An event which contains details about the process node. + */ + event: ProcessEvent; + /** + * projectionMatrix which can be used to convert `position` to screen coordinates. + */ + projectionMatrix: Matrix3; + }) => { + /** + * Convert the position, which is in 'world' coordinates, to screen coordinates. + */ + const [left, top] = applyMatrix3(position, projectionMatrix); + + const style = { + left: (left - 20).toString() + 'px', + top: (top - 20).toString() + 'px', + }; + return ( + + name: {event.data_buffer.process_name} +
+ x: {position[0]} +
+ y: {position[1]} +
+ ); + } + ) +)` + position: absolute; + width: 40px; + height: 40px; + text-align: left; + font-size: 10px; + /** + * Give the element a button-like appearance. + */ + user-select: none; + border: 1px solid black; + box-sizing: border-box; + border-radius: 10%; + padding: 4px; + white-space: nowrap; +`; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_context.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_context.ts new file mode 100644 index 0000000000000..ab7f41d815026 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_context.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { createContext, Context } from 'react'; +import ResizeObserver from 'resize-observer-polyfill'; +import { SideEffectors } from '../types'; + +/** + * React context that provides 'side-effectors' which we need to mock during testing. + */ +const sideEffectors: SideEffectors = { + timestamp: () => Date.now(), + requestAnimationFrame(...args) { + return window.requestAnimationFrame(...args); + }, + cancelAnimationFrame(...args) { + return window.cancelAnimationFrame(...args); + }, + ResizeObserver, +}; + +/** + * The default values are used in production, tests can provide mock values using `SideEffectSimulator`. + */ +export const SideEffectContext: Context = createContext(sideEffectors); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_simulator.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_simulator.ts new file mode 100644 index 0000000000000..3e80b6a8459f7 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/side_effect_simulator.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from '@testing-library/react'; +import { SideEffectSimulator } from '../types'; + +/** + * Create mock `SideEffectors` for `SideEffectContext.Provider`. The `control` + * object is used to control the mocks. + */ +export const sideEffectSimulator: () => SideEffectSimulator = () => { + // The set of mock `ResizeObserver` instances that currently exist + const resizeObserverInstances: Set = new Set(); + + // A map of `Element`s to their fake `DOMRect`s + const contentRects: Map = new Map(); + + /** + * Simulate an element's size changing. This will trigger any `ResizeObserverCallback`s which + * are listening for this element's size changes. It will also cause `element.getBoundingClientRect` to + * return `contentRect` + */ + const simulateElementResize: (target: Element, contentRect: DOMRect) => void = ( + target, + contentRect + ) => { + contentRects.set(target, contentRect); + for (const instance of resizeObserverInstances) { + instance.simulateElementResize(target, contentRect); + } + }; + + /** + * Get the simulate `DOMRect` for `element`. + */ + const contentRectForElement: (target: Element) => DOMRect = target => { + if (contentRects.has(target)) { + return contentRects.get(target)!; + } + const domRect: DOMRect = { + x: 0, + y: 0, + top: 0, + right: 0, + bottom: 0, + left: 0, + width: 0, + height: 0, + toJSON() { + return this; + }, + }; + return domRect; + }; + + /** + * Change `Element.prototype.getBoundingClientRect` to return our faked values. + */ + jest + .spyOn(Element.prototype, 'getBoundingClientRect') + .mockImplementation(function(this: Element) { + return contentRectForElement(this); + }); + + /** + * A mock implementation of `ResizeObserver` that works with our fake `getBoundingClientRect` and `simulateElementResize` + */ + class MockResizeObserver implements ResizeObserver { + constructor(private readonly callback: ResizeObserverCallback) { + resizeObserverInstances.add(this); + } + private elements: Set = new Set(); + /** + * Simulate `target` changing it size to `contentRect`. + */ + simulateElementResize(target: Element, contentRect: DOMRect) { + if (this.elements.has(target)) { + const entries: ResizeObserverEntry[] = [{ target, contentRect }]; + this.callback(entries, this); + } + } + observe(target: Element) { + this.elements.add(target); + } + unobserve(target: Element) { + this.elements.delete(target); + } + disconnect() { + this.elements.clear(); + } + } + + /** + * milliseconds since epoch, faked. + */ + let mockTime: number = 0; + + /** + * A counter allowing us to give a unique ID for each call to `requestAnimationFrame`. + */ + let frameRequestedCallbacksIDCounter: number = 0; + + /** + * A map of requestAnimationFrame IDs to the related callbacks. + */ + const frameRequestedCallbacks: Map = new Map(); + + /** + * Trigger any pending `requestAnimationFrame` callbacks. Passes `mockTime` as the timestamp. + */ + const provideAnimationFrame: () => void = () => { + act(() => { + // Iterate the values, and clear the data set before calling the callbacks because the callbacks will repopulate the dataset synchronously in this testing framework. + const values = [...frameRequestedCallbacks.values()]; + frameRequestedCallbacks.clear(); + for (const callback of values) { + callback(mockTime); + } + }); + }; + + /** + * Provide a fake ms timestamp + */ + const timestamp = jest.fn(() => mockTime); + + /** + * Fake `requestAnimationFrame`. + */ + const requestAnimationFrame = jest.fn((callback: FrameRequestCallback): number => { + const id = frameRequestedCallbacksIDCounter++; + frameRequestedCallbacks.set(id, callback); + return id; + }); + + /** + * fake `cancelAnimationFrame`. + */ + const cancelAnimationFrame = jest.fn((id: number) => { + frameRequestedCallbacks.delete(id); + }); + + const retval: SideEffectSimulator = { + controls: { + provideAnimationFrame, + + /** + * Change the mock time value + */ + set time(nextTime: number) { + mockTime = nextTime; + }, + get time() { + return mockTime; + }, + + simulateElementResize, + }, + mock: { + requestAnimationFrame, + cancelAnimationFrame, + timestamp, + ResizeObserver: MockResizeObserver, + }, + }; + return retval; +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx new file mode 100644 index 0000000000000..85e1d4e694b15 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * This import must be hoisted as it uses `jest.mock`. Is there a better way? Mocking is not good. + */ +import React from 'react'; +import { render, act, RenderResult, fireEvent } from '@testing-library/react'; +import { useCamera } from './use_camera'; +import { Provider } from 'react-redux'; +import * as selectors from '../store/selectors'; +import { storeFactory } from '../store'; +import { + Matrix3, + ResolverAction, + ResolverStore, + ProcessEvent, + SideEffectSimulator, +} from '../types'; +import { SideEffectContext } from './side_effect_context'; +import { applyMatrix3 } from '../lib/vector2'; +import { sideEffectSimulator } from './side_effect_simulator'; + +describe('useCamera on an unpainted element', () => { + let element: HTMLElement; + let projectionMatrix: Matrix3; + const testID = 'camera'; + let reactRenderResult: RenderResult; + let store: ResolverStore; + let simulator: SideEffectSimulator; + beforeEach(async () => { + ({ store } = storeFactory()); + + const Test = function Test() { + const camera = useCamera(); + const { ref, onMouseDown } = camera; + projectionMatrix = camera.projectionMatrix; + return
; + }; + + simulator = sideEffectSimulator(); + + reactRenderResult = render( + + + + + + ); + + const { findByTestId } = reactRenderResult; + element = await findByTestId(testID); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + it('should be usable in React', async () => { + expect(element).toBeInTheDocument(); + }); + test('returns a projectionMatrix that changes everything to 0', () => { + expect(applyMatrix3([0, 0], projectionMatrix)).toEqual([0, 0]); + }); + describe('which has been resized to 800x600', () => { + const width = 800; + const height = 600; + const leftMargin = 20; + const topMargin = 20; + const centerX = width / 2 + leftMargin; + const centerY = height / 2 + topMargin; + beforeEach(() => { + act(() => { + simulator.controls.simulateElementResize(element, { + width, + height, + left: leftMargin, + top: topMargin, + right: leftMargin + width, + bottom: topMargin + height, + x: leftMargin, + y: topMargin, + toJSON() { + return this; + }, + }); + }); + }); + test('provides a projection matrix that inverts the y axis and translates 400,300 (center of the element)', () => { + expect(applyMatrix3([0, 0], projectionMatrix)).toEqual([400, 300]); + }); + describe('when the user presses the mousedown button in the middle of the element', () => { + beforeEach(() => { + fireEvent.mouseDown(element, { + clientX: centerX, + clientY: centerY, + }); + }); + describe('when the user moves the mouse 50 pixels to the right', () => { + beforeEach(() => { + fireEvent.mouseMove(element, { + clientX: centerX + 50, + clientY: centerY, + }); + }); + it('should project [0, 0] in world corrdinates 50 pixels to the right of the center of the element', () => { + expect(applyMatrix3([0, 0], projectionMatrix)).toEqual([450, 300]); + }); + }); + }); + + describe('when the user uses the mousewheel w/ ctrl held down', () => { + beforeEach(() => { + fireEvent.wheel(element, { + ctrlKey: true, + deltaY: -10, + deltaMode: 0, + }); + }); + it('should zoom in', () => { + expect(projectionMatrix).toMatchInlineSnapshot(` + Array [ + 1.0635255481707058, + 0, + 400, + 0, + -1.0635255481707058, + 300, + 0, + 0, + 0, + ] + `); + }); + }); + + it('should not initially request an animation frame', () => { + expect(simulator.mock.requestAnimationFrame).not.toHaveBeenCalled(); + }); + describe('when the camera begins animation', () => { + let process: ProcessEvent; + beforeEach(() => { + // At this time, processes are provided via mock data. In the future, this test will have to provide those mocks. + const processes: ProcessEvent[] = [ + ...selectors + .processNodePositionsAndEdgeLineSegments(store.getState()) + .processNodePositions.keys(), + ]; + process = processes[processes.length - 1]; + simulator.controls.time = 0; + const action: ResolverAction = { + type: 'userBroughtProcessIntoView', + payload: { + time: simulator.controls.time, + process, + }, + }; + act(() => { + store.dispatch(action); + }); + }); + + it('should request animation frames in a loop', () => { + const animationDuration = 1000; + // When the animation begins, the camera should request an animation frame. + expect(simulator.mock.requestAnimationFrame).toHaveBeenCalledTimes(1); + + // Update the time so that the animation is partially complete. + simulator.controls.time = animationDuration / 5; + // Provide the animation frame, allowing the camera to rerender. + simulator.controls.provideAnimationFrame(); + + // The animation is not complete, so the camera should request another animation frame. + expect(simulator.mock.requestAnimationFrame).toHaveBeenCalledTimes(2); + + // Update the camera so that the animation is nearly complete. + simulator.controls.time = (animationDuration / 10) * 9; + + // Provide the animation frame + simulator.controls.provideAnimationFrame(); + + // Since the animation isn't complete, it should request another frame + expect(simulator.mock.requestAnimationFrame).toHaveBeenCalledTimes(3); + + // Animation lasts 1000ms, so this should end it + simulator.controls.time = animationDuration * 1.1; + + // Provide the last frame + simulator.controls.provideAnimationFrame(); + + // Since animation is complete, it should not have requseted another frame + expect(simulator.mock.requestAnimationFrame).toHaveBeenCalledTimes(3); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.ts new file mode 100644 index 0000000000000..54940b8383f7a --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.ts @@ -0,0 +1,307 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { + useCallback, + useState, + useEffect, + useRef, + useLayoutEffect, + useContext, +} from 'react'; +import { useSelector } from 'react-redux'; +import { SideEffectContext } from './side_effect_context'; +import { Matrix3 } from '../types'; +import { useResolverDispatch } from './use_resolver_dispatch'; +import * as selectors from '../store/selectors'; + +export function useCamera(): { + /** + * A function to pass to a React element's `ref` property. Used to attach + * native event listeners and to measure the DOM node. + */ + ref: (node: HTMLDivElement | null) => void; + onMouseDown: React.MouseEventHandler; + /** + * A 3x3 transformation matrix used to convert a `vector2` from 'world' coordinates + * to screen coordinates. + */ + projectionMatrix: Matrix3; +} { + const dispatch = useResolverDispatch(); + const sideEffectors = useContext(SideEffectContext); + + const [ref, setRef] = useState(null); + + /** + * The position of a thing, as a `Vector2`, is multiplied by the projection matrix + * to determine where it belongs on the screen. + * The projection matrix changes over time if the camera is currently animating. + */ + const projectionMatrixAtTime = useSelector(selectors.projectionMatrix); + + /** + * Use a ref to refer to the `projectionMatrixAtTime` function. The rAF loop + * accesses this and sets state during the rAF cycle. If the rAF loop + * effect read this directly from the selector, the rAF loop would need to + * be re-inited each time this function changed. The `projectionMatrixAtTime` function + * changes each frame during an animation, so the rAF loop would be causing + * itself to reinit on each frame. This would necessarily cause a drop in FPS as there + * would be a dead zone between when the rAF loop stopped and restarted itself. + */ + const projectionMatrixAtTimeRef = useRef(); + + /** + * The projection matrix is stateful, depending on the current time. + * When the projection matrix changes, the component should be rerendered. + */ + const [projectionMatrix, setProjectionMatrix] = useState( + projectionMatrixAtTime(sideEffectors.timestamp()) + ); + + const userIsPanning = useSelector(selectors.userIsPanning); + const isAnimatingAtTime = useSelector(selectors.isAnimating); + + const [elementBoundingClientRect, clientRectCallback] = useAutoUpdatingClientRect(); + + /** + * For an event with clientX and clientY, return [clientX, clientY] - the top left corner of the `ref` element + */ + const relativeCoordinatesFromMouseEvent = useCallback( + (event: { clientX: number; clientY: number }): null | [number, number] => { + if (elementBoundingClientRect === null) { + return null; + } + return [ + event.clientX - elementBoundingClientRect.x, + event.clientY - elementBoundingClientRect.y, + ]; + }, + [elementBoundingClientRect] + ); + + const handleMouseDown = useCallback( + (event: React.MouseEvent) => { + const maybeCoordinates = relativeCoordinatesFromMouseEvent(event); + if (maybeCoordinates !== null) { + dispatch({ + type: 'userStartedPanning', + payload: { screenCoordinates: maybeCoordinates, time: sideEffectors.timestamp() }, + }); + } + }, + [dispatch, relativeCoordinatesFromMouseEvent, sideEffectors] + ); + + const handleMouseMove = useCallback( + (event: MouseEvent) => { + const maybeCoordinates = relativeCoordinatesFromMouseEvent(event); + if (maybeCoordinates) { + dispatch({ + type: 'userMovedPointer', + payload: { + screenCoordinates: maybeCoordinates, + time: sideEffectors.timestamp(), + }, + }); + } + }, + [dispatch, relativeCoordinatesFromMouseEvent, sideEffectors] + ); + + const handleMouseUp = useCallback(() => { + if (userIsPanning) { + dispatch({ + type: 'userStoppedPanning', + payload: { + time: sideEffectors.timestamp(), + }, + }); + } + }, [dispatch, sideEffectors, userIsPanning]); + + const handleWheel = useCallback( + (event: WheelEvent) => { + if ( + elementBoundingClientRect !== null && + event.ctrlKey && + event.deltaY !== 0 && + event.deltaMode === 0 + ) { + event.preventDefault(); + dispatch({ + type: 'userZoomed', + payload: { + /** + * we use elementBoundingClientRect to interpret pixel deltas as a fraction of the element's height + * when pinch-zooming in on a mac, deltaY is a negative number but we want the payload to be positive + */ + zoomChange: event.deltaY / -elementBoundingClientRect.height, + time: sideEffectors.timestamp(), + }, + }); + } + }, + [elementBoundingClientRect, dispatch, sideEffectors] + ); + + const refCallback = useCallback( + (node: null | HTMLDivElement) => { + setRef(node); + clientRectCallback(node); + }, + [clientRectCallback] + ); + + useEffect(() => { + window.addEventListener('mouseup', handleMouseUp, { passive: true }); + return () => { + window.removeEventListener('mouseup', handleMouseUp); + }; + }, [handleMouseUp]); + + useEffect(() => { + window.addEventListener('mousemove', handleMouseMove, { passive: true }); + return () => { + window.removeEventListener('mousemove', handleMouseMove); + }; + }, [handleMouseMove]); + + /** + * Register an event handler directly on `elementRef` for the `wheel` event, with no options + * React sets native event listeners on the `window` and calls provided handlers via event propagation. + * As of Chrome 73, `'wheel'` events on `window` are automatically treated as 'passive'. + * If you don't need to call `event.preventDefault` then you should use regular React event handling instead. + */ + useEffect(() => { + if (ref !== null) { + ref.addEventListener('wheel', handleWheel); + return () => { + ref.removeEventListener('wheel', handleWheel); + }; + } + }, [ref, handleWheel]); + + /** + * Allow rAF loop to indirectly read projectionMatrixAtTime via a ref. Since it also + * sets projectionMatrixAtTime, relying directly on it causes considerable jank. + */ + useLayoutEffect(() => { + projectionMatrixAtTimeRef.current = projectionMatrixAtTime; + }, [projectionMatrixAtTime]); + + /** + * Keep the projection matrix state in sync with the selector. + * This isn't needed during animation. + */ + useLayoutEffect(() => { + // Update the projection matrix that we return, rerendering any component that uses this. + setProjectionMatrix(projectionMatrixAtTime(sideEffectors.timestamp())); + }, [projectionMatrixAtTime, sideEffectors]); + + /** + * When animation is happening, run a rAF loop, when it is done, stop. + */ + useLayoutEffect( + () => { + const startDate = sideEffectors.timestamp(); + if (isAnimatingAtTime(startDate)) { + let rafRef: null | number = null; + const handleFrame = () => { + // Get the current timestamp, now that the frame is ready + const date = sideEffectors.timestamp(); + if (projectionMatrixAtTimeRef.current !== undefined) { + // Update the projection matrix, triggering a rerender + setProjectionMatrix(projectionMatrixAtTimeRef.current(date)); + } + // If we are still animating, request another frame, continuing the loop + if (isAnimatingAtTime(date)) { + rafRef = sideEffectors.requestAnimationFrame(handleFrame); + } else { + /** + * `isAnimatingAtTime` was false, meaning that the animation is complete. + * Do not request another animation frame. + */ + rafRef = null; + } + }; + // Kick off the loop by requestion an animation frame + rafRef = sideEffectors.requestAnimationFrame(handleFrame); + + /** + * This function cancels the animation frame request. The cancel function + * will occur when the component is unmounted. It will also occur when a dependency + * changes. + * + * The `isAnimatingAtTime` dependency will be changed if the animation state changes. The animation + * state only changes when the user animates again (e.g. brings a different node into view, or nudges the + * camera.) + */ + return () => { + // Cancel the animation frame. + if (rafRef !== null) { + sideEffectors.cancelAnimationFrame(rafRef); + } + }; + } + }, + /** + * `isAnimatingAtTime` is a function created with `reselect`. The function reference will be changed when + * the animation state changes. When the function reference has changed, you *might* be animating. + */ + [isAnimatingAtTime, sideEffectors] + ); + + useEffect(() => { + if (elementBoundingClientRect !== null) { + dispatch({ + type: 'userSetRasterSize', + payload: [elementBoundingClientRect.width, elementBoundingClientRect.height], + }); + } + }, [dispatch, elementBoundingClientRect]); + + return { + ref: refCallback, + onMouseDown: handleMouseDown, + projectionMatrix, + }; +} + +/** + * Returns a nullable DOMRect and a ref callback. Pass the refCallback to the + * `ref` property of a native element and this hook will return a DOMRect for + * it by calling `getBoundingClientRect`. This hook will observe the element + * with a resize observer and call getBoundingClientRect again after resizes. + * + * Note that the changes to the position of the element aren't automatically + * tracked. So if the element's position moves for some reason, be sure to + * handle that. + */ +function useAutoUpdatingClientRect(): [DOMRect | null, (node: Element | null) => void] { + const [rect, setRect] = useState(null); + const nodeRef = useRef(null); + const ref = useCallback((node: Element | null) => { + nodeRef.current = node; + if (node !== null) { + setRect(node.getBoundingClientRect()); + } + }, []); + const { ResizeObserver } = useContext(SideEffectContext); + useEffect(() => { + if (nodeRef.current !== null) { + const resizeObserver = new ResizeObserver(entries => { + if (nodeRef.current !== null && nodeRef.current === entries[0].target) { + setRect(nodeRef.current.getBoundingClientRect()); + } + }); + resizeObserver.observe(nodeRef.current); + return () => { + resizeObserver.disconnect(); + }; + } + }, [ResizeObserver, nodeRef]); + return [rect, ref]; +} diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_resolver_dispatch.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_resolver_dispatch.ts new file mode 100644 index 0000000000000..a993a4ed595e1 --- /dev/null +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_resolver_dispatch.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useDispatch } from 'react-redux'; +import { ResolverAction } from '../types'; + +/** + * Call `useDispatch`, but only accept `ResolverAction` actions. + */ +export const useResolverDispatch: () => (action: ResolverAction) => unknown = useDispatch; diff --git a/x-pack/plugins/endpoint/public/index.ts b/x-pack/plugins/endpoint/public/index.ts new file mode 100644 index 0000000000000..e6a7683efd9a3 --- /dev/null +++ b/x-pack/plugins/endpoint/public/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializer } from 'kibana/public'; +import { + EndpointPlugin, + EndpointPluginStart, + EndpointPluginSetup, + EndpointPluginStartDependencies, + EndpointPluginSetupDependencies, +} from './plugin'; + +export const plugin: PluginInitializer< + EndpointPluginSetup, + EndpointPluginStart, + EndpointPluginSetupDependencies, + EndpointPluginStartDependencies +> = () => new EndpointPlugin(); diff --git a/x-pack/plugins/endpoint/public/plugin.ts b/x-pack/plugins/endpoint/public/plugin.ts new file mode 100644 index 0000000000000..355364253b2a5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/plugin.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup, AppMountParameters } from 'kibana/public'; +import { IEmbeddableSetup } from 'src/plugins/embeddable/public'; +import { i18n } from '@kbn/i18n'; +import { ResolverEmbeddableFactory } from './embeddables/resolver'; + +export type EndpointPluginStart = void; +export type EndpointPluginSetup = void; +export interface EndpointPluginSetupDependencies { + embeddable: IEmbeddableSetup; +} + +export interface EndpointPluginStartDependencies {} // eslint-disable-line @typescript-eslint/no-empty-interface + +export class EndpointPlugin + implements + Plugin< + EndpointPluginSetup, + EndpointPluginStart, + EndpointPluginSetupDependencies, + EndpointPluginStartDependencies + > { + public setup(core: CoreSetup, plugins: EndpointPluginSetupDependencies) { + core.application.register({ + id: 'endpoint', + title: i18n.translate('xpack.endpoint.pluginTitle', { + defaultMessage: 'Endpoint', + }), + async mount(params: AppMountParameters) { + const [coreStart] = await core.getStartServices(); + const { renderApp } = await import('./applications/endpoint'); + return renderApp(coreStart, params); + }, + }); + + const resolverEmbeddableFactory = new ResolverEmbeddableFactory(); + + plugins.embeddable.registerEmbeddableFactory( + resolverEmbeddableFactory.type, + resolverEmbeddableFactory + ); + } + + public start() {} + + public stop() {} +} diff --git a/x-pack/plugins/endpoint/server/config.test.ts b/x-pack/plugins/endpoint/server/config.test.ts new file mode 100644 index 0000000000000..39f6bca2d43ca --- /dev/null +++ b/x-pack/plugins/endpoint/server/config.test.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EndpointConfigSchema, EndpointConfigType } from './config'; + +describe('test config schema', () => { + it('test config defaults', () => { + const config: EndpointConfigType = EndpointConfigSchema.validate({}); + expect(config.enabled).toEqual(false); + expect(config.endpointResultListDefaultPageSize).toEqual(10); + expect(config.endpointResultListDefaultFirstPageIndex).toEqual(0); + }); +}); diff --git a/x-pack/plugins/endpoint/server/config.ts b/x-pack/plugins/endpoint/server/config.ts new file mode 100644 index 0000000000000..7ce5ebcf4eba9 --- /dev/null +++ b/x-pack/plugins/endpoint/server/config.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema, TypeOf } from '@kbn/config-schema'; +import { Observable } from 'rxjs'; +import { PluginInitializerContext } from 'kibana/server'; + +export type EndpointConfigType = ReturnType extends Observable + ? P + : ReturnType; + +export const EndpointConfigSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), + endpointResultListDefaultFirstPageIndex: schema.number({ defaultValue: 0 }), + endpointResultListDefaultPageSize: schema.number({ defaultValue: 10 }), + alertResultListDefaultFirstPageIndex: schema.number({ defaultValue: 0 }), + alertResultListDefaultPageSize: schema.number({ defaultValue: 10 }), +}); + +export function createConfig$(context: PluginInitializerContext) { + return context.config.create>(); +} diff --git a/x-pack/plugins/endpoint/server/index.ts b/x-pack/plugins/endpoint/server/index.ts new file mode 100644 index 0000000000000..ae603b7e44449 --- /dev/null +++ b/x-pack/plugins/endpoint/server/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializer, PluginInitializerContext } from 'src/core/server'; +import { + EndpointPlugin, + EndpointPluginStart, + EndpointPluginSetup, + EndpointPluginStartDependencies, + EndpointPluginSetupDependencies, +} from './plugin'; +import { EndpointConfigSchema } from './config'; + +export const config = { + schema: EndpointConfigSchema, +}; + +export const plugin: PluginInitializer< + EndpointPluginSetup, + EndpointPluginStart, + EndpointPluginSetupDependencies, + EndpointPluginStartDependencies +> = (initializerContext: PluginInitializerContext) => new EndpointPlugin(initializerContext); diff --git a/x-pack/plugins/endpoint/server/plugin.test.ts b/x-pack/plugins/endpoint/server/plugin.test.ts new file mode 100644 index 0000000000000..7dd878d579043 --- /dev/null +++ b/x-pack/plugins/endpoint/server/plugin.test.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EndpointPlugin, EndpointPluginSetupDependencies } from './plugin'; +import { coreMock } from '../../../../src/core/server/mocks'; +import { PluginSetupContract } from '../../features/server'; + +describe('test endpoint plugin', () => { + let plugin: EndpointPlugin; + let mockCoreSetup: ReturnType; + let mockedEndpointPluginSetupDependencies: jest.Mocked; + let mockedPluginSetupContract: jest.Mocked; + beforeEach(() => { + plugin = new EndpointPlugin( + coreMock.createPluginInitializerContext({ + cookieName: 'sid', + sessionTimeout: 1500, + }) + ); + + mockCoreSetup = coreMock.createSetup(); + mockedPluginSetupContract = { + registerFeature: jest.fn(), + getFeatures: jest.fn(), + getFeaturesUICapabilities: jest.fn(), + registerLegacyAPI: jest.fn(), + }; + mockedEndpointPluginSetupDependencies = { features: mockedPluginSetupContract }; + }); + + it('test properly setup plugin', async () => { + await plugin.setup(mockCoreSetup, mockedEndpointPluginSetupDependencies); + expect(mockedPluginSetupContract.registerFeature).toBeCalledTimes(1); + expect(mockCoreSetup.http.createRouter).toBeCalledTimes(1); + }); +}); diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts new file mode 100644 index 0000000000000..3fed4ca480b85 --- /dev/null +++ b/x-pack/plugins/endpoint/server/plugin.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Plugin, CoreSetup, PluginInitializerContext, Logger } from 'kibana/server'; +import { first } from 'rxjs/operators'; +import { addRoutes } from './routes'; +import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; +import { createConfig$, EndpointConfigType } from './config'; +import { registerEndpointRoutes } from './routes/endpoints'; +import { EndpointAppContext } from './types'; +import { registerAlertRoutes } from './routes/alerts'; + +export type EndpointPluginStart = void; +export type EndpointPluginSetup = void; +export interface EndpointPluginStartDependencies {} // eslint-disable-line @typescript-eslint/no-empty-interface + +export interface EndpointPluginSetupDependencies { + features: FeaturesPluginSetupContract; +} + +export class EndpointPlugin + implements + Plugin< + EndpointPluginSetup, + EndpointPluginStart, + EndpointPluginSetupDependencies, + EndpointPluginStartDependencies + > { + private readonly logger: Logger; + constructor(private readonly initializerContext: PluginInitializerContext) { + this.logger = this.initializerContext.logger.get('endpoint'); + } + public setup(core: CoreSetup, plugins: EndpointPluginSetupDependencies) { + plugins.features.registerFeature({ + id: 'endpoint', + name: 'Endpoint', + icon: 'bug', + navLinkId: 'endpoint', + app: ['endpoint', 'kibana'], + privileges: { + all: { + api: ['resolver'], + savedObject: { + all: [], + read: [], + }, + ui: ['save'], + }, + read: { + api: [], + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + }, + }); + const endpointContext = { + logFactory: this.initializerContext.logger, + config: (): Promise => { + return createConfig$(this.initializerContext) + .pipe(first()) + .toPromise(); + }, + } as EndpointAppContext; + const router = core.http.createRouter(); + addRoutes(router); + registerEndpointRoutes(router, endpointContext); + registerAlertRoutes(router, endpointContext); + } + + public start() { + this.logger.debug('Starting plugin'); + } + public stop() { + this.logger.debug('Stopping plugin'); + } +} diff --git a/x-pack/plugins/endpoint/server/routes/alerts.test.ts b/x-pack/plugins/endpoint/server/routes/alerts.test.ts new file mode 100644 index 0000000000000..e6bd9b8888ef7 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/alerts.test.ts @@ -0,0 +1,191 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + IClusterClient, + IRouter, + IScopedClusterClient, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, + RouteConfig, +} from 'kibana/server'; +import { + elasticsearchServiceMock, + httpServerMock, + httpServiceMock, + loggingServiceMock, +} from '../../../../../src/core/server/mocks'; +import { AlertData, AlertResultList } from '../../common/types'; +import { SearchResponse } from 'elasticsearch'; +import { reqSchema, registerAlertRoutes } from './alerts'; +import { EndpointConfigSchema } from '../config'; +import * as data from '../test_data/all_alerts_data.json'; +import * as dataLegacy from '../test_data/all_alerts_data_legacy.json'; + +describe('test alerts route', () => { + let routerMock: jest.Mocked; + let mockResponse: jest.Mocked; + let mockClusterClient: jest.Mocked; + let mockScopedClient: jest.Mocked; + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + + beforeEach(() => { + mockClusterClient = elasticsearchServiceMock.createClusterClient(); + mockScopedClient = elasticsearchServiceMock.createScopedClusterClient(); + mockClusterClient.asScoped.mockReturnValue(mockScopedClient); + routerMock = httpServiceMock.createRouter(); + mockResponse = httpServerMock.createResponseFactory(); + registerAlertRoutes(routerMock, { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + }); + + it('should correctly calculate legacy alert total', async () => { + const mockRequest = httpServerMock.createKibanaRequest({}); + + const response: SearchResponse = (dataLegacy as unknown) as SearchResponse< + AlertData + >; + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/alerts') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const alertResultList = mockResponse.ok.mock.calls[0][0]?.body as AlertResultList; + expect(alertResultList.total).toEqual(21); + expect(alertResultList.request_page_index).toEqual(0); + expect(alertResultList.result_from_index).toEqual(0); + expect(alertResultList.request_page_size).toEqual(10); + }); + + it('should return the latest of all alerts', async () => { + const mockRequest = httpServerMock.createKibanaRequest({}); + + const response: SearchResponse = (data as unknown) as SearchResponse; + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/alerts') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const alertResultList = mockResponse.ok.mock.calls[0][0]?.body as AlertResultList; + expect(alertResultList.total).toEqual(21); + expect(alertResultList.request_page_index).toEqual(0); + expect(alertResultList.result_from_index).toEqual(0); + expect(alertResultList.request_page_size).toEqual(10); + }); + + it('should return alert results according to pagination params -- POST', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + method: 'post', + body: { + page_size: 6, + page_index: 3, + }, + }); + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(data)); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/alerts') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const alertResultList = mockResponse.ok.mock.calls[0][0]?.body as AlertResultList; + expect(alertResultList.total).toEqual(21); + expect(alertResultList.request_page_index).toEqual(3); + expect(alertResultList.result_from_index).toEqual(18); + expect(alertResultList.request_page_size).toEqual(6); + }); + + it('should return alert results according to pagination params -- GET', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + path: '/api/endpoint/alerts', + query: { + page_size: 3, + page_index: 2, + }, + }); + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(data)); + [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/alerts') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const alertResultList = mockResponse.ok.mock.calls[0][0]?.body as AlertResultList; + expect(alertResultList.total).toEqual(21); + expect(alertResultList.request_page_index).toEqual(2); + expect(alertResultList.result_from_index).toEqual(6); + expect(alertResultList.request_page_size).toEqual(3); + }); + + it('should correctly validate params', async () => { + const validate = () => { + reqSchema.validate({ + page_size: 'abc', + page_index: 0, + }); + }; + expect(validate).toThrow(); + }); +}); diff --git a/x-pack/plugins/endpoint/server/routes/alerts.ts b/x-pack/plugins/endpoint/server/routes/alerts.ts new file mode 100644 index 0000000000000..541cf4af52769 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/alerts.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter, KibanaRequest, RequestHandler } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; +import { schema } from '@kbn/config-schema'; + +import { + getPagingProperties, + buildAlertListESQuery, +} from '../services/endpoint/alert_query_builders'; + +import { AlertData, AlertResultList } from '../../common/types'; +import { AlertRequestParams, EndpointAppContext } from '../types'; + +const ALERTS_ROUTE = '/api/endpoint/alerts'; + +export const reqSchema = schema.object({ + page_size: schema.number({ defaultValue: 10, min: 1, max: 10000 }), + page_index: schema.number({ defaultValue: 0, min: 0 }), +}); + +export function registerAlertRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { + const alertsHandler: RequestHandler = async (ctx, req, res) => { + try { + const queryParams = await getPagingProperties( + req as KibanaRequest, + endpointAppContext + ); + const reqBody = await buildAlertListESQuery(queryParams); + const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser( + 'search', + reqBody + )) as SearchResponse; + return res.ok({ body: mapToAlertResultList(endpointAppContext, queryParams, response) }); + } catch (err) { + return res.internalError({ body: err }); + } + }; + + router.get( + { + path: ALERTS_ROUTE, + validate: { + query: reqSchema, + }, + options: { authRequired: true }, + }, + alertsHandler + ); + + router.post( + { + path: ALERTS_ROUTE, + validate: { + body: reqSchema, + }, + options: { authRequired: true }, + }, + alertsHandler + ); +} + +function mapToAlertResultList( + endpointAppContext: EndpointAppContext, + queryParams: Record, + searchResponse: SearchResponse +): AlertResultList { + interface Total { + value: number; + relation: string; + } + + let totalNumberOfAlerts: number = 0; + let totalIsLowerBound: boolean = false; + + // We handle 2 separate schemas for the response below, due to: https://github.com/elastic/kibana/issues/56694 + if (typeof searchResponse?.hits?.total === 'object') { + const total: Total = searchResponse?.hits?.total as Total; + totalNumberOfAlerts = total?.value || 0; + totalIsLowerBound = total?.relation === 'gte' || false; + } else { + totalNumberOfAlerts = searchResponse?.hits?.total || 0; + } + + if (totalIsLowerBound) { + // This shouldn't happen, as we always try to fetch enough hits to satisfy the current request and the next page. + endpointAppContext.logFactory + .get('endpoint') + .warn('Total hits not counted accurately. Pagination numbers may be inaccurate.'); + } + + return { + request_page_size: queryParams.pageSize, + request_page_index: queryParams.pageIndex, + result_from_index: queryParams.fromIndex, + alerts: searchResponse?.hits?.hits?.map(entry => entry._source), + total: totalNumberOfAlerts, + }; +} diff --git a/x-pack/plugins/endpoint/server/routes/endpoints.test.ts b/x-pack/plugins/endpoint/server/routes/endpoints.test.ts new file mode 100644 index 0000000000000..25c4225495a41 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/endpoints.test.ts @@ -0,0 +1,264 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + IClusterClient, + IRouter, + IScopedClusterClient, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, + RouteConfig, +} from 'kibana/server'; +import { + elasticsearchServiceMock, + httpServerMock, + httpServiceMock, + loggingServiceMock, +} from '../../../../../src/core/server/mocks'; +import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { SearchResponse } from 'elasticsearch'; +import { registerEndpointRoutes } from './endpoints'; +import { EndpointConfigSchema } from '../config'; +import * as data from '../test_data/all_endpoints_data.json'; + +describe('test endpoint route', () => { + let routerMock: jest.Mocked; + let mockResponse: jest.Mocked; + let mockClusterClient: jest.Mocked; + let mockScopedClient: jest.Mocked; + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + + beforeEach(() => { + mockClusterClient = elasticsearchServiceMock.createClusterClient() as jest.Mocked< + IClusterClient + >; + mockScopedClient = elasticsearchServiceMock.createScopedClusterClient(); + mockClusterClient.asScoped.mockReturnValue(mockScopedClient); + routerMock = httpServiceMock.createRouter(); + mockResponse = httpServerMock.createResponseFactory(); + registerEndpointRoutes(routerMock, { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + }); + + it('test find the latest of all endpoints', async () => { + const mockRequest = httpServerMock.createKibanaRequest({}); + + const response: SearchResponse = (data as unknown) as SearchResponse< + EndpointMetadata + >; + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/endpoints') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; + expect(endpointResultList.endpoints.length).toEqual(2); + expect(endpointResultList.total).toEqual(2); + expect(endpointResultList.request_page_index).toEqual(0); + expect(endpointResultList.request_page_size).toEqual(10); + }); + + 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, + }, + ], + }, + }); + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => + Promise.resolve((data as unknown) as SearchResponse) + ); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/endpoints') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(mockScopedClient.callAsCurrentUser.mock.calls[0][1]?.body?.query).toEqual({ + match_all: {}, + }); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; + expect(endpointResultList.endpoints.length).toEqual(2); + expect(endpointResultList.total).toEqual(2); + expect(endpointResultList.request_page_index).toEqual(10); + expect(endpointResultList.request_page_size).toEqual(10); + }); + + 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, + }, + ], + + filter: 'not host.ip:10.140.73.246', + }, + }); + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => + Promise.resolve((data as unknown) as SearchResponse) + ); + [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/endpoints') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(mockScopedClient.callAsCurrentUser.mock.calls[0][1]?.body?.query).toEqual({ + bool: { + must_not: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + 'host.ip': '10.140.73.246', + }, + }, + ], + }, + }, + }, + }); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; + expect(endpointResultList.endpoints.length).toEqual(2); + expect(endpointResultList.total).toEqual(2); + expect(endpointResultList.request_page_index).toEqual(10); + expect(endpointResultList.request_page_size).toEqual(10); + }); + + describe('Endpoint Details route', () => { + it('should return 404 on no results', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ params: { id: 'BADID' } }); + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => + Promise.resolve({ + took: 3, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 9, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + }) + ); + [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/endpoints') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + 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', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + params: { id: (data as any).hits.hits[0]._id }, + }); + const response: SearchResponse = (data as unknown) as SearchResponse< + EndpointMetadata + >; + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); + [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => + path.startsWith('/api/endpoint/endpoints') + )!; + + await routeHandler( + ({ + core: { + elasticsearch: { + dataClient: mockScopedClient, + }, + }, + } as unknown) as RequestHandlerContext, + mockRequest, + mockResponse + ); + + expect(mockScopedClient.callAsCurrentUser).toBeCalled(); + expect(routeConfig.options).toEqual({ authRequired: true }); + expect(mockResponse.ok).toBeCalled(); + const result = mockResponse.ok.mock.calls[0][0]?.body as EndpointMetadata; + expect(result).toHaveProperty('endpoint'); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/server/routes/endpoints.ts b/x-pack/plugins/endpoint/server/routes/endpoints.ts new file mode 100644 index 0000000000000..054172a7f258a --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/endpoints.ts @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; +import { schema } from '@kbn/config-schema'; + +import { + kibanaRequestToEndpointListQuery, + kibanaRequestToEndpointFetchQuery, +} from '../services/endpoint/endpoint_query_builders'; +import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { EndpointAppContext } from '../types'; + +interface HitSource { + _source: EndpointMetadata; +} + +export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { + router.post( + { + path: '/api/endpoint/endpoints', + validate: { + body: schema.nullable( + schema.object({ + paging_properties: schema.nullable( + schema.arrayOf( + schema.oneOf([ + /** + * the number of results to return for this request per page + */ + schema.object({ + page_size: schema.number({ defaultValue: 10, min: 1, max: 10000 }), + }), + /** + * the zero based page index of the the total number of pages of page size + */ + schema.object({ page_index: schema.number({ defaultValue: 0, min: 0 }) }), + ]) + ) + ), + /** + * filter to be applied, it could be a kql expression or discrete filter to be implemented + */ + filter: schema.nullable(schema.oneOf([schema.string()])), + }) + ), + }, + options: { authRequired: true }, + }, + async (context, req, res) => { + try { + const queryParams = await kibanaRequestToEndpointListQuery(req, endpointAppContext); + const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( + 'search', + queryParams + )) as SearchResponse; + return res.ok({ body: mapToEndpointResultList(queryParams, response) }); + } catch (err) { + return res.internalError({ body: err }); + } + } + ); + + router.get( + { + path: '/api/endpoint/endpoints/{id}', + validate: { + params: schema.object({ id: schema.string() }), + }, + options: { authRequired: true }, + }, + async (context, req, res) => { + try { + const query = kibanaRequestToEndpointFetchQuery(req, endpointAppContext); + const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( + 'search', + query + )) as SearchResponse; + + if (response.hits.hits.length === 0) { + return res.notFound({ body: 'Endpoint Not Found' }); + } + + return res.ok({ body: response.hits.hits[0]._source }); + } catch (err) { + return res.internalError({ body: err }); + } + } + ); +} + +function mapToEndpointResultList( + queryParams: Record, + searchResponse: SearchResponse +): EndpointResultList { + const totalNumberOfEndpoints = searchResponse?.aggregations?.total?.value || 0; + if (searchResponse.hits.hits.length > 0) { + return { + request_page_size: queryParams.size, + request_page_index: queryParams.from, + endpoints: searchResponse.hits.hits + .map(response => response.inner_hits.most_recent.hits.hits) + .flatMap(data => data as HitSource) + .map(entry => entry._source), + total: totalNumberOfEndpoints, + }; + } else { + return { + request_page_size: queryParams.size, + request_page_index: queryParams.from, + total: totalNumberOfEndpoints, + endpoints: [], + }; + } +} diff --git a/x-pack/plugins/endpoint/server/routes/index.ts b/x-pack/plugins/endpoint/server/routes/index.ts new file mode 100644 index 0000000000000..8eab6cd384765 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +export function addRoutes(router: IRouter) { + router.get( + { + path: '/api/endpoint/hello-world', + validate: false, + options: { + tags: ['access:resolver'], + }, + }, + async function greetingIndex(_context, _request, response) { + return response.ok({ + body: { hello: 'world' }, + headers: { + 'Content-Type': 'application/json', + }, + }); + } + ); +} diff --git a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts new file mode 100644 index 0000000000000..a4d7de8fdcfdb --- /dev/null +++ b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { httpServerMock, loggingServiceMock } from 'src/core/server/mocks'; +import { EndpointConfigSchema } from '../../config'; +import { getPagingProperties, buildAlertListESQuery } from './alert_query_builders'; + +describe('test query builder', () => { + describe('test query builder request processing', () => { + it('should execute the correct Elasticsearch query for a default request', async () => { + const mockRequest = httpServerMock.createKibanaRequest({}); + const mockCtx = { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }; + const queryParams = await getPagingProperties(mockRequest, mockCtx); + const query = await buildAlertListESQuery(queryParams); + + expect(query).toEqual({ + body: { + query: { + match_all: {}, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + track_total_hits: 10000, + }, + from: 0, + size: 10, + index: 'my-index', + } as Record); + }); + it('should adjust track_total_hits for deep pagination', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + query: { + page_index: 10, + page_size: 1000, + }, + }); + const mockCtx = { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }; + const queryParams = await getPagingProperties(mockRequest, mockCtx); + const query = await buildAlertListESQuery(queryParams); + + expect(query).toEqual({ + body: { + query: { + match_all: {}, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + track_total_hits: 12000, + }, + from: 10000, + size: 1000, + index: 'my-index', + } as Record); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts new file mode 100644 index 0000000000000..a20f2ae1cdecd --- /dev/null +++ b/x-pack/plugins/endpoint/server/services/endpoint/alert_query_builders.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { KibanaRequest } from 'kibana/server'; +import { EndpointAppConstants } from '../../../common/types'; +import { EndpointAppContext, AlertRequestParams, JSONish } from '../../types'; + +export const buildAlertListESQuery = async ( + pagingProperties: Record +): Promise => { + const DEFAULT_TOTAL_HITS = 10000; + + // Calculate minimum total hits set to indicate there's a next page + const totalHitsMin = Math.max( + pagingProperties.fromIndex + pagingProperties.pageSize * 2, + DEFAULT_TOTAL_HITS + ); + + return { + body: { + track_total_hits: totalHitsMin, + query: { + match_all: {}, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + }, + from: pagingProperties.fromIndex, + size: pagingProperties.pageSize, + index: EndpointAppConstants.ALERT_INDEX_NAME, + }; +}; + +export const getPagingProperties = async ( + request: KibanaRequest, + endpointAppContext: EndpointAppContext +): Promise> => { + const config = await endpointAppContext.config(); + const pagingProperties: { page_size?: number; page_index?: number } = {}; + + if (request?.route?.method === 'get') { + pagingProperties.page_index = request.query?.page_index; + pagingProperties.page_size = request.query?.page_size; + } else { + pagingProperties.page_index = request.body?.page_index; + pagingProperties.page_size = request.body?.page_size; + } + + const pageSize = pagingProperties.page_size || config.alertResultListDefaultPageSize; + const pageIndex = pagingProperties.page_index || config.alertResultListDefaultFirstPageIndex; + const fromIndex = pageIndex * pageSize; + + return { pageSize, pageIndex, fromIndex }; +}; diff --git a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts b/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts new file mode 100644 index 0000000000000..bd9986ecf1f97 --- /dev/null +++ b/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.test.ts @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { httpServerMock, loggingServiceMock } from '../../../../../../src/core/server/mocks'; +import { EndpointConfigSchema } from '../../config'; +import { + kibanaRequestToEndpointListQuery, + kibanaRequestToEndpointFetchQuery, +} from './endpoint_query_builders'; + +describe('query builder', () => { + describe('EndpointListQuery', () => { + it('test default query params for all endpoints when no params or body is provided', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + body: {}, + }); + const query = await kibanaRequestToEndpointListQuery(mockRequest, { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + expect(query).toEqual({ + body: { + query: { + match_all: {}, + }, + collapse: { + field: 'host.id.keyword', + inner_hits: { + name: 'most_recent', + size: 1, + sort: [{ 'event.created': 'desc' }], + }, + }, + aggs: { + total: { + cardinality: { + field: 'host.id.keyword', + }, + }, + }, + sort: [ + { + 'event.created': { + order: 'desc', + }, + }, + ], + }, + from: 0, + size: 10, + index: 'endpoint-agent*', + } as Record); + }); + }); + + describe('test query builder with kql filter', () => { + it('test default query params for all endpoints when no params or body is provided', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ + body: { + filter: 'not host.ip:10.140.73.246', + }, + }); + const query = await kibanaRequestToEndpointListQuery(mockRequest, { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + expect(query).toEqual({ + body: { + query: { + bool: { + must_not: { + bool: { + minimum_should_match: 1, + should: [ + { + match: { + 'host.ip': '10.140.73.246', + }, + }, + ], + }, + }, + }, + }, + collapse: { + field: 'host.id.keyword', + inner_hits: { + name: 'most_recent', + size: 1, + sort: [{ 'event.created': 'desc' }], + }, + }, + aggs: { + total: { + cardinality: { + field: 'host.id.keyword', + }, + }, + }, + sort: [ + { + 'event.created': { + order: 'desc', + }, + }, + ], + }, + from: 0, + size: 10, + index: 'endpoint-agent*', + } as Record); + }); + }); + + describe('EndpointFetchQuery', () => { + it('searches for the correct ID', () => { + const mockID = 'AABBCCDD-0011-2233-AA44-DEADBEEF8899'; + const mockRequest = httpServerMock.createKibanaRequest({ + params: { + id: mockID, + }, + }); + const query = kibanaRequestToEndpointFetchQuery(mockRequest, { + logFactory: loggingServiceMock.create(), + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + expect(query).toEqual({ + body: { + query: { match: { 'host.id.keyword': mockID } }, + sort: [{ 'event.created': { order: 'desc' } }], + size: 1, + }, + index: 'endpoint-agent*', + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts b/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts new file mode 100644 index 0000000000000..c143b09ec453c --- /dev/null +++ b/x-pack/plugins/endpoint/server/services/endpoint/endpoint_query_builders.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { KibanaRequest } from 'kibana/server'; +import { EndpointAppConstants } from '../../../common/types'; +import { EndpointAppContext } from '../../types'; +import { esKuery } from '../../../../../../src/plugins/data/server'; + +export const kibanaRequestToEndpointListQuery = async ( + request: KibanaRequest, + endpointAppContext: EndpointAppContext +): Promise> => { + const pagingProperties = await getPagingProperties(request, endpointAppContext); + return { + body: { + query: buildQueryBody(request), + collapse: { + field: 'host.id.keyword', + inner_hits: { + name: 'most_recent', + size: 1, + sort: [{ 'event.created': 'desc' }], + }, + }, + aggs: { + total: { + cardinality: { + field: 'host.id.keyword', + }, + }, + }, + sort: [ + { + 'event.created': { + order: 'desc', + }, + }, + ], + }, + from: pagingProperties.pageIndex * pagingProperties.pageSize, + size: pagingProperties.pageSize, + index: EndpointAppConstants.ENDPOINT_INDEX_NAME, + }; +}; + +async function getPagingProperties( + request: KibanaRequest, + endpointAppContext: EndpointAppContext +) { + const config = await endpointAppContext.config(); + const pagingProperties: { page_size?: number; page_index?: number } = {}; + if (request?.body?.paging_properties) { + for (const property of request.body.paging_properties) { + Object.assign( + pagingProperties, + ...Object.keys(property).map(key => ({ [key]: property[key] })) + ); + } + } + return { + pageSize: pagingProperties.page_size || config.endpointResultListDefaultPageSize, + pageIndex: pagingProperties.page_index || config.endpointResultListDefaultFirstPageIndex, + }; +} + +function buildQueryBody(request: KibanaRequest): Record { + if (typeof request?.body?.filter === 'string') { + return esKuery.toElasticsearchQuery(esKuery.fromKueryExpression(request.body.filter)); + } + return { + match_all: {}, + }; +} + +export const kibanaRequestToEndpointFetchQuery = ( + request: KibanaRequest, + endpointAppContext: EndpointAppContext +) => { + return { + body: { + query: { + match: { + 'host.id.keyword': request.params.id, + }, + }, + sort: [ + { + 'event.created': { + order: 'desc', + }, + }, + ], + size: 1, + }, + index: EndpointAppConstants.ENDPOINT_INDEX_NAME, + }; +}; diff --git a/x-pack/plugins/endpoint/server/test_data/all_alerts_data.json b/x-pack/plugins/endpoint/server/test_data/all_alerts_data.json new file mode 100644 index 0000000000000..128592f0f01df --- /dev/null +++ b/x-pack/plugins/endpoint/server/test_data/all_alerts_data.json @@ -0,0 +1,5471 @@ +{ + "_shards": { + "failed": 0, + "skipped": 0, + "successful": 1, + "total": 1 + }, + "hits": { + "hits": [ + { + "_id": "mguP_G8BsHRXKE0Yiz15", + "_index": "my-index", + "_score": 1.0, + "_source": { + "@timestamp": 1542341895000, + "agent": { + "id": "ced9c68e-b94a-4d66-bb4c-6106514f0a2f", + "type": "endpoint", + "version": "3.0.0" + }, + "ecs": { + "version": "1.1.0" + }, + "event": { + "action": "open", + "category": "malware", + "dataset": "endpoint", + "kind": "alert", + "module": "endpoint", + "type": "access" + }, + "file": { + "accessed": 1542341100, + "created": 1542341100, + "hash": { + "imphash": "835d619dfdf3cc727cebd91300ab3462", + "md5": "4ace3baaa509d08510405e1b169e325b", + "sha1": "27fb21cf5db95ffca43b234affa99becc4023b9d", + "sha256": "6ed1c836dbf099be7845bdab7671def2c157643761b52251e04e9b6ee109ec75" + }, + "mtime": 1542341100, + "owner": "Administrators", + "path": "C:\\Users\\Administrator\\Downloads\\endpointpe-blacklist-test.exe", + "size": 188416 + }, + "file_classification": { + "captured_file": false, + "is_signature_trusted": false, + "malware_classification": { + "compressed_malware_features": { + "data_buffer": "eAHtnU1oHHUUwHsQ7MGDiIIUD4sH8WBBxJtopiLoUY0pYo2ZTbJJ0yQ17m4+ms/NRzeVWpuUWCL4sWlEYvFQ8KJQ6NCTEA8eRD30sIo3PdSriLi7837Pko3LbHZ2M5m+XObHm/d/X////83O7jCZvzacHBpPplNdfalkdjSdyty674Ft59dN71Dpb9v5eKh8LMEHjsCF2wIfVlRKsHROYPGkQO5+gY2vBSYYdWZFYGwEO/cITHMqkxPYnBBY+07gtCuQ9gSGigJ5lPPYGXcE+jA4z3Ad1ZtAUiDUyrEEPYzqRnIKgxd/Rgc7gygPo5wn95PouN7OeEYJ1UXiJgRmvscgp/LOziIkkSyT+xRVnXhZ4DKh5goCkzidRHkGO4uvCyw9LDDtCay8ILCAzrJOJaGuZwUuvSewivJVIPsklq8JbL4qMJsTSCcExrGs83WKU295ZFo5lr2TaZbcUw5FeJy8tgTeLpCy2iGeS67ABXzlgbEi1UC5FxcZnA4y/CLK82Qxi847FGGZRTLsCUxR1aWEwOp1AmOjDRYYzgwusL9WfqBiGJxnVAanixTq7Dp22LBdlWMJzlOx8wmBK2Rx5WmBLJIRwtAijOQE+ooCb2B5xBOYRtlfNeXpLpA7oyZRTqHzGenkmIJPnhBIMrzTwSA6H93CO5l+c1NA99f6IwLH8fUKdjTmDpTbgS50+gGVnECnE4PpooC2guPoaPADSHrcncNHmEHtAFkq3+EI+A37zsrrTvH3WTkvJLoOTyBp10wx2JcgVCRahA4NrICE4a+hrMXsA3qAHItW188E8ejO7XV3eh/KCYwxlamEwCgL8lN2wTntfrhY/U0g/5KAdvUpT+AszWqBdqH7VLeeZrExK9Cv1UgIDKA8g/cx7QAEP+AhAfRaMKB2HOJh+BSFSqKjSytNGBlc6PrpxvK7lCVDxbSG3Z7AhCMwx6gelwgLAltXBXJUTH29j+U1LHdipx/QprfKfGnF0sBpdBYxmEQyTzW0h6/0khcuhhJYRufym+i4VKMocJMs/KvfoW3/UJb4PeZOSZVONThZz4djP/75TAXa/CVfOvX3RgVLIDreLPN1pP1osW7lGmHsEhjBOzf+EPBE4vndvWz5xb/cChxGcv1LAb+tluALKnZ47isf1MXvz1ZMlsCXbXtPceqhrcp1ps6YHwQeBXLEPCf7q23tl9uJui0bGBgYRAccv7uXr/g5Af+2oNTrpgTa/vnpjBvpLAwM4gRBPvIZGBgYGBgYGBgYGBgYGBgYGBgYGBgYNAOc9oMXs4GBgYFBcNBnww5QzDXgRtPSaZ5lg/itsRaslgZ3bnWEEVnhMetIBwiiVnlbCbWrEftrt11zdwWnseFW1QO63w1is3ptD1pV9xG0t+zvfUrzrvh380qwXWAVCw6h78GIfG7ZlzltXu6hd+y92fECRFhjuH3bXG8N43oXEHperdzvUbteaDxhVTUeq25fqhG1X6Ai8mtF6BDXz2wR+dzSgg4Qsxls5T11XMG+82y8GkG+b7kL69xg7mF1SFvhBgYGsYH/Xi7HE+PVkiB2jt1bNZxT+k4558jR53ydz5//1m1KOgYGBgYGBgYGEQfnsYaG2z1sdPJS79XQSu91ndobOAHCaN5vNzUk1bceQVzUpbw3iOuT+UFmR18bHrp3gyhDC56lCd1y85w2+HSNUwVhhdGC7blLf+bV/fqtvhMg1NDjCcugB1QXswbs8ekj/v1BgzFHBIIsyP+HfwFdMpzu", + "decompressed_size": 27831, + "encoding": "zlib" + }, + "identifier": "endpointpe", + "prevention_threshold": 0.66, + "score": 1, + "threshold": 0.66, + "version": "3.0.33" + }, + "signature_signer": "", + "temp_file_path": "C:\\Windows\\TEMP\\581ac9e2-e9ea-499e-8ec6-d7eed985b6c3", + "user_blacklisted": false + }, + "host": { + "hostname": "HD-c15-bc09190a", + "ip": "10.179.244.14", + "name": "HD-c15-bc09190a", + "os": { + "name": "Windows", + "platform": "windows", + "version": "6.1" + } + }, + "process": { + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "command_line": "C:\\Windows\\Explorer.EXE", + "domain": "WIN-Q3DOP1UKA81", + "executable": "C:\\Windows\\explorer.exe", + "file_hash": { + "md5": "ac4c51eb24aa95b77f705ab159189e24", + "sha1": "4583daf9442880204730fb2c8a060430640494b1", + "sha256": "6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a" + }, + "hash": { + "imphash": "6422e341c67ba0880e012f8c7c634c21", + "md5": "ac4c51eb24aa95b77f705ab159189e24", + "sha1": "4583daf9442880204730fb2c8a060430640494b1", + "sha256": "6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a" + }, + "is_endpoint": false, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "modules": [ + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290248516, + "hash": { + "imphash": "6422e341c67ba0880e012f8c7c634c21", + "md5": "ac4c51eb24aa95b77f705ab159189e24", + "sha1": "4583daf9442880204730fb2c8a060430640494b1", + "sha256": "6a671b92a69755de6fd063fcbe4ba926d83b49f78c42dbaeed8cdb6bbc57576a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 4278845440, + "mapped_size": 0, + "path": "C:\\Windows\\Explorer.EXE", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 05:28" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258681, + "hash": { + "imphash": "d41d8cd98f00b204e9800998ecf8427e", + "md5": "3556d5a8bf2cc508bdab51dec38d7c61", + "sha1": "92015f7bbdb9dad35e41c533d2c5b85f1cd63d85", + "sha256": "91e3d98ad3119e8addf8d2aa1dd6795162842fff7101e4c70c5137e847b4ff50" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 2007891968, + "mapped_size": 0, + "path": "C:\\Windows\\SYSTEM32\\ntdll.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258315, + "hash": { + "imphash": "9165b02c931d76a9b666d8d42128111b", + "md5": "7a6326d96d53048fdec542df23d875a0", + "sha1": "5c02af0206c299f5bcab8da4237cfc92e3e93495", + "sha256": "182351570856cd6eedd9df7e2fb8ab76bd4d8fc70be11ad5de6484cfd70c21c6" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 2006712320, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\kernel32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258316, + "hash": { + "imphash": "3f7fb1504bb73a54888bf1c3650fe4cf", + "md5": "da68c291b4ef2dec9c5963266bcae454", + "sha1": "5696e8c68fcf64104499e20e7cd5452b58b4f4ba", + "sha256": "21aa4779fc21e762178517268c95467238c92851ad9160bffc36b2379c58337f" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791760175104, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\KERNELBASE.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534699, + "hash": { + "imphash": "e1ee2d71958d21e0e1bf887dfe76af7f", + "md5": "6df46d2bd74e3da1b45f08f10d172732", + "sha1": "3491f8f9a73c00b158e43a530210d67a4f0598ae", + "sha256": "2dc945f6f2c4a82189bc7da2fcbb7d9a0e2588a909539249e55ba82468e0c677" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791763779584, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ADVAPI32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535038, + "hash": { + "imphash": "8c99b1c0f6cf68b07336751f460f1dba", + "md5": "7319bb10fa1f86e49e3dcf4136f6c957", + "sha1": "3eea5ee8bafb2b9975b236c5c5655df6f4b42aa1", + "sha256": "60de43ab267fd41c9804369b569139add30ed4e295c425f44fc04d3fcc95fca2" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791790780416, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\msvcrt.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535198, + "hash": { + "imphash": "b8ba136689cdc8d8b25fc04902f39a22", + "md5": "83404dcbce4925b6a5a77c5170f46d86", + "sha1": "22bda6b9da4fcf492b4dd16554b0c0e27e1b8667", + "sha256": "d669614d0b4461db244ad99fbe1ba92ceb9b4ed5ec8e987e23764e77d9ac7074" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791793074176, + "mapped_size": 0, + "path": "C:\\Windows\\SYSTEM32\\sechost.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258798, + "hash": { + "imphash": "46876e4adb924a616ddbbb1992d61257", + "md5": "0611473c1ad9e2d991cd9482068417f7", + "sha1": "c4a3fa902dedad5d448e1d8b2d113cae1dcf2f7a", + "sha256": "90afcc2a60350ece27e75e76459132ef0fa28ef283ce88fced4b82735a93ecda" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791762403328, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\RPCRT4.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258001, + "hash": { + "imphash": "51945fdf9aaf56aeb9d6fa1f21b638ce", + "md5": "1084aa52ccc324ea54c7121fa24c2221", + "sha1": "b13ef924708fa88577931ed0337000e90adcdf5b", + "sha256": "6e972cf624f7c0de8190434b3b30279a01c551713109f97b9ebb77fac9364754" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791792615424, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\GDI32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258929, + "hash": { + "imphash": "2cb501375ed127591bf5cfee7f1e52fe", + "md5": "fe70103391a64039a921dbfff9c7ab1b", + "sha1": "e0019d9442aeebd3bb42a24c38aa2fae4c6bd4f5", + "sha256": "f7d219d75037bc98f6c69143b00ab6000a31f8b5e211e0af514f4f4b681522a0" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 2005663744, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\USER32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534943, + "hash": { + "imphash": "919110853c18aa198ad129945337b1dd", + "md5": "d202223587518b13d72d68937b7e3f70", + "sha1": "916a3ce858f074f57dd9dac01be5cd4649f19887", + "sha256": "9db971b866d058adbb518dd99b87c5db8dd1e7c9073755b989ae7e9fb62901e8" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791763714048, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\LPK.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258933, + "hash": { + "imphash": "17bf46cf6bf6c8cae48be5b75615a353", + "md5": "2f8b1e3ee3545d3b5a8d56fa1ae07b65", + "sha1": "66310680ee38904b2852717af13028e53b4e8b8e", + "sha256": "2a3ec01f3bafe7d7d656886437f7ffecce440c0d3f3467804769ab4bf1ff7a99" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791782522880, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\USP10.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258859, + "hash": { + "imphash": "4b37cbf60127ea0550ec30e0b1c52984", + "md5": "eaf32cb8c1f810e4715b4dfbe785c7ff", + "sha1": "3b099b193abb9064e6937101d0c309f04d713882", + "sha256": "db6ad07fded42433e669508ab73faff6daff04575d6f1d016fe3eb6ecec4dd5d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791792091136, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SHLWAPI.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258854, + "hash": { + "imphash": "9c631776d86c9b15258c3cc2a6a7891d", + "md5": "26e716ed95dc48cf6e5ac046089366af", + "sha1": "2bd96b8ae5ae3ad14c16d2a98a91a9a9f26d179d", + "sha256": "f686d557b7ac1688efc7cb48311290d713d3db2e9e61e947098a7c80e3a1b9e9" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791765811200, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SHELL32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258732, + "hash": { + "imphash": "faad2d5bf5c0ca9639e07a49e8c5d8ae", + "md5": "6c60b5aca7442efb794082cdacfc001c", + "sha1": "aae17944782b25f41f7b3a756532b4923f4ae817", + "sha256": "fc1d9124856a70ff232ef3057d66bee803295847624ce23b4d0217f23af52c75" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791783374848, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ole32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258736, + "hash": { + "imphash": "774fed8966de60d3af2dd9070df5be6f", + "md5": "42f05f980f164e084db65b2e8cd8430f", + "sha1": "86498b3c5bbc240b9de0a10f2cb4185e754de6d7", + "sha256": "0813749847b08f6577791d18ad9eca6dff5b41c2f727ab5ee9e5bf9602ed50cb" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791785537536, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\OLEAUT32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258088, + "hash": { + "imphash": "ec96d3f694248151f968633563d10a36", + "md5": "eed05d42d91835064703e2318552ed25", + "sha1": "aa7e817ccad26070bce1161894f97e10aaa56fb9", + "sha256": "e9ee1e2253445b207b76f5d3073c612ed979a982522c1515e0fe8fa9641ae568" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791634935808, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\EXPLORERFRAME.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534886, + "hash": { + "imphash": "c0e1a4a34891e5dd2a6cbaa0895a8d38", + "md5": "8ccde014a4cdf84564e03ace064ca753", + "sha1": "957e29e029fe60b8ff43ff732463c39230b78226", + "sha256": "dd663029b2eb7b12fdb00fce403d8326141e540e3b9ce84cd5871473d3e2e2cf" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791735599104, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\DUser.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534885, + "hash": { + "imphash": "9353143c2b77b94cc82ab55c5fecf99c", + "md5": "3cb6a7286422c72c34dab54a5dff1a34", + "sha1": "5b93896a6abb36c2b8957973e3ce1860c1059367", + "sha256": "98d21efff511e407336a226420701e82554da01fa05661303836b6860d63749d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791721181184, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\DUI70.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534912, + "hash": { + "imphash": "d76d7be0b8ac9aafe17d2cc7deb32b29", + "md5": "aa2c08ce85653b1a0d2e4ab407fa176c", + "sha1": "0119c23d88292a0e4fec04d5cf8629005a44e37c", + "sha256": "83dfd0c119b20aedb07114c9d1cf9ce2dfa938d0f1070256b0591a9e2c3997fa" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791793205248, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\IMM32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535018, + "hash": { + "imphash": "b523fff180cb22465ccf191b827e9923", + "md5": "c431eaf5caa1c82cac2534a2eab348a3", + "sha1": "e425577ccfc9b92efbbcb760d21fcaa478d3e51a", + "sha256": "addf850128dc675e67faba9a3d0d27e684f01f733962ca22927bb94503549e44" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791764697088, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\MSCTF.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535251, + "hash": { + "imphash": "56e651a119cdb899aadd2df3832bbcd1", + "md5": "d29e998e8277666982b4f0303bf4e7af", + "sha1": "e803b0af61ea2ddcd58b5a63b1cfbb73266318ea", + "sha256": "4f19ab5dc173e278ebe45832f6ceaa40e2df6a2eddc81b2828122442fe5d376c" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791742480384, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\UxTheme.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535202, + "hash": { + "imphash": "1c419f7cfacebfcd8e903e6be290407e", + "md5": "716175021bda290504ce434273f666bc", + "sha1": "4f00fbf4e9a88fae9e6682989032831b3d2eba86", + "sha256": "fa18ca2d8a5f4335e051e2933147d3c1e7308f7d446e2aeb6596cdef6e2afc88" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791718690816, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\POWRPROF.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258850, + "hash": { + "imphash": "a7a25e8b145e75fdeb21026d3895033a", + "md5": "5d8e6c95156ed1f79a63d1eade6f9ed5", + "sha1": "cadd211d74385550c5e055d3312303f4d64fdebc", + "sha256": "12130837d7f89a2c7e9d25747a8e5b9001e0a38d545178b49b450c23ae62664a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791788814336, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SETUPAPI.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257756, + "hash": { + "imphash": "5cd9d6761799e2ff681533ef1ffbb31d", + "md5": "2477a28081bdaee622cf045acf8ee124", + "sha1": "304c5f29fa847fbd994ad7a0471214198b928c14", + "sha256": "00a09caf9129e84feea98fa03ce9012c9f961b64fee15c4f268822c0f82acc3c" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791760633856, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CFGMGR32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534817, + "hash": { + "imphash": "2dbdaadf7e151289a49662379e253dfd", + "md5": "06fec9e8117103bb1141a560e98077da", + "sha1": "a8922793a930d602409b62be5ff01d5baec60000", + "sha256": "c5e61b11ddbbbbba3d9488970524f0975ea5fbdf16e2fa31f579f8bfa48353b1" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791760044032, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\DEVOBJ.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534887, + "hash": { + "imphash": "e7f2585307f1db90e7e5e48c40dc7134", + "md5": "da1b7075260f3872585bfcdd668c648b", + "sha1": "f2bd334006d728422721b7c639145a6ec59a459b", + "sha256": "3e10ef6e1a5c341b478322cb78a0ab7bfc70ad8023779b8b4542a7cb4ca756ab" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791742873600, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\dwmapi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535203, + "hash": { + "imphash": "6a5a31c99a1562b9e5e10f4b4445be95", + "md5": "be097f5bb10f9079fceb2dc4e7e20f02", + "sha1": "dd572bac50bc4718126389c628d56a83d5c4d88a", + "sha256": "90a88986c8c5f30fb153ec803feda6572b2c2630a6c9578fcc017800692694d5" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791732256768, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\slc.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257495, + "hash": { + "imphash": "fd8a6a2046d9572b7f8f4288ae251c61", + "md5": "497bfeddaf3950dd909c3b0c5558a25d", + "sha1": "5d55bdc156372f51eb126f7bc2a8af161a1ef254", + "sha256": "980ea189929d95eb36e35980fff0c81f7b78de9422771fde8f4ac7a779f5bd89" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791732453376, + "mapped_size": 0, + "path": "C:\\Windows\\WinSxS\\amd64_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7601.17514_none_2b24536c71ed437a\\gdiplus.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258848, + "hash": { + "imphash": "cc4d63ca30fdbb90048e549782d2116a", + "md5": "858df0795cb5b4bace0f33708925a414", + "sha1": "e629ed78e6e1829263890974760dad8a431edf69", + "sha256": "a9063af8d5c73a722bd269d144d8a65c98db4cfdd9f626e3a8283754e22c8c9c" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791754801152, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\Secur32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258927, + "hash": { + "imphash": "b32250da0d30f7782b5b900d4d9c519a", + "md5": "2a86e54b441ad41557f75dc5609b9793", + "sha1": "83ddcf8a1a0ca423bf8417f5e59b5c431bf50c43", + "sha256": "8fede6909413c0fa5b63d58d39affd0f6c3beeaf19b7b2f8674913abfd79a912" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791756701696, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SSPICLI.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258762, + "hash": { + "imphash": "26c2856b9813d8990c01c5a711b5063a", + "md5": "f06bb4e336ea57511fdbafafcc47de62", + "sha1": "bfee1b9d2269d26d99c8e462825ee8399c8bd4ec", + "sha256": "be43ec62548e9ff89a9495a1722e22dbb76eec3764f86e64057b636f27d15765" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791728259072, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\PROPSYS.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534993, + "hash": { + "imphash": "f0c6fd6831905d958b05645b680db89f", + "md5": "784fa3df338e2e8f5f0389d6fac428af", + "sha1": "6d32c67c91c6d374854e907c6719db2538540867", + "sha256": "9c8aa0cfdeb9e38aaf8eb08626070e0f0364f4f8a793cfe3532ec6c007980c34" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791757291520, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CRYPTBASE.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257499, + "hash": { + "imphash": "cd11f800bc54ae45ead9d98c96048145", + "md5": "7fa8fdc2c2a27817fd0f624e78d3b50c", + "sha1": "b4aa8e16396b1882eb75c28dfbec9949608afdde", + "sha256": "7b63f6aa2cd6d4d07ea3c595b868b1a0749bb11620027a2bd9b935e3055481e4" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791736123392, + "mapped_size": 0, + "path": "C:\\Windows\\WinSxS\\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_fa396087175ac9ac\\comctl32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258974, + "hash": { + "imphash": "b03f7d8315f3384d06c11e961e6fee07", + "md5": "26b73a85855681500bcc25c7cd9ff5b1", + "sha1": "393ed9ebbe380c77935df6d0eda2047cdd2224fe", + "sha256": "94d134a6af53ad629a4505b8b0ea37f61bb43af4db71874e7e87853163a9282a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791724851200, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WindowsCodecs.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535135, + "hash": { + "imphash": "ff720e05e534d67b814b8562265058f5", + "md5": "2c942733a5983dd4502219ff37c7ebc7", + "sha1": "263e8fbf77c0ceead0c9bca56394bffa4a664361", + "sha256": "34b20b6b0d7274e4b5b783f1d2345bc3dd9888964d5c2c65712f041a00cf5b45" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791758143488, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\profapi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257558, + "hash": { + "imphash": "6af6d846a78a6532fcb989d0d8aeb17d", + "md5": "90499f3163a9f815cf196a205ea3cd5d", + "sha1": "f97ff54dc4b132756fcf7041e55d645163f19851", + "sha256": "29b4ed3795cec1177eb367132914ce21c194cdec5db9dc923fd928c85e94d821" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791756898304, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\apphelp.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534778, + "hash": { + "imphash": "37afbae3e0f359c3718e379261f7ccfc", + "md5": "25983de69b57142039ac8d95e71cd9c9", + "sha1": "01691e3b0bfa569e64bdb7dc3d637a867ed2dc08", + "sha256": "a677da7ebcbcb6073d27e8a38809f51e971e83ed379bc599aaad6ef4216348da" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791787700224, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CLBCatQ.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534842, + "hash": { + "imphash": "cbda3eb1c9c46a2121362e9775f60b47", + "md5": "024352feec9042260bb4cfb4d79a206b", + "sha1": "79c23ce566219f87ade8e55a292aaaabe4a639ec", + "sha256": "60cb39086e10c5b66ebc15e4df219620b344b4358d2918ab6bb3448a0ac8be36" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791731994624, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\EhStorShell.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258701, + "hash": { + "imphash": "37dad3873d5388f07576532bc042f677", + "md5": "7bbf670114373ce6a203fa155a9e0d0a", + "sha1": "104d89dde030b661d05c4c63a03fae1f46ab52d2", + "sha256": "36ef0a36c679e53b1b169289bd3c05d7c2839dc20c8c87bf520b633911fde198" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791647518720, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ntshrui.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258920, + "hash": { + "imphash": "2d37f2d4b3c246f361ca150fc7ebf8d4", + "md5": "3a9c9baf610b0dd4967086040b3b62a9", + "sha1": "3207ac7f895eab34623d994548d7810e54be3e79", + "sha256": "e8e9a0f42b1ee7806edceed08aa024d037215d06ca317e3678bd5364ad513d23" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791753228288, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\srvcli.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258010, + "hash": { + "imphash": "2ba777561101c3b07566cc50db3a564c", + "md5": "1bf0cb861a48feb1638228760750f3cb", + "sha1": "fbc77224c1b444a6ec25e99f995f2f355e4d1d26", + "sha256": "37c781a8c546ead8b4d28bd7d730b9ac78eb799599ad69dad9054b6f9f1dd6bd" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791649091584, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\cscapi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:35" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247534897, + "hash": { + "imphash": "5bf52e420b6d5991bdcce16ada0828dc", + "md5": "1d63f4366288b8a7595397e27010fd44", + "sha1": "e459e1227083e4eabd19ee20e13754560fc7e02d", + "sha256": "99ea4ddd88d9c4a4cc9b238f533cb4d2c062d46239173997e8594d8a75811a01" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791735533568, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\IconCodecService.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534998, + "hash": { + "imphash": "77870f98ca4d25a823c74d7404a64bfd", + "md5": "d0c2fbb6d97416b0166478fc7ae2b212", + "sha1": "e290bdf2312ac30a4e9f2a96d7c84714eee84899", + "sha256": "7eab6c37f0a845e645ca44cc060ac6c56e386c7ef7a64716c6786c9602ad8c9d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791750606848, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CRYPTSP.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 17:43" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247535161, + "hash": { + "imphash": "b8c20a01e4d94df61ee21f5350389f9c", + "md5": "5d8874a8c11dddde29e12de0e2013493", + "sha1": "a1c8e3e6ee44dcb68752d44b3b6f4ecce89c388d", + "sha256": "3e9a57137bf622af83e3e4d58971e2c0200559cca7545d16cf263aa03ee9c7d2" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791747461120, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\rsaenh.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258799, + "hash": { + "imphash": "a198edd0f73abd7cdbb54eef82ab1fc6", + "md5": "c2a8cb1275ecb85d246a9ecc02a728e3", + "sha1": "4417207821fc8f5c72ff531683f183caef297882", + "sha256": "3603fadca0060bd201148f9d59e4e2627f024609a6463ab525b5d1ad17bdcd10" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791758012416, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\RpcRtRemote.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258895, + "hash": { + "imphash": "4fe9beaa9bd4aa01f5063a7352325c89", + "md5": "d7f1ef374a90709b31591823b002f918", + "sha1": "336ac44b8ee88a6af3f3eaf461b8bdf94fa657ff", + "sha256": "05fd2837c9b03d14bb2a969c1ad77caef047d93dc5d0f6c2acbf0888e8f7b359" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791730683904, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SndVolSSO.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534873, + "hash": { + "imphash": "0a90384377303e2a2625725018566a89", + "md5": "896f15a6434d93edb42519d5e18e6b50", + "sha1": "b91a3512a80c4201c3fcfaf62abace894fbba328", + "sha256": "9263f0cec58d45ebe3fb9c3061fb9392c55a7933b84b4592e6ee13cfc86d5a50" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791731929088, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\HID.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534952, + "hash": { + "imphash": "98a24f570dbcd3a092d95b3bd4e51a53", + "md5": "227e2c382a1e02f8d4965e664d3bbe43", + "sha1": "c4971ba9c1e4fdf0106c7cfab626a3d8737bbd07", + "sha256": "1cff20a8bf87ace4fa4935ebeed72bfb1a1fe902a754899e2f50798d67df5642" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791729504256, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\MMDevApi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258898, + "hash": { + "imphash": "e99757a4c1beee1b5bf8b7b33b444dcc", + "md5": "1fcb1a72bf5c784f7358e6bef38e4571", + "sha1": "ef944a320de79bf05f0e30f54f3f8b2ba2e82c4a", + "sha256": "12da4240f8c964eda6223257bd9723fd9372e63ae86f00509163b1de12a5f6c5" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791637426176, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\timedate.cpl", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534704, + "hash": { + "imphash": "d6de6fde05f96ac848accdb1aef473e4", + "md5": "58775492ffd419248b08325e583c527f", + "sha1": "b0e9ca05d46cb53049c4ca33fe04bd08989a78f9", + "sha256": "dbb013971f5894f25c222c2d4d50a29db6df3c413792ee9ccc1a9e6d85469093" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791732322304, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ATL.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535266, + "hash": { + "imphash": "fa1e670045065ff088a4ac664f9ac3d7", + "md5": "9f2bacd5e1776a4bb7cc0ec3c3a4f96d", + "sha1": "ad8c7ec85d532e5725b8535830f27c1abcf139b4", + "sha256": "19959d18601712901f03b83150d15e34ebcab355bb4692c9a28511a72f57fc66" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791730618368, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WINBRAND.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257498, + "hash": { + "imphash": "53f2c3eaeaa6e619e0ccd6e671e96145", + "md5": "e6f0f82788e8bd0f7a616350efa0761c", + "sha1": "9aa4aafda89325853ffa66169e697529164a23a2", + "sha256": "13091dcb3e3f4f52c3ff210e93aaf1dce142cfc09f671aeac5b922393b23e67b" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791633952768, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\actxprxy.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535165, + "hash": { + "imphash": "ae5e5f76641aadaf99f0ca29d2e1cadd", + "md5": "1f4492fe41767cdb8b89d17655847cdd", + "sha1": "c836a5e65d56900b6658fdaa3df8579bdd07ec69", + "sha256": "184547fac0c3d7148faa3f601929a7089de393bd19929a137dad743331dd3f77" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791719739392, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ntmarta.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290259030, + "hash": { + "imphash": "f792b6ec2e11bc79d8eb1bb1bcb79a91", + "md5": "4e4ffb09d895aa000dd56d1404f69a7e", + "sha1": "40f5c1890f6de5284f6c897255e6907b0272349a", + "sha256": "d999e04bb35780088480eab322176570591a21e311d204bdcab010a63b34d24c" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791794974720, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WLDAP32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258853, + "hash": { + "imphash": "2507624727988c72eb2a628a990000fd", + "md5": "c4f40f6cacd796a8e16671d0e9a2f319", + "sha1": "0881ae2a2fd3c5f03654410c474e5a25317942b2", + "sha256": "44853c645915d910ed0cc6d38f68b6c222528ec5fcbe990e238010f41204e682" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791729897472, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\shdocvw.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534923, + "hash": { + "imphash": "e52a872655c57d1b906101b6d5449bbf", + "md5": "a0a65d306a5490d2eb8e7de66898ecfd", + "sha1": "880ac520eb1d38ebb591707a26e6dd300df40643", + "sha256": "ce5da408f4edd5e81ce0925867f03c9a35172cf1571fe4c4c052e45ab69822bb" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791729831936, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\LINKINFO.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258932, + "hash": { + "imphash": "0e8a67fa12ce3d22a9e1d18bda5c3260", + "md5": "7a17485dc7d8a7ac81321a42cd034519", + "sha1": "83d1722a35eb16b010d8c9f72c627e97d4642101", + "sha256": "88d8705fa901793fc8c1cfd0175e49a6502bf0fc94a066ba573d2fd13aa5f04a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791745036288, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\USERENV.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258852, + "hash": { + "imphash": "8b5c65294bec1cf89e97325a24b8cfc5", + "md5": "4e9c2db10f7e6ae91bf761139d4b745b", + "sha1": "6e8e6a53269ca8acc8c2456c80cd3a56d8deb98d", + "sha256": "8f63f78294f5585d599a114af449dcc447ccb239d0f0b490bfe6b34a2146e730" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791704207360, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\shacct.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535152, + "hash": { + "imphash": "44b39e98ae2946f304f4dbadcfffa307", + "md5": "5b3ebfc3da142324b388ddcc4465e1ff", + "sha1": "86e20ebf70fd35723eb635c4f3684891a2547a7b", + "sha256": "5d58642305311f9bc9b779c9598bfc4e7433b3ea58404bf1ff9466838a2328c7" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791716069376, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SAMLIB.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258793, + "hash": { + "imphash": "7fec5787890bfedd3b3aa4082f53a08e", + "md5": "fc51229c7d4afa0d6f186133728b95ab", + "sha1": "f7a2f224356e68b612ecce4512c99f5b9c264d7d", + "sha256": "37e58c8e1c8437d1981725a5dcdaca7316cefbb570370cefc8d122f523b96ac0" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791714168832, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\samcli.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258588, + "hash": { + "imphash": "96f28fef38c977afbf3f6e8f39c0d6b9", + "md5": "6ceca4c6a489c9b2e6073afdaae3f607", + "sha1": "b228f6208642cb99e5bcdf2d3ebda2b8bc4fb020", + "sha256": "127506d1db38275614cbeb047c133718ef9d03266ba9c98be55ec7847cfc9c3d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791722426368, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\netutils.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535084, + "hash": { + "imphash": "14bd8d9a93b98b2479e1f6cd57b7c790", + "md5": "7cb3acb163de051169095dc6507b8977", + "sha1": "b891ebebb25655157f7c612d5763e995c86009a2", + "sha256": "45d4deb0695440d8b5e959945b3f7a773e02e2ab305e316123a1064fc1905402" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791703945216, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\msls31.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257535, + "hash": { + "imphash": "bf738a2fc0ab0601eea36f35e4cbcd27", + "md5": "0bee002c68e28ce6da161dcf1376d7d7", + "sha1": "d5cc3bec12c801e11217acc6927e1e6e401fe208", + "sha256": "1d4ee0b9ce22d139478008d5591b8c9f027c235cba601f95a96547cf98159d4b" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791631134720, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\authui.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258007, + "hash": { + "imphash": "76801e47683b36a4115dbe046717edbe", + "md5": "b3bfbd758506ecb50c5804aaa76318f9", + "sha1": "bf6c922467347a6690eb19c5e82be09b3295778b", + "sha256": "34e079a6ab2d41d1e0b3887b6ae31c43941061b7176fff2801c3f465c2c89578" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791630020608, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CRYPTUI.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257999, + "hash": { + "imphash": "04534d8dae5ab230b9bee9b1b0b2829d", + "md5": "3f9f2afa135f0663946a006dd5ffd897", + "sha1": "ea6456859b04b68af8dcd453381dd168af53fc5e", + "sha256": "276d1c9c78c529625c2ef3d77079324628686ea184767971901a1de93681c133" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791760896000, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\CRYPT32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258373, + "hash": { + "imphash": "2e50bc5d9fe777770c8a6b2cfaf6b2e9", + "md5": "884415bd4269c02eaf8e2613bf85500d", + "sha1": "c3a64f05c210b38c69d8f1fc1d74a71b56ada30c", + "sha256": "efe771709ec942694fd206ac8d0a48ed7dcd35036f074268e4aecd68ac982cea" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791759060992, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\MSASN1.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258922, + "hash": { + "imphash": "75124ca243f494ff6127697f3ebc418a", + "md5": "5fada8b707318e1bd63a7e2b81e6c8cb", + "sha1": "c5ad1c9bbc2f565237a144b9cf44711dfcf65ea5", + "sha256": "2590e88cab52fcc1b24cb262d293131c6280a5f234e0c130e77aa8697efa3b5f" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791793401856, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\urlmon.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258980, + "hash": { + "imphash": "248b27a31ddf696c2e3bfe6aed9c3eba", + "md5": "f6c5302e1f4813d552f41a0ac82455e5", + "sha1": "f0ec3ad7e90f559d1bc9b8849cf5668cafba2031", + "sha256": "e3ebf44621efc6381baae0f0efc13c356dcb6ee31bb258137edb3cc3e18549b5" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791786455040, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WININET.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258097, + "hash": { + "imphash": "f6db6123d8a383f58cf318d00d2e7d1d", + "md5": "5180380d353277d395d3b36d790aa93e", + "sha1": "d5622ec5d922233867422d1e143969e226bb9a1c", + "sha256": "89b894eccf65704d00d30ea3bd45b184bfab8345b779f9ae2be66b9fc7226f72" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791780032512, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\iertutil.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535234, + "hash": { + "imphash": "13ecfa3a285149680a7a4b174c8b8f5b", + "md5": "94e026870a55aaeaff7853c1754091e9", + "sha1": "a4f845318e095d841b05e1400747ee4c28e1f28e", + "sha256": "b2f5d5629d12bdfa98dbed3898368f37d9009c7531b6909c7285a2c11c9a0f93" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791743004672, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\VERSION.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290259004, + "hash": { + "imphash": "da0bcac0c5f9dc653d00eecd5fb1c801", + "md5": "0d9764d58c5efd672b7184854b152e5e", + "sha1": "99d78db040987c69b6a70a42af86641ba0413956", + "sha256": "9827b43dabbec39ab2e2294408d9c5304ef27a684903c5234c6070387723d49e" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791758209024, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WINSTA.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535280, + "hash": { + "imphash": "af1203c1d6d810c97729856780869b12", + "md5": "ef2ae43bcd46abb13fc3e5b2b1935c73", + "sha1": "c53e005cd04d99331ce3114ac119256133202313", + "sha256": "81fc06f306f620845d7dd8d06e706309e70bc89b589c81f3478302a3f5f73431" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791683301376, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WINMM.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258936, + "hash": { + "imphash": "7e9874f9ecf2191b91f9a4dfa37f2ba1", + "md5": "1473768973453de50dc738c2955fc4dd", + "sha1": "7b046f6070844e3bc7deae115a1dfe5825030513", + "sha256": "14bc5da2442cb726acc1f277ddbeccf5d61e3a0a3e083a55a0bb610191e35220" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791648239616, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\wdmaud.drv", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535081, + "hash": { + "imphash": "086996ef0b01a463f114deb5244861b9", + "md5": "8560fffc8eb3a806dcd4f82252cfc8c6", + "sha1": "7562bbb63b0db6e4986ebdb86495c4fe284a1eaa", + "sha256": "cc27bc092369a89d6147b16568fedeb68b584d5738cd686c31f7fae22ed17b3b" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 1968373760, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ksuser.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534742, + "hash": { + "imphash": "690cce63d22e22d9aa225c4a9290b2c4", + "md5": "78a1e65207484b7f8d3217507745f47c", + "sha1": "3542a591e9c97b48739f69e2a193dff461ea097c", + "sha256": "35f413adb9d157f3666dd15dd58104d629cd9143198a1ab914b73a4a3c9903dd" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791718625280, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\AVRT.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257517, + "hash": { + "imphash": "64661addcde8896487dcc7cd32a4eda9", + "md5": "dc220ae6f64819099f7ebd6f137e32e7", + "sha1": "5707f15b666c7d3b07dfce9dac665a2e45c39113", + "sha256": "b8fe13b859fa83500dd95637fa6d4a5b8392c2a363e41d014d3b5374f636e1de" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791659118592, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\AUDIOSES.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534992, + "hash": { + "imphash": "3bf8d3fd03f9d07b7821df4b1da2be9d", + "md5": "1b7c3a37362c7b2890168c5fc61c8d9b", + "sha1": "78ba8d596c0ac4c38acb498416957891570a2a1d", + "sha256": "03727930e5bb5f9d91bab901fc9a2e3b795d68e2aee6a2cc3477f356c45a9c54" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791728062464, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\msacm32.drv", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534991, + "hash": { + "imphash": "9611d7fd4fe3c571fbf1db3d718ba82c", + "md5": "10ac5ce9f78dc281a1bbd9b8cc587b8a", + "sha1": "207582f9d9bec00a932fba886d575ee5b6502d42", + "sha256": "72288c0a88916d3c3828dbd948dbdb0928f26106319f8e60102d6c9004514d60" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791716659200, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\MSACM32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535038, + "hash": { + "imphash": "5a8ee2f48e79ef6ac4b33366d6642b50", + "md5": "ca2a0750ed830678997695ff61b04c30", + "sha1": "a27df990dde73e72bb02105f8af689a1ac324e59", + "sha256": "e84860cd97aa3c4565abb2d5d406a5c42b1ad2d8ba1b8cf81fe564d91f15f976" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791727996928, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\midimap.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 07:10" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247535256, + "hash": { + "imphash": "04a5e982c134477b1914ebcd7b6436d0", + "md5": "d6f630c1fd7f436316093ae500363b19", + "sha1": "197897b74f411040ba7df41a5bd3c1030661b904", + "sha256": "73a94b4938430396ea4240b1a6676b4e6c19cfaf8c52efb9a69b4b2175a86307" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791727734784, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\XmlLite.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258889, + "hash": { + "imphash": "8181b1ef70ff3d29984db497f92a2662", + "md5": "c3761661c17c2248a9379a8fb89e3de1", + "sha1": "d2ea41e02bbaa77f8b93b09277596a34cdae8853", + "sha256": "ce3477fa2b4058eb80739e0161fe957545f13cf86d313f6422732901d35f75f2" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791617568768, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\stobject.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257641, + "hash": { + "imphash": "fbe995ff97475c5aa2777a4bc493d4b1", + "md5": "f832eeea97cdda1af577e721f652a0d1", + "sha1": "48f227a1e10d49edf56e3559e05c871bc285c199", + "sha256": "ebbb7ca199ba4df231123922bd310d43de0104c6185b70fe0281b938d5336f2e" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791616782336, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\BatMeter.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535260, + "hash": { + "imphash": "5d8fff13bf206e589cae241fc7f4d464", + "md5": "bd3674be7fc9d8d3732c83e8499576ed", + "sha1": "cb96190d6366e11dd6e6b48f4cdc4332015cfa67", + "sha256": "e6716a5895d629263a4d21959f48840429ab6f4b55a5fa2663ee5e86c9ca2bf1" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791727538176, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WTSAPI32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290259008, + "hash": { + "imphash": "b2ecd39ae0055d9e1b8aa5bc78942cba", + "md5": "eb3f9c2de1236b5d46b2291d82970e43", + "sha1": "0ce9ddc1063256ab571b916389321fd7f572ddc0", + "sha256": "8a43d335f3d573bed98af54bb51e82546c2acc025da8a48d801213eb14e9d5d4" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791759781888, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WINTRUST.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534970, + "hash": { + "imphash": "8accd78cb7feca81ac448f0485be30dc", + "md5": "4166f82be4d24938977dd1746be9b8a0", + "sha1": "5174036d781677f5444d9a23079baf18f4bbda44", + "sha256": "24121751b7306225ad1c808442d7b030def377e9316aa0a3c5c7460e87317881" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791730159616, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\es.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257970, + "hash": { + "imphash": "8c20d7b93902b8c193a7fc1b4b58e9aa", + "md5": "42a9cb6906d9a8bedc83b57163e62924", + "sha1": "50e5592460d91205e912d55f60a2dd3cc4da4329", + "sha256": "e18522d3137653140757829efbfce624a5baa5842e2bba10b9e5ab6c84be49e1" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791614619648, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\dxp.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258826, + "hash": { + "imphash": "1df61af51096e9bbbdc1834405984e4c", + "md5": "2d2a6ec8ead30ec3ace2fd6fb1b3e122", + "sha1": "1e77948378474e155307d290b998994f720206bf", + "sha256": "e7ea375a3bde8fc764cb09524344370b9ee25f98ad6c83e6f37a569eb8d277d6" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791614160896, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\prnfldr.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290259000, + "hash": { + "imphash": "2f59265cb3df847423b60921203365be", + "md5": "0015acfbbdd164a8a730009908868ca7", + "sha1": "671c084513461900550bd49d3dccb58bdbe05adf", + "sha256": "e1ff243ad2cf959fab81efe701592414991c03416ff296adc93906e76b707c4d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791654924288, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WINSPOOL.DRV", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535225, + "hash": { + "imphash": "3d49b728c9125f451e7f2f215e9d3bbb", + "md5": "2bc7c9fd0a9f2c9afc373f3ad1ee3891", + "sha1": "1b7c6960a72509d1f408022d791c6a65acb2a75d", + "sha256": "0a82a475301202791a7c10f978f952eab7db146a702d4ea67e24e2c98bc19638" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791648108544, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\Syncreg.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258789, + "hash": { + "imphash": "c5c69e7d20ca382ddbc49947d651a8e7", + "md5": "10f815be90a66aafc6c713d1bd626064", + "sha1": "3e21f173a6bcdf629c442d89abadc48137c61bb2", + "sha256": "01139fc04bc53594296f6a0e16b8d20b940f64bc8119fe7705c03c4947958f39" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791612325888, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\pnidui.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258791, + "hash": { + "imphash": "6437e4761b1278fdecf142a679216f7b", + "md5": "b9f0a4020aa98b7a20287bf7fe99a1fd", + "sha1": "1f28ac7493ce972b45de191780a190504d1d0c44", + "sha256": "21138f161eeea46198890c7a2d073f2c82829e15676131bdad9f237edc7477cd" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791612194816, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\QUtil.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535239, + "hash": { + "imphash": "deeb658dae29d8df1c8dbb08f06801b0", + "md5": "3c073b0c596a0af84933e7406766b040", + "sha1": "06185554c38353211430f5f075c490558e46fb3d", + "sha256": "4698bba678f553e15ad4b07ad7fb236281f872defee97bfd637114476c8f97b3" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791752769536, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\wevtapi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258586, + "hash": { + "imphash": "97bb6eee9e1ea3e5751077b655b54de5", + "md5": "a42f2c1eb3b66c54fb3c7b79d30c1a6d", + "sha1": "cee705de8d3dfcc9e2a14e0249d6be61fcd54a18", + "sha256": "a63836db3b01835dc1311526a95198d6ebccb1dc9ddafbc38ec36c128cdb98b9" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791609507840, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\netshell.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258138, + "hash": { + "imphash": "0bc508389b6b5577cf3cca214ca523a7", + "md5": "2b81776da02017a37fe26c662827470e", + "sha1": "8c85389640bea73a009d83079f8b4c963697035f", + "sha256": "a656353c50ee08422145d00db9cfd9f6d3e664753b3c454b171e2a56a8aa94dc" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791727210496, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\IPHLPAPI.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535145, + "hash": { + "imphash": "579f52f57e43aa6ff0d07e88af5d0ff5", + "md5": "044fe45ffd6ad40e3bbbe60b7f41babe", + "sha1": "94233c0d4169c02c85514adb1f05cd3298c87f43", + "sha256": "a1688a5e6e0f7037c850699462c2655006a7d873c97f9ab406c59d81749b6f09" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791763648512, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\NSI.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535281, + "hash": { + "imphash": "e710d6d30f2346e7cd91c89ec3b602d9", + "md5": "4c9210e8f4e052f6a4eb87716da0c24c", + "sha1": "d4fa50aded12eb162478d7606f1270b78dd1a44b", + "sha256": "460f7990bdadb7d58d6dc95b094d30a2efdc4ceed444b18a2f36e8d9076fb8b9" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791726948352, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\WINNSI.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258583, + "hash": { + "imphash": "7e01da4b2a8806d2944a3ff2e271958f", + "md5": "2df36f15b2bc1571a6a542a3c2107920", + "sha1": "660a44b660d8e57ef7d7efbbc006ac390a7901fa", + "sha256": "a918f1ee95269df973421af2f5713deeaf15ef0f77baa7e8c515ffb69896fb7a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791735992320, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\nlaapi.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534791, + "hash": { + "imphash": "59b31e42f8fae7b5809ba7fcae732e0c", + "md5": "4cbcc37856ea2039c27a2fb661dda0e5", + "sha1": "cc666108d34168420a1d1942dda1e090154c7296", + "sha256": "74cbfab3092a9564bddfcb84db3e3f8bcfd1492938adf187423d3355d73d21c6" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791723999232, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\dhcpcsvc6.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258981, + "hash": { + "imphash": "1ec347d133df2fe4da3e5f8944caeae8", + "md5": "4bbfa57f594f7e8a8edc8f377184c3f0", + "sha1": "d48aafa576b40a5e386e609bba1010472551154a", + "sha256": "9f3ac5dea5a6250c3dbb97af79c81c0a48429486521f807355a1d7d3d861b75f" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791788486656, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\WS2_32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:35" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257492, + "hash": { + "imphash": "f5d0254c5435291634c8b7357aa536bd", + "md5": "92dbf0a4c9239169010fc6e07859c82e", + "sha1": "634d8c12de82c422dfeba8f9a5fa84d03b7bcd35", + "sha256": "00fb2cf4420f0ffef519afe732a708cf249640121e2a891caa164313abd7f804" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791608655872, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\Actioncenter.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534790, + "hash": { + "imphash": "f17020f0f66b64fbdf51c75b43f3729d", + "md5": "f568f7c08458d69e4fcd8675bbb107e4", + "sha1": "c1e05f0255a6f386711044b11e2d04dfd328b26a", + "sha256": "a5fa25ecf248999a68ccecfbb508bfa1add18a23e20a9a9081a87c41caaa36c0" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791723868160, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\dhcpcsvc.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257996, + "hash": { + "imphash": "eb1c8dd21e1f92a8be35a76b165ce8da", + "md5": "52d3d5e3586988d4d9e34acaac33105c", + "sha1": "2c20246d2c45fb6e8976b37ad62465f5f4255f2b", + "sha256": "c61b60ba962b25b8334f0941c3535ea4aca1cc060b8a196e396ca3e11ceef8a1" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791746412544, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\credssp.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258110, + "hash": { + "imphash": "9ba63732839305b29ebe539451171b45", + "md5": "8130391f82d52d36c0441f714136957f", + "sha1": "e2bb102565986a42d0a43bd3f337f94dbe54eead", + "sha256": "1fd4fee7caf63e450f27729e07ea2a2f09288629fd872dbb6e8710b16d8dbd5d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791608131584, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\imapi2.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258038, + "hash": { + "imphash": "e070eff3751fea77ccd424469a9a07e6", + "md5": "6a5c1a8ac0b572679361026d0e900420", + "sha1": "fd9241fdda4b9d08ff1e205f9d5f78923ab884d8", + "sha256": "b5e693b48b462e97738a3d4e58b60846159649eb15f4d11074b4bc107cc88562" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791607345152, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\hgcpl.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535139, + "hash": { + "imphash": "1e00eab90042e5099339cb82841b434a", + "md5": "f7073c962c4fb7c415565dde109de49f", + "sha1": "671c2e910ff954700b3a1f80608423697895c0a9", + "sha256": "781e7088dcefbc34a808c3e7da41a56112b3f23abe9f54b5ef4d5cd9cd016b1d" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791680090112, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\npmproxy.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258777, + "hash": { + "imphash": "d402ebf00a5cffa66b6682780c262457", + "md5": "6b851e682a36453e1b1ee297ffb6e2ab", + "sha1": "3dc85ba13d1f720e8039865817bcc65dc0f1d35b", + "sha256": "a641d3fd9463c4788b45b8b5584ea4489c1f63a71b4b595ae85ff3482cd5eda6" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791606099968, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\QAgent.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534818, + "hash": { + "imphash": "09bf801b36364c598a2a8fdff079932c", + "md5": "cd1b5ad07e5f7fef30e055dcc9e96180", + "sha1": "4e835fdadd0c67fde44e385f69a1014d6ad11f4f", + "sha256": "63c58551f32b0b09377f64a6ae1fa81af93b8a707a57a8c18722086906ad3046" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791745167360, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\DEVRTL.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258413, + "hash": { + "imphash": "08a9b8e4e42e5520be662b4663289747", + "md5": "1eac1a8ca6874bf5b15e2efb9a9a7b86", + "sha1": "30cff16f17833aa042d8b6cc32d86c4a39c77c67", + "sha256": "e15ed4fefc3010c213694331ddfdc03767682325c898d773ab243e2dc8b08461" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791633100800, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\MsftEdit.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258939, + "hash": { + "imphash": "6ac24d44010fe2db4d5e9e0651b7a3cf", + "md5": "f9959237f106f2b2609e61a290c0652e", + "sha1": "7f7c92c4fe8244a7deac7fed4d5576042bfba29e", + "sha256": "fccc12e5aae1773bf87b1c4bce71d017db1a5a7ac189559058ea1ecc72075a82" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791628709888, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\werconcpl.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535233, + "hash": { + "imphash": "cce75846cdf9d74f85e44fc728ee8440", + "md5": "9689a9c7f7c2a1a423cda2c3b43fff65", + "sha1": "ebe6b3066634239a4f62780a8a6e27f33b0afc87", + "sha256": "914ad22d98975578bc14d821f72e8dfce24f2092f9c299d24ebbaf5408fe8b8b" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791646994432, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\wer.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290257998, + "hash": { + "imphash": "6e52c6bdbfd3d257064382284bd4f59c", + "md5": "1484b9ebf567346582de571b0e164ae0", + "sha1": "6b87eb7005fe659f976732307fe12b96747dfc8d", + "sha256": "9862bf22b2e32dabe7a82acee5b4ea1f0a93bdc3c71b20a6a4e568cccd76a7a6" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791628382208, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\framedynos.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535235, + "hash": { + "imphash": "64b92457c7762d63f903189058d583ca", + "md5": "7e591867422dc788b9e5bd337a669a08", + "sha1": "3bd1b2a2271d6756351d9b4876193efd8a845da0", + "sha256": "484e6bccdf7adce9a1aacad1bc7c7d7694b9e40fa90d94b14d80c607784f6c75" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791628251136, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\wercplsupport.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258497, + "hash": { + "imphash": "2814c7c81c59e8a913c288a8c72a9c1c", + "md5": "5c29199c9f0ede64f17f268084ec4392", + "sha1": "a767e893427f9b24fe06cbb3a155dd54162a402a", + "sha256": "ea9fd588a8c89399dd287399a912b356a4234cfe418239b227d255749f5ddde2" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791652564992, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\msxml6.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:35" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247534858, + "hash": { + "imphash": "2ab209fb6a68c8e15483324a442c1c4c", + "md5": "809ae7d4ace06bbcf621e5c504bf6fc8", + "sha1": "c0e2202d99db67a9efa6c67226410ad3c7b657a6", + "sha256": "0baab89fb57468f27446947d75cbd6ddfc92d9b8f040144a12656803b2f7bf65" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791722491904, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\hcproviders.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:36" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258095, + "hash": { + "imphash": "328b1cd6b239c7c01904019379bede4b", + "md5": "77a8a1791145710c7efe76ea82bf0763", + "sha1": "e421318d7b6d66c9214722c736f5b3d4207acf74", + "sha256": "9488b96e065299d273f9dcc82aa1203b48f0038d4f27324da19e9bfd925ca737" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791627726848, + "mapped_size": 0, + "path": "C:\\Program Files\\Internet Explorer\\ieproxy.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258892, + "hash": { + "imphash": "ec50511b4e46da8b1a467667a84f8047", + "md5": "9cead32e79a62150fe9f8557e58e008b", + "sha1": "4cbd17b96209b5e2da683382e05cef55f48d6107", + "sha256": "afe4c1725ee94d7de0749ae1495a4e5cc33c369f29b2a589da66ffe27ff9777e" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791757357056, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SXS.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258896, + "hash": { + "imphash": "d75a096a9c47b1fd385a268e9c6f2f68", + "md5": "24f4b480f335a6c724af352253c5d98b", + "sha1": "a388cc90338cec7b5eec66e921599de0cc275a2b", + "sha256": "011413b236cad7b78ce0a0eec3e3085d48c7576a3205d025ba6ebfdf590538e4" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791660232704, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\thumbcache.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247527581, + "hash": { + "imphash": "be693a67b5b884d7609eaf574ba00955", + "md5": "d87e1e59c73c1f98d5ded5b3850c40f5", + "sha1": "141c0ebecdd2733b90431f18b188ee0b64456268", + "sha256": "536419bff9f877d4314b5d0c045d9a6e729489c389863fadf07e382050bc84fd" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 2009726976, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\PSAPI.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:36" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258093, + "hash": { + "imphash": "39d5c5468a8e87803234025334b9dc09", + "md5": "f1115299b9f4c983bc4523b33e3a506c", + "sha1": "639946c23b630798284a92117882990ea31d702e", + "sha256": "01a1d8b3e5cf727f92f4a43d5c5f81022127d58a850d29d3f084ad411efbc9dd" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791578836992, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\ieframe.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535131, + "hash": { + "imphash": "84786d42c8a896b9a971b3c9eb8feb4c", + "md5": "9869a4a10b90546dbd56947839fb4b87", + "sha1": "5d9642f314d62dc5834cbd7950230bad3f85d982", + "sha256": "66c84dcf39d9f6896d55b1623184a028891a0a98abe6044de1d4bad60c3c8d72" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791591157760, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\OLEACC.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258887, + "hash": { + "imphash": "e6c083bfcedd032db2c66cd04f74c620", + "md5": "4e81439902079c348b61d7ff027fe147", + "sha1": "4386a5580b459aa4a0701addb753c3f9bf3da6f7", + "sha256": "e652c9ec77745504689532b3c394959f9b5bc29e9c008cb9ee09cda818514fa9" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791658594304, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\StructuredQuery.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258589, + "hash": { + "imphash": "45badcf3f18f69f9f72af5245898d1cb", + "md5": "405f4d32d2185f1f1bd753d8eeaffb3a", + "sha1": "68bc45bac1e1584c789a6b3134bee5a2540f3e56", + "sha256": "cac42c3e09c43be96592b670d70821386014db22d8239a9cfb9e33e54fb5c3d5" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791656890368, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\NetworkExplorer.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258495, + "hash": { + "imphash": "cdb39fb77293fb1bb86c2d5980ea8e88", + "md5": "022b05cee68d7826a93aedb4f1eb369e", + "sha1": "e7055d6cacb8c3fae06dc10ad480c8e6b8b7b592", + "sha256": "3b864d1471ed0949b02f1fa251b987185abeaddcbecd44efdbb6a7b7f03ca8bc" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791625760768, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\msxml3.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258914, + "hash": { + "imphash": "6b6c83729fa36b04c301494d1eb07752", + "md5": "bb074f35b49eb2ea416962b596281e1e", + "sha1": "355fdb9e66ffad42144b1b6ec4d8eb357ed05d52", + "sha256": "e07208204b9616027e5144e2f3ef1ba81168365b7d2a761210b0fbc65b97871e" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791623598080, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\systemcpl.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258594, + "hash": { + "imphash": "2bd8f9f72a13c2803ac3d34b805130b9", + "md5": "764908fe1fa96f93c95b1b67a0fced29", + "sha1": "88d0027e5d10158e3678d9eb2326779fef8a64d1", + "sha256": "26ef25ab307903c5e806a8cc3b750a491049e5d1225ceddfce64dd51aa6f592b" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791722557440, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\NETAPI32.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290259010, + "hash": { + "imphash": "6ad99a405bde55d6a18debafd3f5e5c5", + "md5": "3c91392d448f6e5d525a85b7550d8ba9", + "sha1": "b62eaf7d80617e136a8f3c9161c23464e6f2a171", + "sha256": "6fd0dc73dbe7519e2c643554c2a7f8fbe4f9a678c4241bb54b3c6e65d2abcf3a" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791722295296, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\wkscli.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247534877, + "hash": { + "imphash": "3e340766bf7f54e3e9746a945d4dcb71", + "md5": "a77be7cb3222b4fb0ac6c71d1c2698d4", + "sha1": "e68b4e0058fb130c765e5aa98af36e26563809db", + "sha256": "73566223914bf670df6b5931fa213e546713531b10391ed65b5256bbd7abde7f" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791735926784, + "mapped_size": 0, + "path": "C:\\Windows\\System32\\DSROLE.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258758, + "hash": { + "imphash": "c888173aa662e52d4b6194ed15819a13", + "md5": "db76db15efc6e4d1153a6c5bc895948d", + "sha1": "00dc6172c4507def32e4a269c08e76ab09abc3fe", + "sha256": "71ddf02c7ee2df66a08f1a2a08da39802c354624880a2be93a706ea7476422a3" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791690641408, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\SPPC.DLL", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 07:10" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247535243, + "hash": { + "imphash": "9484a9d0a0e3ef20592c9f66412400a6", + "md5": "666a60f6f5e719856ff6254e0966eff7", + "sha1": "10258e708443bd21997e7a977b5ee36bd758e368", + "sha256": "58c072e7e215991e19c1ca062c476081982f7b9f039714539ae7feb4981c200f" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791716200448, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\wbem\\wbemprox.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 07:10" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258938, + "hash": { + "imphash": "03a62984ba62616e18740e69949df533", + "md5": "7db5aa22a8a8e5c2d335f44853c1f6de", + "sha1": "add6f6e2b6df5f571d06db724de5c7badad4e775", + "sha256": "a734a20357026c42950394682a52cbc3af956d09f1949e1b4e95467e999bc428" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791690051584, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\wbemcomn.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 ca 69 00 00 00 00 00 08 ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535244, + "hash": { + "imphash": "6178a249d43f815225b0a9205f1f4f70", + "md5": "718b6f51ab7f6fe2988a36868f9ad3ab", + "sha1": "7cc84a20d6597f58eebabea5489d72239c6e746b", + "sha256": "76141b4e94c2766e2c34cef523092948771a7893212efadbe88d2171b85ff012" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791683170304, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\wbem\\wbemsvc.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 04 b3 f5 00 00 00 00 00 0d ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 07:10" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1247534846, + "hash": { + "imphash": "c93ca8ec08e734d1b95c2a2d28884c47", + "md5": "a3f5e8ec1316c3e2562b82694a251c9e", + "sha1": "f0cdc2b44e609950ee97d9967c7459055a2af1a8", + "sha256": "f3dc6aa6a9d3b5bbc730668fc52c1d4bb5d515d404578bddd3d4869a7ed58822" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791688675328, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\wbem\\fastprox.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 01 c6 c1 00 00 00 00 00 07 ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "07/13/2009 19:17" + }, + "more_info_link": "http://www.microsoft.com/windows", + "program_name": "Windows System Catalog", + "publisher_link": "" + }, + "compile_time": 1247535150, + "hash": { + "imphash": "29f9ce11d25836037034b49be93790c6", + "md5": "ee26d130808d16c0e417bbbed0451b34", + "sha1": "962d52fb4d8f9965c5fc11a98f2f9048a2a5d918", + "sha256": "4886dce4faef146a40babd492a8000a2022fea542a6135a9bafd4cd09297b4e5" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791688478720, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\NTDSAPI.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + }, + { + "architecture": "x64", + "authenticode": { + "cert_signer": { + "issuer_name": "Microsoft Windows Verification PCA", + "serial_number": "61 15 23 0f 00 00 00 00 00 0a ", + "subject_name": "Microsoft Windows" + }, + "cert_timestamp": { + "issuer_name": "Microsoft Time-Stamp PCA", + "serial_number": "61 03 dc f6 00 00 00 00 00 0c ", + "subject_name": "Microsoft Time-Stamp Service", + "timestamp_string": "11/20/2010 11:37" + }, + "more_info_link": "http://www.microsoft.com", + "program_name": "Microsoft Windows", + "publisher_link": "" + }, + "compile_time": 1290258103, + "hash": { + "imphash": "ba45ab39c8fb40e4076d27cf8e0f4180", + "md5": "b8509dcfcfd577f568be4026bfd982c0", + "sha1": "1923c5995faf94d9b1767aca04e3134a5cedc07a", + "sha256": "e3608e6de15c400fa437349e7295fef10a1a0213ca3b532a58964b8c89749110" + }, + "malware_classification": { + "identifier": "Whitelisted", + "score": 0, + "threshold": 0, + "version": "3.0.0" + }, + "mapped_address": 8791788355584, + "mapped_size": 0, + "path": "C:\\Windows\\system32\\imagehlp.dll", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted" + } + ], + "name": "explorer.exe", + "parent": { + "executable": "", + "name": "" + }, + "pid": 784, + "ppid": 704, + "sid": "S-1-5-21-2016385190-3414718578-1263322444-500", + "signature_signer": "Microsoft Windows", + "signature_status": "trusted", + "start": 1542341500, + "threads": [ + { + "entrypoint": 4279023504, + "id": 1920, + "start": 1542341500, + "uptime": 437 + }, + { + "entrypoint": 2008002240, + "id": 1812, + "start": 1542341500, + "uptime": 437 + }, + { + "entrypoint": 8791783440744, + "id": 2472, + "start": 1542341500, + "uptime": 436 + }, + { + "entrypoint": 8791792141832, + "id": 2468, + "start": 1542341500, + "uptime": 436 + }, + { + "entrypoint": 8791790810108, + "id": 2464, + "start": 1542341500, + "uptime": 436 + }, + { + "entrypoint": 8791792141832, + "id": 2476, + "start": 1542341500, + "uptime": 435 + }, + { + "entrypoint": 2008021952, + "id": 1800, + "start": 1542341500, + "uptime": 434 + }, + { + "entrypoint": 2008021952, + "id": 2516, + "start": 1542341500, + "uptime": 433 + }, + { + "entrypoint": 8791792141832, + "id": 2500, + "start": 1542341500, + "uptime": 433 + }, + { + "entrypoint": 8791792141832, + "id": 1068, + "start": 1542341500, + "uptime": 432 + }, + { + "entrypoint": 8791792141832, + "id": 2676, + "start": 1542341500, + "uptime": 428 + }, + { + "entrypoint": 8791792141832, + "id": 2660, + "start": 1542341500, + "uptime": 428 + }, + { + "entrypoint": 8791792141832, + "id": 2748, + "start": 1542341500, + "uptime": 428 + }, + { + "entrypoint": 8791729529348, + "id": 2636, + "start": 1542341500, + "uptime": 428 + }, + { + "entrypoint": 8791792141832, + "id": 2732, + "start": 1542341500, + "uptime": 424 + }, + { + "entrypoint": 8791783440744, + "id": 1472, + "start": 1542341500, + "uptime": 419 + }, + { + "entrypoint": 2008021952, + "id": 2220, + "start": 1542341500, + "uptime": 415 + }, + { + "entrypoint": 2008021952, + "id": 2332, + "start": 1542341800, + "uptime": 104 + }, + { + "entrypoint": 2008021952, + "id": 3712, + "start": 1542341800, + "uptime": 99 + }, + { + "entrypoint": 8791792141832, + "id": 2080, + "start": 1542341800, + "uptime": 85 + }, + { + "entrypoint": 2008021952, + "id": 4012, + "start": 1542341800, + "uptime": 81 + }, + { + "entrypoint": 2008021952, + "id": 4060, + "start": 1542341800, + "uptime": 81 + }, + { + "entrypoint": 2008021952, + "id": 520, + "start": 1542341800, + "uptime": 77 + }, + { + "entrypoint": 2008021952, + "id": 3236, + "start": 1542341800, + "uptime": 74 + }, + { + "entrypoint": 2008021952, + "id": 3260, + "start": 1542341800, + "uptime": 72 + }, + { + "entrypoint": 8791792141832, + "id": 3680, + "start": 1542341900, + "uptime": 56 + }, + { + "entrypoint": 2008021952, + "id": 3708, + "start": 1542341900, + "uptime": 55 + }, + { + "entrypoint": 2008021952, + "id": 2512, + "start": 1542341900, + "uptime": 55 + }, + { + "entrypoint": 8791792141832, + "id": 3748, + "start": 1542341900, + "uptime": 54 + }, + { + "entrypoint": 8791690668104, + "id": 3872, + "start": 1542341900, + "uptime": 51 + }, + { + "entrypoint": 8791683305488, + "id": 1016, + "start": 1542341900, + "uptime": 26 + }, + { + "entrypoint": 2008021952, + "id": 3520, + "start": 1542341900, + "uptime": 26 + }, + { + "entrypoint": 8791792141832, + "id": 3992, + "start": 1542341900, + "uptime": 13 + }, + { + "entrypoint": 8791760904360, + "id": 3604, + "start": 1542341900, + "uptime": 12 + } + ], + "token": { + "domain": "WIN-Q3DOP1UKA81", + "integrity_level": 12288, + "integrity_level_name": "high", + "privileges": [ + { + "description": "Adjust memory quotas for a process", + "enabled": false, + "name": "SeIncreaseQuotaPrivilege" + }, + { + "description": "Manage auditing and security log", + "enabled": false, + "name": "SeSecurityPrivilege" + }, + { + "description": "Take ownership of files or other objects", + "enabled": false, + "name": "SeTakeOwnershipPrivilege" + }, + { + "description": "Load and unload device drivers", + "enabled": false, + "name": "SeLoadDriverPrivilege" + }, + { + "description": "Profile system performance", + "enabled": false, + "name": "SeSystemProfilePrivilege" + }, + { + "description": "Change the system time", + "enabled": false, + "name": "SeSystemtimePrivilege" + }, + { + "description": "Profile single process", + "enabled": false, + "name": "SeProfileSingleProcessPrivilege" + }, + { + "description": "Increase scheduling priority", + "enabled": false, + "name": "SeIncreaseBasePriorityPrivilege" + }, + { + "description": "Create a pagefile", + "enabled": false, + "name": "SeCreatePagefilePrivilege" + }, + { + "description": "Back up files and directories", + "enabled": false, + "name": "SeBackupPrivilege" + }, + { + "description": "Restore files and directories", + "enabled": false, + "name": "SeRestorePrivilege" + }, + { + "description": "Shut down the system", + "enabled": false, + "name": "SeShutdownPrivilege" + }, + { + "description": "Debug programs", + "enabled": false, + "name": "SeDebugPrivilege" + }, + { + "description": "Modify firmware environment values", + "enabled": false, + "name": "SeSystemEnvironmentPrivilege" + }, + { + "description": "Bypass traverse checking", + "enabled": true, + "name": "SeChangeNotifyPrivilege" + }, + { + "description": "Force shutdown from a remote system", + "enabled": false, + "name": "SeRemoteShutdownPrivilege" + }, + { + "description": "Remove computer from docking station", + "enabled": false, + "name": "SeUndockPrivilege" + }, + { + "description": "Perform volume maintenance tasks", + "enabled": false, + "name": "SeManageVolumePrivilege" + }, + { + "description": "Impersonate a client after authentication", + "enabled": true, + "name": "SeImpersonatePrivilege" + }, + { + "description": "Create global objects", + "enabled": true, + "name": "SeCreateGlobalPrivilege" + }, + { + "description": "Increase a process working set", + "enabled": false, + "name": "SeIncreaseWorkingSetPrivilege" + }, + { + "description": "Change the time zone", + "enabled": false, + "name": "SeTimeZonePrivilege" + }, + { + "description": "Create symbolic links", + "enabled": false, + "name": "SeCreateSymbolicLinkPrivilege" + } + ], + "sid": "S-1-5-21-2016385190-3414718578-1263322444-500", + "type": "tokenPrimary", + "user": "Administrator" + }, + "unique_pid": 35, + "unique_ppid": 0, + "uptime": 437, + "user": "Administrator" + }, + "user": { + "group": {} + } + } + } + ], + "max_score": 1.0, + "total": { + "relation": "eq", + "value": 21 + } + }, + "timed_out": false, + "took": 2 +} diff --git a/x-pack/plugins/endpoint/server/test_data/all_alerts_data_legacy.json b/x-pack/plugins/endpoint/server/test_data/all_alerts_data_legacy.json new file mode 100644 index 0000000000000..3863baed387aa --- /dev/null +++ b/x-pack/plugins/endpoint/server/test_data/all_alerts_data_legacy.json @@ -0,0 +1,15 @@ +{ + "_shards": { + "failed": 0, + "skipped": 0, + "successful": 1, + "total": 1 + }, + "hits": { + "hits": [], + "max_score": 1.0, + "total": 21 + }, + "timed_out": false, + "took": 2 +} diff --git a/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json new file mode 100644 index 0000000000000..f1ad5190c55ff --- /dev/null +++ b/x-pack/plugins/endpoint/server/test_data/all_endpoints_data.json @@ -0,0 +1,200 @@ +{ + "took" : 343, + "timed_out" : false, + "_shards" : { + "total" : 1, + "successful" : 1, + "skipped" : 0, + "failed" : 0 + }, + "hits" : { + "total" : { + "value" : 4, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "endpoint-agent", + "_id" : "WqVo1G8BYQH1gtPUgYkC", + "_score" : null, + "_source" : { + "@timestamp" : 1579816615336, + "event" : { + "created" : "2020-01-23T21:56:55.336Z" + }, + "endpoint" : { + "policy" : { + "id" : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "agent" : { + "version" : "6.8.3", + "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2" + }, + "host" : { + "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265", + "hostname" : "larimer-0.example.com", + "ip" : "10.21.48.136", + "mac" : "77-be-30-f0-e8-d6", + "architecture" : "x86_64", + "os" : { + "name" : "windows 6.2", + "full" : "Windows Server 2012", + "version" : "6.2" + } + } + }, + "fields" : { + "host.id.keyword" : [ + "7141a48b-e19f-4ae3-89a0-6e7179a84265" + ] + }, + "sort" : [ + 1579816615336 + ], + "inner_hits" : { + "most_recent" : { + "hits" : { + "total" : { + "value" : 2, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "endpoint-agent", + "_id" : "WqVo1G8BYQH1gtPUgYkC", + "_score" : null, + "_source" : { + "@timestamp" : 1579816615336, + "event" : { + "created" : "2020-01-23T21:56:55.336Z" + }, + "endpoint" : { + "policy" : { + "id" : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "agent" : { + "version" : "6.8.3", + "id" : "56a75650-3c8a-4e4f-ac17-6dd729c650e2" + }, + "host" : { + "id" : "7141a48b-e19f-4ae3-89a0-6e7179a84265", + "hostname" : "larimer-0.example.com", + "ip" : "10.21.48.136", + "mac" : "77-be-30-f0-e8-d6", + "architecture" : "x86_64", + "os" : { + "name" : "windows 6.2", + "full" : "Windows Server 2012", + "version" : "6.2" + } + } + }, + "sort" : [ + 1579816615336 + ] + } + ] + } + } + } + }, + { + "_index" : "endpoint-agent", + "_id" : "W6Vo1G8BYQH1gtPUgYkC", + "_score" : null, + "_source" : { + "@timestamp" : 1579816615336, + "event" : { + "created" : "2020-01-23T21:56:55.336Z" + }, + "endpoint" : { + "policy" : { + "id" : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "agent" : { + "version" : "6.4.3", + "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312" + }, + "host" : { + "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf", + "hostname" : "hildebrandt-6.example.com", + "ip" : "10.53.92.84", + "mac" : "af-f1-8f-51-25-2a", + "architecture" : "x86_64", + "os" : { + "name" : "windows 10.0", + "full" : "Windows 10", + "version" : "10.0" + } + } + }, + "fields" : { + "host.id.keyword" : [ + "f35ec6c1-6562-45b1-818f-2f14c0854adf" + ] + }, + "sort" : [ + 1579816615336 + ], + "inner_hits" : { + "most_recent" : { + "hits" : { + "total" : { + "value" : 2, + "relation" : "eq" + }, + "max_score" : null, + "hits" : [ + { + "_index" : "endpoint-agent", + "_id" : "W6Vo1G8BYQH1gtPUgYkC", + "_score" : null, + "_source" : { + "@timestamp" : 1579816615336, + "event" : { + "created" : "2020-01-23T21:56:55.336Z" + }, + "endpoint" : { + "policy" : { + "id" : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "agent" : { + "version" : "6.4.3", + "id" : "c2d84d8f-d355-40de-8b54-5d318d4d1312" + }, + "host" : { + "id" : "f35ec6c1-6562-45b1-818f-2f14c0854adf", + "hostname" : "hildebrandt-6.example.com", + "ip" : "10.53.92.84", + "mac" : "af-f1-8f-51-25-2a", + "architecture" : "x86_64", + "os" : { + "name" : "windows 10.0", + "full" : "Windows 10", + "version" : "10.0" + } + } + }, + "sort" : [ + 1579816615336 + ] + } + ] + } + } + } + } + ] + }, + "aggregations" : { + "total" : { + "value" : 2 + } + } +} diff --git a/x-pack/plugins/endpoint/server/types.ts b/x-pack/plugins/endpoint/server/types.ts new file mode 100644 index 0000000000000..a0c9cd4b90266 --- /dev/null +++ b/x-pack/plugins/endpoint/server/types.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { LoggerFactory } from 'kibana/server'; +import { EndpointConfigType } from './config'; + +/** + * A JSON-like structure. + */ +export interface JSONish { + [key: string]: number | string | null | undefined | JSONish | JSONish[]; +} + +/** + * The context for Endpoint apps. + */ +export interface EndpointAppContext { + logFactory: LoggerFactory; + config(): Promise; +} + +/** + * Request params for alert queries. + */ +export interface AlertRequestParams { + page_index?: number; + page_size?: number; +} diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 60165bd8d3e8c..39593adb39362 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -18,6 +18,7 @@ require('@kbn/test').runTestsCli([ require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), require.resolve('../test/detection_engine_api_integration/security_and_spaces/config.ts'), require.resolve('../test/plugin_api_integration/config.js'), + require.resolve('../test/plugin_functional/config'), require.resolve('../test/kerberos_api_integration/config'), require.resolve('../test/kerberos_api_integration/anonymous_access.config'), require.resolve('../test/saml_api_integration/config'), diff --git a/x-pack/test/api_integration/apis/endpoint/alerts.ts b/x-pack/test/api_integration/apis/endpoint/alerts.ts new file mode 100644 index 0000000000000..c08d17e96b180 --- /dev/null +++ b/x-pack/test/api_integration/apis/endpoint/alerts.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + describe('test alerts api', () => { + describe('Tests for alerts API', () => { + before(() => esArchiver.load('endpoint/alerts/api_feature')); + after(() => esArchiver.unload('endpoint/alerts/api_feature')); + it('alerts api should return one entry for each alert with default paging', async () => { + const { body } = await supertest + .post('/api/endpoint/alerts') + .set('kbn-xsrf', 'xxx') + .send({}) + .expect(200); + expect(body.total).to.eql(132); + expect(body.alerts.length).to.eql(10); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + expect(body.result_from_index).to.eql(0); + }); + + it('alerts api should return page based on paging properties passed.', async () => { + const { body } = await supertest + .post('/api/endpoint/alerts') + .set('kbn-xsrf', 'xxx') + .send({ + page_size: 1, + page_index: 1, + }) + .expect(200); + expect(body.total).to.eql(132); + expect(body.alerts.length).to.eql(1); + expect(body.request_page_size).to.eql(1); + expect(body.request_page_index).to.eql(1); + expect(body.result_from_index).to.eql(1); + }); + + it('alerts api should return accurate total alerts if page index produces no result', async () => { + const { body } = await supertest + .get('/api/endpoint/alerts?page_size=100&page_index=3') + .set('kbn-xsrf', 'xxx') + .expect(200); + expect(body.total).to.eql(132); + expect(body.alerts.length).to.eql(0); + expect(body.request_page_size).to.eql(100); + expect(body.request_page_index).to.eql(3); + expect(body.result_from_index).to.eql(300); + }); + + it('alerts api should return 400 when paging properties are below boundaries.', async () => { + const { body } = await supertest + .get('/api/endpoint/alerts?page_size=0') + .set('kbn-xsrf', 'xxx') + .expect(400); + expect(body.message).to.contain('Value is [0] but it must be equal to or greater than [1]'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/endpoint/endpoints.ts b/x-pack/test/api_integration/apis/endpoint/endpoints.ts new file mode 100644 index 0000000000000..210e9d78d7e18 --- /dev/null +++ b/x-pack/test/api_integration/apis/endpoint/endpoints.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + describe('test endpoints api', () => { + describe('POST /api/endpoint/endpoints when index is empty', () => { + it('endpoints api should return empty result when index is empty', async () => { + await esArchiver.unload('endpoint/endpoints/api_feature'); + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + expect(body.total).to.eql(0); + expect(body.endpoints.length).to.eql(0); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + }); + }); + + describe('POST /api/endpoint/endpoints when index is not empty', () => { + before(() => esArchiver.load('endpoint/endpoints/api_feature')); + after(() => esArchiver.unload('endpoint/endpoints/api_feature')); + it('endpoints api should return one entry for each endpoint with default paging', async () => { + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + expect(body.total).to.eql(3); + expect(body.endpoints.length).to.eql(3); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + }); + + it('endpoints api should return page based on paging properties passed.', async () => { + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ + paging_properties: [ + { + page_size: 1, + }, + { + page_index: 1, + }, + ], + }) + .expect(200); + expect(body.total).to.eql(3); + expect(body.endpoints.length).to.eql(1); + expect(body.request_page_size).to.eql(1); + expect(body.request_page_index).to.eql(1); + }); + + /* test that when paging properties produces no result, the total should reflect the actual number of endpoints + in the index. + */ + it('endpoints api should return accurate total endpoints if page index produces no result', async () => { + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ + paging_properties: [ + { + page_size: 10, + }, + { + page_index: 3, + }, + ], + }) + .expect(200); + expect(body.total).to.eql(3); + expect(body.endpoints.length).to.eql(0); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(30); + }); + + it('endpoints api should return 400 when pagingProperties is below boundaries.', async () => { + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ + paging_properties: [ + { + page_size: 0, + }, + { + page_index: 1, + }, + ], + }) + .expect(400); + expect(body.message).to.contain('Value is [0] but it must be equal to or greater than [1]'); + }); + + it('endpoints api should return page based on filters passed.', async () => { + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ filter: 'not host.ip:10.101.149.26' }) + .expect(200); + expect(body.total).to.eql(2); + expect(body.endpoints.length).to.eql(2); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + }); + + it('endpoints api should return page based on filters and paging passed.', async () => { + const notIncludedIp = '10.101.149.26'; + const { body } = await supertest + .post('/api/endpoint/endpoints') + .set('kbn-xsrf', 'xxx') + .send({ + paging_properties: [ + { + page_size: 10, + }, + { + page_index: 0, + }, + ], + filter: `not host.ip:${notIncludedIp}`, + }) + .expect(200); + expect(body.total).to.eql(2); + const resultIps: string[] = [].concat( + ...body.endpoints.map((metadata: Record) => metadata.host.ip) + ); + expect(resultIps).to.eql(['10.192.213.130', '10.70.28.129', '10.46.229.234']); + expect(resultIps).not.include.eql(notIncludedIp); + expect(body.endpoints.length).to.eql(2); + expect(body.request_page_size).to.eql(10); + expect(body.request_page_index).to.eql(0); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/endpoint/index.ts b/x-pack/test/api_integration/apis/endpoint/index.ts new file mode 100644 index 0000000000000..238c63640386a --- /dev/null +++ b/x-pack/test/api_integration/apis/endpoint/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function endpointAPIIntegrationTests({ loadTestFile }: FtrProviderContext) { + describe('Endpoint plugin', function() { + this.tags(['endpoint']); + loadTestFile(require.resolve('./resolver')); + loadTestFile(require.resolve('./endpoints')); + loadTestFile(require.resolve('./alerts')); + }); +} diff --git a/x-pack/test/api_integration/apis/endpoint/resolver.ts b/x-pack/test/api_integration/apis/endpoint/resolver.ts new file mode 100644 index 0000000000000..96d16e0d76e40 --- /dev/null +++ b/x-pack/test/api_integration/apis/endpoint/resolver.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const commonHeaders = { + Accept: 'application/json', + 'kbn-xsrf': 'some-xsrf-token', +}; + +// eslint-disable-next-line import/no-default-export +export default function resolverAPIIntegrationTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + describe('Resolver api', function() { + it('should respond to hello-world', async function() { + const { body } = await supertest + .get('/api/endpoint/hello-world') + .set(commonHeaders) + .expect('Content-Type', /application\/json/) + .expect(200); + + expect(body).to.eql({ hello: 'world' }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index f613473dd87fb..34b2c5e324187 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -114,6 +114,7 @@ export default function({ getService }: FtrProviderContext) { 'maps', 'uptime', 'siem', + 'endpoint', ].sort() ); }); diff --git a/x-pack/test/api_integration/apis/index.js b/x-pack/test/api_integration/apis/index.js index 0e96a04343b64..5af941dde525f 100644 --- a/x-pack/test/api_integration/apis/index.js +++ b/x-pack/test/api_integration/apis/index.js @@ -27,6 +27,7 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./siem')); loadTestFile(require.resolve('./short_urls')); loadTestFile(require.resolve('./lens')); + loadTestFile(require.resolve('./endpoint')); loadTestFile(require.resolve('./ml')); }); } diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index d4c8a3e68c50e..7b1984222404b 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -37,6 +37,7 @@ export default function({ getService }: FtrProviderContext) { uptime: ['all', 'read'], apm: ['all', 'read'], siem: ['all', 'read'], + endpoint: ['all', 'read'], }, global: ['all', 'read'], space: ['all', 'read'], diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.js index 4794a7bee5dfb..6e35a6f31d79b 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.js @@ -25,6 +25,7 @@ export async function getApiIntegrationConfig({ readConfigFile }) { ...xPackFunctionalTestsConfig.get('kbnTestServer.serverArgs'), '--xpack.security.session.idleTimeout=3600000', // 1 hour '--optimize.enabled=false', + '--xpack.endpoint.enabled=true', ], }, esTestCluster: { diff --git a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts new file mode 100644 index 0000000000000..bda336e73c4f8 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['common']); + const spacesService = getService('spaces'); + const testSubjects = getService('testSubjects'); + const appsMenu = getService('appsMenu'); + + describe('spaces', () => { + describe('space with no features disabled', () => { + before(async () => { + await spacesService.create({ + id: 'custom_space', + name: 'custom_space', + disabledFeatures: [], + }); + }); + + after(async () => { + await spacesService.delete('custom_space'); + }); + + it('shows endpoint navlink', async () => { + await pageObjects.common.navigateToApp('home', { + basePath: '/s/custom_space', + }); + const navLinks = (await appsMenu.readLinks()).map(link => link.text); + expect(navLinks).to.contain('EEndpoint'); + }); + + it(`endpoint app shows 'Hello World'`, async () => { + await pageObjects.common.navigateToApp('endpoint', { + basePath: '/s/custom_space', + }); + await testSubjects.existOrFail('welcomeTitle'); + }); + + it(`endpoint management shows 'Manage Endpoints'`, async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management', { + basePath: '/s/custom_space', + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }); + await testSubjects.existOrFail('managementViewTitle'); + }); + }); + + describe('space with endpoint disabled', () => { + before(async () => { + await spacesService.create({ + id: 'custom_space', + name: 'custom_space', + disabledFeatures: ['endpoint'], + }); + }); + + after(async () => { + await spacesService.delete('custom_space'); + }); + + it(`doesn't show endpoint navlink`, async () => { + await pageObjects.common.navigateToApp('home', { + basePath: '/s/custom_space', + }); + const navLinks = (await appsMenu.readLinks()).map(link => link.text); + expect(navLinks).not.to.contain('EEndpoint'); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/endpoint/feature_controls/index.ts b/x-pack/test/functional/apps/endpoint/feature_controls/index.ts new file mode 100644 index 0000000000000..5f7e611fd966c --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/feature_controls/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function() { + this.tags('skipFirefox'); + loadTestFile(require.resolve('./endpoint_spaces')); + }); +} diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts new file mode 100644 index 0000000000000..0ea9344a67aba --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('endpoint', function() { + this.tags('ciGroup7'); + + loadTestFile(require.resolve('./feature_controls')); + loadTestFile(require.resolve('./landing_page')); + loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./policy_list')); + }); +} diff --git a/x-pack/test/functional/apps/endpoint/landing_page.ts b/x-pack/test/functional/apps/endpoint/landing_page.ts new file mode 100644 index 0000000000000..65af91feae407 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/landing_page.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'endpoint']); + + describe('Endpoint landing page', function() { + this.tags('ciGroup7'); + before(async () => { + await pageObjects.common.navigateToApp('endpoint'); + }); + + it('Loads the endpoint app', async () => { + const welcomeEndpointMessage = await pageObjects.endpoint.welcomeEndpointTitle(); + expect(welcomeEndpointMessage).to.be('Hello World'); + }); + }); +}; diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/management.ts new file mode 100644 index 0000000000000..bac87f34ceb82 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/management.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'endpoint']); + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + + describe('Endpoint Management List', function() { + this.tags('ciGroup7'); + before(async () => { + await esArchiver.load('endpoint/endpoints/api_feature'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + }); + + it('finds title', async () => { + const title = await testSubjects.getVisibleText('managementViewTitle'); + expect(title).to.equal('Hosts'); + }); + + it('displays table data', async () => { + const data = await pageObjects.endpoint.getManagementTableData(); + [ + 'Hostnamecadmann-4.example.com', + 'PolicyPolicy Name', + 'Policy StatusPolicy Status', + 'Alerts0', + 'Operating Systemwindows 10.0', + 'IP Address10.192.213.130, 10.70.28.129', + 'Sensor Versionversion', + 'Last Activexxxx', + ].forEach((cellValue, index) => { + expect(data[1][index]).to.equal(cellValue); + }); + }); + + after(async () => { + await esArchiver.unload('endpoint/endpoints/api_feature'); + }); + }); +}; diff --git a/x-pack/test/functional/apps/endpoint/policy_list.ts b/x-pack/test/functional/apps/endpoint/policy_list.ts new file mode 100644 index 0000000000000..1fe2492bed5a0 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/policy_list.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['common', 'endpoint']); + const testSubjects = getService('testSubjects'); + + describe('Endpoint Policy List', function() { + this.tags(['ciGroup7']); + before(async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/policy'); + }); + + it('loads the Policy List Page', async () => { + await testSubjects.existOrFail('policyListPage'); + }); + it('displays page title', async () => { + const policyTitle = await testSubjects.getVisibleText('policyViewTitle'); + expect(policyTitle).to.equal('Policies'); + }); + it('shows policy count total', async () => { + const policyTotal = await testSubjects.getVisibleText('policyTotalCount'); + expect(policyTotal).to.equal('0 Policies'); + }); + it('includes policy list table', async () => { + await testSubjects.existOrFail('policyTable'); + }); + it('has correct table headers', async () => { + const allHeaderCells = await pageObjects.endpoint.tableHeaderVisibleText('policyTable'); + expect(allHeaderCells).to.eql([ + 'Policy Name', + 'Total', + 'Pending', + 'Failed', + 'Created By', + 'Created', + 'Last Updated By', + 'Last Updated', + ]); + }); + }); +} diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 8c5b1a2b9ce3d..a29753b05b8e9 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -56,6 +56,7 @@ export default async function({ readConfigFile }) { resolve(__dirname, './apps/cross_cluster_replication'), resolve(__dirname, './apps/remote_clusters'), resolve(__dirname, './apps/transform'), + resolve(__dirname, './apps/endpoint'), // This license_management file must be last because it is destructive. resolve(__dirname, './apps/license_management'), ], @@ -86,6 +87,7 @@ export default async function({ readConfigFile }) { '--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"', '--telemetry.banner=false', '--timelion.ui.enabled=true', + '--xpack.endpoint.enabled=true', ], }, uiSettings: { @@ -193,6 +195,9 @@ export default async function({ readConfigFile }) { pathname: '/app/kibana/', hash: '/management/elasticsearch/transform', }, + endpoint: { + pathname: '/app/endpoint', + }, }, // choose where esArchiver should load archives from diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/data.json.gz b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/data.json.gz new file mode 100644 index 0000000000000..8d3c9fe5fd233 Binary files /dev/null and b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json new file mode 100644 index 0000000000000..725a58af99325 --- /dev/null +++ b/x-pack/test/functional/es_archives/endpoint/alerts/api_feature/mappings.json @@ -0,0 +1,5265 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "my-index", + "mappings": { + "_meta": { + "version": "1.5.0-dev" + }, + "date_detection": false, + "dynamic": "strict", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "doc_values": false, + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file_classification": { + "properties": { + "captured_file": { + "type": "boolean" + }, + "entry_modified": { + "type": "double" + }, + "is_signature_trusted": { + "type": "boolean" + }, + "macro_details": { + "properties": { + "code_page": { + "type": "long" + }, + "errors": { + "properties": { + "count": { + "type": "long" + }, + "error_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "file_extension": { + "type": "long" + }, + "macro_collection_hashes": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project_file_hashes": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stream_data": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_code_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "quarantine_result": { + "properties": { + "alert_correlation_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "quarantine_path": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "temp_file_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_blacklisted": { + "type": "boolean" + }, + "yara_hits": { + "properties": { + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "matched_data": { + "ignore_above": 1024, + "type": "keyword" + }, + "rule_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "modules": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "compile_time": { + "type": "date" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapped_size": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe_exports": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ordinal": { + "type": "long" + } + }, + "type": "nested" + }, + "pe_imports": { + "properties": { + "dll_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_names": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "argv_list": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_percent": { + "type": "double" + }, + "cwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "defense_evasions": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "delta_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "evasion_subtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "evasion_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_sections": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "thread": { + "properties": { + "thread_id": { + "type": "long" + }, + "thread_start_address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "total_memory_size": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "file_hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "gid": { + "type": "long" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handle": { + "properties": { + "handle_id": { + "type": "long" + }, + "handle_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "handle_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "has_unbacked_execute_memory": { + "type": "boolean" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash_matched_module": { + "type": "boolean" + }, + "is_endpoint": { + "type": "boolean" + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory_percent": { + "type": "double" + }, + "memory_region": { + "properties": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram": { + "properties": { + "histogram_array": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_flavor": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_resolution": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "length": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_tag": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" + } + }, + "type": "nested" + }, + "modules": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "compile_time": { + "type": "date" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapped_size": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe_exports": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ordinal": { + "type": "long" + } + }, + "type": "nested" + }, + "pe_imports": { + "properties": { + "dll_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_names": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "num_threads": { + "type": "long" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe_info": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "compile_time": { + "type": "long" + }, + "entry_point_address": { + "type": "long" + }, + "is_dll": { + "type": "boolean" + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe_exports": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ordinal": { + "type": "long" + } + }, + "type": "nested" + }, + "pe_imports": { + "properties": { + "dll_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_names": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "resources": { + "properties": { + "resource_data": { + "properties": { + "entropy": { + "type": "double" + }, + "size": { + "type": "long" + } + } + }, + "resource_id": { + "type": "long" + }, + "resource_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sections": { + "properties": { + "entropy": { + "type": "double" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_size": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_info": { + "properties": { + "code_page": { + "type": "long" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "language": { + "type": "long" + }, + "value_string": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "pgid": { + "type": "long" + }, + "phys_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "services": { + "ignore_above": 1024, + "type": "keyword" + }, + "session_id": { + "type": "long" + }, + "short_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "threads": { + "properties": { + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "start": { + "type": "date" + }, + "uptime": { + "type": "long" + } + }, + "type": "nested" + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty_device_major_number": { + "type": "integer" + }, + "tty_device_minor_number": { + "type": "integer" + }, + "tty_device_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "type": "long" + }, + "unbacked_execute_byte_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_execute_region_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "unique_pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "unique_ppid": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "argv_list": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_percent": { + "type": "double" + }, + "cwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "defense_evasions": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "delta_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "evasion_subtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "evasion_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_sections": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "thread": { + "properties": { + "thread_id": { + "type": "long" + }, + "thread_start_address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "total_memory_size": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "env_variables": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "file_hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "gid": { + "type": "long" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handle": { + "properties": { + "handle_id": { + "type": "long" + }, + "handle_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "handle_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "has_unbacked_execute_memory": { + "type": "boolean" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash_matched_module": { + "type": "boolean" + }, + "is_endpoint": { + "type": "boolean" + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory_percent": { + "type": "double" + }, + "memory_region": { + "properties": { + "allocation_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocation_protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram": { + "properties": { + "histogram_array": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_flavor": { + "ignore_above": 1024, + "type": "keyword" + }, + "histogram_resolution": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "length": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "permission": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_base": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_tag": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_on_disk": { + "type": "boolean" + } + }, + "type": "nested" + }, + "modules": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "compile_time": { + "type": "date" + }, + "hash": { + "properties": { + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "mapped_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapped_size": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe_exports": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ordinal": { + "type": "long" + } + }, + "type": "nested" + }, + "pe_imports": { + "properties": { + "dll_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_names": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "num_threads": { + "type": "long" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe_info": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "authenticode": { + "properties": { + "cert_signer": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cert_timestamp": { + "properties": { + "issuer_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp_string": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "more_info_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "program_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "publisher_link": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "compile_time": { + "type": "long" + }, + "entry_point_address": { + "type": "long" + }, + "is_dll": { + "type": "boolean" + }, + "malware_classification": { + "properties": { + "compressed_malware_features": { + "properties": { + "data_buffer": { + "ignore_above": 1024, + "type": "keyword" + }, + "decompressed_size": { + "type": "integer" + }, + "encoding": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "identifier": { + "ignore_above": 1024, + "type": "keyword" + }, + "prevention_threshold": { + "type": "double" + }, + "score": { + "type": "double" + }, + "threshold": { + "type": "double" + }, + "upx_packed": { + "type": "boolean" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe_exports": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ordinal": { + "type": "long" + } + }, + "type": "nested" + }, + "pe_imports": { + "properties": { + "dll_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "import_names": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "resources": { + "properties": { + "resource_data": { + "properties": { + "entropy": { + "type": "double" + }, + "size": { + "type": "long" + } + } + }, + "resource_id": { + "type": "long" + }, + "resource_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource_type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sections": { + "properties": { + "entropy": { + "type": "double" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "raw_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_size": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_info": { + "properties": { + "code_page": { + "type": "long" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "language": { + "type": "long" + }, + "value_string": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "pgid": { + "type": "long" + }, + "phys_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "services": { + "ignore_above": 1024, + "type": "keyword" + }, + "session_id": { + "type": "long" + }, + "short_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_signer": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "threads": { + "properties": { + "entrypoint": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "start": { + "type": "date" + }, + "uptime": { + "type": "long" + } + }, + "type": "nested" + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tty_device_major_number": { + "type": "integer" + }, + "tty_device_minor_number": { + "type": "integer" + }, + "tty_device_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "type": "long" + }, + "unbacked_execute_byte_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "unbacked_execute_region_count": { + "ignore_above": 1024, + "type": "keyword" + }, + "unique_pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "unique_ppid": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt_memory_bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "thread": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "thread": { + "properties": { + "call_stack": { + "properties": { + "instruction_pointer": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_section": { + "properties": { + "memory_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_size": { + "ignore_above": 1024, + "type": "keyword" + }, + "protection": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "module_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "rva": { + "ignore_above": 1024, + "type": "keyword" + }, + "symbol_info": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "service_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "start": { + "type": "date" + }, + "start_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_address_module": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "token": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "impersonation_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level": { + "type": "long" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "is_appcontainer": { + "type": "boolean" + }, + "privileges": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "sid": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "refresh_interval": "1s" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json new file mode 100644 index 0000000000000..b481d56df4d52 --- /dev/null +++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/data.json @@ -0,0 +1,364 @@ +{ + "type": "doc", + "value": { + "id": "3KVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579881969541, + "agent": { + "id": "963b081e-60d1-482c-befd-a5815fa8290f", + "version": "6.6.1" + }, + "endpoint": { + "policy": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "event": { + "created": "2020-01-24T16:06:09.541Z" + }, + "host": { + "architecture": "x86", + "hostname": "cadmann-4.example.com", + "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", + "ip": [ + "10.192.213.130", + "10.70.28.129" + ], + "mac": [ + "a9-71-6a-cc-93-85", + "f7-31-84-d3-21-68", + "2-95-12-39-ca-71" + ], + "os": { + "full": "Windows 10", + "name": "windows 10.0", + "version": "10.0" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "3aVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579881969541, + "agent": { + "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", + "version": "6.0.0" + }, + "endpoint": { + "policy": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "event": { + "created": "2020-01-24T16:06:09.541Z" + }, + "host": { + "architecture": "x86_64", + "hostname": "thurlow-9.example.com", + "id": "2f735e3d-be14-483b-9822-bad06e9045ca", + "ip": [ + "10.46.229.234" + ], + "mac": [ + "30-8c-45-55-69-b8", + "e5-36-7e-8f-a3-84", + "39-a1-37-20-18-74" + ], + "os": { + "full": "Windows Server 2016", + "name": "windows 10.0", + "version": "10.0" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "3qVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579881969541, + "agent": { + "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", + "version": "6.8.0" + }, + "endpoint": { + "policy": { + "id": "00000000-0000-0000-0000-000000000000" + } + }, + "event": { + "created": "2020-01-24T16:06:09.541Z" + }, + "host": { + "hostname": "rezzani-7.example.com", + "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", + "ip": [ + "10.101.149.26", + "10.12.85.216" + ], + "mac": [ + "e2-6d-f9-0-46-2e" + ], + "os": { + "full": "Windows 10", + "name": "windows 10.0", + "version": "10.0" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "36VN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579878369541, + "agent": { + "id": "963b081e-60d1-482c-befd-a5815fa8290f", + "version": "6.6.1" + }, + "endpoint": { + "policy": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "event": { + "created": "2020-01-24T15:06:09.541Z" + }, + "host": { + "architecture": "x86", + "hostname": "cadmann-4.example.com", + "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", + "ip": [ + "10.192.213.130", + "10.70.28.129" + ], + "mac": [ + "a9-71-6a-cc-93-85", + "f7-31-84-d3-21-68", + "2-95-12-39-ca-71" + ], + "os": { + "full": "Windows Server 2016", + "name": "windows 10.0", + "version": "10.0" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "4KVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579878369541, + "agent": { + "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", + "version": "6.0.0" + }, + "endpoint": { + "policy": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "event": { + "created": "2020-01-24T15:06:09.541Z" + }, + "host": { + "hostname": "thurlow-9.example.com", + "id": "2f735e3d-be14-483b-9822-bad06e9045ca", + "ip": [ + "10.46.229.234" + ], + "mac": [ + "30-8c-45-55-69-b8", + "e5-36-7e-8f-a3-84", + "39-a1-37-20-18-74" + ], + "os": { + "full": "Windows Server 2012", + "name": "windows 6.2", + "version": "6.2" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "4aVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579878369541, + "agent": { + "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", + "version": "6.8.0" + }, + "endpoint": { + "policy": { + "id": "00000000-0000-0000-0000-000000000000" + } + }, + "event": { + "created": "2020-01-24T15:06:09.541Z" + }, + "host": { + "architecture": "x86", + "hostname": "rezzani-7.example.com", + "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", + "ip": [ + "10.101.149.26", + "10.12.85.216" + ], + "mac": [ + "e2-6d-f9-0-46-2e" + ], + "os": { + "full": "Windows Server 2012", + "name": "windows 6.2", + "version": "6.2" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "4qVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579874769541, + "agent": { + "id": "963b081e-60d1-482c-befd-a5815fa8290f", + "version": "6.6.1" + }, + "endpoint": { + "policy": { + "id": "00000000-0000-0000-0000-000000000000" + } + }, + "event": { + "created": "2020-01-24T14:06:09.541Z" + }, + "host": { + "hostname": "cadmann-4.example.com", + "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", + "ip": [ + "10.192.213.130", + "10.70.28.129" + ], + "mac": [ + "a9-71-6a-cc-93-85", + "f7-31-84-d3-21-68", + "2-95-12-39-ca-71" + ], + "os": { + "full": "Windows Server 2012R2", + "name": "windows 6.3", + "version": "6.3" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "46VN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579874769541, + "agent": { + "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", + "version": "6.0.0" + }, + "endpoint": { + "policy": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" + } + }, + "event": { + "created": "2020-01-24T14:06:09.541Z" + }, + "host": { + "hostname": "thurlow-9.example.com", + "id": "2f735e3d-be14-483b-9822-bad06e9045ca", + "ip": [ + "10.46.229.234" + ], + "mac": [ + "30-8c-45-55-69-b8", + "e5-36-7e-8f-a3-84", + "39-a1-37-20-18-74" + ], + "os": { + "full": "Windows Server 2012R2", + "name": "windows 6.3", + "version": "6.3" + } + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "5KVN2G8BYQH1gtPUuYk7", + "index": "endpoint-agent", + "source": { + "@timestamp": 1579874769541, + "agent": { + "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", + "version": "6.8.0" + }, + "endpoint": { + "policy": { + "id": "00000000-0000-0000-0000-000000000000" + } + }, + "event": { + "created": "2020-01-24T14:06:09.541Z" + }, + "host": { + "architecture": "x86", + "hostname": "rezzani-7.example.com", + "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", + "ip": [ + "10.101.149.26", + "10.12.85.216" + ], + "mac": [ + "e2-6d-f9-0-46-2e" + ], + "os": { + "full": "Windows Server 2012", + "name": "windows 6.2", + "version": "6.2" + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json new file mode 100644 index 0000000000000..11766c12b8fff --- /dev/null +++ b/x-pack/test/functional/es_archives/endpoint/endpoints/api_feature/mappings.json @@ -0,0 +1,147 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "endpoint-agent", + "mappings": { + "properties": { + "@timestamp": { + "type": "long" + }, + "agent": { + "properties": { + "id": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "version": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "endpoint": { + "properties": { + "policy": { + "properties": { + "id": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + } + } + }, + "event": { + "properties": { + "created": { + "type": "date" + } + } + }, + "host": { + "properties": { + "architecture": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "hostname": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "id": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "ip": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "mac": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "os": { + "properties": { + "full": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "version": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + } + } + } + } + }, + "settings": { + "index": { + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts new file mode 100644 index 0000000000000..54f537dd0e8c3 --- /dev/null +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export function EndpointPageProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const table = getService('table'); + + return { + /** + * Finds the Table with the given `selector` (test subject) and returns + * back an array containing the table's header column text + * + * @param selector + * @returns Promise + */ + async tableHeaderVisibleText(selector: string) { + const $ = await (await testSubjects.find('policyTable')).parseDomContent(); + return $('thead tr th') + .toArray() + .map(th => + $(th) + .text() + .replace(/ /g, '') + .trim() + ); + }, + + async welcomeEndpointTitle() { + return await testSubjects.getVisibleText('welcomeTitle'); + }, + + async getManagementTableData() { + return await table.getDataFromTestSubj('managementListTable'); + }, + }; +} diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 7d503c18a7ffd..9479f88085222 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -45,6 +45,7 @@ import { LensPageProvider } from './lens_page'; import { InfraMetricExplorerProvider } from './infra_metric_explorer'; import { RoleMappingsPageProvider } from './role_mappings_page'; import { SpaceSelectorPageProvider } from './space_selector_page'; +import { EndpointPageProvider } from './endpoint_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones @@ -77,4 +78,5 @@ export const pageObjects = { copySavedObjectsToSpace: CopySavedObjectsToSpacePageProvider, lens: LensPageProvider, roleMappings: RoleMappingsPageProvider, + endpoint: EndpointPageProvider, }; diff --git a/x-pack/test/plugin_functional/config.ts b/x-pack/test/plugin_functional/config.ts new file mode 100644 index 0000000000000..6c3c496da71f6 --- /dev/null +++ b/x-pack/test/plugin_functional/config.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { resolve } from 'path'; +import fs from 'fs'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +// the default export of config files must be a config provider +// that returns an object with the projects config values + +/* eslint-disable import/no-default-export */ +export default async function({ readConfigFile }: FtrConfigProviderContext) { + const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); + + // Find all folders in ./plugins since we treat all them as plugin folder + const allFiles = fs.readdirSync(resolve(__dirname, 'plugins')); + const plugins = allFiles.filter(file => + fs.statSync(resolve(__dirname, 'plugins', file)).isDirectory() + ); + + return { + // list paths to the files that contain your plugins tests + testFiles: [resolve(__dirname, './test_suites/resolver')], + + services, + pageObjects, + + servers: xpackFunctionalConfig.get('servers'), + + esTestCluster: xpackFunctionalConfig.get('esTestCluster'), + + kbnTestServer: { + ...xpackFunctionalConfig.get('kbnTestServer'), + serverArgs: [ + ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), + ...plugins.map(pluginDir => `--plugin-path=${resolve(__dirname, 'plugins', pluginDir)}`), + // Required to load new platform plugins via `--plugin-path` flag. + '--env.name=development', + '--xpack.endpoint.enabled=true', + ], + }, + uiSettings: xpackFunctionalConfig.get('uiSettings'), + // the apps section defines the urls that + // `PageObjects.common.navigateTo(appKey)` will use. + // Merge urls for your plugin with the urls defined in + // Kibana's config in order to use this helper + apps: { + ...xpackFunctionalConfig.get('apps'), + resolverTest: { + pathname: '/app/resolver_test', + }, + }, + + // choose where esArchiver should load archives from + esArchiver: { + directory: resolve(__dirname, 'es_archives'), + }, + + // choose where screenshots should be saved + screenshots: { + directory: resolve(__dirname, 'screenshots'), + }, + + junit: { + reportName: 'Chrome X-Pack UI Plugin Functional Tests', + }, + }; +} diff --git a/x-pack/test/plugin_functional/ftr_provider_context.d.ts b/x-pack/test/plugin_functional/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..271f313d4bda9 --- /dev/null +++ b/x-pack/test/plugin_functional/ftr_provider_context.d.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; +import { services } from './services'; +import { pageObjects } from './page_objects'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/plugin_functional/page_objects.ts b/x-pack/test/plugin_functional/page_objects.ts new file mode 100644 index 0000000000000..a216b0f2cd47a --- /dev/null +++ b/x-pack/test/plugin_functional/page_objects.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { pageObjects } from '../functional/page_objects'; diff --git a/x-pack/test/plugin_functional/plugins/resolver_test/kibana.json b/x-pack/test/plugin_functional/plugins/resolver_test/kibana.json new file mode 100644 index 0000000000000..c715a0aaa3b20 --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/resolver_test/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "resolver_test", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "resolver_test"], + "requiredPlugins": ["embeddable"], + "server": false, + "ui": true +} diff --git a/x-pack/test/plugin_functional/plugins/resolver_test/public/applications/resolver_test/index.tsx b/x-pack/test/plugin_functional/plugins/resolver_test/public/applications/resolver_test/index.tsx new file mode 100644 index 0000000000000..498aff62d40d4 --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/resolver_test/public/applications/resolver_test/index.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters } from 'kibana/public'; +import { I18nProvider } from '@kbn/i18n/react'; +import { IEmbeddable } from 'src/plugins/embeddable/public'; +import { useEffect } from 'react'; +import styled from 'styled-components'; + +/** + * Render the Resolver Test app. Returns a cleanup function. + */ +export function renderApp( + { element }: AppMountParameters, + embeddable: Promise +) { + /** + * The application DOM node should take all available space. + */ + element.style.display = 'flex'; + element.style.flexGrow = '1'; + + ReactDOM.render( + + + , + element + ); + + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +} + +const AppRoot = styled( + React.memo( + ({ + embeddable: embeddablePromise, + className, + }: { + /** + * A promise which resolves to the Resolver embeddable. + */ + embeddable: Promise; + /** + * A `className` string provided by `styled` + */ + className?: string; + }) => { + /** + * This state holds the reference to the embeddable, once resolved. + */ + const [embeddable, setEmbeddable] = React.useState(undefined); + /** + * This state holds the reference to the DOM node that will contain the embeddable. + */ + const [renderTarget, setRenderTarget] = React.useState(null); + + /** + * Keep component state with the Resolver embeddable. + * + * If the reference to the embeddablePromise changes, we ignore the stale promise. + */ + useEffect(() => { + /** + * A promise rejection function that will prevent a stale embeddable promise from being resolved + * as the current eembeddable. + * + * If the embeddablePromise itself changes before the old one is resolved, we cancel and restart this effect. + */ + let cleanUp; + + const cleanupPromise = new Promise((_resolve, reject) => { + cleanUp = reject; + }); + + /** + * Either set the embeddable in state, or cancel and restart this process. + */ + Promise.race([cleanupPromise, embeddablePromise]).then(value => { + setEmbeddable(value); + }); + + /** + * If `embeddablePromise` is changed, the cleanup function is run. + */ + return cleanUp; + }, [embeddablePromise]); + + /** + * Render the eembeddable into the DOM node. + */ + useEffect(() => { + if (embeddable && renderTarget) { + embeddable.render(renderTarget); + /** + * If the embeddable or DOM node changes then destroy the old embeddable. + */ + return () => { + embeddable.destroy(); + }; + } + }, [embeddable, renderTarget]); + + return ( +
+ ); + } + ) +)` + /** + * Take all available space. + */ + display: flex; + flex-grow: 1; +`; diff --git a/x-pack/test/plugin_functional/plugins/resolver_test/public/index.ts b/x-pack/test/plugin_functional/plugins/resolver_test/public/index.ts new file mode 100644 index 0000000000000..c5f3c0e19138f --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/resolver_test/public/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializer } from 'kibana/public'; +import { ResolverTestPlugin } from './plugin'; + +export const plugin: PluginInitializer = () => new ResolverTestPlugin(); diff --git a/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts b/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts new file mode 100644 index 0000000000000..045cc56238ac9 --- /dev/null +++ b/x-pack/test/plugin_functional/plugins/resolver_test/public/plugin.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { IEmbeddable, IEmbeddableStart } from '../../../../../../src/plugins/embeddable/public'; + +export type ResolverTestPluginSetup = void; +export type ResolverTestPluginStart = void; +export interface ResolverTestPluginSetupDependencies {} // eslint-disable-line @typescript-eslint/no-empty-interface +export interface ResolverTestPluginStartDependencies { + embeddable: IEmbeddableStart; +} + +export class ResolverTestPlugin + implements + Plugin< + ResolverTestPluginSetup, + ResolverTestPluginStart, + ResolverTestPluginSetupDependencies, + ResolverTestPluginStartDependencies + > { + public setup(core: CoreSetup) { + core.application.register({ + id: 'resolver_test', + title: i18n.translate('xpack.resolver_test.pluginTitle', { + defaultMessage: 'Resolver Test', + }), + mount: async (_context, params) => { + let resolveEmbeddable: ( + value: IEmbeddable | undefined | PromiseLike | undefined + ) => void; + + const promise = new Promise(resolve => { + resolveEmbeddable = resolve; + }); + + (async () => { + const [, { embeddable }] = await core.getStartServices(); + const factory = embeddable.getEmbeddableFactory('resolver'); + resolveEmbeddable!(factory.create({ id: 'test basic render' })); + })(); + + const { renderApp } = await import('./applications/resolver_test'); + /** + * Pass a promise which resolves to the Resolver embeddable. + */ + return renderApp(params, promise); + }, + }); + } + + public start() {} +} diff --git a/x-pack/test/plugin_functional/services.ts b/x-pack/test/plugin_functional/services.ts new file mode 100644 index 0000000000000..5c807720b2867 --- /dev/null +++ b/x-pack/test/plugin_functional/services.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { services } from '../functional/services'; diff --git a/x-pack/test/plugin_functional/test_suites/resolver/index.ts b/x-pack/test/plugin_functional/test_suites/resolver/index.ts new file mode 100644 index 0000000000000..a0735f216e309 --- /dev/null +++ b/x-pack/test/plugin_functional/test_suites/resolver/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['common']); + const testSubjects = getService('testSubjects'); + + describe('Resolver embeddable test app', function() { + this.tags('ciGroup7'); + + beforeEach(async function() { + await pageObjects.common.navigateToApp('resolverTest'); + }); + + it('renders a container div for the embeddable', async function() { + await testSubjects.existOrFail('resolverEmbeddableContainer'); + }); + it('renders resolver', async function() { + await testSubjects.existOrFail('resolverEmbeddable'); + }); + }); +} diff --git a/yarn.lock b/yarn.lock index 398b99fbd4671..f05f76825475b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5712,6 +5712,16 @@ "@types/prop-types" "*" "@types/react" "*" +"@types/react-redux@^7.1.0": + version "7.1.5" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.5.tgz#c7a528d538969250347aa53c52241051cf886bd3" + integrity sha512-ZoNGQMDxh5ENY7PzU7MVonxDzS1l/EWiy8nUhDqxFqUZn4ovboCyvk4Djf68x6COb7vhGTKjyjxHxtFdAA5sUA== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-redux@^7.1.7": version "7.1.7" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a" @@ -25988,6 +25998,11 @@ redux-actions@^2.6.5: reduce-reducers "^0.4.3" to-camel-case "^1.0.0" +redux-devtools-extension@^2.13.8: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + redux-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/redux-observable/-/redux-observable-1.2.0.tgz#ff51b6c6be2598e9b5e89fc36639186bb0e669c7"