Skip to content

Commit

Permalink
[Osquery] 7.14 bug squash (#105387)
Browse files Browse the repository at this point in the history
  • Loading branch information
lykkin authored Jul 20, 2021
1 parent 32206b9 commit dd159f1
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ const ActionResultsSummaryComponent: React.FC<ActionResultsSummaryProps> = ({
sortField: '@timestamp',
isLive,
});
if (expired) {
// @ts-expect-error update types
edges.forEach((edge) => {
if (!edge.fields.completed_at) {
edge.fields['error.keyword'] = edge.fields.error = [
i18n.translate('xpack.osquery.liveQueryActionResults.table.expiredErrorText', {
defaultMessage: 'The action request timed out.',
}),
];
}
});
}

const { data: logsResults } = useAllResults({
actionId,
Expand Down
3 changes: 1 addition & 2 deletions x-pack/plugins/osquery/public/agents/use_all_agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ export const useAllAgents = (
const { isLoading: agentsLoading, data: agentData } = useQuery<GetAgentsResponse>(
['agents', osqueryPolicies, searchValue, perPage],
() => {
const policyFragment = osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ');
let kuery = `last_checkin_status: online and (${policyFragment})`;
let kuery = `${osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ')}`;

if (searchValue) {
kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ const SavedQueriesPageComponent = () => {
const { push } = useHistory();
const newQueryLinkProps = useRouterNavigate('saved_queries/new');
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState('updated_at');
const [pageSize, setPageSize] = useState(20);
const [sortField, setSortField] = useState('attributes.updated_at');
const [sortDirection, setSortDirection] = useState('desc');

const { data } = useSavedQueries({ isLive: true });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { isArray } from 'lodash';
import uuid from 'uuid';
import { produce } from 'immer';

import { useMemo } from 'react';
import { useForm } from '../../shared_imports';
import { formSchema } from '../../scheduled_query_groups/queries/schema';
import { createFormSchema } from '../../scheduled_query_groups/queries/schema';
import { ScheduledQueryGroupFormData } from '../../scheduled_query_groups/queries/use_scheduled_query_group_query_form';
import { useSavedQueries } from '../use_saved_queries';

const SAVED_QUERY_FORM_ID = 'savedQueryForm';

Expand All @@ -20,11 +22,29 @@ interface UseSavedQueryFormProps {
handleSubmit: (payload: unknown) => Promise<void>;
}

export const useSavedQueryForm = ({ defaultValue, handleSubmit }: UseSavedQueryFormProps) =>
useForm({
export const useSavedQueryForm = ({ defaultValue, handleSubmit }: UseSavedQueryFormProps) => {
const { data } = useSavedQueries({});
const ids: string[] = useMemo<string[]>(
() => data?.savedObjects.map((obj) => obj.attributes.id) ?? [],
[data]
);
const idSet = useMemo<Set<string>>(() => {
const res = new Set<string>(ids);
// @ts-expect-error update types
if (defaultValue && defaultValue.id) res.delete(defaultValue.id);
return res;
}, [ids, defaultValue]);
const formSchema = useMemo<ReturnType<typeof createFormSchema>>(() => createFormSchema(idSet), [
idSet,
]);
return useForm({
id: SAVED_QUERY_FORM_ID + uuid.v4(),
schema: formSchema,
onSubmit: handleSubmit,
onSubmit: async (formData, isValid) => {
if (isValid) {
return handleSubmit(formData);
}
},
options: {
stripEmptyFields: false,
},
Expand Down Expand Up @@ -62,3 +82,4 @@ export const useSavedQueryForm = ({ defaultValue, handleSubmit }: UseSavedQueryF
};
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ export const useCreateSavedQuery = ({ withRedirect }: UseCreateSavedQueryProps)
throw new Error('CurrentUser is missing');
}

const conflictingEntries = await savedObjects.client.find({
type: savedQuerySavedObjectType,
// @ts-expect-error update types
search: payload.id,
searchFields: ['id'],
});
if (conflictingEntries.savedObjects.length) {
// @ts-expect-error update types
throw new Error(`Saved query with id ${payload.id} already exists.`);
}
return savedObjects.client.create(savedQuerySavedObjectType, {
// @ts-expect-error update types
...payload,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps)
throw new Error('CurrentUser is missing');
}

const conflictingEntries = await savedObjects.client.find({
type: savedQuerySavedObjectType,
// @ts-expect-error update types
search: payload.id,
searchFields: ['id'],
});
if (conflictingEntries.savedObjects.length) {
// @ts-expect-error update types
throw new Error(`Saved query with id ${payload.id} already exists.`);
}

return savedObjects.client.update(savedQuerySavedObjectType, savedQueryId, {
// @ts-expect-error update types
...payload,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
field.value,
]);

const uniqueQueryIds = useMemo<string[]>(
() =>
field.value && field.value[0].streams.length
? field.value[0].streams.reduce((acc, stream) => {
if (stream.vars?.id.value) {
acc.push(stream.vars?.id.value);
}

return acc;
}, [] as string[])
: [],
[field.value]
);

return (
<>
<EuiFlexGroup justifyContent="flexEnd">
Expand Down Expand Up @@ -256,13 +270,15 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({
{<OsqueryPackUploader onChange={handlePackUpload} />}
{showAddQueryFlyout && (
<QueryFlyout
uniqueQueryIds={uniqueQueryIds}
integrationPackageVersion={integrationPackageVersion}
onSave={handleAddQuery}
onClose={handleHideAddFlyout}
/>
)}
{showEditQueryFlyout != null && showEditQueryFlyout >= 0 && (
<QueryFlyout
uniqueQueryIds={uniqueQueryIds}
defaultValue={field.value[0].streams[showEditQueryFlyout]?.vars}
integrationPackageVersion={integrationPackageVersion}
onSave={handleEditQuery}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@ import { SavedQueriesDropdown } from '../../saved_queries/saved_queries_dropdown
const CommonUseField = getUseField({ component: Field });

interface QueryFlyoutProps {
uniqueQueryIds: string[];
defaultValue?: UseScheduledQueryGroupQueryFormProps['defaultValue'] | undefined;
integrationPackageVersion?: string | undefined;
onSave: (payload: OsqueryManagerPackagePolicyConfigRecord) => Promise<void>;
onClose: () => void;
}

const QueryFlyoutComponent: React.FC<QueryFlyoutProps> = ({
uniqueQueryIds,
defaultValue,
integrationPackageVersion,
onSave,
onClose,
}) => {
const [isEditMode] = useState(!!defaultValue);
const { form } = useScheduledQueryGroupQueryForm({
uniqueQueryIds,
defaultValue,
handleSubmit: (payload, isValid) =>
new Promise((resolve) => {
Expand All @@ -65,7 +68,7 @@ const QueryFlyoutComponent: React.FC<QueryFlyoutProps> = ({
}),
});

/* Platform and version fields are supported since osquer_manger@0.3.0 */
/* Platform and version fields are supported since osquery_manager@0.3.0 */
const isFieldSupported = useMemo(
() => (integrationPackageVersion ? satisfies(integrationPackageVersion, '>=0.3.0') : false),
[integrationPackageVersion]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ import { FormattedMessage } from '@kbn/i18n/react';

import { FIELD_TYPES } from '../../shared_imports';

import { idFieldValidations, intervalFieldValidation, queryFieldValidation } from './validations';
import {
createIdFieldValidations,
intervalFieldValidation,
queryFieldValidation,
} from './validations';

export const formSchema = {
export const createFormSchema = (ids: Set<string>) => ({
id: {
type: FIELD_TYPES.TEXT,
label: i18n.translate('xpack.osquery.scheduledQueryGroup.queryFlyoutForm.idFieldLabel', {
defaultMessage: 'ID',
}),
validations: idFieldValidations.map((validator) => ({ validator })),
validations: createIdFieldValidations(ids).map((validator) => ({ validator })),
},
description: {
type: FIELD_TYPES.TEXT,
Expand Down Expand Up @@ -69,4 +73,4 @@ export const formSchema = {
) as unknown) as string,
validations: [],
},
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
* 2.0.
*/

import { isArray } from 'lodash';
import { isArray, xor } from 'lodash';
import uuid from 'uuid';
import { produce } from 'immer';

import { useMemo } from 'react';
import { FormConfig, useForm } from '../../shared_imports';
import { OsqueryManagerPackagePolicyConfigRecord } from '../../../common/types';
import { formSchema } from './schema';
import { createFormSchema } from './schema';

const FORM_ID = 'editQueryFlyoutForm';

export interface UseScheduledQueryGroupQueryFormProps {
uniqueQueryIds: string[];
defaultValue?: OsqueryManagerPackagePolicyConfigRecord | undefined;
handleSubmit: FormConfig<
OsqueryManagerPackagePolicyConfigRecord,
Expand All @@ -32,12 +34,26 @@ export interface ScheduledQueryGroupFormData {
}

export const useScheduledQueryGroupQueryForm = ({
uniqueQueryIds,
defaultValue,
handleSubmit,
}: UseScheduledQueryGroupQueryFormProps) =>
useForm<OsqueryManagerPackagePolicyConfigRecord, ScheduledQueryGroupFormData>({
}: UseScheduledQueryGroupQueryFormProps) => {
const idSet = useMemo<Set<string>>(
() =>
new Set<string>(xor(uniqueQueryIds, defaultValue?.id.value ? [defaultValue.id.value] : [])),
[uniqueQueryIds, defaultValue]
);
const formSchema = useMemo<ReturnType<typeof createFormSchema>>(() => createFormSchema(idSet), [
idSet,
]);

return useForm<OsqueryManagerPackagePolicyConfigRecord, ScheduledQueryGroupFormData>({
id: FORM_ID + uuid.v4(),
onSubmit: handleSubmit,
onSubmit: async (formData, isValid) => {
if (isValid && handleSubmit) {
return handleSubmit(formData, isValid);
}
},
options: {
stripEmptyFields: false,
},
Expand Down Expand Up @@ -75,3 +91,4 @@ export const useScheduledQueryGroupQueryForm = ({
},
schema: formSchema,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,28 @@ const idSchemaValidation: ValidationFunc<any, string, string> = ({ value }) => {
}
};

export const idFieldValidations = [
const createUniqueIdValidation = (ids: Set<string>) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const uniqueIdCheck: ValidationFunc<any, string, string> = ({ value }) => {
if (ids.has(value)) {
return {
message: i18n.translate('xpack.osquery.scheduledQueryGroup.queryFlyoutForm.uniqueIdError', {
defaultMessage: 'ID must be unique',
}),
};
}
};
return uniqueIdCheck;
};

export const createIdFieldValidations = (ids: Set<string>) => [
fieldValidators.emptyField(
i18n.translate('xpack.osquery.scheduledQueryGroup.queryFlyoutForm.emptyIdError', {
defaultMessage: 'ID is required',
})
),
idSchemaValidation,
createUniqueIdValidation(ids),
];

export const intervalFieldValidation: ValidationFunc<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const useScheduledQueryGroup = ({
() => http.get(packagePolicyRouteService.getInfoPath(scheduledQueryGroupId)),
{
keepPreviousData: true,
enabled: !skip,
enabled: !skip || !scheduledQueryGroupId,
select: (response) => response.item,
}
);
Expand Down
23 changes: 7 additions & 16 deletions x-pack/plugins/osquery/server/routes/usage/recorder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { usageMetricSavedObjectType } from '../../../common/types';

import {
CounterValue,
createMetricObjects,
getOrCreateMetricObject,
getRouteMetric,
incrementCount,
RouteString,
Expand Down Expand Up @@ -45,31 +45,22 @@ describe('Usage metric recorder', () => {
get.mockClear();
create.mockClear();
});
it('should seed route metrics objects', async () => {
it('should create metrics that do not exist', async () => {
get.mockRejectedValueOnce('stub value');
create.mockReturnValueOnce('stub value');
const result = await createMetricObjects(savedObjectsClient);
const result = await getOrCreateMetricObject(savedObjectsClient, 'live_query');
checkGetCalls(get.mock.calls);
checkCreateCalls(create.mock.calls);
expect(result).toBe(true);
expect(result).toBe('stub value');
});

it('should handle previously seeded objects properly', async () => {
it('should handle previously created objects properly', async () => {
get.mockReturnValueOnce('stub value');
create.mockRejectedValueOnce('stub value');
const result = await createMetricObjects(savedObjectsClient);
const result = await getOrCreateMetricObject(savedObjectsClient, 'live_query');
checkGetCalls(get.mock.calls);
checkCreateCalls(create.mock.calls, []);
expect(result).toBe(true);
});

it('should report failure to create the metrics object', async () => {
get.mockRejectedValueOnce('stub value');
create.mockRejectedValueOnce('stub value');
const result = await createMetricObjects(savedObjectsClient);
checkGetCalls(get.mock.calls);
checkCreateCalls(create.mock.calls);
expect(result).toBe(false);
expect(result).toBe('stub value');
});
});

Expand Down
Loading

0 comments on commit dd159f1

Please sign in to comment.