Skip to content

Commit

Permalink
[8.x] [Security Solution][Notes] - fix incorrect get_notes api for do…
Browse files Browse the repository at this point in the history
…cumentIds and savedObjectIds query parameters and adding api integration tests (elastic#196225) (elastic#196394)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Security Solution][Notes] - fix incorrect get_notes api for
documentIds and savedObjectIds query parameters and adding api
integration tests
(elastic#196225)](elastic#196225)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Philippe
Oberti","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-15T17:09:22Z","message":"[Security
Solution][Notes] - fix incorrect get_notes api for documentIds and
savedObjectIds query parameters and adding api integration tests
(elastic#196225)","sha":"07642611899034fd4d9ab8362b6303405871c055","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["backport","release_note:skip","v9.0.0","Team:Threat
Hunting:Investigations","v8.16.0"],"title":"[Security Solution][Notes] -
fix incorrect get_notes api for documentIds and savedObjectIds query
parameters and adding api integration
tests","number":196225,"url":"https://github.com/elastic/kibana/pull/196225","mergeCommit":{"message":"[Security
Solution][Notes] - fix incorrect get_notes api for documentIds and
savedObjectIds query parameters and adding api integration tests
(elastic#196225)","sha":"07642611899034fd4d9ab8362b6303405871c055"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196225","number":196225,"mergeCommit":{"message":"[Security
Solution][Notes] - fix incorrect get_notes api for documentIds and
savedObjectIds query parameters and adding api integration tests
(elastic#196225)","sha":"07642611899034fd4d9ab8362b6303405871c055"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Philippe Oberti <[email protected]>
  • Loading branch information
kibanamachine and PhilippeOberti authored Oct 15, 2024
1 parent f0ec68a commit c54e6a9
Show file tree
Hide file tree
Showing 3 changed files with 440 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type { IKibanaResponse } from '@kbn/core-http-server';
import { transformError } from '@kbn/securitysolution-es-utils';
import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { buildRouteValidationWithZod } from '@kbn/zod-helpers';
import type { SavedObjectsFindOptions } from '@kbn/core-saved-objects-api-server';
import { nodeBuilder } from '@kbn/es-query';
import { timelineSavedObjectType } from '../../saved_object_mappings';
import type { SecuritySolutionPluginRouter } from '../../../../types';
import { MAX_UNASSOCIATED_NOTES, NOTE_URL } from '../../../../../common/constants';
Expand Down Expand Up @@ -43,78 +45,90 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter) => {
uiSettings: { client: uiSettingsClient },
} = await frameworkRequest.context.core;
const maxUnassociatedNotes = await uiSettingsClient.get<number>(MAX_UNASSOCIATED_NOTES);

// if documentIds is provided, we will search for all the notes associated with the documentIds
const documentIds = queryParams.documentIds ?? null;
const savedObjectIds = queryParams.savedObjectIds ?? null;
if (documentIds != null) {
// search for multiple document ids (like retrieving all the notes for all the alerts within a table)
if (Array.isArray(documentIds)) {
const docIdSearchString = documentIds?.join(' | ');
const options = {
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
search: docIdSearchString,
filter: nodeBuilder.or(
documentIds.map((documentId: string) =>
nodeBuilder.is(`${noteSavedObjectType}.attributes.eventId`, documentId)
)
),
page: 1,
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
} else {
const options = {
type: noteSavedObjectType,
search: documentIds,
page: 1,
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
return response.ok({ body: res ?? {} });
}
} else if (savedObjectIds != null) {

// searching for all the notes associated with a specific document id
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
filter: nodeBuilder.is(`${noteSavedObjectType}.attributes.eventId`, documentIds),
page: 1,
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
return response.ok({ body: res ?? {} });
}

// if savedObjectIds is provided, we will search for all the notes associated with the savedObjectIds
const savedObjectIds = queryParams.savedObjectIds ?? null;
if (savedObjectIds != null) {
// search for multiple saved object ids
if (Array.isArray(savedObjectIds)) {
const soIdSearchString = savedObjectIds?.join(' | ');
const options = {
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
hasReference: {
hasReference: savedObjectIds.map((savedObjectId: string) => ({
type: timelineSavedObjectType,
id: soIdSearchString,
},
id: savedObjectId,
})),
page: 1,
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
} else {
const options = {
type: noteSavedObjectType,
hasReference: {
type: timelineSavedObjectType,
id: savedObjectIds,
},
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
}
} else {
const perPage = queryParams?.perPage ? parseInt(queryParams.perPage, 10) : 10;
const page = queryParams?.page ? parseInt(queryParams.page, 10) : 1;
const search = queryParams?.search ?? undefined;
const sortField = queryParams?.sortField ?? undefined;
const sortOrder = (queryParams?.sortOrder as SortOrder) ?? undefined;
const filter = queryParams?.filter;
const options = {

// searching for all the notes associated with a specific saved object id
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
perPage,
page,
search,
sortField,
sortOrder,
filter,
hasReference: {
type: timelineSavedObjectType,
id: savedObjectIds,
},
perPage: maxUnassociatedNotes,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
}

// retrieving all the notes following the query parameters
const perPage = queryParams?.perPage ? parseInt(queryParams.perPage, 10) : 10;
const page = queryParams?.page ? parseInt(queryParams.page, 10) : 1;
const search = queryParams?.search ?? undefined;
const sortField = queryParams?.sortField ?? undefined;
const sortOrder = (queryParams?.sortOrder as SortOrder) ?? undefined;
const filter = queryParams?.filter;
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
perPage,
page,
search,
sortField,
sortOrder,
filter,
};
const res = await getAllSavedNote(frameworkRequest, options);
const body: GetNotesResponse = res ?? {};
return response.ok({ body });
} catch (err) {
const error = transformError(err);
const siemResponse = buildSiemResponse(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

import type SuperTest from 'supertest';
import { v4 as uuidv4 } from 'uuid';
import { TimelineTypeEnum } from '@kbn/security-solution-plugin/common/api/timeline';
import { BareNote, TimelineTypeEnum } from '@kbn/security-solution-plugin/common/api/timeline';
import { NOTE_URL } from '@kbn/security-solution-plugin/common/constants';
import type { Client } from '@elastic/elasticsearch';
import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { noteSavedObjectType } from '@kbn/security-solution-plugin/server/lib/timeline/saved_object_mappings';

export const createBasicTimeline = async (supertest: SuperTest.Agent, titleToSaved: string) =>
await supertest
Expand Down Expand Up @@ -38,3 +42,37 @@ export const createBasicTimelineTemplate = async (
timelineType: TimelineTypeEnum.template,
},
});

export const deleteAllNotes = async (es: Client): Promise<void> => {
await es.deleteByQuery({
index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX,
q: `type:${noteSavedObjectType}`,
wait_for_completion: true,
refresh: true,
body: {},
});
};

export const createNote = async (
supertest: SuperTest.Agent,
note: {
documentId?: string;
savedObjectId?: string;
user?: string;
text: string;
}
) =>
await supertest
.patch(NOTE_URL)
.set('kbn-xsrf', 'true')
.send({
note: {
eventId: note.documentId || '',
timelineId: note.savedObjectId || '',
created: Date.now(),
createdBy: note.user || 'elastic',
updated: Date.now(),
updatedBy: note.user || 'elastic',
note: note.text,
} as BareNote,
});
Loading

0 comments on commit c54e6a9

Please sign in to comment.