mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(MISP Node): Rest search operations (#9196)
This commit is contained in:
parent
9b3ac1648f
commit
b694e7743e
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
|
@ -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
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
|
@ -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,
|
||||||
|
];
|
|
@ -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,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in a new issue