first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { TLPs } from './AnalyzerInterface';
|
||||
|
||||
export const analyzersOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
required: true,
|
||||
description: 'Choose an operation',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
},
|
||||
},
|
||||
default: 'execute',
|
||||
options: [
|
||||
{
|
||||
name: 'Execute',
|
||||
value: 'execute',
|
||||
description: 'Execute Analyzer',
|
||||
action: 'Execute an analyzer',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const analyzerFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Analyzer Type Name or ID',
|
||||
name: 'analyzer',
|
||||
type: 'options',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadActiveAnalyzers',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Choose the analyzer. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Observable Type Name or ID',
|
||||
name: 'observableType',
|
||||
type: 'options',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
hide: {
|
||||
analyzer: [''],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadObservableOptions',
|
||||
loadOptionsDependsOn: ['analyzer'],
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'Choose the observable type. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
|
||||
// Observable type != file
|
||||
{
|
||||
displayName: 'Observable Value',
|
||||
name: 'observableValue',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
hide: {
|
||||
observableType: ['file'],
|
||||
analyzer: [''],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Enter the observable value',
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output File in Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
observableType: ['file'],
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
},
|
||||
hint: 'The name of the output binary field to put the file in',
|
||||
},
|
||||
{
|
||||
displayName: 'TLP',
|
||||
name: 'tlp',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
hide: {
|
||||
observableType: [''],
|
||||
analyzer: [''],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'White',
|
||||
value: TLPs.white,
|
||||
},
|
||||
{
|
||||
name: 'Green',
|
||||
value: TLPs.green,
|
||||
},
|
||||
{
|
||||
name: 'Amber',
|
||||
value: TLPs.amber,
|
||||
},
|
||||
{
|
||||
name: 'Red',
|
||||
value: TLPs.red,
|
||||
},
|
||||
],
|
||||
default: 2,
|
||||
description: 'The TLP of the analyzed observable',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['analyzer'],
|
||||
operation: ['execute'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Force',
|
||||
name: 'force',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to force bypassing the cache',
|
||||
},
|
||||
{
|
||||
displayName: 'Timeout (Seconds)',
|
||||
name: 'timeout',
|
||||
type: 'number',
|
||||
default: 3,
|
||||
description:
|
||||
'Timeout to wait for the report in case it is not available at the time the query was made',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,91 @@
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
|
||||
export const JobStatuses = {
|
||||
WAITING: 'Waiting',
|
||||
INPROGRESS: 'InProgress',
|
||||
SUCCESS: 'Success',
|
||||
FAILURE: 'Failure',
|
||||
DELETED: 'Deleted',
|
||||
} as const;
|
||||
|
||||
export type JobStatus = (typeof JobStatuses)[keyof typeof JobStatuses];
|
||||
|
||||
export const TLPs = {
|
||||
white: 0,
|
||||
green: 1,
|
||||
amber: 2,
|
||||
red: 3,
|
||||
} as const;
|
||||
|
||||
export type TLP = (typeof TLPs)[keyof typeof TLPs];
|
||||
|
||||
export const ObservableDataTypes = {
|
||||
domain: 'domain',
|
||||
file: 'file',
|
||||
filename: 'filename',
|
||||
fqdn: 'fqdn',
|
||||
hash: 'hash',
|
||||
ip: 'ip',
|
||||
mail: 'mail',
|
||||
mail_subject: 'mail_subject',
|
||||
other: 'other',
|
||||
regexp: 'regexp',
|
||||
registry: 'registry',
|
||||
uri_path: 'uri_path',
|
||||
url: 'url',
|
||||
'user-agent': 'user-agent',
|
||||
} as const;
|
||||
|
||||
export type ObservableDataType = (typeof ObservableDataTypes)[keyof typeof ObservableDataTypes];
|
||||
|
||||
export interface IJob {
|
||||
id?: string;
|
||||
organization?: string;
|
||||
analyzerDefinitionId?: string;
|
||||
analyzerId?: string;
|
||||
analyzerName?: string;
|
||||
dataType?: ObservableDataType;
|
||||
status?: JobStatus;
|
||||
data?: string;
|
||||
attachment?: IDataObject;
|
||||
parameters?: IDataObject;
|
||||
message?: string;
|
||||
tlp?: TLP;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
createdAt?: Date;
|
||||
createdBy?: string;
|
||||
updatedAt?: Date;
|
||||
updatedBy?: Date;
|
||||
report?: IDataObject | string;
|
||||
}
|
||||
export interface IAnalyzer {
|
||||
id?: string;
|
||||
analyzerDefinitionId?: string;
|
||||
name?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
author?: string;
|
||||
url?: string;
|
||||
license?: string;
|
||||
dataTypeList?: ObservableDataType[];
|
||||
baseConfig?: string;
|
||||
jobCache?: number;
|
||||
rate?: number;
|
||||
rateUnit?: string;
|
||||
configuration?: IDataObject;
|
||||
createdBy?: string;
|
||||
updatedAt?: Date;
|
||||
updatedBy?: Date;
|
||||
}
|
||||
|
||||
export interface IResponder {
|
||||
id?: string;
|
||||
name?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
dataTypeList?: string[];
|
||||
maxTlp?: number;
|
||||
maxPap?: number;
|
||||
cortexIds?: string[] | undefined;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.cortex",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Development", "Analytics"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/cortex/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.cortex/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
import * as changeCase from 'change-case';
|
||||
import { createHash } from 'crypto';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionTypes } from 'n8n-workflow';
|
||||
|
||||
import { analyzerFields, analyzersOperations } from './AnalyzerDescriptions';
|
||||
import type { IJob } from './AnalyzerInterface';
|
||||
import { cortexApiRequest, getEntityLabel, prepareParameters, splitTags } from './GenericFunctions';
|
||||
import { jobFields, jobOperations } from './JobDescription';
|
||||
import { responderFields, respondersOperations } from './ResponderDescription';
|
||||
|
||||
export class Cortex implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Cortex',
|
||||
name: 'cortex',
|
||||
icon: 'file:cortex.svg',
|
||||
group: ['transform'],
|
||||
subtitle: '={{$parameter["operation"]+ ": " + $parameter["resource"]}}',
|
||||
version: 1,
|
||||
description: 'Apply the Cortex analyzer/responder on the given entity',
|
||||
defaults: {
|
||||
name: 'Cortex',
|
||||
},
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'cortexApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
// Node properties which the user gets displayed and
|
||||
// can change on the node.
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Analyzer',
|
||||
value: 'analyzer',
|
||||
},
|
||||
{
|
||||
name: 'Job',
|
||||
value: 'job',
|
||||
},
|
||||
{
|
||||
name: 'Responder',
|
||||
value: 'responder',
|
||||
},
|
||||
],
|
||||
default: 'analyzer',
|
||||
description: 'Choose a resource',
|
||||
required: true,
|
||||
},
|
||||
...analyzersOperations,
|
||||
...analyzerFields,
|
||||
...respondersOperations,
|
||||
...responderFields,
|
||||
...jobOperations,
|
||||
...jobFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async loadActiveAnalyzers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
// request the enabled analyzers from instance
|
||||
const requestResult = await cortexApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'/analyzer/_search?range=all',
|
||||
);
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
|
||||
for (const analyzer of requestResult) {
|
||||
returnData.push({
|
||||
name: analyzer.name as string,
|
||||
value: `${analyzer.id as string}::${analyzer.name as string}`,
|
||||
description: analyzer.description as string,
|
||||
});
|
||||
}
|
||||
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async loadActiveResponders(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
// request the enabled responders from instance
|
||||
const requestResult = await cortexApiRequest.call(this, 'GET', '/responder');
|
||||
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const responder of requestResult) {
|
||||
returnData.push({
|
||||
name: responder.name as string,
|
||||
value: `${responder.id as string}::${responder.name as string}`,
|
||||
description: responder.description as string,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async loadObservableOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const selectedAnalyzerId = (this.getNodeParameter('analyzer') as string).split('::')[0];
|
||||
// request the analyzers from instance
|
||||
const requestResult = await cortexApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/analyzer/${selectedAnalyzerId}`,
|
||||
);
|
||||
|
||||
// parse supported observable types into options
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const dataType of requestResult.dataTypeList) {
|
||||
returnData.push({
|
||||
name: upperFirst(dataType as string),
|
||||
value: dataType as string,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
||||
async loadDataTypeOptions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const selectedResponderId = (this.getNodeParameter('responder') as string).split('::')[0];
|
||||
// request the responder from instance
|
||||
const requestResult = await cortexApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/responder/${selectedResponderId}`,
|
||||
);
|
||||
// parse the accepted dataType into options
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
for (const dataType of requestResult.dataTypeList) {
|
||||
returnData.push({
|
||||
value: (dataType as string).split(':')[1],
|
||||
name: changeCase.capitalCase((dataType as string).split(':')[1]),
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
const length = items.length;
|
||||
let responseData;
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
if (resource === 'analyzer') {
|
||||
//https://github.com/TheHive-Project/CortexDocs/blob/master/api/api-guide.md#run
|
||||
if (operation === 'execute') {
|
||||
let force = false;
|
||||
|
||||
const analyzer = this.getNodeParameter('analyzer', i) as string;
|
||||
|
||||
const observableType = this.getNodeParameter('observableType', i) as string;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
|
||||
const tlp = this.getNodeParameter('tlp', i) as string;
|
||||
|
||||
const body: IDataObject = {
|
||||
dataType: observableType,
|
||||
tlp,
|
||||
};
|
||||
|
||||
if (additionalFields.force === true) {
|
||||
force = true;
|
||||
}
|
||||
|
||||
if (observableType === 'file') {
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
|
||||
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
||||
const fileBufferData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
||||
|
||||
const options = {
|
||||
formData: {
|
||||
data: {
|
||||
value: fileBufferData,
|
||||
options: {
|
||||
contentType: binaryData.mimeType,
|
||||
filename: binaryData.fileName,
|
||||
},
|
||||
},
|
||||
_json: JSON.stringify({
|
||||
dataType: observableType,
|
||||
tlp,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
responseData = (await cortexApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/analyzer/${analyzer.split('::')[0]}/run`,
|
||||
{},
|
||||
{ force },
|
||||
'',
|
||||
options,
|
||||
)) as IJob;
|
||||
} else {
|
||||
const observableValue = this.getNodeParameter('observableValue', i) as string;
|
||||
|
||||
body.data = observableValue;
|
||||
|
||||
responseData = (await cortexApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/analyzer/${analyzer.split('::')[0]}/run`,
|
||||
body,
|
||||
{ force },
|
||||
)) as IJob;
|
||||
}
|
||||
|
||||
if (additionalFields.timeout) {
|
||||
responseData = await cortexApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/job/${responseData.id}/waitreport`,
|
||||
{},
|
||||
{ atMost: `${additionalFields.timeout}second` },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'job') {
|
||||
//https://github.com/TheHive-Project/CortexDocs/blob/master/api/api-guide.md#get-details-1
|
||||
if (operation === 'get') {
|
||||
const jobId = this.getNodeParameter('jobId', i) as string;
|
||||
|
||||
responseData = await cortexApiRequest.call(this, 'GET', `/job/${jobId}`);
|
||||
}
|
||||
//https://github.com/TheHive-Project/CortexDocs/blob/master/api/api-guide.md#get-details-and-report
|
||||
if (operation === 'report') {
|
||||
const jobId = this.getNodeParameter('jobId', i) as string;
|
||||
|
||||
responseData = await cortexApiRequest.call(this, 'GET', `/job/${jobId}/report`);
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'responder') {
|
||||
if (operation === 'execute') {
|
||||
const responderId = (this.getNodeParameter('responder', i) as string).split('::')[0];
|
||||
|
||||
const entityType = this.getNodeParameter('entityType', i) as string;
|
||||
|
||||
const isJSON = this.getNodeParameter('jsonObject', i) as boolean;
|
||||
let body: IDataObject;
|
||||
|
||||
if (isJSON) {
|
||||
const entityJson = JSON.parse(this.getNodeParameter('objectData', i) as string);
|
||||
|
||||
body = {
|
||||
responderId,
|
||||
label: getEntityLabel(entityJson as IDataObject),
|
||||
dataType: `thehive:${entityType}`,
|
||||
data: entityJson,
|
||||
tlp: entityJson.tlp || 2,
|
||||
pap: entityJson.pap || 2,
|
||||
message: entityJson.message || '',
|
||||
parameters: [],
|
||||
};
|
||||
} else {
|
||||
const values = (this.getNodeParameter('parameters', i) as IDataObject)
|
||||
.values as IDataObject;
|
||||
|
||||
body = {
|
||||
responderId,
|
||||
dataType: `thehive:${entityType}`,
|
||||
data: {
|
||||
_type: entityType,
|
||||
...prepareParameters(values),
|
||||
},
|
||||
};
|
||||
if (entityType === 'alert') {
|
||||
// deal with alert artifacts
|
||||
const artifacts = (body.data as IDataObject).artifacts as IDataObject;
|
||||
|
||||
if (artifacts) {
|
||||
const artifactValues = artifacts.artifactValues as IDataObject[];
|
||||
|
||||
if (artifactValues) {
|
||||
const artifactData = [];
|
||||
|
||||
for (const artifactvalue of artifactValues) {
|
||||
const element: IDataObject = {};
|
||||
|
||||
element.message = artifactvalue.message as string;
|
||||
|
||||
element.tags = splitTags(artifactvalue.tags as string);
|
||||
|
||||
element.dataType = artifactvalue.dataType as string;
|
||||
|
||||
element.data = artifactvalue.data as string;
|
||||
|
||||
if (artifactvalue.dataType === 'file') {
|
||||
const binaryPropertyName = artifactvalue.binaryProperty as string;
|
||||
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
||||
|
||||
element.data = `${binaryData.fileName};${binaryData.mimeType};${binaryData.data}`;
|
||||
}
|
||||
|
||||
artifactData.push(element);
|
||||
}
|
||||
|
||||
(body.data as IDataObject).artifacts = artifactData;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entityType === 'case_artifact') {
|
||||
// deal with file observable
|
||||
|
||||
if ((body.data as IDataObject).dataType === 'file') {
|
||||
const binaryPropertyName = (body.data as IDataObject)
|
||||
.binaryPropertyName as string;
|
||||
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
||||
const fileBufferData = await this.helpers.getBinaryDataBuffer(
|
||||
i,
|
||||
binaryPropertyName,
|
||||
);
|
||||
const sha256 = createHash('sha256').update(fileBufferData).digest('hex');
|
||||
|
||||
(body.data as IDataObject).attachment = {
|
||||
name: binaryData.fileName,
|
||||
hashes: [
|
||||
sha256,
|
||||
createHash('sha1').update(fileBufferData).digest('hex'),
|
||||
createHash('md5').update(fileBufferData).digest('hex'),
|
||||
],
|
||||
size: fileBufferData.byteLength,
|
||||
contentType: binaryData.mimeType,
|
||||
id: sha256,
|
||||
};
|
||||
|
||||
delete (body.data as IDataObject).binaryPropertyName;
|
||||
}
|
||||
}
|
||||
// add the job label after getting all entity attributes
|
||||
body = {
|
||||
label: getEntityLabel(body.data as IDataObject),
|
||||
...body,
|
||||
};
|
||||
}
|
||||
responseData = (await cortexApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/responder/${responderId}/run`,
|
||||
body,
|
||||
)) as IJob;
|
||||
}
|
||||
}
|
||||
|
||||
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)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import moment from 'moment-timezone';
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
IHttpRequestMethods,
|
||||
ILoadOptionsFunctions,
|
||||
IRequestOptions,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function cortexApiRequest(
|
||||
this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
uri?: string,
|
||||
option: IDataObject = {},
|
||||
): Promise<any> {
|
||||
const credentials = await this.getCredentials('cortexApi');
|
||||
|
||||
let options: IRequestOptions = {
|
||||
headers: {},
|
||||
method,
|
||||
qs: query,
|
||||
uri: uri || `${credentials.host}/api${resource}`,
|
||||
body,
|
||||
json: true,
|
||||
};
|
||||
if (Object.keys(option).length !== 0) {
|
||||
options = Object.assign({}, options, option);
|
||||
}
|
||||
if (Object.keys(body as IDataObject).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
if (Object.keys(query).length === 0) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
return await this.helpers.requestWithAuthentication.call(this, 'cortexApi', options);
|
||||
}
|
||||
|
||||
export function getEntityLabel(entity: IDataObject): string {
|
||||
let label = '';
|
||||
switch (entity._type) {
|
||||
case 'case':
|
||||
label = `#${entity.caseId} ${entity.title}`;
|
||||
break;
|
||||
case 'case_artifact':
|
||||
//@ts-ignore
|
||||
label = `[${entity.dataType}] ${entity.data ? entity.data : entity.attachment.name}`;
|
||||
break;
|
||||
case 'alert':
|
||||
label = `[${entity.source}:${entity.sourceRef}] ${entity.title}`;
|
||||
break;
|
||||
case 'case_task_log':
|
||||
label = `${entity.message} from ${entity.createdBy}`;
|
||||
break;
|
||||
case 'case_task':
|
||||
label = `${entity.title} (${entity.status})`;
|
||||
break;
|
||||
case 'job':
|
||||
label = `${entity.analyzerName} (${entity.status})`;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
export function splitTags(tags: string): string[] {
|
||||
return tags.split(',').filter((tag) => tag !== ' ' && tag);
|
||||
}
|
||||
|
||||
export function prepareParameters(values: IDataObject): IDataObject {
|
||||
const response: IDataObject = {};
|
||||
for (const key in values) {
|
||||
if (values[key] !== undefined && values[key] !== null && values[key] !== '') {
|
||||
if (moment(values[key] as string, moment.ISO_8601).isValid()) {
|
||||
response[key] = Date.parse(values[key] as string);
|
||||
} else if (key === 'tags') {
|
||||
response[key] = splitTags(values[key] as string);
|
||||
} else {
|
||||
response[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const jobOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
description: 'Choose an operation',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['job'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get job details',
|
||||
action: 'Get a job',
|
||||
},
|
||||
{
|
||||
name: 'Report',
|
||||
value: 'report',
|
||||
description: 'Get job report',
|
||||
action: 'Get a job report',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
},
|
||||
];
|
||||
|
||||
export const jobFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Job ID',
|
||||
name: 'jobId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['job'],
|
||||
operation: ['get', 'report'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'ID of the job',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,785 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { TLPs } from './AnalyzerInterface';
|
||||
|
||||
export const respondersOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
required: true,
|
||||
description: 'Choose an operation',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Execute',
|
||||
value: 'execute',
|
||||
description: 'Execute Responder',
|
||||
action: 'Execute a responder',
|
||||
},
|
||||
],
|
||||
default: 'execute',
|
||||
},
|
||||
];
|
||||
|
||||
export const responderFields: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Responder Type Name or ID',
|
||||
name: 'responder',
|
||||
type: 'options',
|
||||
required: true,
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadActiveResponders',
|
||||
},
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'Choose the responder. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Entity Type Name or ID',
|
||||
name: 'entityType',
|
||||
type: 'options',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'loadDataTypeOptions',
|
||||
loadOptionsDependsOn: ['responder'],
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'Choose the Data type. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'JSON Parameters',
|
||||
name: 'jsonObject',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
|
||||
description: 'Choose between providing JSON object or seperated attributes',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Entity Object (JSON)',
|
||||
name: 'objectData',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [true],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Parameters',
|
||||
name: 'parameters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Parameter',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Case Attributes',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Description of the case',
|
||||
},
|
||||
{
|
||||
displayName: 'Flag',
|
||||
name: 'flag',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-description-boolean-without-whether
|
||||
description: 'Flag of the case default=false',
|
||||
},
|
||||
{
|
||||
displayName: 'Owner',
|
||||
name: 'owner',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'User who owns the case. This is automatically set to current user when status is set to InProgress.',
|
||||
},
|
||||
{
|
||||
displayName: 'Severity',
|
||||
name: 'severity',
|
||||
type: 'options',
|
||||
default: 2,
|
||||
options: [
|
||||
{
|
||||
name: 'Low',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: 'Medium',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
name: 'High',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
description: 'Severity of the case. Default=Medium.',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Date',
|
||||
name: 'startDate',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Date and time of the begin of the case default=now',
|
||||
},
|
||||
{
|
||||
displayName: 'Tags',
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'tag1,tag2,...',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Title of the case',
|
||||
},
|
||||
{
|
||||
displayName: 'TLP',
|
||||
name: 'tlp',
|
||||
type: 'options',
|
||||
default: 2,
|
||||
options: [
|
||||
{
|
||||
name: 'White',
|
||||
value: TLPs.white,
|
||||
},
|
||||
{
|
||||
name: 'Green',
|
||||
value: TLPs.green,
|
||||
},
|
||||
{
|
||||
name: 'Amber',
|
||||
value: TLPs.amber,
|
||||
},
|
||||
{
|
||||
name: 'Red',
|
||||
value: TLPs.red,
|
||||
},
|
||||
],
|
||||
description: 'Traffic Light Protocol (TLP). Default=Amber.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['entityType'],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [false],
|
||||
entityType: ['case'],
|
||||
},
|
||||
hide: {
|
||||
entityType: ['', 'alert', 'case_artifact', 'case_task', 'case_task_log'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
{
|
||||
displayName: 'Parameters',
|
||||
name: 'parameters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Parameter',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Alert Attributes',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Artifacts',
|
||||
name: 'artifacts',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add an artifact',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
multipleValueButtonText: 'Add an Artifact',
|
||||
},
|
||||
default: [],
|
||||
options: [
|
||||
{
|
||||
displayName: 'Artifact',
|
||||
name: 'artifactValues',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Binary Field',
|
||||
name: 'binaryProperty',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
dataType: ['file'],
|
||||
},
|
||||
},
|
||||
default: 'data',
|
||||
},
|
||||
{
|
||||
displayName: 'Data',
|
||||
name: 'data',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
dataType: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Data Type',
|
||||
name: 'dataType',
|
||||
type: 'options',
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Domain',
|
||||
value: 'domain',
|
||||
},
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
name: 'Filename',
|
||||
value: 'filename',
|
||||
},
|
||||
{
|
||||
name: 'Fqdn',
|
||||
value: 'fqdn',
|
||||
},
|
||||
{
|
||||
name: 'Hash',
|
||||
value: 'hash',
|
||||
},
|
||||
{
|
||||
name: 'IP',
|
||||
value: 'ip',
|
||||
},
|
||||
{
|
||||
name: 'Mail',
|
||||
value: 'mail',
|
||||
},
|
||||
{
|
||||
name: 'Mail Subject',
|
||||
value: 'mail_subject',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
{
|
||||
name: 'Regexp',
|
||||
value: 'regexp',
|
||||
},
|
||||
{
|
||||
name: 'Registry',
|
||||
value: 'registry',
|
||||
},
|
||||
{
|
||||
name: 'Uri Path',
|
||||
value: 'uri_path',
|
||||
},
|
||||
{
|
||||
name: 'URL',
|
||||
value: 'url',
|
||||
},
|
||||
{
|
||||
name: 'User Agent',
|
||||
value: 'user-agent',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Tags',
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Date',
|
||||
name: 'date',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Date and time when the alert was raised default=now',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Description of the alert',
|
||||
},
|
||||
{
|
||||
displayName: 'Follow',
|
||||
name: 'follow',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
displayName: 'Severity',
|
||||
name: 'severity',
|
||||
type: 'options',
|
||||
default: 2,
|
||||
options: [
|
||||
{
|
||||
name: 'Low',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: 'Medium',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
name: 'High',
|
||||
value: 3,
|
||||
},
|
||||
],
|
||||
description: 'Severity of the case. Default=Medium.',
|
||||
},
|
||||
{
|
||||
displayName: 'Source',
|
||||
name: 'source',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Source of the alert',
|
||||
},
|
||||
{
|
||||
displayName: 'SourceRef',
|
||||
name: 'sourceRef',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Source reference of the alert',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
default: 'New',
|
||||
options: [
|
||||
{
|
||||
name: 'New',
|
||||
value: 'New',
|
||||
},
|
||||
{
|
||||
name: 'Updated',
|
||||
value: 'Updated',
|
||||
},
|
||||
{
|
||||
name: 'Ignored',
|
||||
value: 'Ignored',
|
||||
},
|
||||
{
|
||||
name: 'Imported',
|
||||
value: 'Imported',
|
||||
},
|
||||
],
|
||||
description: 'Status of the alert. Default=New.',
|
||||
},
|
||||
{
|
||||
displayName: 'Tags',
|
||||
name: 'tags',
|
||||
type: 'string',
|
||||
placeholder: 'tag1,tag2,...',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Title of the alert',
|
||||
},
|
||||
{
|
||||
displayName: 'TLP',
|
||||
name: 'tlp',
|
||||
type: 'options',
|
||||
default: 2,
|
||||
options: [
|
||||
{
|
||||
name: 'White',
|
||||
value: TLPs.white,
|
||||
},
|
||||
{
|
||||
name: 'Green',
|
||||
value: TLPs.green,
|
||||
},
|
||||
{
|
||||
name: 'Amber',
|
||||
value: TLPs.amber,
|
||||
},
|
||||
{
|
||||
name: 'Red',
|
||||
value: TLPs.red,
|
||||
},
|
||||
],
|
||||
description: 'Traffic Light Protocol (TLP). Default=Amber.',
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Type of the alert',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['entityType'],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [false],
|
||||
entityType: ['alert'],
|
||||
},
|
||||
hide: {
|
||||
responder: [''],
|
||||
entityType: ['', 'case', 'case_artifact', 'case_task', 'case_task_log'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
{
|
||||
displayName: 'Parameters',
|
||||
name: 'parameters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Parameter',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Observable Attributes',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Input Binary Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
dataType: ['file'],
|
||||
},
|
||||
},
|
||||
hint: 'The name of the input binary field containing the attachement data',
|
||||
},
|
||||
{
|
||||
displayName: 'Data',
|
||||
name: 'data',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
dataType: ['file'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'DataType',
|
||||
name: 'dataType',
|
||||
type: 'options',
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Domain',
|
||||
value: 'domain',
|
||||
},
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
name: 'Filename',
|
||||
value: 'filename',
|
||||
},
|
||||
{
|
||||
name: 'Fqdn',
|
||||
value: 'fqdn',
|
||||
},
|
||||
{
|
||||
name: 'Hash',
|
||||
value: 'hash',
|
||||
},
|
||||
{
|
||||
name: 'IP',
|
||||
value: 'ip',
|
||||
},
|
||||
{
|
||||
name: 'Mail',
|
||||
value: 'mail',
|
||||
},
|
||||
{
|
||||
name: 'Mail Subject',
|
||||
value: 'mail_subject',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
{
|
||||
name: 'Regexp',
|
||||
value: 'regexp',
|
||||
},
|
||||
{
|
||||
name: 'Registry',
|
||||
value: 'registry',
|
||||
},
|
||||
{
|
||||
name: 'Uri Path',
|
||||
value: 'uri_path',
|
||||
},
|
||||
{
|
||||
name: 'URL',
|
||||
value: 'url',
|
||||
},
|
||||
{
|
||||
name: 'User Agent',
|
||||
value: 'user-agent',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'IOC',
|
||||
name: 'ioc',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether the observable is an IOC (Indicator of compromise)',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Date',
|
||||
name: 'startDate',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Date and time of the begin of the case default=now',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Ok',
|
||||
value: 'Ok',
|
||||
},
|
||||
{
|
||||
name: 'Deleted',
|
||||
value: 'Deleted',
|
||||
},
|
||||
],
|
||||
description: 'Status of the observable (Ok or Deleted) default=Ok',
|
||||
},
|
||||
{
|
||||
displayName: 'TLP',
|
||||
name: 'tlp',
|
||||
type: 'options',
|
||||
default: 2,
|
||||
options: [
|
||||
{
|
||||
name: 'White',
|
||||
value: TLPs.white,
|
||||
},
|
||||
{
|
||||
name: 'Green',
|
||||
value: TLPs.green,
|
||||
},
|
||||
{
|
||||
name: 'Amber',
|
||||
value: TLPs.amber,
|
||||
},
|
||||
{
|
||||
name: 'Red',
|
||||
value: TLPs.red,
|
||||
},
|
||||
],
|
||||
description: 'Traffic Light Protocol (TLP). Default=Amber.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['entityType'],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [false],
|
||||
entityType: ['case_artifact'],
|
||||
},
|
||||
hide: {
|
||||
responder: [''],
|
||||
entityType: ['', 'case', 'alert', 'case_task', 'case_task_log'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
{
|
||||
displayName: 'Parameters',
|
||||
name: 'parameters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Parameter',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Task Attributes',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Title of the task',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
default: 'Waiting',
|
||||
options: [
|
||||
{
|
||||
name: 'Waiting',
|
||||
value: 'Waiting',
|
||||
},
|
||||
{
|
||||
name: 'InProgress',
|
||||
value: 'InProgress',
|
||||
},
|
||||
{
|
||||
name: 'Completed',
|
||||
value: 'Completed',
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
value: 'Cancel',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Flag',
|
||||
name: 'flag',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['entityType'],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [false],
|
||||
entityType: ['case_task'],
|
||||
},
|
||||
hide: {
|
||||
responder: [''],
|
||||
entityType: ['', 'case', 'alert', 'case_artifact', 'case_task_log'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
{
|
||||
displayName: 'Parameters',
|
||||
name: 'parameters',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Parameter',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Log Attributes',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Date',
|
||||
name: 'startDate',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Date and time of the begin of the case default=now',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
required: true,
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
name: 'Ok',
|
||||
value: 'Ok',
|
||||
},
|
||||
{
|
||||
name: 'Deleted',
|
||||
value: 'Deleted',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: ['entityType'],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['responder'],
|
||||
jsonObject: [false],
|
||||
entityType: ['case_task_log'],
|
||||
},
|
||||
hide: {
|
||||
responder: [''],
|
||||
entityType: ['', 'case', 'alert', 'case_artifact', 'case_task'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 300 300"><defs><style>.cls-2{fill:#fff}</style></defs><path fill="#54c4c4" d="M290 150A140 140 0 1 1 150 10a140 140 0 0 1 140 140"/><path d="M231.17 159.29c0-12.87-6.51-14.17-18-15.36-10.18-1.06-21.71-2.25-26.73-16.65a1.9 1.9 0 0 1 .08-1.46 1.93 1.93 0 0 1 3.55.2c4.22 12.09 14 13.1 23.5 14.09 4.68.48 10 1 14.12 3.4a1.4 1.4 0 0 0 1.45 0 1.39 1.39 0 0 0 .64-1.3c-2.23-25.82-13.16-38.79-21.94-45.12a35.5 35.5 0 0 0-12.07-5.8 1.39 1.39 0 0 0-1.56 2 49.6 49.6 0 0 1 4 11c2.18 7.91 4.06 14.74 12.62 16.44a1.93 1.93 0 0 1 1.51 2.26 1.93 1.93 0 0 1-1.88 1.55 1.6 1.6 0 0 1-.38 0c-10.9-2.16-13.28-10.82-15.59-19.23-1.55-5.61-3-10.91-7-14.2-5.61-4.65-15-7.78-23.37-7.78a29 29 0 0 0-4.05.27 18.5 18.5 0 0 0-9.68 4.07 1.4 1.4 0 0 0-.47 1 1.38 1.38 0 0 0 .37 1 73.9 73.9 0 0 1 11.13 16.84 1.92 1.92 0 0 1-.85 2.58 2 2 0 0 1-2.59-.86c-3.87-7.76-8.11-13.86-12.58-18.12-6.31-6-11.94-7.25-15.56-7.25a14.7 14.7 0 0 0-2.85.27c-5.1 1-8.5 4.35-9.51 7a1.44 1.44 0 0 0 .12 1.23 32.6 32.6 0 0 1 2.62 5.94c2.32 6.38 4.51 12.4 15.48 12.91a1.88 1.88 0 0 1 1.33.62 1.9 1.9 0 0 1 .5 1.39 1.89 1.89 0 0 1-2 1.83c-13.53-.62-16.52-8.84-18.91-15.44a25.6 25.6 0 0 0-2.62-5.75 1 1 0 0 0-.14-.16 2 2 0 0 1-.24-.32 2 2 0 0 0-.17-.22 5.88 5.88 0 0 0-4.76-1.9c-4.69 0-8.26 1.47-10.62 4.4a13.5 13.5 0 0 0-2.12 3.84 2 2 0 0 0-.07.26 2.2 2.2 0 0 1-.15.5 1 1 0 0 0-.07.17 25.75 25.75 0 0 0-.3 12.29c1.6 7.48 6.24 16.89 19.27 19.25a22.5 22.5 0 0 0 3.92.32 71 71 0 0 0 9.77-1 83.6 83.6 0 0 1 12-1.12 33.73 33.73 0 0 1 17 4.21 1.43 1.43 0 0 0 1.16.1 1.4 1.4 0 0 0 .83-.81c2.3-5.79 5.74-9.55 8.51-12.57 5.55-6.06 9.56-10.44 3.79-25.68a1.92 1.92 0 0 1 .05-1.47 1.9 1.9 0 0 1 1.07-1 2 2 0 0 1 2.34 1.05c6.6 17.45 1.43 23.1-4.56 29.65-3 3.23-6.32 6.89-8.31 12.79a1.4 1.4 0 0 0 .51 1.58c13.47 9.74 20.52 13 27.92 13a25 25 0 0 0 2.83-.16 1.93 1.93 0 0 1 1.7 3.12 1.88 1.88 0 0 1-1.29.7 30 30 0 0 1-3.33.19c-8.5 0-16.58-3.82-32-15.17a29.36 29.36 0 0 0-18.21-5.8 80 80 0 0 0-11.55 1.1 73 73 0 0 1-10.3 1 26 26 0 0 1-4.62-.39c-11.8-2.14-19.74-10-22.36-22.22a33.7 33.7 0 0 1-.69-6.26 1.41 1.41 0 0 0-2.36-1c-11 10.25-11.68 20.34-11.09 25.44.93 8.21 5.87 14.26 10.27 16a13 13 0 0 0 4.89.84 47 47 0 0 0 6.16-.58 61.5 61.5 0 0 1 8.46-.72c8.41 0 16.19 2.86 24.51 9a1.92 1.92 0 0 1 .42 2.68 2 2 0 0 1-2.69.42c-7.63-5.62-14.69-8.23-22.21-8.23a58 58 0 0 0-8 .69 51 51 0 0 1-6.68.6 16.8 16.8 0 0 1-6.29-1.1c-5.42-2.14-10.13-8.22-12-15.48a1.4 1.4 0 0 0-1.2-1.02 1.38 1.38 0 0 0-1.41.81c-5.25 11.33-2.3 22.75 3 29a19 19 0 0 0 14.9 7.15 26.43 26.43 0 0 0 13-3.8 1.38 1.38 0 0 0 .67-.88 25.8 25.8 0 0 1 2.83-7 2 2 0 0 1 2.64-.65 1.93 1.93 0 0 1 .65 2.64c-3.74 6.18-4.8 16.88.58 24.55 4.11 5.85 11 8.82 20.56 8.82.83 0 1.68 0 2.55-.07a1.42 1.42 0 0 0 1.2-.82A1.37 1.37 0 0 0 134 192c-4.91-6.44.5-13.39 5.74-20.11s10.6-13.6 8.48-21.23a1.92 1.92 0 0 1 1.33-2.36 1.94 1.94 0 0 1 2.37 1.33c2.64 9.49-3.9 17.88-9.15 24.62-5.94 7.63-9.1 12.15-4.85 16.4a6.77 6.77 0 0 0 5 2.35c2.73 0 5.92-1.69 12-5.46 5.24-3.23 12.41-7.66 21.64-10.41a1.41 1.41 0 0 0 .2-2.61 16.2 16.2 0 0 1-6-4.52c-2.39-3.06-3-6.84-1.91-11.24a2 2 0 0 1 2.33-1.39 1.93 1.93 0 0 1 1.4 2.34c-.81 3.19-.4 5.86 1.21 7.93 2 2.64 7.28 6 20.7 6.82 2.23 0 4.41 0 6.69.2a54.2 54.2 0 0 0 19.34-3.53 1.4 1.4 0 0 0 .52-2.23c-1.3-1.45-3.14-2.3-5.59-3.35-3.81-1.61-8.54-3.62-9.18-10.19a1.93 1.93 0 0 1 1.73-2.11 1.91 1.91 0 0 1 2.1 1.73c.41 4.29 3.25 5.5 6.86 7 2.53 1.08 5.68 2.41 7.72 5.3a1.41 1.41 0 0 0 2 .28c2.95-2.4 4.45-5.21 4.45-8.34" class="cls-2"/><path d="M200 177.4c-1.1 0-2.15-.09-3.29-.15-17.52.28-29 7.36-37.35 12.52a66 66 0 0 1-8.53 4.69 1.4 1.4 0 0 0-.22 2.47 37.6 37.6 0 0 0 4.61 2.42 19.8 19.8 0 0 0 5.63 1.44c5 .74 9 1.33 10.77 15.26a1.4 1.4 0 0 0 1.39 1.22h9.87a1.4 1.4 0 0 0 1.39-1.6c-1.93-13.8-.17-14.81 3.71-17a28 28 0 0 0 4.37-2.93c3.83-3.23 7.8-6.56 9-16.75a1.38 1.38 0 0 0-.33-1.08 1.4 1.4 0 0 0-1-.48" class="cls-2"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
Reference in New Issue
Block a user