UNPKG

n8n-nodes-binalyze-air

Version:

Binalyze AIR nodes for automating DFIR with n8n workflows

643 lines 24.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InterACTOperations = void 0; exports.executeInterACT = executeInterACT; exports.getCasesByOrganization = getCasesByOrganization; exports.getAssetsByOrganizationForInteract = getAssetsByOrganizationForInteract; const n8n_workflow_1 = require("n8n-workflow"); const helpers_1 = require("../utils/helpers"); const interact_1 = require("../api/interact/interact"); const tasks_1 = require("../api/tasks/tasks"); const organizations_1 = require("./organizations"); const cases_1 = require("../api/cases/cases"); const assets_1 = require("../api/assets/assets"); function isValidCase(entity) { return entity && entity._id && typeof entity._id === 'string'; } function extractCaseId(entity) { if (!entity) return ''; return entity._id || ''; } function isValidAsset(entity) { return entity && entity._id && typeof entity._id === 'string'; } function extractAssetId(entity) { if (!entity) return ''; return entity._id || ''; } exports.InterACTOperations = [ { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['interact'], }, }, options: [ { name: 'Close Session', value: 'closeSession', description: 'Close an InterACT session', action: 'Close a session', }, { name: 'Create InterACT Session', value: 'createSession', description: 'Create a new InterACT shell session for an asset', action: 'Create an interact session', }, { name: 'Execute Async Command', value: 'executeAsyncCommand', description: 'Execute an asynchronous command in an InterACT session', action: 'Execute an async command', }, { name: 'Execute Command', value: 'executeCommand', description: 'Execute a command in an InterACT session', action: 'Execute a command', }, { name: 'Get Command Response', value: 'getCommandMessage', description: 'Get the result of a command execution', action: 'Get a command response', }, { name: 'Interrupt Command', value: 'interruptCommand', description: 'Interrupt a running command', action: 'Interrupt a command', }, { name: 'Wait for Session to Be Live', value: 'waitForSession', description: 'Wait for an InterACT session to become live by monitoring task status', action: 'Wait for session to be live', }, ], default: 'createSession', }, { displayName: 'Organization', name: 'organizationId', type: 'resourceLocator', default: { mode: 'list', value: '' }, placeholder: 'Select an organization...', displayOptions: { show: { resource: ['interact'], operation: ['createSession'], }, }, modes: [ { displayName: 'From List', name: 'list', type: 'list', placeholder: 'Select an organization...', typeOptions: { searchListMethod: 'getOrganizations', searchable: true, }, }, { displayName: 'By ID', name: 'id', type: 'string', validation: [ { type: 'regex', properties: { regex: '^[0-9]+$', errorMessage: 'Not a valid organization ID (must be a positive number)', }, }, ], placeholder: 'Enter Organization ID', }, { displayName: 'By Name', name: 'name', type: 'string', placeholder: 'Enter organization name', }, ], required: true, description: 'The organization that the asset belongs to', }, { displayName: 'Case', name: 'caseId', type: 'resourceLocator', default: { mode: 'list', value: '' }, placeholder: 'Select a case...', displayOptions: { show: { resource: ['interact'], operation: ['createSession'], }, }, modes: [ { displayName: 'From List', name: 'list', type: 'list', placeholder: 'Select a case...', typeOptions: { searchListMethod: 'getCasesByOrganization', searchable: true, }, }, { displayName: 'By ID', name: 'id', type: 'string', validation: [ { type: 'regex', properties: { regex: '^[a-zA-Z0-9-_]+$', errorMessage: 'Not a valid case ID (must contain only letters, numbers, hyphens, and underscores)', }, }, ], placeholder: 'Enter case ID', }, ], description: 'The case to associate with the InterACT session (optional)', }, { displayName: 'Asset', name: 'assetId', type: 'resourceLocator', default: { mode: 'list', value: '' }, placeholder: 'Select an asset...', displayOptions: { show: { resource: ['interact'], operation: ['createSession'], }, }, modes: [ { displayName: 'From List', name: 'list', type: 'list', placeholder: 'Select an asset...', typeOptions: { searchListMethod: 'getAssetsByOrganizationForInteract', searchable: true, }, }, { displayName: 'By ID', name: 'id', type: 'string', validation: [ { type: 'regex', properties: { regex: '^[a-zA-Z0-9-_]+$', errorMessage: 'Not a valid asset ID (must contain only letters, numbers, hyphens, and underscores)', }, }, ], placeholder: 'Enter asset ID', }, ], required: true, description: 'The asset to create the InterACT session for', }, { displayName: 'Session ID', name: 'sessionId', type: 'string', default: '', placeholder: 'Enter session ID', displayOptions: { show: { resource: ['interact'], operation: ['executeCommand', 'executeAsyncCommand', 'closeSession', 'getCommandMessage', 'interruptCommand', 'waitForSession'], }, }, required: true, description: 'The InterACT session ID', }, { displayName: 'Task ID', name: 'taskId', type: 'string', default: '', placeholder: 'Enter task ID', displayOptions: { show: { resource: ['interact'], operation: ['waitForSession'], }, }, required: true, description: 'The InterACT session task ID to monitor for status changes', }, { displayName: 'Message ID', name: 'messageId', type: 'string', default: '', placeholder: 'Enter message ID', displayOptions: { show: { resource: ['interact'], operation: ['getCommandMessage', 'interruptCommand'], }, }, required: true, description: 'The command message ID', }, { displayName: 'Command', name: 'command', type: 'string', default: '', placeholder: 'Enter command to execute', displayOptions: { show: { resource: ['interact'], operation: ['executeCommand', 'executeAsyncCommand'], }, }, required: true, description: 'The command to execute in the InterACT session', }, { displayName: 'Response Type', name: 'responseType', type: 'options', default: 'json', displayOptions: { show: { resource: ['interact'], operation: ['executeCommand', 'executeAsyncCommand'], }, }, options: [ { name: 'JSON', value: 'json', description: 'Return response as JSON', }, { name: 'Text', value: 'text', description: 'Return response as plain text', }, ], description: 'The format of the command response', }, { displayName: 'Timeout (Seconds)', name: 'timeout', type: 'number', default: 60, placeholder: 'Enter timeout in seconds', displayOptions: { show: { resource: ['interact'], operation: ['waitForSession'], }, }, required: true, description: 'Maximum time to wait for the session to become live (in seconds). Set to 0 to wait indefinitely.', typeOptions: { minValue: 0, }, }, ]; async function executeInterACT() { const items = this.getInputData(); const returnData = []; const operation = this.getNodeParameter('operation', 0); try { const credentials = await (0, helpers_1.getAirCredentials)(this); for (let i = 0; i < items.length; i++) { try { let responseData; switch (operation) { case 'createSession': responseData = await handleCreateInterACTSession(this, credentials, i); break; case 'executeCommand': responseData = await handleExecuteCommand(this, credentials, i); break; case 'executeAsyncCommand': responseData = await handleExecuteAsyncCommand(this, credentials, i); break; case 'interruptCommand': responseData = await handleInterruptCommand(this, credentials, i); break; case 'closeSession': responseData = await handleCloseSession(this, credentials, i); break; case 'getCommandMessage': responseData = await handleGetCommandMessage(this, credentials, i); break; case 'waitForSession': responseData = await handleWaitForSession(this, credentials, i); break; default: throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The operation '${operation}' is not supported`); } const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } }); returnData.push(...executionData); } catch (error) { (0, helpers_1.handleExecuteError)(this, error, i, returnData); } } } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to execute InterACT operation: ${error instanceof Error ? error.message : String(error)}`); } return [returnData]; } async function handleCreateInterACTSession(context, credentials, itemIndex) { const organizationIdParam = context.getNodeParameter('organizationId', itemIndex); const caseIdParam = context.getNodeParameter('caseId', itemIndex, ''); const assetIdParam = context.getNodeParameter('assetId', itemIndex); if (typeof organizationIdParam === 'object' && organizationIdParam.mode === 'name') { const orgResult = await (0, organizations_1.findOrganizationByName)(context, credentials, organizationIdParam.value); if (!orgResult) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Organization not found'); } } let caseId = null; if (caseIdParam) { if (typeof caseIdParam === 'object' && caseIdParam.value) { caseId = caseIdParam.value; } else if (typeof caseIdParam === 'string' && caseIdParam) { caseId = caseIdParam; } } let assetId; if (typeof assetIdParam === 'object' && assetIdParam.value) { assetId = assetIdParam.value; } else if (typeof assetIdParam === 'string') { assetId = assetIdParam; } else { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Asset ID is required'); } const data = { assetId: assetId, caseId: caseId, taskConfig: { choice: 'use-policy' } }; const response = await interact_1.api.createInterACTSession(context, credentials, data); if (response.result && response.result.data) { const result = response.result; const baseUrl = credentials.instanceUrl.replace(/\/$/, ''); return { sessionId: result.data.sessionId, assetId: result.assetId || assetId, taskId: result.id, type: result.type, loginUrl: baseUrl + result.loginUrl, shellUrl: baseUrl + result.shellUrl, reportUrl: baseUrl + result.reportUrl }; } else { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Failed to create InterACT session'); } } async function handleExecuteCommand(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const command = context.getNodeParameter('command', itemIndex); const responseType = context.getNodeParameter('responseType', itemIndex); const data = { command, accept: responseType, }; const response = await interact_1.api.executeCommand(context, credentials, sessionId, data); return response.result; } async function handleExecuteAsyncCommand(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const command = context.getNodeParameter('command', itemIndex); const responseType = context.getNodeParameter('responseType', itemIndex); const data = { command, accept: responseType, }; const response = await interact_1.api.executeAsyncCommand(context, credentials, sessionId, data); return response.result; } async function handleInterruptCommand(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const messageId = context.getNodeParameter('messageId', itemIndex); const response = await interact_1.api.interruptCommand(context, credentials, sessionId, messageId); if (response.success && response.result === null) { return { success: true, messageId: messageId, sessionId: sessionId, status: 'interrupted', message: 'Command interrupted successfully' }; } return response.result || { success: response.success, messageId: messageId, sessionId: sessionId, status: 'interrupted', message: 'Command interrupted' }; } async function handleCloseSession(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const response = await interact_1.api.closeSession(context, credentials, sessionId); return response.result; } async function handleGetCommandMessage(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const messageId = context.getNodeParameter('messageId', itemIndex); const response = await interact_1.api.getCommandMessage(context, credentials, sessionId, messageId); return response.result; } async function handleWaitForSession(context, credentials, itemIndex) { const sessionId = context.getNodeParameter('sessionId', itemIndex); const taskId = context.getNodeParameter('taskId', itemIndex); const timeout = context.getNodeParameter('timeout', itemIndex); const startTime = Date.now(); const pollInterval = 60000; while (true) { try { const taskResponse = await tasks_1.api.getTaskById(context, credentials, taskId); const task = taskResponse.result; if (task && task.type === 'interact-shell') { if (task.status === 'processing') { return { sessionId: sessionId, taskId: taskId, status: 'live', message: 'InterACT session is ready - task is processing', taskStatus: task.status, taskType: task.type, organizationId: task.organizationId, createdAt: task.createdAt, updatedAt: task.updatedAt }; } else if (task.status === 'completed') { return { sessionId: sessionId, taskId: taskId, status: 'completed', message: 'InterACT session task completed', taskStatus: task.status, taskType: task.type, organizationId: task.organizationId, createdAt: task.createdAt, updatedAt: task.updatedAt }; } else if (task.status === 'cancelled') { return { sessionId: sessionId, taskId: taskId, status: 'cancelled', message: 'InterACT session task was cancelled', error: 'Task was cancelled before becoming live', taskStatus: task.status, taskType: task.type, organizationId: task.organizationId, createdAt: task.createdAt, updatedAt: task.updatedAt }; } else if (task.status === 'failed') { return { sessionId: sessionId, taskId: taskId, status: 'failed', message: 'InterACT session task failed', error: 'Task failed during execution', taskStatus: task.status, taskType: task.type, organizationId: task.organizationId, createdAt: task.createdAt, updatedAt: task.updatedAt }; } } else { throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Invalid task: Task ID ${taskId} is not an InterACT session task or was not found`); } } catch (error) { if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } console.log(`Task ${taskId} status check failed: ${error instanceof Error ? error.message : String(error)}`); } if (timeout > 0) { const elapsedTime = (Date.now() - startTime) / 1000; if (elapsedTime >= timeout) { return { sessionId: sessionId, taskId: taskId, status: 'timeout', message: `InterACT session did not become live within ${timeout} seconds`, error: `Timeout exceeded. Task is still waiting to become live`, taskStatus: 'unknown' }; } } await new Promise(resolve => setTimeout(resolve, pollInterval)); } } async function getCasesByOrganization(searchTerm) { var _a; try { const credentials = await (0, helpers_1.getAirCredentials)(this); let organizationId = '0'; try { const currentParams = this.getCurrentNodeParameters(); if (currentParams && currentParams.organizationId) { const orgResource = currentParams.organizationId; if (typeof orgResource === 'object') { if (orgResource.mode === 'id' || orgResource.mode === 'list') { organizationId = orgResource.value || '0'; } } else if (typeof orgResource === 'string') { organizationId = orgResource; } } } catch (error) { } const additionalParams = {}; if (searchTerm) { additionalParams.searchTerm = searchTerm; } const response = await cases_1.api.getCases(this, credentials, organizationId, additionalParams); const cases = ((_a = response.result) === null || _a === void 0 ? void 0 : _a.entities) || []; return (0, helpers_1.createListSearchResults)(cases, isValidCase, (caseItem) => ({ name: caseItem.name || `Case ${caseItem._id}`, value: extractCaseId(caseItem), }), searchTerm); } catch (error) { throw (0, helpers_1.catchAndFormatError)(error, 'loading cases'); } } async function getAssetsByOrganizationForInteract(searchTerm) { var _a; try { const credentials = await (0, helpers_1.getAirCredentials)(this); let organizationId = '0'; try { const currentParams = this.getCurrentNodeParameters(); if (currentParams && currentParams.organizationId) { const orgResource = currentParams.organizationId; if (typeof orgResource === 'object') { if (orgResource.mode === 'id' || orgResource.mode === 'list') { organizationId = orgResource.value || '0'; } } else if (typeof orgResource === 'string') { organizationId = orgResource; } } } catch (error) { } const queryParams = { 'filter[managedStatus]': 'managed', }; if (searchTerm) { queryParams['filter[searchTerm]'] = searchTerm; } const response = await assets_1.api.getAssets(this, credentials, organizationId, queryParams); const assets = ((_a = response.result) === null || _a === void 0 ? void 0 : _a.entities) || []; return (0, helpers_1.createListSearchResults)(assets, isValidAsset, (asset) => ({ name: `${asset.name} (${asset.onlineStatus || 'Unknown'} - ${asset.ipAddress || 'No IP'})`, value: extractAssetId(asset), }), searchTerm); } catch (error) { throw (0, helpers_1.catchAndFormatError)(error, 'loading assets'); } } //# sourceMappingURL=interact.js.map