n8n/packages/nodes-base/nodes/TheHive/TheHive.node.ts
Iván Ovejero 0448feec56
refactor: Apply eslint-plugin-n8n-nodes-base autofixable rules (#3174)
*  Initial setup

* 👕 Update `.eslintignore`

* 👕 Autofix node-param-default-missing (#3173)

* 🔥 Remove duplicate key

* 👕 Add exceptions

* 📦 Update package-lock.json

* 👕 Apply `node-class-description-inputs-wrong-trigger-node` (#3176)

* 👕 Apply `node-class-description-inputs-wrong-regular-node` (#3177)

* 👕 Apply `node-class-description-outputs-wrong` (#3178)

* 👕 Apply `node-execute-block-double-assertion-for-items` (#3179)

* 👕 Apply `node-param-default-wrong-for-collection` (#3180)

* 👕 Apply node-param-default-wrong-for-boolean (#3181)

* Autofixed default missing

* Autofixed booleans, worked well

*  Fix params

*  Undo exempted autofixes

* 📦 Update package-lock.json

* 👕 Apply node-class-description-missing-subtitle (#3182)

*  Fix missing comma

* 👕 Apply `node-param-default-wrong-for-fixed-collection` (#3184)

* 👕 Add exception for `node-class-description-missing-subtitle`

* 👕 Apply `node-param-default-wrong-for-multi-options` (#3185)

* 👕 Apply `node-param-collection-type-unsorted-items` (#3186)

* Missing coma

* 👕 Apply `node-param-default-wrong-for-simplify` (#3187)

* 👕 Apply `node-param-description-comma-separated-hyphen` (#3190)

* 👕 Apply `node-param-description-empty-string` (#3189)

* 👕 Apply `node-param-description-excess-inner-whitespace` (#3191)

* Rule looks good

* Add whitespace rule in eslint config

* :zao: fix

* 👕 Apply `node-param-description-identical-to-display-name` (#3193)

* 👕 Apply `node-param-description-missing-for-ignore-ssl-issues` (#3195)

*  Revert ":zao: fix"

This reverts commit ef8a76f3df.

* 👕 Apply `node-param-description-missing-for-simplify`  (#3196)

* 👕 Apply `node-param-description-missing-final-period` (#3194)

* Rule working as intended

* Add rule to eslint

* 👕 Apply node-param-description-missing-for-return-all (#3197)

*  Restore `lintfix` command

Co-authored-by: agobrech <45268029+agobrech@users.noreply.github.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
Co-authored-by: agobrech <ael.gobrecht@gmail.com>
Co-authored-by: Michael Kret <michael.k@radency.com>
2022-04-22 18:29:51 +02:00

2113 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',
},
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');
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');
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');
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');
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;
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');
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');
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');
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');
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');
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');
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');
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');
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');
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');
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');
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)];
}
}