[Osquery] Fix support for disabled security (#110547) (#111250)

Co-authored-by: Patryk Kopyciński <patryk.kopycinski@elastic.co>
This commit is contained in:
Kibana Machine 2021-09-06 05:34:36 -04:00 committed by GitHub
parent de660291d4
commit effc87beff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 280 additions and 115 deletions

View file

@ -349,7 +349,7 @@
"react-moment-proptypes": "^1.7.0",
"react-monaco-editor": "^0.41.2",
"react-popper-tooltip": "^2.10.1",
"react-query": "^3.21.0",
"react-query": "^3.21.1",
"react-redux": "^7.2.0",
"react-resizable": "^1.7.5",
"react-resize-detector": "^4.2.0",

View file

@ -6,28 +6,16 @@
*/
import { useQuery } from 'react-query';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../common/lib/kibana';
import { useErrorToast } from '../common/hooks/use_error_toast';
export const useActionResultsPrivileges = () => {
const { http } = useKibana().services;
const setErrorToast = useErrorToast();
return useQuery(
['actionResultsPrivileges'],
() => http.get('/internal/osquery/privileges_check'),
{
keepPreviousData: true,
select: (response) => response?.has_all_requested ?? false,
onSuccess: () => setErrorToast(),
onError: (error: Error) =>
setErrorToast(error, {
title: i18n.translate('xpack.osquery.action_results_privileges.fetchError', {
defaultMessage: 'Error while fetching action results privileges',
}),
}),
}
);
};

View file

@ -27,8 +27,6 @@ export interface DynamicPagePathValues {
[key: string]: string;
}
export const BASE_PATH = '/app/fleet';
// If routing paths are changed here, please also check to see if
// `pagePathGetters()`, below, needs any modifications
export const PAGE_ROUTING_PATHS = {

View file

@ -5,6 +5,8 @@
* 2.0.
*/
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiTabs, EuiTab } from '@elastic/eui';
@ -14,10 +16,17 @@ import { Container, Nav, Wrapper } from './layouts';
import { OsqueryAppRoutes } from '../routes';
import { useRouterNavigate } from '../common/lib/kibana';
import { ManageIntegrationLink } from './manage_integration_link';
import { useOsqueryIntegrationStatus } from '../common/hooks';
import { OsqueryAppEmptyState } from './empty_state';
const OsqueryAppComponent = () => {
const location = useLocation();
const section = useMemo(() => location.pathname.split('/')[1] ?? 'overview', [location.pathname]);
const { data: osqueryIntegration, isFetched } = useOsqueryIntegrationStatus();
if (isFetched && osqueryIntegration.install_status !== 'installed') {
return <OsqueryAppEmptyState />;
}
return (
<Container>

View file

@ -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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton } from '@elastic/eui';
import { KibanaPageTemplate } from '../../../../../src/plugins/kibana_react/public';
import { INTEGRATIONS_PLUGIN_ID } from '../../../fleet/common';
import { pagePathGetters } from '../../../fleet/public';
import { isModifiedEvent, isLeftClickEvent, useKibana } from '../common/lib/kibana';
import { OsqueryIcon } from './osquery_icon';
import { useBreadcrumbs } from '../common/hooks/use_breadcrumbs';
import { OSQUERY_INTEGRATION_NAME } from '../../common';
const OsqueryAppEmptyStateComponent = () => {
useBreadcrumbs('base');
const {
application: { getUrlForApp, navigateToApp },
} = useKibana().services;
const integrationHref = useMemo(() => {
return getUrlForApp(INTEGRATIONS_PLUGIN_ID, {
path: pagePathGetters.integration_details_overview({
pkgkey: OSQUERY_INTEGRATION_NAME,
})[1],
});
}, [getUrlForApp]);
const integrationClick = useCallback(
(event) => {
if (!isModifiedEvent(event) && isLeftClickEvent(event)) {
event.preventDefault();
return navigateToApp(INTEGRATIONS_PLUGIN_ID, {
path: pagePathGetters.integration_details_overview({
pkgkey: OSQUERY_INTEGRATION_NAME,
})[1],
});
}
},
[navigateToApp]
);
const pageHeader = useMemo(
() => ({
iconType: OsqueryIcon,
pageTitle: (
<FormattedMessage
id="xpack.osquery.emptyState.pageTitle"
defaultMessage="Add Osquery Manager"
/>
),
description: (
<FormattedMessage
id="xpack.osquery.emptyState.pageDescription"
defaultMessage="Add this integration to run and schedule queries for Elastic Agent."
/>
),
rightSideItems: [
// eslint-disable-next-line @elastic/eui/href-or-on-click
<EuiButton
key="button"
fill
href={integrationHref}
onClick={integrationClick}
iconType="plusInCircleFilled"
>
<FormattedMessage
id="xpack.osquery.emptyState.addOsqueryManagerButton"
defaultMessage="Add Osquery Manager"
/>
</EuiButton>,
],
}),
[integrationClick, integrationHref]
);
return <KibanaPageTemplate isEmptyState={true} pageHeader={pageHeader} />;
};
export const OsqueryAppEmptyState = React.memo(OsqueryAppEmptyStateComponent);

View file

@ -24,11 +24,9 @@ const ManageIntegrationLinkComponent = () => {
const integrationHref = useMemo(() => {
if (osqueryIntegration) {
return getUrlForApp(INTEGRATIONS_PLUGIN_ID, {
path:
'#' +
pagePathGetters.integration_details_policies({
pkgkey: `${osqueryIntegration.name}-${osqueryIntegration.version}`,
})[1],
path: pagePathGetters.integration_details_policies({
pkgkey: `${osqueryIntegration.name}-${osqueryIntegration.version}`,
})[1],
});
}
}, [getUrlForApp, osqueryIntegration]);
@ -39,11 +37,9 @@ const ManageIntegrationLinkComponent = () => {
event.preventDefault();
if (osqueryIntegration) {
return navigateToApp(INTEGRATIONS_PLUGIN_ID, {
path:
'#' +
pagePathGetters.integration_details_policies({
pkgkey: `${osqueryIntegration.name}-${osqueryIntegration.version}`,
})[1],
path: pagePathGetters.integration_details_policies({
pkgkey: `${osqueryIntegration.name}-${osqueryIntegration.version}`,
})[1],
});
}
}

View file

@ -114,7 +114,7 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
),
});
const { setFieldValue, submit } = form;
const { setFieldValue, submit, isSubmitting } = form;
const actionId = useMemo(() => data?.actions[0].action_id, [data?.actions]);
const agentIds = useMemo(() => data?.actions[0].agents, [data?.actions]);
@ -185,7 +185,10 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiButton disabled={!agentSelected || !queryValueProvided} onClick={submit}>
<EuiButton
disabled={!agentSelected || !queryValueProvided || isSubmitting}
onClick={submit}
>
<FormattedMessage
id="xpack.osquery.liveQueryForm.form.submitButtonLabel"
defaultMessage="Submit"
@ -196,13 +199,14 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
</>
),
[
agentSelected,
permissions.writeSavedQueries,
handleShowSaveQueryFlout,
queryComponentProps,
singleAgentMode,
permissions.writeSavedQueries,
agentSelected,
queryValueProvided,
resultsStatus,
singleAgentMode,
handleShowSaveQueryFlout,
isSubmitting,
submit,
]
);

