mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 12:44:07 -08:00
7ce7285f7a
* Changes to types so that credentials can be always loaded from DB This first commit changes all return types from the execute functions and calls to get credentials to be async so we can use await. This is a first step as previously credentials were loaded in memory and always available. We will now be loading them from the DB which requires turning the whole call chain async. * Fix updated files * Removed unnecessary credential loading to improve performance * Fix typo * ⚡ Fix issue * Updated new nodes to load credentials async * ⚡ Remove not needed comment Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
2126 lines
53 KiB
TypeScript
2126 lines
53 KiB
TypeScript
import {
|
|
IExecuteFunctions
|
|
} from 'n8n-core';
|
|
|
|
import {
|
|
IBinaryData,
|
|
IDataObject,
|
|
ILoadOptionsFunctions,
|
|
INodeExecutionData,
|
|
INodeParameters,
|
|
INodePropertyOptions,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
NodeOperationError,
|
|
} from 'n8n-workflow';
|
|
|
|
import {
|
|
alertFields,
|
|
alertOperations,
|
|
} from './descriptions/AlertDescription';
|
|
|
|
import {
|
|
observableFields,
|
|
observableOperations,
|
|
} from './descriptions/ObservableDescription';
|
|
|
|
import {
|
|
caseFields,
|
|
caseOperations,
|
|
} from './descriptions/CaseDescription';
|
|
|
|
import {
|
|
taskFields,
|
|
taskOperations,
|
|
} from './descriptions/TaskDescription';
|
|
|
|
import {
|
|
logFields,
|
|
logOperations,
|
|
} from './descriptions/LogDescription';
|
|
|
|
import {
|
|
Buffer,
|
|
} from 'buffer';
|
|
|
|
import {
|
|
And,
|
|
Between,
|
|
ContainsString,
|
|
Eq,
|
|
Id,
|
|
In,
|
|
IQueryObject,
|
|
Parent,
|
|
} from './QueryFunctions';
|
|
|
|
import {
|
|
buildCustomFieldSearch,
|
|
mapResource,
|
|
prepareCustomFields,
|
|
prepareOptional,
|
|
prepareRangeQuery,
|
|
prepareSortQuery,
|
|
splitTags,
|
|
theHiveApiRequest,
|
|
} from './GenericFunctions';
|
|
|
|
export class TheHive implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'TheHive',
|
|
name: 'theHive',
|
|
icon: 'file:thehive.svg',
|
|
group: ['transform'],
|
|
subtitle: '={{$parameter["operation"]}} : {{$parameter["resource"]}}',
|
|
version: 1,
|
|
description: 'Consume TheHive API',
|
|
defaults: {
|
|
name: 'TheHive',
|
|
color: '#f3d02f',
|
|
},
|
|
inputs: ['main'],
|
|
outputs: ['main'],
|
|
credentials: [
|
|
{
|
|
name: 'theHiveApi',
|
|
required: true,
|
|
},
|
|
],
|
|
properties: [
|
|
{
|
|
displayName: 'Resource',
|
|
name: 'resource',
|
|
type: 'options',
|
|
required: true,
|
|
options: [
|
|
{
|
|
name: 'Alert',
|
|
value: 'alert',
|
|
},
|
|
{
|
|
name: 'Case',
|
|
value: 'case',
|
|
},
|
|
{
|
|
name: 'Log',
|
|
value: 'log',
|
|
},
|
|
{
|
|
name: 'Observable',
|
|
value: 'observable',
|
|
},
|
|
{
|
|
name: 'Task',
|
|
value: 'task',
|
|
},
|
|
],
|
|
default: 'alert',
|
|
},
|
|
// Alert
|
|
...alertOperations,
|
|
...alertFields,
|
|
// Observable
|
|
...observableOperations,
|
|
...observableFields,
|
|
// Case
|
|
...caseOperations,
|
|
...caseFields,
|
|
// Task
|
|
...taskOperations,
|
|
...taskFields,
|
|
// Log
|
|
...logOperations,
|
|
...logFields,
|
|
],
|
|
};
|
|
methods = {
|
|
loadOptions: {
|
|
async loadResponders(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
// request the analyzers from instance
|
|
const resource = mapResource(this.getNodeParameter('resource') as string);
|
|
const resourceId = this.getNodeParameter('id');
|
|
const endpoint = `/connector/cortex/responder/${resource}/${resourceId}`;
|
|
|
|
const responders = await theHiveApiRequest.call(
|
|
this,
|
|
'GET',
|
|
endpoint as string,
|
|
);
|
|
|
|
const returnData: INodePropertyOptions[] = [];
|
|
|
|
for (const responder of responders) {
|
|
returnData.push({
|
|
name: responder.name as string,
|
|
value: responder.id,
|
|
description: responder.description as string,
|
|
});
|
|
}
|
|
return returnData;
|
|
},
|
|
|
|
async loadAnalyzers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
// request the analyzers from instance
|
|
const dataType = this.getNodeParameter('dataType') as string;
|
|
const endpoint = `/connector/cortex/analyzer/type/${dataType}`;
|
|
const requestResult = await theHiveApiRequest.call(
|
|
this,
|
|
'GET',
|
|
endpoint as string,
|
|
);
|
|
const returnData: INodePropertyOptions[] = [];
|
|
|
|
for (const analyzer of requestResult) {
|
|
for (const cortexId of analyzer.cortexIds) {
|
|
returnData.push({
|
|
name: `[${cortexId}] ${analyzer.name}`,
|
|
value: `${analyzer.id as string}::${cortexId as string}`,
|
|
description: analyzer.description as string,
|
|
});
|
|
}
|
|
}
|
|
return returnData;
|
|
},
|
|
async loadCustomFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const credentials = await this.getCredentials('theHiveApi');
|
|
if (credentials === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'Credentials could not be obtained');
|
|
}
|
|
const version = credentials.apiVersion;
|
|
const endpoint = version === 'v1' ? '/customField' : '/list/custom_fields';
|
|
|
|
const requestResult = await theHiveApiRequest.call(
|
|
this,
|
|
'GET',
|
|
endpoint as string,
|
|
);
|
|
|
|
const returnData: INodePropertyOptions[] = [];
|
|
|
|
// Convert TheHive3 response to the same format as TheHive 4
|
|
const customFields = version === 'v1' ? requestResult : Object.keys(requestResult).map(key => requestResult[key]);
|
|
|
|
for (const field of customFields) {
|
|
returnData.push({
|
|
name: `${field.name}: ${field.reference}`,
|
|
value: field.reference,
|
|
description: `${field.type}: ${field.description}`,
|
|
} as INodePropertyOptions);
|
|
}
|
|
|
|
return returnData;
|
|
},
|
|
async loadObservableOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
// if v1 is not used we remove 'count' option
|
|
const version = (await this.getCredentials('theHiveApi'))?.apiVersion;
|
|
|
|
const options = [
|
|
...(version === 'v1') ? [{ name: 'Count', value: 'count', description: 'Count observables' }] : [],
|
|
{ name: 'Create', value: 'create', description: 'Create observable' },
|
|
{ name: 'Execute Analyzer', value: 'executeAnalyzer', description: 'Execute an responder on selected observable' },
|
|
{ name: 'Execute Responder', value: 'executeResponder', description: 'Execute a responder on selected observable' },
|
|
{ name: 'Get All', value: 'getAll', description: 'Get all observables of a specific case' },
|
|
{ name: 'Get', value: 'get', description: 'Get a single observable' },
|
|
{ name: 'Search', value: 'search', description: 'Search observables' },
|
|
{ name: 'Update', value: 'update', description: 'Update observable' },
|
|
];
|
|
return options;
|
|
},
|
|
async loadObservableTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const version = (await this.getCredentials('theHiveApi'))?.apiVersion;
|
|
const endpoint = version === 'v1' ? '/observable/type?range=all' : '/list/list_artifactDataType';
|
|
|
|
const dataTypes = await theHiveApiRequest.call(
|
|
this,
|
|
'GET',
|
|
endpoint as string,
|
|
);
|
|
|
|
let returnData: INodePropertyOptions[] = [];
|
|
|
|
if (version === 'v1') {
|
|
returnData = dataTypes.map((dataType: IDataObject) => {
|
|
return {
|
|
name: dataType.name as string,
|
|
value: dataType.name as string,
|
|
};
|
|
});
|
|
}
|
|
else {
|
|
returnData = Object.keys(dataTypes).map(key => {
|
|
const dataType = dataTypes[key] as string;
|
|
|
|
return {
|
|
name: dataType,
|
|
value: dataType,
|
|
};
|
|
});
|
|
}
|
|
|
|
// Sort the array by option name
|
|
returnData.sort((a, b) => {
|
|
if (a.name < b.name) {
|
|
return -1;
|
|
}
|
|
if (a.name > b.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
|
|
return returnData;
|
|
},
|
|
async loadTaskOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const credentials = await this.getCredentials('theHiveApi');
|
|
if (credentials === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'Credentials could not be obtained');
|
|
}
|
|
const version = credentials.apiVersion;
|
|
const options = [
|
|
...(version === 'v1') ? [{ name: 'Count', value: 'count', description: 'Count tasks' }] : [],
|
|
{ name: 'Create', value: 'create', description: 'Create a task' },
|
|
{ name: 'Execute Responder', value: 'executeResponder', description: 'Execute a responder on the specified task' },
|
|
{ name: 'Get All', value: 'getAll', description: 'Get all asks of a specific case' },
|
|
{ name: 'Get', value: 'get', description: 'Get a single task' },
|
|
{ name: 'Search', value: 'search', description: 'Search tasks' },
|
|
{ name: 'Update', value: 'update', description: 'Update a task' },
|
|
];
|
|
return options;
|
|
},
|
|
async loadAlertOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const credentials = await this.getCredentials('theHiveApi');
|
|
if (credentials === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'Credentials could not be obtained');
|
|
}
|
|
const version = credentials.apiVersion;
|
|
const options = [
|
|
...(version === 'v1') ? [{ name: 'Count', value: 'count', description: 'Count alerts' }] : [],
|
|
{ name: 'Create', value: 'create', description: 'Create alert' },
|
|
{ name: 'Execute Responder', value: 'executeResponder', description: 'Execute a responder on the specified alert' },
|
|
{ name: 'Get', value: 'get', description: 'Get an alert' },
|
|
{ name: 'Get All', value: 'getAll', description: 'Get all alerts' },
|
|
{ name: 'Mark as Read', value: 'markAsRead', description: 'Mark the alert as read' },
|
|
{ name: 'Mark as Unread', value: 'markAsUnread', description: 'Mark the alert as unread' },
|
|
{ name: 'Merge', value: 'merge', description: 'Merge alert into an existing case' },
|
|
{ name: 'Promote', value: 'promote', description: 'Promote an alert into a case' },
|
|
{ name: 'Update', value: 'update', description: 'Update alert' },
|
|
];
|
|
return options;
|
|
},
|
|
async loadCaseOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
|
const credentials = await this.getCredentials('theHiveApi');
|
|
if (credentials === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'Credentials could not be obtained');
|
|
}
|
|
const version = credentials.apiVersion;
|
|
const options = [
|
|
...(version === 'v1') ? [{ name: 'Count', value: 'count', description: 'Count a case' }] : [],
|
|
{ name: 'Create', value: 'create', description: 'Create a case' },
|
|
{ name: 'Execute Responder', value: 'executeResponder', description: 'Execute a responder on the specified case' },
|
|
{ name: 'Get All', value: 'getAll', description: 'Get all cases' },
|
|
{ name: 'Get', value: 'get', description: 'Get a single case' },
|
|
{ name: 'Update', value: 'update', description: 'Update a case' },
|
|
];
|
|
return options;
|
|
},
|
|
},
|
|
};
|
|
|
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
const items = this.getInputData();
|
|
const returnData: IDataObject[] = [];
|
|
const length = (items.length as unknown) as number;
|
|
const qs: IDataObject = {};
|
|
let responseData;
|
|
const resource = this.getNodeParameter('resource', 0) as string;
|
|
const operation = this.getNodeParameter('operation', 0) as string;
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
try {
|
|
if (resource === 'alert') {
|
|
if (operation === 'count') {
|
|
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
|
const countQueryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
|
|
|
const _countSearchQuery: IQueryObject = And();
|
|
|
|
if ('customFieldsUi' in filters) {
|
|
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
|
const searchQueries = buildCustomFieldSearch(customFields);
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
|
}
|
|
|
|
for (const key of Object.keys(countQueryAttributs)) {
|
|
if (key === 'tags') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, countQueryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'title') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, countQueryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, countQueryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
const body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listAlert',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _countSearchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
body['query'].push(
|
|
{
|
|
'_name': 'count',
|
|
},
|
|
);
|
|
|
|
qs.name = 'count-Alert';
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/v1/query',
|
|
body,
|
|
qs,
|
|
);
|
|
|
|
responseData = { count: responseData };
|
|
}
|
|
|
|
if (operation === 'create') {
|
|
const additionalFields = this.getNodeParameter('additionalFields', i) as INodeParameters;
|
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
|
|
|
const customFields = await prepareCustomFields.call(this, additionalFields, jsonParameters);
|
|
const body: IDataObject = {
|
|
title: this.getNodeParameter('title', i),
|
|
description: this.getNodeParameter('description', i),
|
|
severity: this.getNodeParameter('severity', i),
|
|
date: Date.parse(this.getNodeParameter('date', i) as string),
|
|
tags: splitTags(this.getNodeParameter('tags', i) as string),
|
|
tlp: this.getNodeParameter('tlp', i),
|
|
status: this.getNodeParameter('status', i),
|
|
type: this.getNodeParameter('type', i),
|
|
source: this.getNodeParameter('source', i),
|
|
sourceRef: this.getNodeParameter('sourceRef', i),
|
|
follow: this.getNodeParameter('follow', i, true),
|
|
customFields,
|
|
...prepareOptional(additionalFields),
|
|
};
|
|
|
|
const artifactUi = this.getNodeParameter('artifactUi', i) as IDataObject;
|
|
|
|
if (artifactUi) {
|
|
|
|
const artifactValues = (artifactUi as IDataObject).artifactValues as IDataObject[];
|
|
|
|
if (artifactValues) {
|
|
|
|
const artifactData = [];
|
|
|
|
for (const artifactvalue of artifactValues) {
|
|
|
|
const element: IDataObject = {};
|
|
|
|
element.message = artifactvalue.message as string;
|
|
|
|
element.tags = (artifactvalue.tags as string).split(',') as string[];
|
|
|
|
element.dataType = artifactvalue.dataType as string;
|
|
|
|
element.data = artifactvalue.data as string;
|
|
|
|
if (artifactvalue.dataType === 'file') {
|
|
|
|
const item = items[i];
|
|
|
|
if (item.binary === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
|
}
|
|
|
|
const binaryPropertyName = artifactvalue.binaryProperty as string;
|
|
|
|
if (item.binary[binaryPropertyName] === undefined) {
|
|
throw new NodeOperationError(this.getNode(), `No binary data property '${binaryPropertyName}' does not exists on item!`);
|
|
}
|
|
|
|
const binaryData = item.binary[binaryPropertyName] as IBinaryData;
|
|
|
|
element.data = `${binaryData.fileName};${binaryData.mimeType};${binaryData.data}`;
|
|
}
|
|
|
|
artifactData.push(element);
|
|
}
|
|
body.artifacts = artifactData;
|
|
}
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/alert' as string,
|
|
body,
|
|
);
|
|
}
|
|
|
|
/*
|
|
Execute responder feature differs from Cortex execute responder
|
|
if it doesn't interfere with n8n standards then we should keep it
|
|
*/
|
|
|
|
if (operation === 'executeResponder') {
|
|
const alertId = this.getNodeParameter('id', i);
|
|
const responderId = this.getNodeParameter('responder', i) as string;
|
|
let body: IDataObject;
|
|
let response;
|
|
responseData = [];
|
|
body = {
|
|
responderId,
|
|
objectId: alertId,
|
|
objectType: 'alert',
|
|
};
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/action' as string,
|
|
body,
|
|
);
|
|
body = {
|
|
query: [
|
|
{
|
|
'_name': 'listAction',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': [
|
|
{
|
|
'_field': 'cortexId',
|
|
'_value': response.cortexId,
|
|
},
|
|
{
|
|
'_field': 'objectId',
|
|
'_value': response.objectId,
|
|
},
|
|
{
|
|
'_field': 'startDate',
|
|
'_value': response.startDate,
|
|
},
|
|
|
|
],
|
|
},
|
|
],
|
|
};
|
|
qs.name = 'log-actions';
|
|
do {
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/v1/query`,
|
|
body,
|
|
qs,
|
|
);
|
|
} while (response.status === 'Waiting' || response.status === 'InProgress');
|
|
|
|
responseData = response;
|
|
}
|
|
|
|
if (operation === 'get') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'GET',
|
|
`/alert/${alertId}`,
|
|
{},
|
|
);
|
|
}
|
|
if (operation === 'getAll') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
|
const queryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
const _searchQuery: IQueryObject = And();
|
|
|
|
if ('customFieldsUi' in filters) {
|
|
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
|
const searchQueries = buildCustomFieldSearch(customFields);
|
|
(_searchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
|
}
|
|
|
|
for (const key of Object.keys(queryAttributs)) {
|
|
if (key === 'tags') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, queryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'title') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, queryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, queryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listAlert',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _searchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'alerts';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/alert/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = _searchQuery;
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'markAsRead') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/alert/${alertId}/markAsRead`,
|
|
);
|
|
}
|
|
|
|
if (operation === 'markAsUnread') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/alert/${alertId}/markAsUnread`,
|
|
);
|
|
}
|
|
|
|
if (operation === 'merge') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
|
|
const caseId = this.getNodeParameter('caseId', i) as string;
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/alert/${alertId}/merge/${caseId}`,
|
|
{},
|
|
);
|
|
}
|
|
|
|
if (operation === 'promote') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
|
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
|
|
|
const body: IDataObject = {};
|
|
|
|
Object.assign(body, additionalFields);
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/alert/${alertId}/createCase`,
|
|
body,
|
|
);
|
|
}
|
|
|
|
if (operation === 'update') {
|
|
const alertId = this.getNodeParameter('id', i) as string;
|
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
|
|
|
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
|
const customFields = await prepareCustomFields.call(this, updateFields, jsonParameters);
|
|
|
|
const artifactUi = updateFields.artifactUi as IDataObject;
|
|
|
|
delete updateFields.artifactUi;
|
|
|
|
const body: IDataObject = {
|
|
customFields,
|
|
};
|
|
|
|
Object.assign(body, updateFields);
|
|
|
|
if (artifactUi) {
|
|
const artifactValues = (artifactUi as IDataObject).artifactValues as IDataObject[];
|
|
|
|
if (artifactValues) {
|
|
const artifactData = [];
|
|
|
|
for (const artifactvalue of artifactValues) {
|
|
|
|
const element: IDataObject = {};
|
|
|
|
element.message = artifactvalue.message as string;
|
|
|
|
element.tags = (artifactvalue.tags as string).split(',') as string[];
|
|
|
|
element.dataType = artifactvalue.dataType as string;
|
|
|
|
element.data = artifactvalue.data as string;
|
|
|
|
if (artifactvalue.dataType === 'file') {
|
|
const item = items[i];
|
|
|
|
if (item.binary === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
|
}
|
|
|
|
const binaryPropertyName = artifactvalue.binaryProperty as string;
|
|
|
|
if (item.binary[binaryPropertyName] === undefined) {
|
|
throw new NodeOperationError(this.getNode(), `No binary data property '${binaryPropertyName}' does not exists on item!`);
|
|
}
|
|
|
|
const binaryData = item.binary[binaryPropertyName] as IBinaryData;
|
|
|
|
element.data = `${binaryData.fileName};${binaryData.mimeType};${binaryData.data}`;
|
|
}
|
|
|
|
artifactData.push(element);
|
|
}
|
|
body.artifacts = artifactData;
|
|
}
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'PATCH',
|
|
`/alert/${alertId}` as string,
|
|
body,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (resource === 'observable') {
|
|
if (operation === 'count') {
|
|
const countQueryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
|
|
|
const _countSearchQuery: IQueryObject = And();
|
|
|
|
for (const key of Object.keys(countQueryAttributs)) {
|
|
if (key === 'dataType' || key === 'tags') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, countQueryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'keywork' || key === 'message') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, countQueryAttributs[key] as string),
|
|
);
|
|
} else if (key === 'range') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
Between(
|
|
'startDate',
|
|
countQueryAttributs['range']['dateRange']['fromDate'],
|
|
countQueryAttributs['range']['dateRange']['toDate'],
|
|
),
|
|
);
|
|
} else {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, countQueryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
const body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listObservable',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _countSearchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
body['query'].push(
|
|
{
|
|
'_name': 'count',
|
|
},
|
|
);
|
|
|
|
qs.name = 'count-observables';
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/v1/query',
|
|
body,
|
|
qs,
|
|
);
|
|
|
|
responseData = { count: responseData };
|
|
}
|
|
|
|
if (operation === 'executeAnalyzer') {
|
|
const observableId = this.getNodeParameter('id', i);
|
|
const analyzers = (this.getNodeParameter('analyzers', i) as string[])
|
|
.map(analyzer => {
|
|
const parts = analyzer.split('::');
|
|
return {
|
|
analyzerId: parts[0],
|
|
cortexId: parts[1],
|
|
};
|
|
});
|
|
let response: any; // tslint:disable-line:no-any
|
|
let body: IDataObject;
|
|
responseData = [];
|
|
for (const analyzer of analyzers) {
|
|
body = {
|
|
...analyzer,
|
|
artifactId: observableId,
|
|
};
|
|
// execute the analyzer
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/job' as string,
|
|
body,
|
|
qs,
|
|
);
|
|
const jobId = response.id;
|
|
qs.name = 'observable-jobs';
|
|
// query the job result (including the report)
|
|
do {
|
|
responseData = await theHiveApiRequest.call(this, 'GET', `/connector/cortex/job/${jobId}`, body, qs);
|
|
} while (responseData.status === 'Waiting' || responseData.status === 'InProgress');
|
|
}
|
|
|
|
}
|
|
|
|
if (operation === 'executeResponder') {
|
|
const observableId = this.getNodeParameter('id', i);
|
|
const responderId = this.getNodeParameter('responder', i) as string;
|
|
let body: IDataObject;
|
|
let response;
|
|
responseData = [];
|
|
body = {
|
|
responderId,
|
|
objectId: observableId,
|
|
objectType: 'case_artifact',
|
|
};
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/action' as string,
|
|
body,
|
|
);
|
|
body = {
|
|
query: [
|
|
{
|
|
'_name': 'listAction',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': [
|
|
{
|
|
'_field': 'cortexId',
|
|
'_value': response.cortexId,
|
|
},
|
|
{
|
|
'_field': 'objectId',
|
|
'_value': response.objectId,
|
|
},
|
|
{
|
|
'_field': 'startDate',
|
|
'_value': response.startDate,
|
|
},
|
|
|
|
],
|
|
},
|
|
],
|
|
};
|
|
qs.name = 'log-actions';
|
|
do {
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/v1/query`,
|
|
body,
|
|
qs,
|
|
);
|
|
} while (response.status === 'Waiting' || response.status === 'InProgress');
|
|
|
|
responseData = response;
|
|
}
|
|
|
|
if (operation === 'create') {
|
|
const caseId = this.getNodeParameter('caseId', i);
|
|
|
|
let body: IDataObject = {
|
|
dataType: this.getNodeParameter('dataType', i) as string,
|
|
message: this.getNodeParameter('message', i) as string,
|
|
startDate: Date.parse(this.getNodeParameter('startDate', i) as string),
|
|
tlp: this.getNodeParameter('tlp', i) as number,
|
|
ioc: this.getNodeParameter('ioc', i) as boolean,
|
|
sighted: this.getNodeParameter('sighted', i) as boolean,
|
|
status: this.getNodeParameter('status', i) as string,
|
|
...prepareOptional(this.getNodeParameter('options', i, {}) as INodeParameters),
|
|
};
|
|
|
|
let options: IDataObject = {};
|
|
|
|
if (body.dataType === 'file') {
|
|
const item = items[i];
|
|
|
|
if (item.binary === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
|
}
|
|
|
|
const binaryPropertyName = this.getNodeParameter('binaryProperty', i) as string;
|
|
|
|
if (item.binary[binaryPropertyName] === undefined) {
|
|
throw new NodeOperationError(this.getNode(), `No binary data property '${binaryPropertyName}' does not exists on item!`);
|
|
}
|
|
|
|
const binaryData = item.binary[binaryPropertyName] as IBinaryData;
|
|
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
|
|
options = {
|
|
formData: {
|
|
attachment: {
|
|
value: dataBuffer,
|
|
options: {
|
|
contentType: binaryData.mimeType,
|
|
filename: binaryData.fileName,
|
|
},
|
|
},
|
|
_json: JSON.stringify(body),
|
|
},
|
|
};
|
|
body = {};
|
|
} else {
|
|
body.data = this.getNodeParameter('data', i) as string;
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/case/${caseId}/artifact` as string,
|
|
body,
|
|
qs,
|
|
'',
|
|
options,
|
|
);
|
|
}
|
|
|
|
if (operation === 'get') {
|
|
const observableId = this.getNodeParameter('id', i) as string;
|
|
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
if (version === 'v1') {
|
|
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getObservable',
|
|
'idOrName': observableId,
|
|
},
|
|
],
|
|
};
|
|
|
|
qs.name = `get-observable-${observableId}`;
|
|
|
|
} else {
|
|
|
|
method = 'GET';
|
|
|
|
endpoint = `/case/artifact/${observableId}`;
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'getAll') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
const caseId = this.getNodeParameter('caseId', i);
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getCase',
|
|
'idOrName': caseId,
|
|
},
|
|
{
|
|
'_name': 'observables',
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'observables';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/artifact/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = Parent('case', Id(caseId as string));
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'search') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const queryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
|
|
|
const _searchQuery: IQueryObject = And();
|
|
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
for (const key of Object.keys(queryAttributs)) {
|
|
if (key === 'dataType' || key === 'tags') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, queryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'keywork' || key === 'message') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, queryAttributs[key] as string),
|
|
);
|
|
} else if (key === 'range') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
Between(
|
|
'startDate',
|
|
queryAttributs['range']['dateRange']['fromDate'],
|
|
queryAttributs['range']['dateRange']['toDate'],
|
|
),
|
|
);
|
|
} else {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, queryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listObservable',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _searchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'observables';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/artifact/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = _searchQuery;
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'update') {
|
|
const id = this.getNodeParameter('id', i) as string;
|
|
|
|
const body: IDataObject = {
|
|
...prepareOptional(this.getNodeParameter('updateFields', i, {}) as INodeParameters),
|
|
};
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'PATCH',
|
|
`/case/artifact/${id}` as string,
|
|
body,
|
|
qs,
|
|
);
|
|
|
|
responseData = { success: true };
|
|
}
|
|
}
|
|
|
|
if (resource === 'case') {
|
|
if (operation === 'count') {
|
|
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
|
const countQueryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
|
|
|
const _countSearchQuery: IQueryObject = And();
|
|
|
|
if ('customFieldsUi' in filters) {
|
|
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
|
const searchQueries = buildCustomFieldSearch(customFields);
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
|
}
|
|
|
|
for (const key of Object.keys(countQueryAttributs)) {
|
|
if (key === 'tags') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, countQueryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'summary' || key === 'title') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, countQueryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, countQueryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
const body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listCase',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _countSearchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
body['query'].push(
|
|
{
|
|
'_name': 'count',
|
|
},
|
|
);
|
|
|
|
qs.name = 'count-cases';
|
|
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/v1/query',
|
|
body,
|
|
qs,
|
|
);
|
|
|
|
responseData = { count: responseData };
|
|
}
|
|
|
|
if (operation === 'executeResponder') {
|
|
const caseId = this.getNodeParameter('id', i);
|
|
const responderId = this.getNodeParameter('responder', i) as string;
|
|
let body: IDataObject;
|
|
let response;
|
|
responseData = [];
|
|
body = {
|
|
responderId,
|
|
objectId: caseId,
|
|
objectType: 'case',
|
|
};
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/action' as string,
|
|
body,
|
|
);
|
|
body = {
|
|
query: [
|
|
{
|
|
'_name': 'listAction',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': [
|
|
{
|
|
'_field': 'cortexId',
|
|
'_value': response.cortexId,
|
|
},
|
|
{
|
|
'_field': 'objectId',
|
|
'_value': response.objectId,
|
|
},
|
|
{
|
|
'_field': 'startDate',
|
|
'_value': response.startDate,
|
|
},
|
|
|
|
],
|
|
},
|
|
],
|
|
};
|
|
qs.name = 'log-actions';
|
|
do {
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/v1/query`,
|
|
body,
|
|
qs,
|
|
);
|
|
} while (response.status === 'Waiting' || response.status === 'InProgress');
|
|
|
|
responseData = response;
|
|
}
|
|
|
|
if (operation === 'create') {
|
|
const options = this.getNodeParameter('options', i, {}) as INodeParameters;
|
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
|
const customFields = await prepareCustomFields.call(this, options, jsonParameters);
|
|
|
|
const body: IDataObject = {
|
|
title: this.getNodeParameter('title', i),
|
|
description: this.getNodeParameter('description', i),
|
|
severity: this.getNodeParameter('severity', i),
|
|
startDate: Date.parse(this.getNodeParameter('startDate', i) as string),
|
|
owner: this.getNodeParameter('owner', i),
|
|
flag: this.getNodeParameter('flag', i),
|
|
tlp: this.getNodeParameter('tlp', i),
|
|
tags: splitTags(this.getNodeParameter('tags', i) as string),
|
|
customFields,
|
|
...prepareOptional(options),
|
|
};
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/case' as string,
|
|
body,
|
|
);
|
|
}
|
|
|
|
if (operation === 'get') {
|
|
const caseId = this.getNodeParameter('id', i) as string;
|
|
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
if (version === 'v1') {
|
|
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getCase',
|
|
'idOrName': caseId,
|
|
},
|
|
],
|
|
};
|
|
|
|
qs.name = `get-case-${caseId}`;
|
|
|
|
} else {
|
|
|
|
method = 'GET';
|
|
|
|
endpoint = `/case/${caseId}`;
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'getAll') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const filters = this.getNodeParameter('filters', i, {}) as INodeParameters;
|
|
const queryAttributs: any = prepareOptional(filters); // tslint:disable-line:no-any
|
|
|
|
const _searchQuery: IQueryObject = And();
|
|
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
if ('customFieldsUi' in filters) {
|
|
const customFields = await prepareCustomFields.call(this, filters) as IDataObject;
|
|
const searchQueries = buildCustomFieldSearch(customFields);
|
|
(_searchQuery['_and'] as IQueryObject[]).push(...searchQueries);
|
|
}
|
|
|
|
for (const key of Object.keys(queryAttributs)) {
|
|
if (key === 'tags') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
In(key, queryAttributs[key] as string[]),
|
|
);
|
|
} else if (key === 'description' || key === 'summary' || key === 'title') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, queryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, queryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listCase',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _searchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'cases';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = _searchQuery;
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'update') {
|
|
const id = this.getNodeParameter('id', i) as string;
|
|
const updateFields = this.getNodeParameter('updateFields', i, {}) as INodeParameters;
|
|
const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
|
|
|
const customFields = await prepareCustomFields.call(this, updateFields, jsonParameters);
|
|
|
|
const body: IDataObject = {
|
|
customFields,
|
|
...prepareOptional(updateFields),
|
|
};
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'PATCH',
|
|
`/case/${id}` as string,
|
|
body,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (resource === 'task') {
|
|
if (operation === 'count') {
|
|
const countQueryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
|
|
|
const _countSearchQuery: IQueryObject = And();
|
|
|
|
for (const key of Object.keys(countQueryAttributs)) {
|
|
if (key === 'title' || key === 'description') {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, countQueryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_countSearchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, countQueryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
const body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listTask',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _countSearchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
body['query'].push(
|
|
{
|
|
'_name': 'count',
|
|
},
|
|
);
|
|
|
|
qs.name = 'count-tasks';
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/v1/query',
|
|
body,
|
|
qs,
|
|
);
|
|
|
|
responseData = { count: responseData };
|
|
}
|
|
|
|
if (operation === 'create') {
|
|
const caseId = this.getNodeParameter('caseId', i) as string;
|
|
|
|
const body: IDataObject = {
|
|
title: this.getNodeParameter('title', i) as string,
|
|
status: this.getNodeParameter('status', i) as string,
|
|
flag: this.getNodeParameter('flag', i),
|
|
...prepareOptional(this.getNodeParameter('options', i, {}) as INodeParameters),
|
|
};
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/case/${caseId}/task` as string,
|
|
body,
|
|
);
|
|
}
|
|
|
|
if (operation === 'executeResponder') {
|
|
const taskId = this.getNodeParameter('id', i);
|
|
const responderId = this.getNodeParameter('responder', i) as string;
|
|
let body: IDataObject;
|
|
let response;
|
|
responseData = [];
|
|
body = {
|
|
responderId,
|
|
objectId: taskId,
|
|
objectType: 'case_task',
|
|
};
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/action' as string,
|
|
body,
|
|
);
|
|
body = {
|
|
query: [
|
|
{
|
|
'_name': 'listAction',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': [
|
|
{
|
|
'_field': 'cortexId',
|
|
'_value': response.cortexId,
|
|
},
|
|
{
|
|
'_field': 'objectId',
|
|
'_value': response.objectId,
|
|
},
|
|
{
|
|
'_field': 'startDate',
|
|
'_value': response.startDate,
|
|
},
|
|
|
|
],
|
|
},
|
|
],
|
|
};
|
|
qs.name = 'task-actions';
|
|
do {
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/v1/query`,
|
|
body,
|
|
qs,
|
|
);
|
|
} while (response.status === 'Waiting' || response.status === 'InProgress');
|
|
|
|
responseData = response;
|
|
}
|
|
|
|
if (operation === 'get') {
|
|
const taskId = this.getNodeParameter('id', i) as string;
|
|
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getTask',
|
|
'idOrName': taskId,
|
|
},
|
|
],
|
|
};
|
|
|
|
qs.name = `get-task-${taskId}`;
|
|
|
|
} else {
|
|
method = 'GET';
|
|
|
|
endpoint = `/case/task/${taskId}`;
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'getAll') {
|
|
// get all require a case id (it retursn all tasks for a specific case)
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const caseId = this.getNodeParameter('caseId', i) as string;
|
|
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getCase',
|
|
'idOrName': caseId,
|
|
},
|
|
{
|
|
'_name': 'tasks',
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'case-tasks';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/task/_search';
|
|
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = And(Parent('case', Id(caseId)));
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'search') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const queryAttributs: any = prepareOptional(this.getNodeParameter('filters', i, {}) as INodeParameters); // tslint:disable-line:no-any
|
|
|
|
const _searchQuery: IQueryObject = And();
|
|
|
|
const options = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
for (const key of Object.keys(queryAttributs)) {
|
|
if (key === 'title' || key === 'description') {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
ContainsString(key, queryAttributs[key] as string),
|
|
);
|
|
} else {
|
|
(_searchQuery['_and'] as IQueryObject[]).push(
|
|
Eq(key, queryAttributs[key] as string),
|
|
);
|
|
}
|
|
}
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'listTask',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': _searchQuery['_and'],
|
|
},
|
|
],
|
|
};
|
|
|
|
//@ts-ignore
|
|
prepareSortQuery(options.sort, body);
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'tasks';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/task/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = _searchQuery;
|
|
|
|
Object.assign(qs, prepareOptional(options));
|
|
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'update') {
|
|
const id = this.getNodeParameter('id', i) as string;
|
|
|
|
const body: IDataObject = {
|
|
...prepareOptional(this.getNodeParameter('updateFields', i, {}) as INodeParameters),
|
|
};
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'PATCH',
|
|
`/case/task/${id}` as string,
|
|
body,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (resource === 'log') {
|
|
if (operation === 'create') {
|
|
|
|
const taskId = this.getNodeParameter('taskId', i) as string;
|
|
|
|
let body: IDataObject = {
|
|
message: this.getNodeParameter('message', i),
|
|
startDate: Date.parse(this.getNodeParameter('startDate', i) as string),
|
|
status: this.getNodeParameter('status', i),
|
|
};
|
|
const optionals = this.getNodeParameter('options', i) as IDataObject;
|
|
|
|
let options: IDataObject = {};
|
|
|
|
if (optionals.attachementUi) {
|
|
const attachmentValues = (optionals.attachementUi as IDataObject).attachmentValues as IDataObject;
|
|
|
|
if (attachmentValues) {
|
|
const item = items[i];
|
|
|
|
if (item.binary === undefined) {
|
|
throw new NodeOperationError(this.getNode(), 'No binary data exists on item!');
|
|
}
|
|
|
|
const binaryPropertyName = attachmentValues.binaryProperty as string;
|
|
|
|
if (item.binary[binaryPropertyName] === undefined) {
|
|
throw new NodeOperationError(this.getNode(), `No binary data property '${binaryPropertyName}' does not exists on item!`);
|
|
}
|
|
|
|
const binaryData = item.binary[binaryPropertyName] as IBinaryData;
|
|
const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
|
|
options = {
|
|
formData: {
|
|
attachment: {
|
|
value: dataBuffer,
|
|
options: {
|
|
contentType: binaryData.mimeType,
|
|
filename: binaryData.fileName,
|
|
},
|
|
},
|
|
_json: JSON.stringify(body),
|
|
},
|
|
};
|
|
|
|
body = {};
|
|
}
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/case/task/${taskId}/log` as string,
|
|
body,
|
|
qs,
|
|
'',
|
|
options,
|
|
);
|
|
}
|
|
|
|
if (operation === 'executeResponder') {
|
|
const logId = this.getNodeParameter('id', i);
|
|
const responderId = this.getNodeParameter('responder', i) as string;
|
|
let body: IDataObject;
|
|
let response;
|
|
responseData = [];
|
|
body = {
|
|
responderId,
|
|
objectId: logId,
|
|
objectType: 'case_task_log',
|
|
};
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
'/connector/cortex/action' as string,
|
|
body,
|
|
);
|
|
body = {
|
|
query: [
|
|
{
|
|
'_name': 'listAction',
|
|
},
|
|
{
|
|
'_name': 'filter',
|
|
'_and': [
|
|
{
|
|
'_field': 'cortexId',
|
|
'_value': response.cortexId,
|
|
},
|
|
{
|
|
'_field': 'objectId',
|
|
'_value': response.objectId,
|
|
},
|
|
{
|
|
'_field': 'startDate',
|
|
'_value': response.startDate,
|
|
},
|
|
|
|
],
|
|
},
|
|
],
|
|
};
|
|
qs.name = 'log-actions';
|
|
do {
|
|
response = await theHiveApiRequest.call(
|
|
this,
|
|
'POST',
|
|
`/v1/query`,
|
|
body,
|
|
qs,
|
|
);
|
|
} while (response.status === 'Waiting' || response.status === 'InProgress');
|
|
|
|
responseData = response;
|
|
}
|
|
|
|
if (operation === 'get') {
|
|
const logId = this.getNodeParameter('id', i) as string;
|
|
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
if (version === 'v1') {
|
|
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
query: [
|
|
{
|
|
_name: 'getLog',
|
|
idOrName: logId,
|
|
},
|
|
],
|
|
};
|
|
|
|
qs.name = `get-log-${logId}`;
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/task/log/_search';
|
|
|
|
body.query = { _id: logId };
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
|
|
if (operation === 'getAll') {
|
|
const credentials = await this.getCredentials('theHiveApi') as IDataObject;
|
|
|
|
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
|
|
|
const version = credentials.apiVersion;
|
|
|
|
const taskId = this.getNodeParameter('taskId', i) as string;
|
|
|
|
let endpoint;
|
|
|
|
let method;
|
|
|
|
let body: IDataObject = {};
|
|
|
|
let limit = undefined;
|
|
|
|
if (returnAll === false) {
|
|
limit = this.getNodeParameter('limit', i) as number;
|
|
}
|
|
|
|
if (version === 'v1') {
|
|
endpoint = '/v1/query';
|
|
|
|
method = 'POST';
|
|
|
|
body = {
|
|
'query': [
|
|
{
|
|
'_name': 'getTask',
|
|
'idOrName': taskId,
|
|
},
|
|
{
|
|
'_name': 'logs',
|
|
},
|
|
],
|
|
};
|
|
|
|
if (limit !== undefined) {
|
|
//@ts-ignore
|
|
prepareRangeQuery(`0-${limit}`, body);
|
|
}
|
|
|
|
qs.name = 'case-task-logs';
|
|
|
|
} else {
|
|
method = 'POST';
|
|
|
|
endpoint = '/case/task/log/_search';
|
|
|
|
if (limit !== undefined) {
|
|
qs.range = `0-${limit}`;
|
|
}
|
|
|
|
body.query = And(Parent(
|
|
'task',
|
|
Id(taskId),
|
|
));
|
|
}
|
|
|
|
responseData = await theHiveApiRequest.call(
|
|
this,
|
|
method,
|
|
endpoint as string,
|
|
body,
|
|
qs,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(responseData)) {
|
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
|
|
|
} else if (responseData !== undefined) {
|
|
returnData.push(responseData as IDataObject);
|
|
}
|
|
} catch (error) {
|
|
if (this.continueOnFail()) {
|
|
returnData.push({ error: error.message });
|
|
continue;
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
return [this.helpers.returnJsonArray(returnData)];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|