From af6577f4c39e8f994f13a1c543da8cd278e4dde5 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Tue, 23 Apr 2024 08:58:15 +0300 Subject: [PATCH 1/3] :zap: search attribute operation --- packages/nodes-base/nodes/Misp/Misp.node.ts | 34 ++++++ .../Misp/descriptions/AttributeDescription.ts | 108 ++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/packages/nodes-base/nodes/Misp/Misp.node.ts b/packages/nodes-base/nodes/Misp/Misp.node.ts index a32b3d9dcf15d..3f1af9b0caf0a 100644 --- a/packages/nodes-base/nodes/Misp/Misp.node.ts +++ b/packages/nodes-base/nodes/Misp/Misp.node.ts @@ -6,6 +6,7 @@ import type { INodeType, INodeTypeDescription, } from 'n8n-workflow'; +import { jsonParse } from 'n8n-workflow'; import { mispApiRequest, @@ -233,6 +234,39 @@ export class Misp implements INodeType { // ---------------------------------------- responseData = await mispApiRequestAllItems.call(this, '/attributes'); + } else if (operation === 'search') { + // ---------------------------------------- + // attribute: search + // ---------------------------------------- + let body: IDataObject = {}; + const useJson = this.getNodeParameter('useJson', i) as boolean; + + if (useJson) { + const json = this.getNodeParameter('jsonOutput', i); + if (typeof json === 'string') { + body = jsonParse(json); + } else { + body = json as IDataObject; + } + } else { + const value = this.getNodeParameter('value', i) as string; + const additionalFields = this.getNodeParameter('additionalFields', i); + + 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 = '/attributes/restSearch'; + const { response } = await mispApiRequest.call(this, 'POST', endpoint, body); + responseData = response?.Attribute || []; } else if (operation === 'update') { // ---------------------------------------- // attribute: update diff --git a/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts b/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts index 7462013d34f10..9d7c6aea916f9 100644 --- a/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts +++ b/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts @@ -1,4 +1,102 @@ import type { INodeProperties } from 'n8n-workflow'; +import { updateDisplayOptions } from '../../../utils/utilities'; + +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: '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, + }, + ], + }, +]; + +const searchDisplayOptions = { + show: { + resource: ['attribute'], + operation: ['search'], + }, +}; + +export const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties); export const attributeOperations: INodeProperties[] = [ { @@ -32,6 +130,11 @@ export const attributeOperations: INodeProperties[] = [ value: 'getAll', action: 'Get many attributes', }, + { + name: 'Search', + value: 'search', + action: 'Get a filtered list of attributes', + }, { name: 'Update', value: 'update', @@ -226,6 +329,11 @@ export const attributeFields: INodeProperties[] = [ }, }, + // ---------------------------------------- + // attribute: search + // ---------------------------------------- + ...searchDescription, + // ---------------------------------------- // attribute: update // ---------------------------------------- From 5b4ef0521e3ab6118899de02d09777aa19fec9c8 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Tue, 23 Apr 2024 10:28:42 +0300 Subject: [PATCH 2/3] :zap: events and objects search --- .../nodes-base/nodes/Misp/GenericFunctions.ts | 53 ++++++++++- packages/nodes-base/nodes/Misp/Misp.node.ts | 56 ++++++------ .../Misp/descriptions/AttributeDescription.ts | 91 +------------------ .../Misp/descriptions/EventDescription.ts | 21 +++++ .../Misp/descriptions/ObjectDescription.ts | 41 +++++++++ .../Misp/descriptions/common.descriptions.ts | 89 ++++++++++++++++++ .../nodes/Misp/descriptions/index.ts | 1 + 7 files changed, 233 insertions(+), 119 deletions(-) create mode 100644 packages/nodes-base/nodes/Misp/descriptions/ObjectDescription.ts create mode 100644 packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts diff --git a/packages/nodes-base/nodes/Misp/GenericFunctions.ts b/packages/nodes-base/nodes/Misp/GenericFunctions.ts index a0e3e03597fd7..bdf59bda34e08 100644 --- a/packages/nodes-base/nodes/Misp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Misp/GenericFunctions.ts @@ -7,7 +7,7 @@ import type { IHttpRequestMethods, IRequestOptions, } from 'n8n-workflow'; -import { NodeApiError, NodeOperationError } from 'n8n-workflow'; +import { NodeApiError, NodeOperationError, jsonParse } from 'n8n-workflow'; import type { MispCredentials } from './types'; @@ -79,6 +79,57 @@ export async function mispApiRequestAllItems(this: IExecuteFunctions, endpoint: 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( this: IExecuteFunctions, resource: string, diff --git a/packages/nodes-base/nodes/Misp/Misp.node.ts b/packages/nodes-base/nodes/Misp/Misp.node.ts index 3f1af9b0caf0a..55ef467b57a4a 100644 --- a/packages/nodes-base/nodes/Misp/Misp.node.ts +++ b/packages/nodes-base/nodes/Misp/Misp.node.ts @@ -6,11 +6,11 @@ import type { INodeType, INodeTypeDescription, } from 'n8n-workflow'; -import { jsonParse } from 'n8n-workflow'; import { mispApiRequest, mispApiRequestAllItems, + mispApiRestSearch, throwOnEmptyUpdate, throwOnInvalidUrl, throwOnMissingSharingGroup, @@ -29,6 +29,8 @@ import { galaxyOperations, noticelistFields, noticelistOperations, + objectOperations, + objectFields, organisationFields, organisationOperations, tagFields, @@ -92,6 +94,10 @@ export class Misp implements INodeType { name: 'Noticelist', value: 'noticelist', }, + { + name: 'Object', + value: 'object', + }, { name: 'Organisation', value: 'organisation', @@ -123,6 +129,8 @@ export class Misp implements INodeType { ...galaxyFields, ...noticelistOperations, ...noticelistFields, + ...objectOperations, + ...objectFields, ...organisationOperations, ...organisationFields, ...tagOperations, @@ -238,35 +246,8 @@ export class Misp implements INodeType { // ---------------------------------------- // attribute: search // ---------------------------------------- - let body: IDataObject = {}; - const useJson = this.getNodeParameter('useJson', i) as boolean; - - if (useJson) { - const json = this.getNodeParameter('jsonOutput', i); - if (typeof json === 'string') { - body = jsonParse(json); - } else { - body = json as IDataObject; - } - } else { - const value = this.getNodeParameter('value', i) as string; - const additionalFields = this.getNodeParameter('additionalFields', i); - - 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 = '/attributes/restSearch'; - const { response } = await mispApiRequest.call(this, 'POST', endpoint, body); - responseData = response?.Attribute || []; + responseData = await mispApiRestSearch.call(this, 'attributes', i); } else if (operation === 'update') { // ---------------------------------------- // attribute: update @@ -334,6 +315,12 @@ export class Misp implements INodeType { // ---------------------------------------- responseData = await mispApiRequestAllItems.call(this, '/events'); + } else if (operation === 'search') { + // ---------------------------------------- + // event: search + // ---------------------------------------- + + responseData = await mispApiRestSearch.call(this, 'events', i); } else if (operation === 'publish') { // ---------------------------------------- // event: publish @@ -534,6 +521,17 @@ export class Misp implements INodeType { }>; 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') { // ********************************************************************** // organisation diff --git a/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts b/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts index 9d7c6aea916f9..09e54912b4d64 100644 --- a/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts +++ b/packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts @@ -1,93 +1,6 @@ import type { INodeProperties } from 'n8n-workflow'; import { updateDisplayOptions } from '../../../utils/utilities'; - -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: '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, - }, - ], - }, -]; +import { searchProperties } from './common.descriptions'; const searchDisplayOptions = { show: { @@ -96,7 +9,7 @@ const searchDisplayOptions = { }, }; -export const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties); +const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties); export const attributeOperations: INodeProperties[] = [ { diff --git a/packages/nodes-base/nodes/Misp/descriptions/EventDescription.ts b/packages/nodes-base/nodes/Misp/descriptions/EventDescription.ts index 098d6c5553b74..ca46869f7f3ac 100644 --- a/packages/nodes-base/nodes/Misp/descriptions/EventDescription.ts +++ b/packages/nodes-base/nodes/Misp/descriptions/EventDescription.ts @@ -1,4 +1,15 @@ 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[] = [ { @@ -37,6 +48,11 @@ export const eventOperations: INodeProperties[] = [ value: 'publish', action: 'Publish an event', }, + { + name: 'Search', + value: 'search', + action: 'Get a filtered list of events', + }, { name: 'Unpublish', value: 'unpublish', @@ -295,6 +311,11 @@ export const eventFields: INodeProperties[] = [ }, }, + // ---------------------------------------- + // event: search + // ---------------------------------------- + ...searchDescription, + // ---------------------------------------- // event: update // ---------------------------------------- diff --git a/packages/nodes-base/nodes/Misp/descriptions/ObjectDescription.ts b/packages/nodes-base/nodes/Misp/descriptions/ObjectDescription.ts new file mode 100644 index 0000000000000..ad38ede1c8554 --- /dev/null +++ b/packages/nodes-base/nodes/Misp/descriptions/ObjectDescription.ts @@ -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, +]; diff --git a/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts b/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts new file mode 100644 index 0000000000000..f540fe15c5637 --- /dev/null +++ b/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts @@ -0,0 +1,89 @@ +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: '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, + }, + ], + }, +]; diff --git a/packages/nodes-base/nodes/Misp/descriptions/index.ts b/packages/nodes-base/nodes/Misp/descriptions/index.ts index 9bd7d6ae364d2..a7800c3b30d71 100644 --- a/packages/nodes-base/nodes/Misp/descriptions/index.ts +++ b/packages/nodes-base/nodes/Misp/descriptions/index.ts @@ -4,6 +4,7 @@ export * from './EventTagDescription'; export * from './FeedDescription'; export * from './GalaxyDescription'; export * from './NoticelistDescription'; +export * from './ObjectDescription'; export * from './OrganisationDescription'; export * from './TagDescription'; export * from './UserDescription'; From 39bca172fd67f2352b958af2c1c56ad3f146ca66 Mon Sep 17 00:00:00 2001 From: Michael Kret Date: Thu, 25 Apr 2024 14:11:53 +0300 Subject: [PATCH 3/3] :zap: searchall parameter --- .../nodes/Misp/descriptions/common.descriptions.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts b/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts index f540fe15c5637..ffa040e481b33 100644 --- a/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts +++ b/packages/nodes-base/nodes/Misp/descriptions/common.descriptions.ts @@ -63,6 +63,19 @@ export const searchProperties: INodeProperties[] = [ 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',