new: [attributes/enrich] endpoint added

- simply post a list of modules you wish to enrich the attribute by
- url: /attributes/enrich/[attrribute_id|attribute_uuid]
- post body in the format of `{"dns":1, "foo_bar_baz": 1}` listing all modules to execute
pull/9764/head
iglocska 2024-06-05 11:13:39 +02:00
parent ad71ddf11e
commit 148e20a24f
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
3 changed files with 209 additions and 1 deletions

View File

@ -501,6 +501,57 @@ class EventShell extends AppShell
$log->createLogEntry($user, 'publish', 'GalaxyCluster', $clusterId, 'GalaxyCluster (' . $clusterId . '): published.', 'published () => (1)');
}
public function attribute_enrichment()
{
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Run attribute enrichment'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$id = $this->args[1];
$modulesRaw = $this->args[2];
try {
$modules = json_decode($modulesRaw, true);
} catch (Exception $e) {
die('Invalid module JSON');
}
if (!empty($this->args[3])) {
$jobId = $this->args[3];
} else {
$this->Job->create();
$data = [
'worker' => 'default',
'job_type' => 'enrichment',
'job_input' => 'Attribute: ' . $id . ' modules: ' . $modulesRaw,
'status' => 0,
'retries' => 0,
'org' => $user['Organisation']['name'],
'message' => 'Enriching event.',
];
$this->Job->save($data);
$jobId = $this->Job->id;
}
$job = $this->Job->read(null, $jobId);
$options = array(
'user' => $user,
'id' => $id,
'modules' => $modules
);
$result = $this->Attribute->enrichment($options);
$job['Job']['progress'] = 100;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
if ($result) {
$job['Job']['message'] = 'Added ' . $result . ' attribute' . ($result > 1 ? 's.' : '.');
} else {
$job['Job']['message'] = 'Enrichment finished, but no attributes added.';
}
echo $job['Job']['message'] . PHP_EOL;
$this->Job->save($job);
$log = ClassRegistry::init('Log');
$log->createLogEntry($user, 'enrichment', 'Attribute', $id, 'Attribute (' . $id . '): enriched.', 'enriched () => (1)');
}
public function enrichment()
{
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
@ -546,7 +597,7 @@ class EventShell extends AppShell
} else {
$job['Job']['message'] = 'Enrichment finished, but no attributes added.';
}
echo $job['Job']['message'] . PHP_EOL;
echo $job['Job']['message'] . PHP_EOL;
$this->Job->save($job);
$log = ClassRegistry::init('Log');
$log->createLogEntry($user, 'enrichment', 'Event', $eventId, 'Event (' . $eventId . '): enriched.', 'enriched () => (1)');

View File

@ -3050,4 +3050,29 @@ class AttributesController extends AppController
$this->set('object', $attribute[0]['Attribute']);
$this->set('seed', $seed);
}
public function enrich($id)
{
$conditions = $this->__idToConditions($id);
$attributes = $this->Attribute->fetchAttributes($this->Auth->user(), ['conditions' => $conditions, 'flatten' => true]);
if (empty($attributes)) {
throw new MethodNotAllowedException(__('Invalid Attribute'));
}
$attribute = $attributes[0];
if (!$this->request->is('post') || !$this->_isRest()) {
throw new MethodNotAllowedException(__('This endpoint allows for API POST requests only.'));
}
$modules = [];
foreach ($this->request->data as $module => $enabled) {
if ($enabled) {
$modules[] = $module;
}
}
$result = $this->Attribute->enrichmentRouter([
'user' => $this->Auth->user(),
'id' => $attribute['Attribute']['id'],
'modules' => $modules
]);
return $this->RestResponse->successResponse(0, $result);
}
}

View File

