Skip to content

Commit b694e77

Browse files
feat(MISP Node): Rest search operations (#9196)
1 parent 9b3ac16 commit b694e77

File tree

7 files changed

+270
-1
lines changed

7 files changed

+270
-1
lines changed

packages/nodes-base/nodes/Misp/GenericFunctions.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
IHttpRequestMethods,
88
IRequestOptions,
99
} from 'n8n-workflow';
10-
import { NodeApiError, NodeOperationError } from 'n8n-workflow';
10+
import { NodeApiError, NodeOperationError, jsonParse } from 'n8n-workflow';
1111

1212
import type { MispCredentials } from './types';
1313

@@ -79,6 +79,57 @@ export async function mispApiRequestAllItems(this: IExecuteFunctions, endpoint:
7979
return responseData;
8080
}
8181

82+
export async function mispApiRestSearch(
83+
this: IExecuteFunctions,
84+
resource: 'attributes' | 'events' | 'objects',
85+
itemIndex: number,
86+
) {
87+
let body: IDataObject = {};
88+
const useJson = this.getNodeParameter('useJson', itemIndex) as boolean;
89+
90+
if (useJson) {
91+
const json = this.getNodeParameter('jsonOutput', itemIndex);
92+
if (typeof json === 'string') {
93+
body = jsonParse(json);
94+
} else {
95+
body = json as IDataObject;
96+
}
97+
} else {
98+
const value = this.getNodeParameter('value', itemIndex) as string;
99+
const additionalFields = this.getNodeParameter('additionalFields', itemIndex);
100+
101+
body.value = value;
102+
103+
if (Object.keys(additionalFields).length) {
104+
if (additionalFields.tags) {
105+
additionalFields.tags = (additionalFields.tags as string)
106+
.split(',')
107+
.map((tag) => tag.trim());
108+
}
109+
Object.assign(body, additionalFields);
110+
}
111+
}
112+
113+
const endpoint = `/${resource}/restSearch`;
114+
const { response } = await mispApiRequest.call(this, 'POST', endpoint, body);
115+
116+
if (response) {
117+
if (resource === 'attributes') {
118+
return response.Attribute;
119+
}
120+
121+
if (resource === 'events') {
122+
return (response as IDataObject[]).map((event) => event.Event);
123+
}
124+
125+
if (resource === 'objects') {
126+
return (response as IDataObject[]).map((obj) => obj.Object);
127+
}
128+
} else {
129+
return [];
130+
}
131+
}
132+
82133
export function throwOnEmptyUpdate(
83134
this: IExecuteFunctions,
84135
resource: string,

packages/nodes-base/nodes/Misp/Misp.node.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
import {
1111
mispApiRequest,
1212
mispApiRequestAllItems,
13+
mispApiRestSearch,
1314
throwOnEmptyUpdate,
1415
throwOnInvalidUrl,
1516
throwOnMissingSharingGroup,
@@ -28,6 +29,8 @@ import {
2829
galaxyOperations,
2930
noticelistFields,
3031
noticelistOperations,
32+
objectOperations,
33+
objectFields,
3134
organisationFields,
3235
organisationOperations,
3336
tagFields,
@@ -91,6 +94,10 @@ export class Misp implements INodeType {
9194
name: 'Noticelist',
9295
value: 'noticelist',
9396
},
97+
{
98+
name: 'Object',
99+
value: 'object',
100+
},
94101
{
95102
name: 'Organisation',
96103
value: 'organisation',
@@ -122,6 +129,8 @@ export class Misp implements INodeType {
122129
...galaxyFields,
123130
...noticelistOperations,
124131
...noticelistFields,
132+
...objectOperations,
133+
...objectFields,
125134
...organisationOperations,
126135
...organisationFields,
127136
...tagOperations,
@@ -233,6 +242,12 @@ export class Misp implements INodeType {
233242
// ----------------------------------------
234243

235244
responseData = await mispApiRequestAllItems.call(this, '/attributes');
245+
} else if (operation === 'search') {
246+
// ----------------------------------------
247+
// attribute: search
248+
// ----------------------------------------
249+
250+
responseData = await mispApiRestSearch.call(this, 'attributes', i);
236251
} else if (operation === 'update') {
237252
// ----------------------------------------
238253
// attribute: update
@@ -300,6 +315,12 @@ export class Misp implements INodeType {
300315
// ----------------------------------------
301316

302317
responseData = await mispApiRequestAllItems.call(this, '/events');
318+
} else if (operation === 'search') {
319+
// ----------------------------------------
320+
// event: search
321+
// ----------------------------------------
322+
323+
responseData = await mispApiRestSearch.call(this, 'events', i);
303324
} else if (operation === 'publish') {
304325
// ----------------------------------------
305326
// event: publish
@@ -500,6 +521,17 @@ export class Misp implements INodeType {
500521
}>;
501522
responseData = responseData.map((entry) => entry.Noticelist);
502523
}
524+
} else if (resource === 'object') {
525+
// **********************************************************************
526+
// object
527+
// **********************************************************************
528+
if (operation === 'search') {
529+
// ----------------------------------------
530+
// attribute: search
531+
// ----------------------------------------
532+
533+
responseData = await mispApiRestSearch.call(this, 'objects', i);
534+
}
503535
} else if (resource === 'organisation') {
504536
// **********************************************************************
505537
// organisation

packages/nodes-base/nodes/Misp/descriptions/AttributeDescription.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
import type { INodeProperties } from 'n8n-workflow';
2+
import { updateDisplayOptions } from '../../../utils/utilities';
3+
import { searchProperties } from './common.descriptions';
4+
5+
const searchDisplayOptions = {
6+
show: {
7+
resource: ['attribute'],
8+
operation: ['search'],
9+
},
10+
};
11+
12+
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
213

314
export const attributeOperations: INodeProperties[] = [
415
{
@@ -32,6 +43,11 @@ export const attributeOperations: INodeProperties[] = [
3243
value: 'getAll',
3344
action: 'Get many attributes',
3445
},
46+
{
47+
name: 'Search',
48+
value: 'search',
49+
action: 'Get a filtered list of attributes',
50+
},
3551
{
3652
name: 'Update',
3753
value: 'update',
@@ -226,6 +242,11 @@ export const attributeFields: INodeProperties[] = [
226242
},
227243
},
228244

245+
// ----------------------------------------
246+
// attribute: search
247+
// ----------------------------------------
248+
...searchDescription,
249+
229250
// ----------------------------------------
230251
// attribute: update
231252
// ----------------------------------------

packages/nodes-base/nodes/Misp/descriptions/EventDescription.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
import type { INodeProperties } from 'n8n-workflow';
2+
import { updateDisplayOptions } from '../../../utils/utilities';
3+
import { searchProperties } from './common.descriptions';
4+
5+
const searchDisplayOptions = {
6+
show: {
7+
resource: ['event'],
8+
operation: ['search'],
9+
},
10+
};
11+
12+
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
213

314
export const eventOperations: INodeProperties[] = [
415
{
@@ -37,6 +48,11 @@ export const eventOperations: INodeProperties[] = [
3748
value: 'publish',
3849
action: 'Publish an event',
3950
},
51+
{
52+
name: 'Search',
53+
value: 'search',
54+
action: 'Get a filtered list of events',
55+
},
4056
{
4157
name: 'Unpublish',
4258
value: 'unpublish',
@@ -295,6 +311,11 @@ export const eventFields: INodeProperties[] = [
295311
},
296312
},
297313

314+
// ----------------------------------------
315+
// event: search
316+
// ----------------------------------------
317+
...searchDescription,
318+
298319
// ----------------------------------------
299320
// event: update
300321
// ----------------------------------------
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { INodeProperties } from 'n8n-workflow';
2+
import { updateDisplayOptions } from '../../../utils/utilities';
3+
import { searchProperties } from './common.descriptions';
4+
5+
const searchDisplayOptions = {
6+
show: {
7+
resource: ['object'],
8+
operation: ['search'],
9+
},
10+
};
11+
12+
const searchDescription = updateDisplayOptions(searchDisplayOptions, searchProperties);
13+
14+
export const objectOperations: INodeProperties[] = [
15+
{
16+
displayName: 'Operation',
17+
name: 'operation',
18+
type: 'options',
19+
displayOptions: {
20+
show: {
21+
resource: ['object'],
22+
},
23+
},
24+
noDataExpression: true,
25+
options: [
26+
{
27+
name: 'Search',
28+
value: 'search',
29+
action: 'Get a filtered list of objects',
30+
},
31+
],
32+
default: 'search',
33+
},
34+
];
35+
36+
export const objectFields: INodeProperties[] = [
37+
// ----------------------------------------
38+
// event: search
39+
// ----------------------------------------
40+
...searchDescription,
41+
];
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type { INodeProperties } from 'n8n-workflow';
2+
3+
export const searchProperties: INodeProperties[] = [
4+
{
5+
displayName: 'Use JSON to Specify Fields',
6+
name: 'useJson',
7+
type: 'boolean',
8+
default: false,
9+
description: 'Whether to use JSON to specify the fields for the search request',
10+
},
11+
{
12+
displayName: 'JSON',
13+
name: 'jsonOutput',
14+
type: 'json',
15+
description:
16+
'Get more info at {YOUR_BASE_URL_SPECIFIED_IN_CREDENTIALS}/api/openapi#operation/restSearchAttributes',
17+
typeOptions: {
18+
rows: 5,
19+
},
20+
default: '{\n "value": "search value",\n "type": "text"\n}\n',
21+
validateType: 'object',
22+
displayOptions: {
23+
show: {
24+
useJson: [true],
25+
},
26+
},
27+
},
28+
{
29+
displayName: 'Value',
30+
name: 'value',
31+
type: 'string',
32+
required: true,
33+
placeholder: 'e.g. 127.0.0.1',
34+
default: '',
35+
displayOptions: {
36+
show: {
37+
useJson: [false],
38+
},
39+
},
40+
},
41+
{
42+
displayName: 'Additional Fields',
43+
name: 'additionalFields',
44+
type: 'collection',
45+
placeholder: 'Add Field',
46+
default: {},
47+
displayOptions: {
48+
show: {
49+
useJson: [false],
50+
},
51+
},
52+
options: [
53+
{
54+
displayName: 'Category',
55+
name: 'category',
56+
type: 'string',
57+
placeholder: 'e.g. Internal reference',
58+
default: '',
59+
},
60+
{
61+
displayName: 'Deleted',
62+
name: 'deleted',
63+
type: 'boolean',
64+
default: false,
65+
},
66+
{
67+
displayName: 'Search All',
68+
name: 'searchall',
69+
type: 'string',
70+
description:
71+
'Search by matching any tag names, event descriptions, attribute values or attribute comments',
72+
default: '',
73+
displayOptions: {
74+
hide: {
75+
'/resource': ['attribute'],
76+
},
77+
},
78+
},
79+
{
80+
displayName: 'Tags',
81+
name: 'tags',
82+
type: 'string',
83+
placeholder: 'e.g. tag1,tag2',
84+
hint: 'Comma-separated list of tags',
85+
default: '',
86+
},
87+
{
88+
displayName: 'Type',
89+
name: 'type',
90+
type: 'string',
91+
placeholder: 'e.g. text',
92+
default: '',
93+
},
94+
{
95+
displayName: 'Published',
96+
name: 'published',
97+
type: 'boolean',
98+
default: false,
99+
},
100+
],
101+
},
102+
];

packages/nodes-base/nodes/Misp/descriptions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from './EventTagDescription';
44
export * from './FeedDescription';
55
export * from './GalaxyDescription';
66
export * from './NoticelistDescription';
7+
export * from './ObjectDescription';
78
export * from './OrganisationDescription';
89
export * from './TagDescription';
910
export * from './UserDescription';

0 commit comments

Comments
 (0)