View file

@ -51,7 +51,7 @@ const AddPackQueryFormComponent = ({ handleSubmit }) => {
},
},
});
const { submit } = form;
const { submit, isSubmitting } = form;
const createSavedQueryMutation = useMutation(
(payload) => http.post(`/internal/osquery/saved_query`, { body: JSON.stringify(payload) }),
@ -108,7 +108,7 @@ const AddPackQueryFormComponent = ({ handleSubmit }) => {
<EuiSpacer />
<CommonUseField path="interval" />
<EuiSpacer />
<EuiButton fill onClick={submit}>
<EuiButton isLoading={isSubmitting} fill onClick={submit}>
{'Add query'}
</EuiButton>
</Form>

View file

@ -40,7 +40,7 @@ const PackFormComponent = ({ data, handleSubmit }) => {
},
},
});
const { submit } = form;
const { submit, isSubmitting } = form;
return (
<Form form={form}>
@ -50,7 +50,7 @@ const PackFormComponent = ({ data, handleSubmit }) => {
<EuiSpacer />
<CommonUseField path="queries" component={PackQueriesField} />
<EuiSpacer />
<EuiButton fill onClick={submit}>
<EuiButton isLoading={isSubmitting} fill onClick={submit}>
{'Save pack'}
</EuiButton>
</Form>

View file

@ -38,6 +38,7 @@ const EditSavedQueryFormComponent: React.FC<EditSavedQueryFormProps> = ({
defaultValue,
handleSubmit,
});
const { submit, isSubmitting } = form;
return (
<Form form={form}>
@ -58,12 +59,12 @@ const EditSavedQueryFormComponent: React.FC<EditSavedQueryFormProps> = ({
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
// isLoading={isLoading}
isLoading={isSubmitting}
color="primary"
fill
size="m"
iconType="save"
onClick={form.submit}
onClick={submit}
>
<FormattedMessage
id="xpack.osquery.editSavedQuery.form.updateQueryButtonLabel"

View file

@ -36,6 +36,7 @@ const NewSavedQueryFormComponent: React.FC<NewSavedQueryFormProps> = ({
defaultValue,
handleSubmit,
});
const { submit, isSubmitting } = form;
return (
<Form form={form}>
@ -54,12 +55,12 @@ const NewSavedQueryFormComponent: React.FC<NewSavedQueryFormProps> = ({
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
// isLoading={isLoading}
isLoading={isSubmitting}
color="primary"
fill
size="m"
iconType="save"
onClick={form.submit}
onClick={submit}
>
<FormattedMessage
id="xpack.osquery.addSavedQuery.form.saveQueryButtonLabel"

View file

@ -25,7 +25,10 @@ const EditScheduledQueryGroupPageComponent = () => {
const { data } = useScheduledQueryGroup({ scheduledQueryGroupId });
useBreadcrumbs('scheduled_query_group_edit', { scheduledQueryGroupName: data?.name ?? '' });
useBreadcrumbs('scheduled_query_group_edit', {
scheduledQueryGroupId: data?.id ?? '',
scheduledQueryGroupName: data?.name ?? '',
});
const LeftColumn = useMemo(
() => (

View file

@ -6,3 +6,4 @@
*/
export const SAVED_QUERIES_ID = 'savedQueryList';
export const SAVED_QUERY_ID = 'savedQuery';

View file

@ -42,6 +42,7 @@ const SavedQueryFlyoutComponent: React.FC<AddQueryFlyoutProps> = ({ defaultValue
defaultValue,
handleSubmit,
});
const { submit, isSubmitting } = form;
return (
<EuiPortal>
@ -72,7 +73,7 @@ const SavedQueryFlyoutComponent: React.FC<AddQueryFlyoutProps> = ({ defaultValue
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={form.submit} fill>
<EuiButton isLoading={isSubmitting} onClick={submit} fill>
<FormattedMessage
id="xpack.osquery.scheduledQueryGroup.queryFlyoutForm.saveButtonLabel"
defaultMessage="Save"

View file

@ -12,8 +12,7 @@ import { useKibana } from '../common/lib/kibana';
import { savedQuerySavedObjectType } from '../../common/types';
import { pagePathGetters } from '../common/page_paths';
import { useErrorToast } from '../common/hooks/use_error_toast';
export const SAVED_QUERY_ID = 'savedQuery';
import { SAVED_QUERY_ID } from './constants';
interface UseSavedQueryProps {
savedQueryId: string;

View file

@ -12,7 +12,7 @@ import { useKibana } from '../common/lib/kibana';
import { savedQuerySavedObjectType } from '../../common/types';
import { PLUGIN_ID } from '../../common';
import { pagePathGetters } from '../common/page_paths';
import { SAVED_QUERIES_ID } from './constants';
import { SAVED_QUERIES_ID, SAVED_QUERY_ID } from './constants';
import { useErrorToast } from '../common/hooks/use_error_toast';
interface UseUpdateSavedQueryProps {
@ -62,6 +62,7 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps)
},
onSuccess: (payload) => {
queryClient.invalidateQueries(SAVED_QUERIES_ID);
queryClient.invalidateQueries([SAVED_QUERY_ID, { savedQueryId }]);
navigateToApp(PLUGIN_ID, { path: pagePathGetters.saved_queries() });
toasts.addSuccess(
i18n.translate('xpack.osquery.editSavedQuery.successToastMessageText', {

View file

@ -88,7 +88,7 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
`scheduled_query_groups/${editMode ? defaultValue?.id : ''}`
);
const { isLoading, mutateAsync } = useMutation(
const { mutateAsync } = useMutation(
(payload: Record<string, unknown>) =>
editMode && defaultValue?.id
? http.put(packagePolicyRouteService.getUpdatePath(defaultValue.id), {
@ -248,7 +248,7 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
),
});
const { setFieldValue, submit } = form;
const { setFieldValue, submit, isSubmitting } = form;
const policyIdEuiFieldProps = useMemo(
() => ({ isDisabled: !!defaultValue, options: agentPolicyOptions }),
@ -368,7 +368,7 @@ const ScheduledQueryGroupFormComponent: React.FC<ScheduledQueryGroupFormProps> =
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
isLoading={isLoading}
isLoading={isSubmitting}
color="primary"
fill
size="m"

View file

@ -342,7 +342,8 @@ const getEcsFieldValidator = (editForm: boolean) => (
)
)(args);
if (fieldRequiredError && (!!(!editForm && args.formData.value?.field.length) || editForm)) {
// @ts-expect-error update types
if (fieldRequiredError && ((!editForm && args.formData['value.field'].length) || editForm)) {
return fieldRequiredError;
}

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { isEmpty } from 'lodash';
import {
EuiCallOut,
EuiFlyout,
@ -66,7 +67,7 @@ const QueryFlyoutComponent: React.FC<QueryFlyoutProps> = ({
if (isValid && ecsFieldValue) {
onSave({
...payload,
ecs_mapping: ecsFieldValue,
...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }),
});
onClose();
}
@ -81,7 +82,7 @@ const QueryFlyoutComponent: React.FC<QueryFlyoutProps> = ({
[integrationPackageVersion]
);
const { submit, setFieldValue, reset } = form;
const { submit, setFieldValue, reset, isSubmitting } = form;
const [{ query }] = useFormData({
form,
@ -245,7 +246,7 @@ const QueryFlyoutComponent: React.FC<QueryFlyoutProps> = ({
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={submit} fill>
<EuiButton isLoading={isSubmitting} onClick={submit} fill>
<FormattedMessage
id="xpack.osquery.scheduledQueryGroup.queryFlyoutForm.saveButtonLabel"
defaultMessage="Save"

View file

@ -21,14 +21,14 @@ import {
EuiPanel,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n/react';
import moment from 'moment-timezone';
import {
TypedLensByValueInput,
PersistedIndexPatternLayer,
PieVisualizationState,
} from '../../../lens/public';
import { FilterStateStore } from '../../../../../src/plugins/data/common';
import { FilterStateStore, IndexPattern } from '../../../../../src/plugins/data/common';
import { useKibana, isModifiedEvent, isLeftClickEvent } from '../common/lib/kibana';
import { OsqueryManagerPackagePolicyInputStream } from '../../common/types';
import { ScheduledQueryErrorsTable } from './scheduled_query_errors_table';
@ -391,16 +391,21 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
toggleErrors,
expanded,
}) => {
const data = useKibana().services.data;
const [logsIndexPattern, setLogsIndexPattern] = useState<IndexPattern | undefined>(undefined);
const { data: lastResultsData, isFetched } = useScheduledQueryGroupQueryLastResults({
actionId,
agentIds,
interval,
logsIndexPattern,
});
const { data: errorsData, isFetched: errorsFetched } = useScheduledQueryGroupQueryErrors({
actionId,
agentIds,
interval,
logsIndexPattern,
});
const handleErrorsToggle = useCallback(() => toggleErrors({ queryId, interval }), [
@ -409,20 +414,41 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
toggleErrors,
]);
useEffect(() => {
const fetchLogsIndexPattern = async () => {
const indexPattern = await data.indexPatterns.find('logs-*');
setLogsIndexPattern(indexPattern[0]);
};
fetchLogsIndexPattern();
}, [data.indexPatterns]);
if (!isFetched || !errorsFetched) {
return <EuiLoadingSpinner />;
}
if (!lastResultsData) {
if (!lastResultsData && !errorsData?.total) {
return <>{'-'}</>;
}
return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={4}>
{lastResultsData.first_event_ingested_time?.value ? (
<EuiToolTip content={lastResultsData.first_event_ingested_time?.value}>
<>{moment(lastResultsData.first_event_ingested_time?.value).fromNow()}</>
{lastResultsData?.['@timestamp'] ? (
<EuiToolTip
content={
<>
<FormattedDate
value={lastResultsData['@timestamp']}
year="numeric"
month="short"
day="2-digit"
/>{' '}
<FormattedTime value={lastResultsData['@timestamp']} timeZoneName="short" />
</>
}
>
<FormattedRelative value={lastResultsData['@timestamp']} />
</EuiToolTip>
) : (
'-'
@ -432,10 +458,17 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.doc_count ?? 0}
{lastResultsData?.docCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>{'Documents'}</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.documentLabelText"
defaultMessage="{count, plural, one {Document} other {Documents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: lastResultsData?.docCount as number }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
@ -443,10 +476,17 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.unique_agents?.value ?? 0}
{lastResultsData?.uniqueAgentsCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>{'Agents'}</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.agentsLabelText"
defaultMessage="{count, plural, one {Agent} other {Agents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: agentIds?.length }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
@ -458,7 +498,15 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>{'Errors'}</EuiFlexItem>
<EuiFlexItem grow={false}>
{' '}
<FormattedMessage
id="xpack.osquery.queriesStatusTable.errorsLabelText"
defaultMessage="{count, plural, one {Error} other {Errors}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: errorsData?.total as number }}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon

View file

@ -6,7 +6,7 @@
*/
import { useQuery } from 'react-query';
import { SortDirection } from '../../../../../src/plugins/data/common';
import { IndexPattern, SortDirection } from '../../../../../src/plugins/data/common';
import { useKibana } from '../common/lib/kibana';
@ -14,6 +14,7 @@ interface UseScheduledQueryGroupQueryErrorsProps {
actionId: string;
agentIds?: string[];
interval: number;
logsIndexPattern?: IndexPattern;
skip?: boolean;
}
@ -21,6 +22,7 @@ export const useScheduledQueryGroupQueryErrors = ({
actionId,
agentIds,
interval,
logsIndexPattern,
skip = false,
}: UseScheduledQueryGroupQueryErrorsProps) => {
const data = useKibana().services.data;
@ -28,9 +30,8 @@ export const useScheduledQueryGroupQueryErrors = ({
return useQuery(
['scheduledQueryErrors', { actionId, interval }],
async () => {
const indexPattern = await data.indexPatterns.find('logs-*');
const searchSource = await data.search.searchSource.create({
index: indexPattern[0],
index: logsIndexPattern,
fields: ['*'],
sort: [
{
@ -80,7 +81,7 @@ export const useScheduledQueryGroupQueryErrors = ({
},
{
keepPreviousData: true,
enabled: !!(!skip && actionId && interval && agentIds?.length),
enabled: !!(!skip && actionId && interval && agentIds?.length && logsIndexPattern),
select: (response) => response.rawResponse.hits ?? [],
refetchOnReconnect: false,
refetchOnWindowFocus: false,

View file

@ -6,13 +6,14 @@
*/
import { useQuery } from 'react-query';
import { IndexPattern } from '../../../../../src/plugins/data/common';
import { useKibana } from '../common/lib/kibana';
interface UseScheduledQueryGroupQueryLastResultsProps {
actionId: string;
agentIds?: string[];
interval: number;
logsIndexPattern?: IndexPattern;
skip?: boolean;
}
@ -20,6 +21,7 @@ export const useScheduledQueryGroupQueryLastResults = ({
actionId,
agentIds,
interval,
logsIndexPattern,
skip = false,
}: UseScheduledQueryGroupQueryLastResultsProps) => {
const data = useKibana().services.data;
@ -27,23 +29,9 @@ export const useScheduledQueryGroupQueryLastResults = ({
return useQuery(
['scheduledQueryLastResults', { actionId }],
async () => {
const indexPattern = await data.indexPatterns.find('logs-*');
const searchSource = await data.search.searchSource.create({
index: indexPattern[0],
size: 0,
aggs: {
runs: {
terms: {
field: 'response_id',
order: { first_event_ingested_time: 'desc' },
size: 1,
},
aggs: {
first_event_ingested_time: { min: { field: '@timestamp' } },
unique_agents: { cardinality: { field: 'agent.id' } },
},
},
},
const lastResultsSearchSource = await data.search.searchSource.create({
index: logsIndexPattern,
size: 1,
query: {
// @ts-expect-error update types
bool: {
@ -59,26 +47,62 @@ export const useScheduledQueryGroupQueryLastResults = ({
action_id: actionId,
},
},
{
range: {
'@timestamp': {
gte: `now-${interval * 2}s`,
lte: 'now',
},
},
},
],
},
},
});
return searchSource.fetch$().toPromise();
const lastResultsResponse = await lastResultsSearchSource.fetch$().toPromise();
const responseId = lastResultsResponse.rawResponse?.hits?.hits[0]?._source?.response_id;
if (responseId) {
const aggsSearchSource = await data.search.searchSource.create({
index: logsIndexPattern,
size: 0,
aggs: {
unique_agents: { cardinality: { field: 'agent.id' } },
},
query: {
// @ts-expect-error update types
bool: {
should: agentIds?.map((agentId) => ({
match_phrase: {
'agent.id': agentId,
},
})),
minimum_should_match: 1,
filter: [
{
match_phrase: {
action_id: actionId,
},
},
{
match_phrase: {
response_id: responseId,
},
},
],
},
},
});
const aggsResponse = await aggsSearchSource.fetch$().toPromise();
return {
'@timestamp': lastResultsResponse.rawResponse?.hits?.hits[0]?.fields?.['@timestamp'],
// @ts-expect-error update types
uniqueAgentsCount: aggsResponse.rawResponse.aggregations?.unique_agents?.value,
docCount: aggsResponse.rawResponse?.hits?.total,
};
}
return null;
},
{
keepPreviousData: true,
enabled: !!(!skip && actionId && interval && agentIds?.length),
// @ts-expect-error update types
select: (response) => response.rawResponse.aggregations?.runs?.buckets[0] ?? [],
enabled: !!(!skip && actionId && interval && agentIds?.length && logsIndexPattern),
refetchOnReconnect: false,
refetchOnWindowFocus: false,
}

View file

@ -9,7 +9,6 @@ import { OSQUERY_INTEGRATION_NAME, PLUGIN_ID } from '../../../common';
import { IRouter } from '../../../../../../src/core/server';
import { OsqueryAppContext } from '../../lib/osquery_app_context_services';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const privilegesCheckRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => {
router.get(
{
@ -20,23 +19,26 @@ export const privilegesCheckRoute = (router: IRouter, osqueryContext: OsqueryApp
},
},
async (context, request, response) => {
const esClient = context.core.elasticsearch.client.asCurrentUser;
const privileges = (
await esClient.security.hasPrivileges({
body: {
index: [
{
names: [`logs-${OSQUERY_INTEGRATION_NAME}.result*`],
privileges: ['read'],
},
],
if (osqueryContext.security.authz.mode.useRbacForRequest(request)) {
const checkPrivileges = osqueryContext.security.authz.checkPrivilegesDynamicallyWithRequest(
request
);
const { hasAllRequested } = await checkPrivileges({
elasticsearch: {
cluster: [],
index: {
[`logs-${OSQUERY_INTEGRATION_NAME}.result*`]: ['read'],
},
},
})
).body;
});
return response.ok({
body: `${hasAllRequested}`,
});
}
return response.ok({
body: privileges,
body: 'true',
});
}
);

View file

@ -23399,10 +23399,10 @@ react-popper@^2.2.4:
react-fast-compare "^3.0.1"
warning "^4.0.2"
react-query@^3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.21.0.tgz#2e099a7906c38eeeb750e8b9b12121a21fa8d9ef"
integrity sha512-5rY5J8OD9f4EdkytjSsdCO+pqbJWKwSIMETfh/UyxqyjLURHE0IhlB+IPNPrzzu/dzK0rRxi5p0IkcCdSfizDQ==
react-query@^3.21.1:
version "3.21.1"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.21.1.tgz#8fe4df90bf6c6a93e0552ea9baff211d1b28f6e0"
integrity sha512-aKFLfNJc/m21JBXJk7sR9tDUYPjotWA4EHAKvbZ++GgxaY+eI0tqBxXmGBuJo0Pisis1W4pZWlZgoRv9yE8yjA==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"