feat(MISP Node): Rest search operations (#9196)

This commit is contained in:
Michael Kret 2024-04-26 11:12:22 +03:00 committed by GitHub
parent 9b3ac1648f
commit b694e7743e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 270 additions and 1 deletions

View file

@ -7,7 +7,7 @@ import type {
IHttpRequestMethods, IHttpRequestMethods,
IRequestOptions, IRequestOptions,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { NodeApiError, NodeOperationError } from 'n8n-workflow'; import { NodeApiError, NodeOperationError, jsonParse } from 'n8n-workflow';
import type { MispCredentials } from './types'; import type { MispCredentials } from './types';
@ -79,6 +79,57 @@ export async function mispApiRequestAllItems(this: IExecuteFunctions, endpoint:
return responseData; return responseData;
} }
export async function mispApiRestSearch(
this: IExecuteFunctions,
resource: 'attributes' | 'events' | 'objects',
itemIndex: number,
) {
let body: IDataObject = {};
const useJson = this.getNodeParameter('useJson', itemIndex) as boolean;
if (useJson) {
const json = this.getNodeParameter('jsonOutput', itemIndex);
if (typeof json === 'string') {
body = jsonParse(json);
} else {
body = json as IDataObject;
}
} else {
const value = this.getNodeParameter('value', itemIndex) as string;
const additionalFields = this.getNodeParameter('additionalFields', itemIndex);
body.value = value;
if (Object.keys(additionalFields).length) {
if (additionalFields.tags) {
additionalFields.tags = (additionalFields.tags as string)
.split(',')
.map((tag) => tag.trim());
}
Object.assign(body, additionalFields);
}
}
const endpoint = `/${resource}/restSearch`;
const { response } = await mispApiRequest.call(this, 'POST', endpoint, body);
if (response) {
if (resource === 'attributes') {
return response.Attribute;
}
if (resource === 'events') {
return (response as IDataObject[]).map((event) => event.Event);
}
if (resource === 'objects') {
return (response as IDataObject[]).map((obj) => obj.Object);
}
} else {
return [];
}
}
export function throwOnEmptyUpdate( export function throwOnEmptyUpdate(
this: IExecuteFunctions, this: IExecuteFunctions,
resource: string, resource: string,

View file

@ -10,6 +10,7 @@ import type {
import { import {
mispApiRequest, mispApiRequest,
mispApiRequestAllItems, mispApiRequestAllItems,
mispApiRestSearch,
throwOnEmptyUpdate, throwOnEmptyUpdate,
throwOnInvalidUrl, throwOnInvalidUrl,
throwOnMissingSharingGroup, throwOnMissingSharingGroup,
@ -28,6 +29,8 @@ import {
galaxyOperations, galaxyOperations,
noticelistFields, noticelistFields,
noticelistOperations, noticelistOperations,
objectOperations,
objectFields,
organisationFields, organisationFields,
organisationOperations, organisationOperations,
tagFields, tagFields,
@ -91,6 +94,10 @@ export class Misp implements INodeType {
name: 'Noticelist', name: 'Noticelist',
value: 'noticelist', value: 'noticelist',
}, },
{
name: 'Object',
value: 'object',
},
{ {
name: 'Organisation', name: 'Organisation',
value: 'organisation', value: 'organisation',
@ -122,6 +129,8 @@ export class Misp implements INodeType {
...galaxyFields, ...galaxyFields,
...noticelistOperations, ...noticelistOperations,
...noticelistFields, ...noticelistFields,
...objectOperations,
...objectFields,
...organisationOperations, ...organisationOperations,
...organisationFields, ...organisationFields,
...tagOperations, ...tagOperations,
@ -233,6 +242,12 @@ export class Misp implements INodeType {
// ---------------------------------------- // ----------------------------------------
responseData = await mispApiRequestAllItems.call(this, '/attributes'); responseData = await mispApiRequestAllItems.call(this, '/attributes');
} else if (operation === 'search') {
// ----------------------------------------
// attribute: search
// ----------------------------------------
responseData = await mispApiRestSearch.call(this, 'attributes', i);
} else if (operation === 'update') { } else if (operation === 'update') {
// ---------------------------------------- // ----------------------------------------
// attribute: update // attribute: update
@ -300,6 +315,12 @@ export class Misp implements INodeType {
// ---------------------------------------- // ----------------------------------------
responseData = await mispApiRequestAllItems.call(this, '/events'); responseData = await mispApiRequestAllItems.call(this, '/events');
} else if (operation === 'search') {
// ----------------------------------------
// event: search
// ----------------------------------------
responseData = await mispApiRestSearch.call(this, 'events', i);
} else if (operation === 'publish') { } else if (operation === 'publish') {
// ---------------------------------------- // ----------------------------------------
// event: publish // event: publish
@ -500,6 +521,17 @@ export class Misp implements INodeType {
}>; }>;
responseData = responseData.map((entry) => entry.Noticelist); responseData = responseData.map((entry) => entry.Noticelist);
} }
} else if (resource === 'object') {
// **********************************************************************
// object
// **********************************************************************
if (operation === 'search') {
// ----------------------------------------
// attribute: search
// ----------------------------------------
responseData = await mispApiRestSearch.call(this, 'objects', i);
}
} else if (resource === 'organisation') { } else if (resource === 'organisation') {
// ********************************************************************** // **********************************************************************
// organisation // organisation

View file

@ -1,4 +1,15 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { updateDisplayOptions } from '../../../utils/utilities';
import { searchProperties } from './common.descriptions';
const searchDisplayOptions = {
show: {
resource: ['attribute'],
operation: ['search'],
},
};
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
export const attributeOperations: INodeProperties[] = [ export const attributeOperations: INodeProperties[] = [
{ {
@ -32,6 +43,11 @@ export const attributeOperations: INodeProperties[] = [
value: 'getAll', value: 'getAll',
action: 'Get many attributes', action: 'Get many attributes',
}, },
{
name: 'Search',
value: 'search',
action: 'Get a filtered list of attributes',
},
{ {
name: 'Update', name: 'Update',
value: 'update', value: 'update',
@ -226,6 +242,11 @@ export const attributeFields: INodeProperties[] = [
}, },
}, },
// ----------------------------------------
// attribute: search
// ----------------------------------------
...searchDescription,
// ---------------------------------------- // ----------------------------------------
// attribute: update // attribute: update
// ---------------------------------------- // ----------------------------------------

View file

@ -1,4 +1,15 @@
import type { INodeProperties } from 'n8n-workflow'; import type { INodeProperties } from 'n8n-workflow';
import { updateDisplayOptions } from '../../../utils/utilities';
import { searchProperties } from './common.descriptions';
const searchDisplayOptions = {
show: {
resource: ['event'],
operation: ['search'],
},
};
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
export const eventOperations: INodeProperties[] = [ export const eventOperations: INodeProperties[] = [
{ {
@ -37,6 +48,11 @@ export const eventOperations: INodeProperties[] = [
value: 'publish', value: 'publish',
action: 'Publish an event', action: 'Publish an event',
}, },
{
name: 'Search',
value: 'search',
action: 'Get a filtered list of events',
},
{ {
name: 'Unpublish', name: 'Unpublish',
value: 'unpublish', value: 'unpublish',
@ -295,6 +311,11 @@ export const eventFields: INodeProperties[] = [
}, },
}, },
// ----------------------------------------
// event: search
// ----------------------------------------
...searchDescription,
// ---------------------------------------- // ----------------------------------------
// event: update // event: update
// ---------------------------------------- // ----------------------------------------

View file

@ -0,0 +1,41 @@
import type { INodeProperties } from 'n8n-workflow';
import { updateDisplayOptions } from '../../../utils/utilities';
import { searchProperties } from './common.descriptions';
const searchDisplayOptions = {
show: {
resource: ['object'],
operation: ['search'],
},
};
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
export const objectOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: ['object'],
},
},
noDataExpression: true,
options: [
{
name: 'Search',
value: 'search',
action: 'Get a filtered list of objects',
},
],
default: 'search',
},
];
export const objectFields: INodeProperties[] = [
// ----------------------------------------
// event: search
// ----------------------------------------
...searchDescription,
];

View file

@ -0,0 +1,102 @@
import type { INodeProperties } from 'n8n-workflow';
export const searchProperties: INodeProperties[] = [
{
displayName: 'Use JSON to Specify Fields',
name: 'useJson',
type: 'boolean',
default: false,
description: 'Whether to use JSON to specify the fields for the search request',
},
{
displayName: 'JSON',
name: 'jsonOutput',
type: 'json',
description:
'Get more info at {YOUR_BASE_URL_SPECIFIED_IN_CREDENTIALS}/api/openapi#operation/restSearchAttributes',
typeOptions: {
rows: 5,
},
default: '{\n "value": "search value",\n "type": "text"\n}\n',
validateType: 'object',
displayOptions: {
show: {
useJson: [true],
},
},
},
{
displayName: 'Value',
name: 'value',
type: 'string',
required: true,
placeholder: 'e.g. 127.0.0.1',
default: '',
displayOptions: {
show: {
useJson: [false],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
useJson: [false],
},
},
options: [
{
displayName: 'Category',
name: 'category',
type: 'string',
placeholder: 'e.g. Internal reference',
default: '',
},
{
displayName: 'Deleted',
name: 'deleted',
type: 'boolean',
default: false,
},
{
displayName: 'Search All',
name: 'searchall',
type: 'string',
description:
'Search by matching any tag names, event descriptions, attribute values or attribute comments',
default: '',
displayOptions: {
hide: {
'/resource': ['attribute'],
},
},
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
placeholder: 'e.g. tag1,tag2',
hint: 'Comma-separated list of tags',
default: '',
},
{
displayName: 'Type',
name: 'type',
type: 'string',
placeholder: 'e.g. text',
default: '',
},
{
displayName: 'Published',
name: 'published',
type: 'boolean',
default: false,
},
],
},
];

View file

@ -4,6 +4,7 @@ export * from './EventTagDescription';
export * from './FeedDescription'; export * from './FeedDescription';
export * from './GalaxyDescription'; export * from './GalaxyDescription';
export * from './NoticelistDescription'; export * from './NoticelistDescription';
export * from './ObjectDescription';
export * from './OrganisationDescription'; export * from './OrganisationDescription';
export * from './TagDescription'; export * from './TagDescription';
export * from './UserDescription'; export * from './UserDescription';