"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResultsTable = void 0; var _fp = require("lodash/fp"); var _eui = require("@elastic/eui"); var _i18n = require("@kbn/i18n"); var _i18nReact = require("@kbn/i18n-react"); var _react = _interopRequireWildcard(require("react")); var _public = require("@kbn/fleet-plugin/public"); var _add_to_timeline_button = require("../timelines/add_to_timeline_button"); var _use_all_results = require("./use_all_results"); var _search_strategy = require("../../common/search_strategy"); var _kibana = require("../common/lib/kibana"); var _use_action_results = require("../action_results/use_action_results"); var _translations = require("./translations"); var _pack_queries_status_table = require("../packs/pack_queries_status_table"); var _use_action_privileges = require("../action_results/use_action_privileges"); var _common = require("../../common"); var _add_to_cases = require("../cases/add_to_cases"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const DataContext = /*#__PURE__*/(0, _react.createContext)([]); const euiDataGridCss = { ':not(.euiDataGrid--fullScreen)': { '.euiDataGrid__virtualized': { height: '100% !important', maxHeight: '500px' } } }; const euiProgressCss = { marginTop: '-2px' }; const resultsTableContainerCss = { width: '100%', maxWidth: '1200px' }; const ResultsTableComponent = ({ actionId, agentIds, ecsMapping, startDate, endDate, liveQueryActionId, error }) => { var _allResultsData$total; const [isLive, setIsLive] = (0, _react.useState)(true); const { data: hasActionResultsPrivileges } = (0, _use_action_privileges.useActionResultsPrivileges)(); const { // @ts-expect-error update types data: { aggregations } } = (0, _use_action_results.useActionResults)({ actionId, activePage: 0, agentIds, limit: 0, direction: _search_strategy.Direction.asc, sortField: '@timestamp', isLive, skip: !hasActionResultsPrivileges }); const expired = (0, _react.useMemo)(() => !endDate ? false : new Date(endDate) < new Date(), [endDate]); const { application: { getUrlForApp }, appName, timelines } = (0, _kibana.useKibana)().services; const getFleetAppUrl = (0, _react.useCallback)(agentId => getUrlForApp('fleet', { path: _public.pagePathGetters.agent_details({ agentId })[1] }), [getUrlForApp]); const [pagination, setPagination] = (0, _react.useState)({ pageIndex: 0, pageSize: 50 }); const onChangeItemsPerPage = (0, _react.useCallback)(pageSize => setPagination(currentPagination => ({ ...currentPagination, pageSize, pageIndex: 0 })), [setPagination]); const onChangePage = (0, _react.useCallback)(pageIndex => setPagination(currentPagination => ({ ...currentPagination, pageIndex })), [setPagination]); const [sortingColumns, setSortingColumns] = (0, _react.useState)([{ id: 'agent.name', direction: _search_strategy.Direction.asc }]); const [columns, setColumns] = (0, _react.useState)([]); const { data: allResultsData, isLoading } = (0, _use_all_results.useAllResults)({ actionId, activePage: pagination.pageIndex, limit: pagination.pageSize, isLive, sort: sortingColumns.map(sortedColumn => ({ field: sortedColumn.id, direction: sortedColumn.direction })), skip: !hasActionResultsPrivileges }); const [visibleColumns, setVisibleColumns] = (0, _react.useState)([]); const columnVisibility = (0, _react.useMemo)(() => ({ visibleColumns, setVisibleColumns }), [visibleColumns, setVisibleColumns]); const ecsMappingColumns = (0, _react.useMemo)(() => (0, _fp.keys)(ecsMapping || {}), [ecsMapping]); const renderCellValue = (0, _react.useMemo)(() => // eslint-disable-next-line react/display-name ({ rowIndex, columnId }) => { var _data; // eslint-disable-next-line react-hooks/rules-of-hooks const data = (0, _react.useContext)(DataContext); // @ts-expect-error update types const value = (_data = data[rowIndex % pagination.pageSize]) === null || _data === void 0 ? void 0 : _data.fields[columnId]; if (columnId === 'agent.name') { var _data2; // @ts-expect-error update types const agentIdValue = (_data2 = data[rowIndex % pagination.pageSize]) === null || _data2 === void 0 ? void 0 : _data2.fields['agent.id']; return /*#__PURE__*/_react.default.createElement(_eui.EuiLink, { href: getFleetAppUrl(agentIdValue) }, value); } if (ecsMappingColumns.includes(columnId)) { var _data3; const ecsFieldValue = (0, _fp.get)(columnId, (_data3 = data[rowIndex % pagination.pageSize]) === null || _data3 === void 0 ? void 0 : _data3._source); if ((0, _fp.isArray)(ecsFieldValue) || (0, _fp.isObject)(ecsFieldValue)) { try { return JSON.stringify(ecsFieldValue, null, 2); // eslint-disable-next-line no-empty } catch (e) {} } return ecsFieldValue !== null && ecsFieldValue !== void 0 ? ecsFieldValue : '-'; } return !(0, _fp.isEmpty)(value) ? value : '-'; }, [ecsMappingColumns, getFleetAppUrl, pagination.pageSize]); const tableSorting = (0, _react.useMemo)(() => ({ columns: sortingColumns, onSort: setSortingColumns }), [sortingColumns]); const tablePagination = (0, _react.useMemo)(() => ({ ...pagination, pageSizeOptions: [10, 50, 100], onChangeItemsPerPage, onChangePage }), [onChangeItemsPerPage, onChangePage, pagination]); const ecsMappingConfig = (0, _react.useMemo)(() => { if (!ecsMapping) return; return (0, _fp.reduce)((acc, [key, value]) => { if (value !== null && value !== void 0 && value.field) { var _acc$value$field; acc[value === null || value === void 0 ? void 0 : value.field] = [...((_acc$value$field = acc[value === null || value === void 0 ? void 0 : value.field]) !== null && _acc$value$field !== void 0 ? _acc$value$field : []), key]; } return acc; }, {}, Object.entries(ecsMapping)); }, [ecsMapping]); const getHeaderDisplay = (0, _react.useCallback)(columnName => { if (ecsMappingConfig && ecsMappingConfig[columnName]) { return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, columnName, ' ', /*#__PURE__*/_react.default.createElement(_eui.EuiIconTip, { size: "s", content: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.osquery.liveQueryResults.table.fieldMappedLabel", defaultMessage: "Field is mapped to" }), `:`, /*#__PURE__*/_react.default.createElement("ul", null, ecsMappingConfig[columnName].map(fieldName => /*#__PURE__*/_react.default.createElement("li", { key: fieldName }, fieldName)))), type: "indexMapping" })); } }, [ecsMappingConfig]); (0, _react.useEffect)(() => { if (!(allResultsData !== null && allResultsData !== void 0 && allResultsData.columns.length)) { return; } const fields = ['agent.name', ...ecsMappingColumns.sort(), ...((allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.columns) || [])]; const newColumns = fields.reduce((acc, fieldName) => { const { data, seen } = acc; if (fieldName === 'agent.name') { if (!seen.has(fieldName)) { data.push({ id: fieldName, displayAsText: _i18n.i18n.translate('xpack.osquery.liveQueryResults.table.agentColumnTitle', { defaultMessage: 'agent' }), defaultSortDirection: _search_strategy.Direction.asc }); seen.add(fieldName); } return acc; } if (ecsMappingColumns.includes(fieldName)) { if (!seen.has(fieldName)) { data.push({ id: fieldName, displayAsText: fieldName, defaultSortDirection: _search_strategy.Direction.asc }); seen.add(fieldName); } return acc; } if (fieldName.startsWith('osquery.')) { const displayAsText = fieldName.split('.')[1]; const hasNumberType = fields.includes(`${fieldName}.number`); if (!seen.has(displayAsText)) { const id = hasNumberType ? fieldName + '.number' : fieldName; data.push({ id, displayAsText, display: getHeaderDisplay(displayAsText), defaultSortDirection: _search_strategy.Direction.asc, ...(hasNumberType ? { schema: 'numeric' } : {}) }); seen.add(displayAsText); } return acc; } return acc; }, { data: [], seen: new Set() }).data; setColumns(currentColumns => !(0, _fp.isEqual)((0, _fp.map)('id', currentColumns), (0, _fp.map)('id', newColumns)) ? newColumns : currentColumns); setVisibleColumns((0, _fp.map)('id', newColumns)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.columns.length, ecsMappingColumns, getHeaderDisplay]); const leadingControlColumns = (0, _react.useMemo)(() => { const data = allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.edges; if (timelines && data) { return [{ id: 'timeline', width: 38, headerCellRender: () => null, rowCellRender: actionProps => { var _data$visibleRowIndex; const { visibleRowIndex } = actionProps; const eventId = (_data$visibleRowIndex = data[visibleRowIndex]) === null || _data$visibleRowIndex === void 0 ? void 0 : _data$visibleRowIndex._id; return /*#__PURE__*/_react.default.createElement(_add_to_timeline_button.AddToTimelineButton, { field: "_id", value: eventId, isIcon: true }); } }]; } return []; }, [allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.edges, timelines]); const toolbarVisibility = (0, _react.useMemo)(() => ({ showDisplaySelector: false, showFullScreenSelector: appName === _common.PLUGIN_NAME, additionalControls: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_pack_queries_status_table.ViewResultsInDiscoverAction, { actionId: actionId, buttonType: _pack_queries_status_table.ViewResultsActionButtonType.button, endDate: endDate, startDate: startDate }), /*#__PURE__*/_react.default.createElement(_pack_queries_status_table.ViewResultsInLensAction, { actionId: actionId, buttonType: _pack_queries_status_table.ViewResultsActionButtonType.button, endDate: endDate, startDate: startDate }), /*#__PURE__*/_react.default.createElement(_add_to_timeline_button.AddToTimelineButton, { field: "action_id", value: actionId }), liveQueryActionId && /*#__PURE__*/_react.default.createElement(_add_to_cases.AddToCaseWrapper, { actionId: liveQueryActionId, queryId: actionId, agentIds: agentIds })) }), [actionId, agentIds, appName, endDate, liveQueryActionId, startDate]); (0, _react.useEffect)(() => setIsLive(() => { if (!(agentIds !== null && agentIds !== void 0 && agentIds.length) || expired || error) return false; return !!(aggregations.totalResponded !== (agentIds === null || agentIds === void 0 ? void 0 : agentIds.length) || (allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.total) !== (aggregations === null || aggregations === void 0 ? void 0 : aggregations.totalRowCount) || allResultsData !== null && allResultsData !== void 0 && allResultsData.total && !(allResultsData !== null && allResultsData !== void 0 && allResultsData.edges.length)); }), [agentIds === null || agentIds === void 0 ? void 0 : agentIds.length, aggregations.totalResponded, aggregations === null || aggregations === void 0 ? void 0 : aggregations.totalRowCount, allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.edges.length, allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.total, error, expired]); if (isLoading) { return /*#__PURE__*/_react.default.createElement(_eui.EuiSkeletonText, { lines: 5 }); } if (!hasActionResultsPrivileges) { return /*#__PURE__*/_react.default.createElement(_eui.EuiCallOut, { title: /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.osquery.liveQuery.permissionDeniedPromptTitle", defaultMessage: "Permission denied" }), color: "danger", iconType: "warning" }, /*#__PURE__*/_react.default.createElement("p", null, /*#__PURE__*/_react.default.createElement(_i18nReact.FormattedMessage, { id: "xpack.osquery.liveQuery.permissionDeniedPromptBody", defaultMessage: "To view query results, ask your administrator to update your user role to have index {read} privileges on the {logs} index." // eslint-disable-next-line react-perf/jsx-no-new-object-as-prop , values: { read: /*#__PURE__*/_react.default.createElement(_eui.EuiCode, null, "read"), logs: /*#__PURE__*/_react.default.createElement(_eui.EuiCode, null, "logs-", _common.OSQUERY_INTEGRATION_NAME, ".result*") } }))); } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isLive && /*#__PURE__*/_react.default.createElement(_eui.EuiProgress, { color: "primary", size: "xs", css: euiProgressCss }), !(allResultsData !== null && allResultsData !== void 0 && allResultsData.edges.length) ? /*#__PURE__*/_react.default.createElement(_eui.EuiPanel, { hasShadow: false, "data-test-subj": 'osqueryResultsPanel' }, /*#__PURE__*/_react.default.createElement(_eui.EuiCallOut, { title: (0, _translations.generateEmptyDataMessage)(aggregations.totalResponded) })) : /*#__PURE__*/_react.default.createElement(DataContext.Provider, { value: allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.edges }, /*#__PURE__*/_react.default.createElement("div", { css: resultsTableContainerCss }, /*#__PURE__*/_react.default.createElement(_eui.EuiDataGrid, { css: euiDataGridCss, "data-test-subj": "osqueryResultsTable", "aria-label": "Osquery results", columns: columns, columnVisibility: columnVisibility, rowCount: (_allResultsData$total = allResultsData === null || allResultsData === void 0 ? void 0 : allResultsData.total) !== null && _allResultsData$total !== void 0 ? _allResultsData$total : 0, renderCellValue: renderCellValue, leadingControlColumns: leadingControlColumns, sorting: tableSorting, pagination: tablePagination, toolbarVisibility: toolbarVisibility })))); }; const ResultsTable = /*#__PURE__*/_react.default.memo(ResultsTableComponent); exports.ResultsTable = ResultsTable;