mirror of https://github.com/MISP/MISP
301 lines
9.2 KiB
PHP
301 lines
9.2 KiB
PHP
<?php
|
|
class WorkflowBaseModule
|
|
{
|
|
public $is_misp_module = false;
|
|
public $blocking = false;
|
|
public $is_custom = false;
|
|
public $expect_misp_core_format = false;
|
|
public $id = 'to-override';
|
|
public $name = 'to-override';
|
|
public $version = '0.1';
|
|
public $description = 'to-override';
|
|
public $icon = '';
|
|
public $icon_class = '';
|
|
public $inputs = 0;
|
|
public $outputs = 0;
|
|
public $multiple_output_connection = false;
|
|
public $support_filters = false;
|
|
public $saved_filters = [
|
|
['text' => 'selector', 'value' => ''],
|
|
['text' => 'value', 'value' => ''],
|
|
['text' => 'operator', 'value' => ''],
|
|
['text' => 'path', 'value' => ''],
|
|
];
|
|
public $params = [];
|
|
|
|
private $Event;
|
|
|
|
/** @var PubSubTool */
|
|
private static $loadedPubSubTool;
|
|
|
|
public function __construct()
|
|
{
|
|
}
|
|
|
|
protected function mergeNodeConfigIntoParameters($node): array
|
|
{
|
|
$fullIndexedParams = [];
|
|
foreach ($this->params as $param) {
|
|
$param['value'] = $nodeParamByID[$param['id']]['value'] ?? null;
|
|
$param['value'] = $node['data']['indexed_params'][$param['id']] ?? null;
|
|
$fullIndexedParams[$param['id']] = $param;
|
|
}
|
|
return $fullIndexedParams;
|
|
}
|
|
|
|
protected function getParamsWithValues($node): array
|
|
{
|
|
$indexedParams = $this->mergeNodeConfigIntoParameters($node);
|
|
foreach ($indexedParams as $id => $param) {
|
|
$indexedParams[$id]['value'] = $param['value'] ?? ($param['default'] ?? '');
|
|
}
|
|
return $indexedParams;
|
|
}
|
|
|
|
protected function filtersEnabled($node): bool
|
|
{
|
|
$indexedFilters = $this->getFilters($node);
|
|
foreach ($indexedFilters as $k => $v) {
|
|
if ($v != '') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function getFilters($node): array
|
|
{
|
|
$indexedFilters = [];
|
|
$nodeParam = [];
|
|
foreach ($node['data']['saved_filters'] as $name => $value) {
|
|
$nodeParam[$name] = $value;
|
|
}
|
|
foreach ($this->saved_filters as $filter) {
|
|
$filter['value'] = $nodeParam[$filter['text']] ?? $filter['value'];
|
|
$indexedFilters[$filter['text']] = $filter['value'];
|
|
}
|
|
return $indexedFilters;
|
|
}
|
|
|
|
public function getConfig(): array
|
|
{
|
|
$reflection = new ReflectionObject($this);
|
|
$properties = [];
|
|
foreach ($reflection->getProperties() as $property) {
|
|
if ($property->isPublic()) {
|
|
$properties[$property->getName()] = $property->getValue($this);
|
|
}
|
|
}
|
|
return $properties;
|
|
}
|
|
|
|
public function exec(array $node, WorkflowRoamingData $roamingData, array &$errors=[]): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected function push_zmq($message)
|
|
{
|
|
if (!self::$loadedPubSubTool) {
|
|
App::uses('PubSubTool', 'Tools');
|
|
$pubSubTool = new PubSubTool();
|
|
$pubSubTool->initTool();
|
|
self::$loadedPubSubTool = $pubSubTool;
|
|
}
|
|
$pubSubTool = self::$loadedPubSubTool;
|
|
$pubSubTool->workflow_push($message);
|
|
}
|
|
|
|
protected function render_jinja_template($template, array $data): string
|
|
{
|
|
$mispModule = ClassRegistry::init('Module');
|
|
$postData = [
|
|
'module' => 'jinja_template_rendering',
|
|
'text' => JsonTool::encode([
|
|
'template' => $template,
|
|
'data' => $data,
|
|
])
|
|
];
|
|
$result = $mispModule->queryModuleServer($postData, false, 'Enrichment', false, [], true);
|
|
if (!empty($result['error'])) {
|
|
return '';
|
|
}
|
|
$rendered = $result['results'][0]['values'][0];
|
|
return $rendered;
|
|
}
|
|
|
|
protected function logError($message)
|
|
{
|
|
$this->Log = ClassRegistry::init('Log');
|
|
$this->Log->createLogEntry('SYSTEM', 'exec_module', 'Workflow', $this->id, $message);
|
|
}
|
|
|
|
public function checkLoading()
|
|
{
|
|
return 'The Factory Must Grow';
|
|
}
|
|
|
|
protected function extractData($data, $path)
|
|
{
|
|
$extracted = $data;
|
|
if (!empty($path)) {
|
|
try {
|
|
$extracted = Hash::extract($data, $path);
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
return $extracted;
|
|
}
|
|
|
|
protected function getMatchingItemsForAttributes(array $node, array $rData): array
|
|
{
|
|
if ($this->filtersEnabled($node)) {
|
|
$filters = $this->getFilters($node);
|
|
$extracted = $this->extractData($rData, $filters['selector']);
|
|
if ($extracted === false) {
|
|
return false;
|
|
}
|
|
$matchingItems = $this->getItemsMatchingCondition($extracted, $filters['value'], $filters['operator'], $filters['path']);
|
|
} else {
|
|
$matchingItems = Hash::extract($rData, 'Event._AttributeFlattened.{n}');
|
|
}
|
|
return $matchingItems;
|
|
}
|
|
|
|
protected function extractDataForFilters(array $node, WorkflowRoamingData $roamingData)
|
|
{
|
|
$rData = $roamingData->getData();
|
|
if (empty($this->support_filters)) {
|
|
return $rData;
|
|
}
|
|
$filters = $this->getFilters($node);
|
|
if (in_array(null, array_values($filters))) {
|
|
return $rData;
|
|
}
|
|
$extracted = $this->extractData($rData, $filters['selector']);
|
|
if ($extracted === false) {
|
|
return $rData;
|
|
}
|
|
$matchingItems = $this->getItemsMatchingCondition($extracted, $filters['value'], $filters['operator'], $filters['path']);
|
|
return $matchingItems;
|
|
}
|
|
|
|
protected function evaluateCondition($data, $operator, $value): bool
|
|
{
|
|
if ($operator == 'in') {
|
|
return is_array($data) && in_array($value, $data);
|
|
} elseif ($operator == 'not_in') {
|
|
return is_array($data) && !in_array($value, $data);
|
|
} elseif ($operator == 'equals') {
|
|
return !is_array($data) && $data == $value;
|
|
} elseif ($operator == 'not_equals') {
|
|
return !is_array($data) && $data != $value;
|
|
} elseif ($operator == 'in_or' || $operator == 'in_and' || $operator == 'not_in_or' || $operator == 'not_in_and') {
|
|
if (!is_array($data) || !is_array($value)) {
|
|
return false;
|
|
}
|
|
$matching = array_filter($data, function($item) use ($value) {
|
|
return in_array($item, $value);
|
|
});
|
|
if ($operator == 'in_or') {
|
|
return !empty($matching);
|
|
} elseif ($operator == 'in_and') {
|
|
return array_values($matching) == array_values($value);
|
|
} elseif ($operator == 'not_in_or') {
|
|
return empty($matching);
|
|
} elseif ($operator == 'not_in_and') {
|
|
return array_values($matching) != array_values($value);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected function getItemsMatchingCondition($items, $value, $operator, $path)
|
|
{
|
|
foreach ($items as $i => $item) {
|
|
$subItem = $this->extractData($item, $path, $operator);
|
|
if (in_array($operator, ['equals', 'not_equals'])) {
|
|
$subItem = !empty($subItem) ? $subItem[0] : $subItem;
|
|
}
|
|
if (!$this->evaluateCondition($subItem, $operator, $value)) {
|
|
unset($items[$i]);
|
|
}
|
|
}
|
|
return $items;
|
|
}
|
|
|
|
protected function addNotification(array $errors, string $severity, string $text, string $description='', array $details=[], bool $showInSidebar=false, bool $showInNode=false): array
|
|
{
|
|
$errors[$severity][] = [
|
|
'text' => $text,
|
|
'description' => $description,
|
|
'details' => $details,
|
|
'__show_in_sidebar' => $showInSidebar,
|
|
'__show_in_node' => $showInNode,
|
|
];
|
|
return $errors;
|
|
}
|
|
|
|
public function diagnostic(): array
|
|
{
|
|
return [];
|
|
}
|
|
}
|
|
|
|
class WorkflowBaseTriggerModule extends WorkflowBaseModule
|
|
{
|
|
const OVERHEAD_LOW = 1;
|
|
const OVERHEAD_MEDIUM = 2;
|
|
const OVERHEAD_HIGH = 3;
|
|
|
|
public $scope = 'others';
|
|
public $blocking = false;
|
|
public $misp_core_format = false;
|
|
public $trigger_overhead = self::OVERHEAD_LOW;
|
|
public $trigger_overhead_message = '';
|
|
public $inputs = 0;
|
|
public $outputs = 1;
|
|
|
|
/**
|
|
* normalizeData Massage the data before entering the workflow
|
|
*
|
|
* @param array $data
|
|
* @return array|false
|
|
*/
|
|
public function normalizeData(array $data)
|
|
{
|
|
if (!empty($this->misp_core_format)) {
|
|
$converted = $this->convertData($data);
|
|
if (empty($converted)) {
|
|
return false;
|
|
}
|
|
return $converted;
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* convertData function
|
|
*
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
protected function convertData(array $data): array
|
|
{
|
|
App::uses('WorkflowFormatConverterTool', 'Tools');
|
|
return WorkflowFormatConverterTool::convert($data);
|
|
}
|
|
}
|
|
|
|
class WorkflowBaseLogicModule extends WorkflowBaseModule
|
|
{
|
|
public $blocking = false;
|
|
public $inputs = 1;
|
|
public $outputs = 2;
|
|
}
|
|
|
|
class WorkflowBaseActionModule extends WorkflowBaseModule
|
|
{
|
|
}
|