@ -3877,4 +3877,136 @@ class Attribute extends AppModel
'fields' => ['Attribute.id', 'Attribute.uuid']
]);
}
public function enrichmentRouter($options)
{
if (Configure::read('MISP.background_jobs')) {
/** @var Job $job */
$job = ClassRegistry::init('Job');
$jobId = $job->createJob(
$options['user'],
Job::WORKER_PRIO,
'enrichment',
'Attribute ID: ' . $options['id'] . ' modules: ' . json_encode($options['modules']),
'Enriching attribute.'
);
$this->getBackgroundJobsTool()->enqueue(
BackgroundJobsTool::PRIO_QUEUE,
BackgroundJobsTool::CMD_EVENT,
[
'attribute_enrichment',
$options['user']['id'],
$options['id'],
json_encode($options['modules']),
$jobId
],
true,
$jobId
);
return __('Job queued (job ID: %s).', $jobId);
} else {
$result = $this->enrichment($options);
return __('#' . $result . ' attributes have been created during the enrichment process.');
}
}
public function enrichment($params)
{
$option_fields = ['user', 'id', 'modules'];
foreach ($option_fields as $option_field) {
if (empty($params[$option_field])) {
throw new MethodNotAllowedException(__('%s not set', $option_field));
}
}
$attribute = $this->fetchAttributes($params['user'], [
'conditions' => [
'Attribute.id' => $params['id'],
],
'withAttachments' => 1,
]);
if (empty($attribute)) {
throw new MethodNotAllowedException('Invalid attribute.');
}
$attribute = $attribute[0]['Attribute'];
$this->Module = ClassRegistry::init('Module');
$enabledModules = $this->Module->getEnabledModules($params['user']);
if (empty($enabledModules) || is_string($enabledModules)) {
return true;
}
$options = array();
foreach ($enabledModules['modules'] as $k => $temp) {
if (isset($temp['meta']['config'])) {
$settings = array();
foreach ($temp['meta']['config'] as $conf) {
$settings[$conf] = Configure::read('Plugin.Enrichment_' . $temp['name'] . '_' . $conf);
}
$enabledModules['modules'][$k]['config'] = $settings;
}
}
$attributes_added = 0;
$initial_objects = array();
$event_id = $attribute['event_id'];
$event = $this->Event->find('first', ['conditions' => ['Event.id' => $event_id], 'recursive' => -1]);
if (empty($event)) {
throw new MethodNotAllowedException('Invalid event.');
}
$object_id = $attribute['object_id'];
if ($object_id != '0' && empty($initial_objects[$object_id])) {
$initial_objects[$object_id] = $this->Event->fetchInitialObject($event_id, $object_id);
}
foreach ($enabledModules['modules'] as $module) {
if (in_array($module['name'], $params['modules'])) {
if (in_array($attribute['type'], $module['mispattributes']['input'])) {
$data = array('module' => $module['name'], 'event_id' => $event_id, 'attribute_uuid' => $attribute['uuid']);
if (!empty($module['config'])) {
$data['config'] = $module['config'];
}
if (!empty($module['mispattributes']['format']) && $module['mispattributes']['format'] == 'misp_standard') {
$data['attribute'] = $attribute;
} else {
$data[$attribute['type']] = $attribute['value'];
}
if ($object_id != '0' && !empty($initial_objects[$object_id])) {
$attribute['Object'] = $initial_objects[$object_id]['Object'];
}
$triggerData = $event;
$triggerData['Attribute'] = [$attribute];
$result = $this->Module->queryModuleServer($data, false, 'Enrichment', false, $triggerData);
if ($result === false) {
throw new MethodNotAllowedException(h($module['name']) . ' service not reachable.');
} else if (!is_array($result)) {
continue;
}
if (!empty($module['mispattributes']['format']) && $module['mispattributes']['format'] == 'misp_standard') {
if ($object_id != '0' && !empty($initial_objects[$object_id])) {
$result['initialObject'] = $initial_objects[$object_id];
}
$default_comment = $attribute['value'] . ': enriched via the ' . $module['name'] . ' module.';
$attributes_added += $this->Event->processModuleResultsData($params['user'], $result['results'], $event_id, $default_comment, false, false, true);
} else {
$attributes = $this->Event->handleModuleResult($result, $event_id);
foreach ($attributes as $a) {
$this->create();
$a['distribution'] = $attribute['distribution'];
$a['sharing_group_id'] = $attribute['sharing_group_id'];
$comment = 'Attribute #' . $attribute['id'] . ' enriched by ' . $module['name'] . '.';
if (!empty($a['comment'])) {
$a['comment'] .= PHP_EOL . $comment;
} else {
$a['comment'] = $comment;
}
$a['type'] = empty($a['default_type']) ? $a['types'][0] : $a['default_type'];
$result = $this->save($a);
if ($result) {
$attributes_added++;
}
}
}
}
}
}
return $attributes_added;
}
}