mirror of https://github.com/MISP/MISP
Merge branch '2.4' of github.com:MISP/MISP into stix
commit
05fd501389
|
@ -1200,7 +1200,7 @@ INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modi
|
|||
VALUES (1, 'admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
|
||||
|
||||
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
|
||||
VALUES (2, 'Org Admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0);
|
||||
VALUES (2, 'Org Admin', NOW(), NOW(), 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0);
|
||||
|
||||
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
|
||||
VALUES (3, 'User', NOW(), NOW(), 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1);
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":4, "hotfix":89}
|
||||
{"major":2, "minor":4, "hotfix":90}
|
||||
|
|
|
@ -479,4 +479,42 @@ class EventShell extends AppShell
|
|||
$log->createLogEntry($user, 'publish', 'Event', $id, 'Event (' . $id . '): published.', 'published () => (1)');
|
||||
}
|
||||
|
||||
public function enrichment() {
|
||||
file_put_contents('/var/www/MISP4/app/tmp/test', "0");
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['enrichment'] . PHP_EOL);
|
||||
}
|
||||
$userId = $this->args[0];
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
if (empty($user)) die('Invalid user.');
|
||||
$eventId = $this->args[1];
|
||||
$modules = $this->args[2];
|
||||
try {
|
||||
$modules = json_decode($modules);
|
||||
} catch (Exception $e) {
|
||||
die('Invalid module JSON');
|
||||
}
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
} else {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'enrichment',
|
||||
'job_input' => 'Event: ' . $eventId . ' modules: ' . $modules,
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'message' => 'Enriching event.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
}
|
||||
$options = array(
|
||||
'user' => $user,
|
||||
'event_id' => $eventId,
|
||||
'modules' => $modules
|
||||
);
|
||||
$result = $this->Event->enrichment($options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class AppController extends Controller {
|
|||
|
||||
public $helpers = array('Utility', 'OrgImg');
|
||||
|
||||
private $__queryVersion = '33';
|
||||
private $__queryVersion = '35';
|
||||
public $pyMispVersion = '2.4.89';
|
||||
public $phpmin = '5.6.5';
|
||||
public $phprec = '7.0.16';
|
||||
|
|
|
@ -1768,10 +1768,7 @@ class AttributesController extends AppController {
|
|||
$this->Attribute->contain(array('AttributeTag' => array('Tag')));
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
// merge in private conditions
|
||||
$conditions = $this->Attribute->buildConditions($this->Auth->user());
|
||||
$this->paginate = Set::merge($this->paginate, array(
|
||||
'conditions' => $conditions
|
||||
));
|
||||
$this->paginate['conditions'] = array('AND' => array($conditions, $this->Attribute->buildConditions($this->Auth->user())));
|
||||
}
|
||||
$idList = array();
|
||||
$attributeIdList = array();
|
||||
|
|
|
@ -99,6 +99,7 @@ class ACLComponent extends Component {
|
|||
'downloadOpenIOCEvent' => array('*'),
|
||||
'downloadSearchResult' => array('*'),
|
||||
'edit' => array('perm_add'),
|
||||
'enrichEvent' => array('perm_add'),
|
||||
'export' => array('*'),
|
||||
'exportChoice' => array('*'),
|
||||
'filterEventIdsForPush' => array('perm_sync'),
|
||||
|
|
|
@ -51,6 +51,20 @@ class RestResponseComponent extends Component {
|
|||
'params' => array('event_id')
|
||||
)
|
||||
),
|
||||
'Feed' => array(
|
||||
'add' => array(
|
||||
'description' => "POST a MISP Feed descriptor JSON to this API to add a Feed.",
|
||||
'mandatory' => array('source_format', 'url', 'name', 'input_source', 'provider'),
|
||||
'optional' => array('enabled', 'caching_enabled', 'lookup_visible', 'delete_local_file', 'headers', 'fixed_event', 'target_event', 'settings', 'publish', 'override_ids', 'delta_merge', 'distribution', 'sharing_group_id', 'tag_id', 'pull_rules', 'rules', 'event_id'),
|
||||
'params' => array()
|
||||
),
|
||||
'edit' => array(
|
||||
'description' => "POST a MISP Feed descriptor JSON to this API to edit a Feed.",
|
||||
'mandatory' => array(),
|
||||
'optional' => array('source_format', 'url', 'name', 'enabled', 'caching_enabled', 'lookup_visible', 'provider', 'input_source', 'delete_local_file', 'headers', 'fixed_event', 'target_event', 'settings', 'publish', 'override_ids', 'delta_merge', 'distribution', 'sharing_group_id', 'tag_id', 'pull_rules', 'rules', 'event_id'),
|
||||
'params' => array('feed_id')
|
||||
),
|
||||
),
|
||||
'Organisation' => array(
|
||||
'admin_add' => array(
|
||||
'description' => "POST an Organisation object in JSON format to this API to create a new organsiation.",
|
||||
|
|
|
@ -628,15 +628,20 @@ class EventsController extends AppController {
|
|||
)
|
||||
)
|
||||
));
|
||||
foreach ($eventTags as $et) {
|
||||
$et['EventTag']['Tag'] = $et['Tag'];
|
||||
unset($et['Tag']);
|
||||
if (empty($event_tag_objects[$et['EventTag']['event_id']])) {
|
||||
$event_tag_objects[$et['EventTag']['event_id']] = array($et['EventTag']);
|
||||
foreach ($eventTags as $ket => $et) {
|
||||
if (empty($et['Tag']['id'])) {
|
||||
unset($eventTags[$ket]);
|
||||
} else {
|
||||
$event_tag_objects[$et['EventTag']['event_id']][] = $et['EventTag'];
|
||||
$et['EventTag']['Tag'] = $et['Tag'];
|
||||
unset($et['Tag']);
|
||||
if (empty($event_tag_objects[$et['EventTag']['event_id']])) {
|
||||
$event_tag_objects[$et['EventTag']['event_id']] = array($et['EventTag']);
|
||||
} else {
|
||||
$event_tag_objects[$et['EventTag']['event_id']][] = $et['EventTag'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$eventTags = array_values($eventTags);
|
||||
for ($j = 0; $j < $elements; $j++) {
|
||||
if (!empty($event_tag_objects[$events[($i*1000) + $j]['Event']['id']])) {
|
||||
$events[($i*1000) + $j]['EventTag'] = $event_tag_objects[$events[($i*1000) + $j]['Event']['id']];
|
||||
|
@ -4300,6 +4305,8 @@ class EventsController extends AppController {
|
|||
public function viewGraph($id) {
|
||||
$event = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id));
|
||||
if (empty($event)) throw new MethodNotAllowedException('Invalid Event.');
|
||||
|
||||
$this->set('ajax', $this->request->is('ajax'));
|
||||
$this->set('event', $event[0]);
|
||||
$this->set('scope', 'event');
|
||||
$this->set('id', $id);
|
||||
|
@ -4348,6 +4355,7 @@ class EventsController extends AppController {
|
|||
public function getEventGraphReferences($id, $type = 'event') {
|
||||
$validTools = array('event');
|
||||
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');
|
||||
$this->loadModel('Tag');
|
||||
App::uses('EventGraphTool', 'Tools');
|
||||
$grapher = new EventGraphTool();
|
||||
$data = $this->request->is('post') ? $this->request->data : array();
|
||||
|
@ -4358,7 +4366,7 @@ class EventsController extends AppController {
|
|||
$extended = 0;
|
||||
}
|
||||
|
||||
$grapher->construct($this->Event, $this->Auth->user(), $data['filtering'], $extended);
|
||||
$grapher->construct($this->Event, $this->Tag, $this->Auth->user(), $data['filtering'], $extended);
|
||||
$json = $grapher->get_references($id);
|
||||
|
||||
array_walk_recursive($json, function(&$item, $key){
|
||||
|
@ -4373,6 +4381,7 @@ class EventsController extends AppController {
|
|||
public function getEventGraphTags($id, $type = 'event') {
|
||||
$validTools = array('event');
|
||||
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');
|
||||
$this->loadModel('Tag');
|
||||
App::uses('EventGraphTool', 'Tools');
|
||||
$grapher = new EventGraphTool();
|
||||
$data = $this->request->is('post') ? $this->request->data : array();
|
||||
|
@ -4383,7 +4392,7 @@ class EventsController extends AppController {
|
|||
$extended = 0;
|
||||
}
|
||||
|
||||
$grapher->construct($this->Event, $this->Auth->user(), $data['filtering'], $extended);
|
||||
$grapher->construct($this->Event, $this->Tag, $this->Auth->user(), $data['filtering'], $extended);
|
||||
$json = $grapher->get_tags($id);
|
||||
|
||||
array_walk_recursive($json, function(&$item, $key){
|
||||
|
@ -4398,6 +4407,7 @@ class EventsController extends AppController {
|
|||
public function getEventGraphGeneric($id, $type = 'event') {
|
||||
$validTools = array('event');
|
||||
if (!in_array($type, $validTools)) throw new MethodNotAllowedException('Invalid type.');
|
||||
$this->loadModel('Tag');
|
||||
App::uses('EventGraphTool', 'Tools');
|
||||
$grapher = new EventGraphTool();
|
||||
$data = $this->request->is('post') ? $this->request->data : array();
|
||||
|
@ -4408,7 +4418,7 @@ class EventsController extends AppController {
|
|||
$extended = 0;
|
||||
}
|
||||
|
||||
$grapher->construct($this->Event, $this->Auth->user(), $data['filtering'], $extended);
|
||||
$grapher->construct($this->Event, $this->Tag, $this->Auth->user(), $data['filtering'], $extended);
|
||||
if (!array_key_exists('keyType', $data)) {
|
||||
$keyType = ''; // empty key
|
||||
} else {
|
||||
|
@ -4832,13 +4842,17 @@ class EventsController extends AppController {
|
|||
}
|
||||
|
||||
public function getEventInfoById($id) {
|
||||
if (empty($id)) throw new MethodNotAllowedException('Invalid ID.');
|
||||
$conditions = array('Event.id' => $id);
|
||||
if (Validation::uuid($id)) {
|
||||
$conditions = array('Event.uuid' => $id);
|
||||
} else if (!is_numeric($id)) {
|
||||
$conditions = array('Event.uuid' => -1);
|
||||
}
|
||||
$event = $this->Event->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'fields' => array('Event.id', 'Event.distribution', 'Event.sharing_group_id', 'Event.info', 'Event.org_id'),
|
||||
'fields' => array('Event.id', 'Event.distribution', 'Event.sharing_group_id', 'Event.info', 'Event.org_id', 'Event.date', 'Event.threat_level_id', 'Event.analysis'),
|
||||
'contain' => array('Orgc.id', 'Orgc.name', 'EventTag' => array('Tag.id', 'Tag.name', 'Tag.colour'), 'ThreatLevel.name'),
|
||||
'recursive' => -1
|
||||
));
|
||||
if (!empty($event) && !$this->_isSiteAdmin() && $event['Event']['org_id'] != $this->Auth->user('org_id')) {
|
||||
|
@ -4853,6 +4867,54 @@ class EventsController extends AppController {
|
|||
}
|
||||
}
|
||||
}
|
||||
return $this->RestResponse->viewData($event, $this->response->type());
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($event, $this->response->type());
|
||||
} else {
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->layout = 'ajax';
|
||||
}
|
||||
$this->set('analysisLevels', $this->Event->analysisLevels);
|
||||
$this->set('validUuid', Validation::uuid($id));
|
||||
$this->set('id', $id);
|
||||
$this->set('event', $event);
|
||||
}
|
||||
}
|
||||
|
||||
public function enrichEvent($id) {
|
||||
if (Validation::uuid($id)) {
|
||||
$conditions = array('Event.uuid' => $id);
|
||||
} else {
|
||||
$conditions = array('Event.id' => $id);
|
||||
}
|
||||
$event = $this->Event->find('first', array('conditions' => $conditions, 'recursive' => -1));
|
||||
if (empty($event) || (!$this->_isSiteAdmin() && ($this->Auth->user('org_id') != $event['Event']['orgc_id'] || !$this->userRole['perm_modify']))) {
|
||||
throw new MethodNotAllowedException('Invalid Event');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$modules = array();
|
||||
foreach ($this->request->data['Event'] as $module => $enabled) {
|
||||
if ($enabled) $modules[] = $module;
|
||||
}
|
||||
$result = $this->Event->enrichmentRouter(array(
|
||||
'user' => $this->Auth->user(),
|
||||
'event_id' => $event['Event']['id'],
|
||||
'modules' => $modules
|
||||
));
|
||||
if ($this->_isRest()) {
|
||||
|
||||
} else {
|
||||
if ($result === true) {
|
||||
$result = __('Enrichment task queued for background processing. Check back later to see the results.');
|
||||
}
|
||||
$this->Session->setFlash($result);
|
||||
$this->redirect('/events/view/' . $id);
|
||||
}
|
||||
} else {
|
||||
$this->loadModel('Module');
|
||||
$modules = $this->Module->getEnabledModules($this->Auth->user(), 'expansion');
|
||||
$this->layout = 'ajax';
|
||||
$this->set('modules', $modules);
|
||||
$this->render('ajax/enrich_event');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,13 +117,25 @@ class FeedsController extends AppController {
|
|||
$tags[0] = 'None';
|
||||
$this->set('tags', $tags);
|
||||
if ($this->request->is('post')) {
|
||||
if ($this->_isRest()) {
|
||||
if (empty($this->request->data['Feed'])) {
|
||||
$this->request->data['Feed'] = $this->request->data;
|
||||
if (empty($this->request->data['Feed']['source_format'])) {
|
||||
$this->request->data['Feed']['source_format'] = 'freetext';
|
||||
}
|
||||
if (empty($this->request->data['Feed']['fixed_event'])) {
|
||||
$this->request->data['Feed']['source_format'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
$error = false;
|
||||
if (isset($this->request->data['Feed']['pull_rules'])) $this->request->data['Feed']['rules'] = $this->request->data['Feed']['pull_rules'];
|
||||
if (!isset($this->request->data['Feed']['distribution'])) $this->request->data['Feed']['distribution'] = 0;
|
||||
if ($this->request->data['Feed']['distribution'] != 4) $this->request->data['Feed']['sharing_group_id'] = 0;
|
||||
$this->request->data['Feed']['default'] = 0;
|
||||
if ($this->request->data['Feed']['source_format'] == 'freetext') {
|
||||
if ($this->request->data['Feed']['fixed_event'] == 1) {
|
||||
if (is_numeric($this->request->data['Feed']['target_event'])) {
|
||||
if (!empty($this->request->data['Feed']['target_event']) && is_numeric($this->request->data['Feed']['target_event'])) {
|
||||
$this->request->data['Feed']['event_id'] = $this->request->data['Feed']['target_event'];
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +159,8 @@ class FeedsController extends AppController {
|
|||
}
|
||||
if (empty($this->request->data['Feed']['input_source'])) {
|
||||
$this->request->data['Feed']['input_source'] = 'network';
|
||||
} else if (!in_array($this->request->data['Feed']['input_source'], array('network', 'file'))) {
|
||||
$this->request->data['Feed']['input_source'] = 'network';
|
||||
}
|
||||
if (!isset($this->request->data['Feed']['delete_local_file'])) {
|
||||
$this->request->data['Feed']['delete_local_file'] = 0;
|
||||
|
@ -156,11 +170,24 @@ class FeedsController extends AppController {
|
|||
if (!$error) {
|
||||
$result = $this->Feed->save($this->request->data);
|
||||
if ($result) {
|
||||
$this->Session->setFlash('Feed added.');
|
||||
$message = __('Feed added.');
|
||||
if ($this->_isRest()) {
|
||||
$feed = $this->Feed->find('first', array('conditions' => array('Feed.id' => $this->Feed->id), 'recursive' => -1));
|
||||
return $this->RestResponse->viewData($feed, $this->response->type());
|
||||
}
|
||||
$this->Session->setFlash($message);
|
||||
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
|
||||
}
|
||||
else $this->Session->setFlash('Feed could not be added. Invalid field: ' . array_keys($this->Feed->validationErrors)[0]);
|
||||
else {
|
||||
$messsage = __('Feed could not be added. Invalid field: %s', array_keys($this->Feed->validationErrors)[0]);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Feeds', 'add', false, $message, $this->response->type());
|
||||
}
|
||||
$this->Session->setFlash($message);
|
||||
}
|
||||
}
|
||||
} else if ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('Feeds', 'add', false, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,12 +216,17 @@ class FeedsController extends AppController {
|
|||
$this->Feed->data['Feed']['settings'] = json_decode($this->Feed->data['Feed']['settings'], true);
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if ($this->_isRest()) {
|
||||
if (empty($this->request->data['Feed'])) {
|
||||
$this->request->data['Feed'] = $this->request->data;
|
||||
}
|
||||
}
|
||||
if (isset($this->request->data['Feed']['pull_rules'])) $this->request->data['Feed']['rules'] = $this->request->data['Feed']['pull_rules'];
|
||||
if ($this->request->data['Feed']['distribution'] != 4) $this->request->data['Feed']['sharing_group_id'] = 0;
|
||||
if (isset($this->request->data['Feed']['distribution']) && $this->request->data['Feed']['distribution'] != 4) $this->request->data['Feed']['sharing_group_id'] = 0;
|
||||
$this->request->data['Feed']['id'] = $feedId;
|
||||
if ($this->request->data['Feed']['source_format'] == 'freetext' || $this->request->data['Feed']['source_format'] == 'csv') {
|
||||
if (!empty($this->request->data['Feed']['source_format']) && ($this->request->data['Feed']['source_format'] == 'freetext' || $this->request->data['Feed']['source_format'] == 'csv')) {
|
||||
if ($this->request->data['Feed']['fixed_event'] == 1) {
|
||||
if (is_numeric($this->request->data['Feed']['target_event'])) {
|
||||
if (isset($this->request->data['Feed']['target_event']) && is_numeric($this->request->data['Feed']['target_event'])) {
|
||||
$this->request->data['Feed']['event_id'] = $this->request->data['Feed']['target_event'];
|
||||
} else {
|
||||
$this->request->data['Feed']['event_id'] = 0;
|
||||
|
@ -226,12 +258,24 @@ class FeedsController extends AppController {
|
|||
if (file_exists($feedCache)) {
|
||||
unlink($feedCache);
|
||||
}
|
||||
$this->Session->setFlash('Feed updated.');
|
||||
$message = __('Feed added.');
|
||||
if ($this->_isRest()) {
|
||||
$feed = $this->Feed->find('first', array('conditions' => array('Feed.id' => $this->Feed->id), 'recursive' => -1));
|
||||
return $this->RestResponse->viewData($feed, $this->response->type());
|
||||
}
|
||||
$this->Session->setFlash($message);
|
||||
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
|
||||
} else {
|
||||
$this->Session->setFlash('Feed could not be updated. Invalid fields: ' . implode(', ', array_keys($this->Feed->validationErrors)));
|
||||
$message = __('Feed could not be updated. Invalid fields: %s', implode(', ', array_keys($this->Feed->validationErrors)));
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Feeds', 'add', false, $message, $this->response->type());
|
||||
}
|
||||
$this->Session->setFlash($message);
|
||||
}
|
||||
} else {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('Feeds', 'edit', false, $this->response->type());
|
||||
}
|
||||
if (!isset($this->request->data['Feed'])) {
|
||||
$this->request->data = $this->Feed->data;
|
||||
if ($this->Feed->data['Feed']['event_id']) {
|
||||
|
@ -246,8 +290,18 @@ class FeedsController extends AppController {
|
|||
if (!$this->request->is('post')) throw new MethodNotAllowedException('This action requires a post request.');
|
||||
$this->Feed->id = $feedId;
|
||||
if (!$this->Feed->exists()) throw new NotFoundException('Invalid feed.');
|
||||
if ($this->Feed->delete($feedId)) $this->Session->setFlash('Feed deleted.');
|
||||
else $this->Session->setFlash('Feed could not be deleted.');
|
||||
if ($this->Feed->delete($feedId)) {
|
||||
$message = 'Feed deleted.';
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Feeds', 'delete', $feedId, false, $message);
|
||||
}
|
||||
} else {
|
||||
$message = 'Feed could not be deleted.';
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Feeds', 'delete', false, $message, $this->response->type());
|
||||
}
|
||||
}
|
||||
$this->Session->setFlash($message);
|
||||
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ class ObjectsController extends AppController {
|
|||
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
|
||||
$this->set('distributionData', $distributionData);
|
||||
$this->set('event', $event);
|
||||
$this->set('ajax', false);
|
||||
$this->set('ajax', $this->request->is('ajax'));
|
||||
$this->set('action', 'add');
|
||||
$this->set('template', $template);
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ class TagsController extends AppController {
|
|||
if ($this->Tag->save($this->request->data)) {
|
||||
if ($this->_isRest()) {
|
||||
$tag = $this->Tag->find('first', array(
|
||||
'contidions' => array(
|
||||
'conditions' => array(
|
||||
'Tag.id' => $this->Tag->id
|
||||
),
|
||||
'recursive' => -1
|
||||
|
|
|
@ -10,11 +10,16 @@
|
|||
private $__related_events = array();
|
||||
private $__related_attributes = array();
|
||||
|
||||
public function construct($eventModel, $user, $filterRules, $extended_view=0) {
|
||||
public function construct($eventModel, $tagModel, $user, $filterRules, $extended_view=0) {
|
||||
$this->__eventModel = $eventModel;
|
||||
$this->__Tag = $tagModel;
|
||||
$this->__user = $user;
|
||||
$this->__filterRules = $filterRules;
|
||||
$this->__json = array();
|
||||
$this->__json['existing_tags'] = $this->__Tag->find('list', array(
|
||||
'fields' => array('Tag.id', 'Tag.name'),
|
||||
'sort' => array('lower(Tag.name) asc'),
|
||||
));
|
||||
$this->__extended_view = $extended_view;
|
||||
$this->__lookupTables = array(
|
||||
'analysisLevels' => $this->__eventModel->analysisLevels,
|
||||
|
@ -56,24 +61,28 @@
|
|||
private function __get_filtered_event($id) {
|
||||
$event = $this->__get_event($id);
|
||||
if (empty($this->__filterRules)) return $event;
|
||||
$filtered = array('Object' => array(), 'Attribute' => array());
|
||||
|
||||
// perform filtering
|
||||
foreach($event['Object'] as $obj) {
|
||||
if ($this->__satisfy_obj_filtering($obj)) {
|
||||
array_push($filtered['Object'], $obj);
|
||||
foreach($event['Object'] as $i => $obj) {
|
||||
$check1 = $this->__satisfy_obj_filtering($obj);
|
||||
$check2 = $this->__satisfy_obj_tag($obj);
|
||||
if (!($check1 && $check2)) {
|
||||
unset($event['Object'][$i]);
|
||||
}
|
||||
}
|
||||
foreach($event['Attribute'] as $attr) {
|
||||
if ($this->__satisfy_val_filtering($attr, false)) {
|
||||
array_push($filtered['Attribute'], $attr);
|
||||
foreach($event['Attribute'] as $i => $attr) {
|
||||
$check1 = $this->__satisfy_val_filtering($attr, false);
|
||||
$check2 = $this->__satisfy_attr_tag($attr);
|
||||
if (!($check1 && $check2)) {
|
||||
unset($event['Attribute'][$i]);
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
return $event;
|
||||
}
|
||||
|
||||
// NOT OPTIMIZED: But allow clearer code
|
||||
// perform filtering on obj_rel presence and then perform filtering on obj_rel value
|
||||
private function __satisfy_obj_filtering($obj) {
|
||||
// presence rule - search in the object's attribute
|
||||
$presenceMatch = true;
|
||||
|
@ -132,6 +141,64 @@
|
|||
}
|
||||
}
|
||||
|
||||
// iterate over all filter rules for obj
|
||||
private function __satisfy_obj_tag($obj) {
|
||||
foreach ($this->__filterRules['tag_presence'] as $rule) {
|
||||
$relation = $rule[0];
|
||||
$tagName = $rule[1];
|
||||
if ($relation === "Contains") {
|
||||
$presenceMatch = $this->__contain_tag($obj['Attribute'], $tagName);
|
||||
} else if ($relation === "Do not contain") {
|
||||
$presenceMatch = !$this->__contain_tag($obj['Attribute'], $tagName);
|
||||
}
|
||||
if (!$presenceMatch) { // Does not match, can stop filtering
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// iterate over all filter rules for attr
|
||||
private function __satisfy_attr_tag($attr) {
|
||||
foreach ($this->__filterRules['tag_presence'] as $rule) {
|
||||
$relation = $rule[0];
|
||||
$tagName = $rule[1];
|
||||
if ($relation === "Contains") {
|
||||
$presenceMatch = $this->__contain_tag(array($attr), $tagName);
|
||||
} else if ($relation === "Do not contain") {
|
||||
$presenceMatch = !$this->__contain_tag(array($attr), $tagName);
|
||||
}
|
||||
if (!$presenceMatch) { // Does not match, can stop filtering
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// iterate over all attributes
|
||||
private function __contain_tag($attrList, $tagName) {
|
||||
foreach ($attrList as $attr) {
|
||||
if (empty($attr['AttributeTag'])) {
|
||||
continue;
|
||||
}
|
||||
$presenceMatch = $this->__tag_in_AttributeTag($attr['AttributeTag'], $tagName);
|
||||
if ($presenceMatch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate over all tags
|
||||
private function __tag_in_AttributeTag($attrTag, $tagName) {
|
||||
foreach($attrTag as $tag) {
|
||||
if($tag['Tag']['name'] === $tagName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function __contain_object_relation($attrList, $obj_rel) {
|
||||
foreach ($attrList as $attr) {
|
||||
if ($attr['object_relation'] === $obj_rel) {
|
||||
|
@ -145,6 +212,7 @@
|
|||
$event = $this->__get_filtered_event($id);
|
||||
$this->__json['items'] = array();
|
||||
$this->__json['relations'] = array();
|
||||
|
||||
$this->__json['existing_object_relation'] = array();
|
||||
if (empty($event)) return $this->__json;
|
||||
|
||||
|
@ -167,6 +235,7 @@
|
|||
'uuid' => $attr['uuid'],
|
||||
'type' => $attr['type'],
|
||||
'label' => $attr['value'],
|
||||
'event_id' => $attr['event_id'],
|
||||
'node_type' => 'attribute',
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
@ -182,6 +251,7 @@
|
|||
'node_type' => 'object',
|
||||
'meta-category' => $obj['meta-category'],
|
||||
'template_uuid' => $obj['template_uuid'],
|
||||
'event_id' => $obj['event_id'],
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
||||
|
@ -198,6 +268,7 @@
|
|||
'to' => $rel['referenced_id'],
|
||||
'type' => $rel['relationship_type'],
|
||||
'comment' => $rel['comment'],
|
||||
'event_id' => $rel['event_id'],
|
||||
);
|
||||
array_push($this->__json['relations'], $toPush);
|
||||
}
|
||||
|
@ -236,8 +307,8 @@
|
|||
'uuid' => $attr['uuid'],
|
||||
'type' => $attr['type'],
|
||||
'label' => $attr['value'],
|
||||
'event_id' => $attr['event_id'],
|
||||
'node_type' => 'attribute',
|
||||
//'Tag' => $Tags,
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
||||
|
@ -264,6 +335,7 @@
|
|||
'node_type' => 'object',
|
||||
'meta-category' => $obj['meta-category'],
|
||||
'template_uuid' => $obj['template_uuid'],
|
||||
'event_id' => $obj['event_id'],
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
||||
|
@ -342,6 +414,7 @@
|
|||
'uuid' => $attr['uuid'],
|
||||
'type' => $attr['type'],
|
||||
'label' => $attr['value'],
|
||||
'event_id' => $attr['event_id'],
|
||||
'node_type' => 'attribute',
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
@ -369,6 +442,7 @@
|
|||
'node_type' => 'object',
|
||||
'meta-category' => $obj['meta-category'],
|
||||
'template_uuid' => $obj['template_uuid'],
|
||||
'event_id' => $obj['event_id'],
|
||||
);
|
||||
array_push($this->__json['items'], $toPush);
|
||||
|
||||
|
|
|
@ -961,10 +961,10 @@ class Attribute extends AppModel {
|
|||
break;
|
||||
case 'hostname':
|
||||
case 'domain':
|
||||
if (preg_match("#^[A-Z0-9.\-_]+\.[A-Z0-9\-]{2,}$#i", $value)) {
|
||||
if (preg_match("#^[A-Z0-9.\-_]+\.[A-Z0-9\-]{2,}[\.]?$#i", $value)) {
|
||||
$returnValue = true;
|
||||
} else {
|
||||
$returnValue = 'Domain name has an invalid format. Please double check the value or select type "other".';
|
||||
$returnValue = ucfirst($type) . ' name has an invalid format. Please double check the value or select type "other".';
|
||||
}
|
||||
break;
|
||||
case 'hostname|port':
|
||||
|
@ -2571,6 +2571,7 @@ class Attribute extends AppModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
$results = array_values($results);
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
|
|
@ -2700,6 +2700,7 @@ class Event extends AppModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
throw new Exception();
|
||||
}
|
||||
if ($fromXml) $created_id = $this->id;
|
||||
if (!empty($data['Event']['published']) && 1 == $data['Event']['published']) {
|
||||
|
@ -3608,7 +3609,8 @@ class Event extends AppModel {
|
|||
$this->__fTool = new FinancialTool();
|
||||
}
|
||||
if ($object['type'] == 'attachment' && preg_match('/.*\.(jpg|png|jpeg|gif)$/i', $object['value'])) {
|
||||
$object['image'] = $this->Attribute->base64EncodeAttachment($object);
|
||||
if (!empty($object['data'])) $object['image'] = $object['data'];
|
||||
else $object['image'] = $this->Attribute->base64EncodeAttachment($object);
|
||||
}
|
||||
if (isset($object['distribution']) && $object['distribution'] != 4) unset($object['SharingGroup']);
|
||||
if ($object['objectType'] !== 'object') {
|
||||
|
@ -4162,4 +4164,98 @@ class Event extends AppModel {
|
|||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
public function enrichmentRouter($options) {
|
||||
if (Configure::read('MISP.background_jobs')) {
|
||||
$job = ClassRegistry::init('Job');
|
||||
$job->create();
|
||||
$this->ResqueStatus = new ResqueStatus\ResqueStatus(Resque::redis());
|
||||
$workers = $this->ResqueStatus->getWorkers();
|
||||
$workerType = 'default';
|
||||
foreach ($workers as $worker) {
|
||||
if ($worker['queue'] === 'prio') {
|
||||
$workerType = 'prio';
|
||||
}
|
||||
}
|
||||
$data = array(
|
||||
'worker' => $workerType,
|
||||
'job_type' => 'enrichment',
|
||||
'job_input' => 'Event ID: ' . $options['event_id'] . ' modules: ' . json_encode($options['modules']),
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org_id' => $options['user']['org_id'],
|
||||
'org' => $options['user']['Organisation']['name'],
|
||||
'message' => 'Enriching event.',
|
||||
);
|
||||
$job->save($data);
|
||||
$jobId = $job->id;
|
||||
$process_id = CakeResque::enqueue(
|
||||
'prio',
|
||||
'EventShell',
|
||||
array('enrichment', $options['user']['id'], $options['event_id'], json_encode($options['modules']), $jobId),
|
||||
true
|
||||
);
|
||||
$job->saveField('process_id', $process_id);
|
||||
return true;
|
||||
} else {
|
||||
$result = $this->enrichment($options);
|
||||
return __('#' . $result . ' attributes have been created during the enrichment process.');
|
||||
}
|
||||
}
|
||||
|
||||
public function enrichment($params) {
|
||||
$option_fields = array('user', 'event_id', 'modules');
|
||||
foreach ($option_fields as $option_field) {
|
||||
if (empty($params[$option_field])) {
|
||||
throw new MethodNotAllowedException(__('%s not set', $params[$option_field]));
|
||||
}
|
||||
}
|
||||
$event = $this->fetchEvent($params['user'], array('eventid' => $params['event_id'], 'includeAttachments' => 1, 'flatten' => 1));
|
||||
$this->Module = ClassRegistry::init('Module');
|
||||
$enabledModules = $this->Module->getEnabledModules($params['user']);
|
||||
if (empty($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;
|
||||
}
|
||||
}
|
||||
if (empty($event)) throw new MethodNotAllowedException('Invalid event.');
|
||||
$attributes_added = 0;
|
||||
foreach ($event[0]['Attribute'] as $attribute) {
|
||||
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'], $attribute['type'] => $attribute['value'], 'event_id' => $attribute['event_id'], 'attribute_uuid' => $attribute['uuid']);
|
||||
if (!empty($module['config'])) $data['config'] = $module['config'];
|
||||
$data = json_encode($data);
|
||||
$result = $this->Module->queryModuleServer('/query', $data, false, 'Enrichment');
|
||||
if (!$result) throw new MethodNotAllowedException($type . ' service not reachable.');
|
||||
if (isset($result['error'])) $this->Session->setFlash($result['error']);
|
||||
if (!is_array($result)) throw new Exception($result);
|
||||
$attributes = $this->handleModuleResult($result, $attribute['event_id']);
|
||||
foreach ($attributes as $a) {
|
||||
$this->Attribute->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->Attribute->save($a);
|
||||
if ($result) $attributes_added++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $attributes_added;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,45 +16,45 @@ class Log extends AppModel {
|
|||
public $validate = array(
|
||||
'action' => array(
|
||||
'rule' => array('inList', array(
|
||||
'accept',
|
||||
'accept_delegation',
|
||||
'add',
|
||||
'admin_email',
|
||||
'auth',
|
||||
'auth_fail',
|
||||
'blacklisted',
|
||||
'change_pw',
|
||||
'delete',
|
||||
'disable',
|
||||
'discard',
|
||||
'edit',
|
||||
'email',
|
||||
'enable',
|
||||
'error',
|
||||
'export',
|
||||
'file_upload',
|
||||
'galaxy',
|
||||
'login',
|
||||
'login_fail',
|
||||
'logout',
|
||||
'add',
|
||||
'edit',
|
||||
'change_pw',
|
||||
'delete',
|
||||
'merge',
|
||||
'pruneUpdateLogs',
|
||||
'publish',
|
||||
'accept',
|
||||
'discard',
|
||||
'publish alert',
|
||||
'pull',
|
||||
'push',
|
||||
'blacklisted',
|
||||
'admin_email',
|
||||
'tag',
|
||||
'publish alert',
|
||||
'warning',
|
||||
'error',
|
||||
'email',
|
||||
'serverSettingsEdit',
|
||||
'remove_dead_workers',
|
||||
'upload_sample',
|
||||
'request_delegation',
|
||||
'reset_auth_key',
|
||||
'serverSettingsEdit',
|
||||
'tag',
|
||||
'undelete',
|
||||
'update',
|
||||
'update_database',
|
||||
'upgrade_24',
|
||||
'upload_sample',
|
||||
'version_warning',
|
||||
'auth',
|
||||
'auth_fail',
|
||||
'reset_auth_key',
|
||||
'update',
|
||||
'enable',
|
||||
'disable',
|
||||
'accept_delegation',
|
||||
'request_delegation',
|
||||
'merge',
|
||||
'undelete',
|
||||
'file_upload',
|
||||
'export',
|
||||
'pruneUpdateLogs',
|
||||
'galaxy'
|
||||
'warning'
|
||||
)),
|
||||
'message' => 'Options : ...'
|
||||
)
|
||||
|
|
|
@ -89,7 +89,7 @@ class Module extends AppModel {
|
|||
$modules = $this->getModules($type, $moduleFamily);
|
||||
if (is_array($modules)) {
|
||||
foreach ($modules['modules'] as $k => $module) {
|
||||
if (!Configure::read('Plugin.' . $moduleFamily . '_' . $module['name'] . '_enabled') || ($type && in_array(strtolower($type), $module['meta']['module-type']))) {
|
||||
if (!Configure::read('Plugin.' . $moduleFamily . '_' . $module['name'] . '_enabled') || ($type && !in_array(strtolower($type), $module['meta']['module-type']))) {
|
||||
unset($modules['modules'][$k]);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -43,11 +43,7 @@ class Server extends AppModel {
|
|||
'url' => array( // TODO add extra validation to refuse multiple time the same url from the same org
|
||||
'url' => array(
|
||||
'rule' => array('url'),
|
||||
'message' => 'Please enter a valid base-url.',
|
||||
//'allowEmpty' => false,
|
||||
//'required' => false,
|
||||
//'last' => false, // Stop validation after this rule
|
||||
//'on' => 'create', // Limit validation to 'create' or 'update' operations
|
||||
'message' => 'Please enter a valid base-url.'
|
||||
)
|
||||
),
|
||||
'authkey' => array(
|
||||
|
@ -113,7 +109,8 @@ class Server extends AppModel {
|
|||
'pull' => 'MISP/app/Console/cake Server pull [user_id] [server_id] [full|update]',
|
||||
'push' => 'MISP/app/Console/cake Server push [user_id] [server_id]',
|
||||
'cacheFeed' => 'MISP/app/Console/cake Server cacheFeed [user_id] [feed_id|all|csv|text|misp]',
|
||||
'fetchFeed' => 'MISP/app/Console/cake Server fetchFeed [user_id] [feed_id|all|csv|text|misp]'
|
||||
'fetchFeed' => 'MISP/app/Console/cake Server fetchFeed [user_id] [feed_id|all|csv|text|misp]',
|
||||
'enrichment' => 'MISP/app/Console/cake Event enrichEvent [user_id] [event_id] [json_encoded_module_list]'
|
||||
);
|
||||
|
||||
public $serverSettings = array(
|
||||
|
@ -1066,11 +1063,19 @@ class Server extends AppModel {
|
|||
),
|
||||
'timeout' => array(
|
||||
'level' => 0,
|
||||
'description' => 'The timeout duration of sessions (in MINUTES). Keep in mind that autoregenerate can be used to extend the session on user activity.',
|
||||
'description' => 'The timeout duration of sessions (in MINUTES).',
|
||||
'value' => '',
|
||||
'errorMessage' => '',
|
||||
'test' => 'testForNumeric',
|
||||
'type' => 'string',
|
||||
'type' => 'string'
|
||||
),
|
||||
'cookie_timeout' => array(
|
||||
'level' => 0,
|
||||
'description' => 'The expiration of the cookie (in MINUTES). The session timeout gets refreshed frequently, however the cookies do not. Generally it is recommended to have a much higher cookie_timeout than timeout.',
|
||||
'value' => '',
|
||||
'errorMessage' => '',
|
||||
'test' => 'testForNumeric',
|
||||
'type' => 'numeric'
|
||||
)
|
||||
),
|
||||
'Plugin' => array(
|
||||
|
@ -2729,7 +2734,7 @@ class Server extends AppModel {
|
|||
Configure::write($settingFix, $arrayElements);
|
||||
}
|
||||
}
|
||||
$settingsToSave = array('debug', 'MISP', 'GnuPG', 'SMIME', 'Proxy', 'SecureAuth', 'Security', 'Session.defaults', 'Session.timeout', 'Session.autoRegenerate', 'site_admin_debug', 'Plugin', 'CertAuth', 'ApacheShibbAuth', 'ApacheSecureAuth');
|
||||
$settingsToSave = array('debug', 'MISP', 'GnuPG', 'SMIME', 'Proxy', 'SecureAuth', 'Security', 'Session.defaults', 'Session.timeout', 'Session.cookie_timeout', 'Session.autoRegenerate', 'site_admin_debug', 'Plugin', 'CertAuth', 'ApacheShibbAuth', 'ApacheSecureAuth');
|
||||
$settingsArray = array();
|
||||
foreach ($settingsToSave as $setting) {
|
||||
$settingsArray[$setting] = Configure::read($setting);
|
||||
|
|
|
@ -477,6 +477,7 @@ class User extends AppModel {
|
|||
} catch (Exception $e) {
|
||||
$result[2] ='GnuPG is not configured on this system.';
|
||||
$result[0] = true;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
$result = array();
|
||||
|
|
|
@ -31,12 +31,16 @@
|
|||
?>
|
||||
<div class="input clear"></div>
|
||||
<?php
|
||||
|
||||
echo $this->Form->input('distribution', array(
|
||||
$distArray = array(
|
||||
'options' => array($distributionLevels),
|
||||
'label' => __('Distribution ') . $this->element('formInfo', array('type' => 'distribution')),
|
||||
'selected' => $initialDistribution,
|
||||
));
|
||||
);
|
||||
|
||||
if ($action == 'add') {
|
||||
$distArray['selected'] = $initialDistribution;
|
||||
}
|
||||
|
||||
echo $this->Form->input('distribution', $distArray);
|
||||
?>
|
||||
<div id="SGContainer" style="display:none;">
|
||||
<?php
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
if ($attribute['Attribute']['disable_correlation']) {
|
||||
echo __('Re-enable the correlation for this attribute.');
|
||||
} else {
|
||||
echo __('This will remove all correlations that already exist for this attribute and prevents any attributes to be related as long as this setting is disabled. Make sure you understand the downasides of disabling correlations.');
|
||||
echo __('This will remove all correlations that already exist for this attribute and prevents any attributes to be related as long as this setting is disabled. Make sure you understand the downsides of disabling correlations.');
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
|
|
|
@ -283,7 +283,7 @@
|
|||
<?php
|
||||
endif;
|
||||
else:
|
||||
if ($isSiteAdmin || !$mayModify):
|
||||
if ($isAclAdd && ($isSiteAdmin || !$mayModify)):
|
||||
if (isset($modules) && isset($modules['types'][$object['type']])):
|
||||
?>
|
||||
<span class="icon-asterisk useCursorPointer" title="<?php echo __('Query enrichment');?>" role="button" tabindex="0" aria-label="<?php echo __('Query enrichment');?>" onClick="simplePopup('<?php echo $baseurl;?>/events/queryEnrichment/<?php echo h($object['id']);?>/ShadowAttribute');" title="<?php echo __('Propose enrichment');?>"> </span>
|
||||
|
|
|
@ -1,23 +1,40 @@
|
|||
<div style="display:inline-block;">
|
||||
<span style="display:inline-block;">
|
||||
<?php
|
||||
$full = $isAclTagger && $tagAccess;
|
||||
foreach ($tags as $tag):
|
||||
$tagClass = $full ? 'tagFirstHalf' : 'tag';
|
||||
$tagData = "";
|
||||
foreach ($tags as $tag) {
|
||||
$aStyle = 'display:inline-block; background-color:' . h($tag['Tag']['colour']) . ';color:' . $this->TextColour->getTextColour($tag['Tag']['colour']) . ';';
|
||||
$aClass = $full ? 'tagFirstHalf' : 'tag';
|
||||
$aText = h($tag['Tag']['name']);
|
||||
$aSearchTagUrl = $baseurl . '/events/index/searchtag: ' . h($tag['Tag']['id']);
|
||||
$span1 = sprintf('<a href"%s" style="%s" class="%s">%s</a>', $aSearchTagUrl, $aStyle, $aClass, $aText);
|
||||
$span2 = '';
|
||||
if ($full) {
|
||||
$spanClass = "tagSecondHalf useCursorPointer noPrint";
|
||||
$spanTitle = __('Remove tag');
|
||||
$spanRole = "button";
|
||||
$spanTabIndex = "0";
|
||||
$spanAriaLabel = __('Remove tag %s', h($tag['Tag']['name']));
|
||||
$spanOnClick = "removeObjectTagPopup('event', '" . h($event['Event']['id']) . "', '" . h($tag['Tag']['id']) . "')";
|
||||
$span2 = sprintf('<span class="%s" title="%s" role="%s" tabindex="%s" aria-label="%s" onClick="%s">x</span>', $spanClass, $spanTitle, $spanRole, $spanTabIndex, $spanAriaLabel, $spanOnClick);
|
||||
}
|
||||
$tagData .= '<span style="white-space:nowrap;">' . $span1 . $span2 . '</span> ';
|
||||
}
|
||||
$buttonData = " ";
|
||||
if ($full) {
|
||||
$buttonVars = array(
|
||||
'addTagButton',
|
||||
__('Add a tag'),
|
||||
'button',
|
||||
'0',
|
||||
__('Add a tag'),
|
||||
'btn btn-inverse noPrint',
|
||||
'line-height:10px; padding: 4px 4px;',
|
||||
'getPopup(\'' . h($event['Event']['id']) . '\', \'tags\', \'selectTaxonomy\');'
|
||||
);
|
||||
$buttonData = vsprintf('<button id="%s" title="%s" role ="%s" tabindex="%s" aria-label="%s" class="%s" style="%s" onClick="%s">+</button>', $buttonVars);
|
||||
}
|
||||
$tagData .= $buttonData;
|
||||
?>
|
||||
<div style="padding:1px; overflow:hidden; white-space:nowrap; display:flex; float:left; margin-right:2px;">
|
||||
<a href="<?php echo $baseurl;?>/events/index/searchtag:<?php echo h($tag['Tag']['id']); ?>" class="<?php echo $tagClass; ?>" style="display:inline-block; background-color:<?php echo h($tag['Tag']['colour']);?>;color:<?php echo $this->TextColour->getTextColour($tag['Tag']['colour']);?>"><?php echo h($tag['Tag']['name']); ?></a>
|
||||
<?php if ($full): ?>
|
||||
<div class="tagSecondHalf useCursorPointer noPrint" title="<?php echo __('Remove tag');?>" role="button" tabindex="0" aria-label="<?php echo __('Remove tag %s', h($tag['Tag']['name']));?>" onClick="removeObjectTagPopup('event', '<?php echo h($event['Event']['id']); ?>', '<?php echo h($tag['Tag']['id']); ?>');">x</div>
|
||||
<?php endif;?>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
?>
|
||||
<div style="float:left">
|
||||
<?php if ($full): ?>
|
||||
<button id="addTagButton" title="<?php echo __('Add a tag');?>" role="button" tabindex="0" aria-label="<?php echo __('Add a tag');?>" class="btn btn-inverse noPrint" style="line-height:10px; padding: 4px 4px;" onClick="getPopup('<?php echo h($event['Event']['id']); ?>', 'tags', 'selectTaxonomy');">+</button>
|
||||
<?php else:?>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<span style="padding:1px; display:flex; display: inline-block; margin-right:2px;word-wrap:break-word;"><?php echo $tagData; ?></span>
|
||||
</span>
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
if ($cluster_field['key'] == 'refs') {
|
||||
$value = array();
|
||||
foreach ($cluster_field['value'] as $k => $v) {
|
||||
$value[$k] = '<a href="' . h($v) . '">' . h($v) . '</a>';
|
||||
$v_name = $v;
|
||||
if (strlen($v_name) > 30) {
|
||||
$v_name = substr($v, 0, 30) . '...';
|
||||
}
|
||||
$value[$k] = '<a href="' . h($v) . '" title="' . h($v) . '">' . h($v_name) . '</a>';
|
||||
}
|
||||
echo nl2br(implode("\n", $value));
|
||||
} else if($cluster_field['key'] == 'country') {
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
<?php if ($menuItem === 'populateFromtemplate'): ?>
|
||||
<li class="active"><a href="<?php echo $baseurl;?>/templates/populateEventFromTemplate/<?php echo $template_id . '/' . h($event['Event']['id']); ?>"><?php echo __('Populate From Template');?></a></li>
|
||||
<?php endif; ?>
|
||||
<li id='lienrichEvent'><a href="#" onClick="genericPopup('<?php echo $baseurl?>/events/enrichEvent/<?php echo h($event['Event']['id']); ?>', '#confirmation_box');" style="cursor:pointer;"><?php echo __('Enrich event');?></a></li>
|
||||
<li id='merge'><a href="<?php echo $baseurl;?>/events/merge/<?php echo h($event['Event']['id']);?>"><?php echo __('Merge attributes from…');?></a></li>
|
||||
<?php endif; ?>
|
||||
<?php if (($isSiteAdmin && (!isset($mayModify) || !$mayModify)) || (!isset($mayModify) || !$mayModify)): ?>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
|
||||
<span class="shortcut-help btn btn-xs btn-info">?</span>
|
||||
<span class="fullscreen-btn btn btn-xs btn-primary" data-toggle="tooltip" data-placement="top" data-title="<?php echo __('Toggle fullscreen');?>"><span class="fa fa-desktop"></span></span>
|
||||
<span id="fullscreen-btn-eventgraph" class="fullscreen-btn btn btn-xs btn-primary" data-toggle="tooltip" data-placement="top" data-title="<?php echo __('Toggle fullscreen');?>"><span class="fa fa-desktop"></span></span>
|
||||
|
||||
<div id="eventgraph_shortcuts_background" class="eventgraph_network_background"></div>
|
||||
<div id="eventgraph_network" class="eventgraph_network" data-event-id="<?php echo h($event['Event']['id']); ?>" data-user-manipulation="<?php echo $mayModify || $isSiteAdmin ? 'true' : 'false'; ?>" data-extended="<?php echo $extended; ?>"></div>
|
||||
|
|
|
@ -54,6 +54,9 @@
|
|||
'class' => 'form-control span6',
|
||||
'placeholder' => __('Event UUID or ID. Leave blank if not applicable.')
|
||||
));
|
||||
?>
|
||||
<div id="extended_event_preview" style="width:446px;"></div>
|
||||
<?php
|
||||
echo $this->Form->input('Event.submittedgfi', array(
|
||||
'label' => '<b>GFI sandbox</b>',
|
||||
'type' => 'file',
|
||||
|
@ -92,6 +95,10 @@ echo $this->Form->end();
|
|||
initPopoverContent('Event');
|
||||
});
|
||||
|
||||
$("#EventExtendsUuid").keyup(function() {
|
||||
previewEventBasedOnUuids();
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
if ($('#EventDistribution').val() == 4) $('#SGContainer').show();
|
||||
else $('#SGContainer').hide();
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<div class="confirmation">
|
||||
<legend><?php echo __('Enrich Event'); ?></legend>
|
||||
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
|
||||
<p><?php echo __('Select the enrichments you wish to run');?></p>
|
||||
<?php
|
||||
echo $this->Form->create('', array('style' => 'margin-bottom:0px;'));
|
||||
foreach ($modules['modules'] as $module) {
|
||||
echo $this->Form->input($module['name'], array('type' => 'checkbox', 'label' => h($module['name'])));
|
||||
}
|
||||
?>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="vertical-align:top">
|
||||
<?php
|
||||
echo $this->Form->submit('Enrich', array('class' => 'btn btn-primary'));
|
||||
?>
|
||||
</td>
|
||||
<td style="width:540px;">
|
||||
</td>
|
||||
<td style="vertical-align:top;">
|
||||
<span role="button" tabindex="0" aria-label="<?php echo __('Cancel');?>" title="<?php echo __('Cancel');?>" class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt();"><?php echo __('Cancel');?></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
resizePopoverBody();
|
||||
});
|
||||
|
||||
$(window).resize(function() {
|
||||
resizePopoverBody();
|
||||
});
|
||||
</script>
|
|
@ -53,6 +53,7 @@ $mayPublish = ($isAclPublish && $event['Event']['orgc_id'] == $me['org_id']);
|
|||
'placeholder' => __('Event UUID or ID. Leave blank if not applicable.')
|
||||
));
|
||||
?>
|
||||
<div id="extended_event_preview" style="width:446px;"></div>
|
||||
</fieldset>
|
||||
<?php
|
||||
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
|
||||
|
@ -88,10 +89,15 @@ echo $this->Form->end();
|
|||
initPopoverContent('Event');
|
||||
});
|
||||
|
||||
$("#EventExtendsUuid").keyup(function() {
|
||||
previewEventBasedOnUuids();
|
||||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
if ($('#EventDistribution').val() == 4) $('#SGContainer').show();
|
||||
else $('#SGContainer').hide();
|
||||
initPopoverContent('Event');
|
||||
previewEventBasedOnUuids();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<div class="info_container_form">
|
||||
<div class="bold blue">Matched event</div>
|
||||
<?php
|
||||
if (empty($event)) {
|
||||
$message = __('No matching events found.');
|
||||
if ($validUuid) $message .= ' ' . __('This will still allow you to store the UUID. It will extend the assigned event as soon as it is created / becomes visible.');
|
||||
echo '<div class="red bold">' . $message . '</div>';
|
||||
} else {
|
||||
$fields = array(
|
||||
'id' => 'Event.id',
|
||||
'analysis' => 'Event.analysis',
|
||||
'threat level' => 'ThreatLevel.name',
|
||||
'tags' => 'Tag',
|
||||
'info' => 'Event.info'
|
||||
);
|
||||
foreach ($fields as $field => $fieldData) {
|
||||
if ($field == 'tags') {
|
||||
echo '<div><span class="blue bold">Tags</span>: ';
|
||||
if (!empty($event['EventTag'])) {
|
||||
echo '<span>' . $this->element('ajaxTags', array('event' => $event, 'tags' => $event['EventTag'], 'tagAccess' => false)) . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
} else {
|
||||
$data = Hash::extract($event, $fieldData);
|
||||
if ($field == 'analysis') {
|
||||
$data[0] = $analysisLevels[intval($data[0])];
|
||||
}
|
||||
echo '<span class="blue bold">' . ucfirst(__($field)) . '</span>: ' . h($data[0]) . '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
|
@ -361,6 +361,9 @@
|
|||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="eventgraph_toggle" data-toggle-type="eventgraph" onclick="enable_interactive_graph();">
|
||||
<span class="icon-plus icon-white" title="<?php echo __('Toggle Event graph');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle Event graph');?>" style="vertical-align:top;"></span><?php echo __('Event graph');?>
|
||||
</button>
|
||||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="correlationgraph_toggle" data-toggle-type="correlationgraph" onclick="enable_correlation_graph();">
|
||||
<span class="icon-plus icon-white" title="<?php echo __('Toggle Correlation graph');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle Correlation graph');?>" style="vertical-align:top;"></span><?php echo __('Correlation graph');?>
|
||||
</button>
|
||||
<button class="btn btn-inverse toggle qet galaxy-toggle-button" id="attributes_toggle" data-toggle-type="attributes">
|
||||
<span class="icon-minus icon-white" title="<?php echo __('Toggle attributes');?>" role="button" tabindex="0" aria-label="<?php echo __('Toggle attributes');?>" style="vertical-align:top;"></span><?php echo __('Attributes');?>
|
||||
</button>
|
||||
|
@ -380,6 +383,8 @@
|
|||
<div id="eventgraph_div" class="info_container_eventgraph_network" style="display: none;" data-fullscreen="false">
|
||||
<?php echo $this->element('view_event_graph'); ?>
|
||||
</div>
|
||||
<div id="correlationgraph_div" class="info_container_eventgraph_network" style="display: none;" data-fullscreen="false">
|
||||
</div>
|
||||
<div id="attributes_div">
|
||||
<?php echo $this->element('eventattribute'); ?>
|
||||
</div>
|
||||
|
@ -399,9 +404,15 @@ $(document).ready(function () {
|
|||
delay: { show: 500, hide: 100 }
|
||||
});
|
||||
|
||||
$.get("/threads/view/<?php echo $event['Event']['id']; ?>/true", function(data) {
|
||||
$.get("/threads/view/<?php echo h($event['Event']['id']); ?>/true", function(data) {
|
||||
$("#discussions_div").html(data);
|
||||
});
|
||||
});
|
||||
|
||||
function enable_correlation_graph() {
|
||||
$.get("/events/viewGraph/<?php echo h($event['Event']['id']); ?>", function(data) {
|
||||
$("#correlationgraph_div").html(data);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<input type="hidden" value="/shortcuts/event_view.json" class="keyboardShortcutsConfig" />
|
||||
|
|
|
@ -8,24 +8,33 @@
|
|||
echo $this->Html->script('d3');
|
||||
echo $this->Html->script('correlation-graph');
|
||||
?>
|
||||
<div class="view">
|
||||
<div id="chart" style="width:100%;height:100%"></div>
|
||||
<div id="hover-menu-container" class="menu-container">
|
||||
<span class="bold hidden" id="hover-header"><?php echo __('Hover target');?></span><br />
|
||||
<ul id="hover-menu" class="menu">
|
||||
<?php
|
||||
if (!$ajax):
|
||||
?>
|
||||
<div class="view">
|
||||
<?php endif; ?>
|
||||
<span id="fullscreen-btn-correlation" class="fullscreen-btn-correlation btn btn-xs btn-primary" data-toggle="tooltip" data-placement="top" data-title="<?php echo __('Toggle fullscreen');?>"><span class="fa fa-desktop"></span></span>
|
||||
<div id="chart" style="width:100%;height:100%"></div>
|
||||
<div id="hover-menu-container" class="menu-container">
|
||||
<span class="bold hidden" id="hover-header"><?php echo __('Hover target');?></span><br />
|
||||
<ul id="hover-menu" class="menu">
|
||||
</ul>
|
||||
</div>
|
||||
<div id="selected-menu-container" class="menu-container">
|
||||
<span class="bold hidden" id="selected-header"><?php echo __('Selected');?></span><br />
|
||||
<ul id = "selected-menu" class="menu">
|
||||
</ul>
|
||||
</div>
|
||||
<ul id="context-menu" class="menu">
|
||||
<li id="expand"><?php echo __('Expand');?></li>
|
||||
<li id="context-delete"><?php echo __('Delete');?></li>
|
||||
</ul>
|
||||
<?php
|
||||
if (!$ajax):
|
||||
?>
|
||||
</div>
|
||||
<div id="selected-menu-container" class="menu-container">
|
||||
<span class="bold hidden" id="selected-header"><?php echo __('Selected');?></span><br />
|
||||
<ul id = "selected-menu" class="menu">
|
||||
</ul>
|
||||
</div>
|
||||
<ul id="context-menu" class="menu">
|
||||
<li id="expand"><?php echo __('Expand');?></li>
|
||||
<li id="context-delete"><?php echo __('Delete');?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="graph_init" class="hidden" data-id="<?php echo h($id);?>" data-scope="<?php echo h($scope);?>">
|
||||
<?php endif; ?>
|
||||
<div id="graph_init" class="hidden" data-id="<?php echo h($id);?>" data-scope="<?php echo h($scope);?>" data-ajax="<?php echo $ajax ? 'true' : 'false'; ?>">
|
||||
</div>
|
||||
<?php
|
||||
$scope_list = array(
|
||||
|
@ -46,5 +55,8 @@
|
|||
$params['taxonomy'] = $taxonomy['Taxonomy']['id'];
|
||||
}
|
||||
}
|
||||
echo $this->element('side_menu', $params);
|
||||
|
||||
if (!$ajax) {
|
||||
echo $this->element('side_menu', $params);
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -129,6 +129,8 @@ if (h($user['User']['change_pw']) == 1) {
|
|||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
<a href="<?php echo $baseurl . '/admin/users/view/' . h($user['User']['id']) . '.json'; ?>" class="btn btn-inverse" download>Download user profile for data portability</a>
|
||||
<br />
|
||||
<div id="userEvents"></div>
|
||||
</div>
|
||||
<?php
|
||||
|
|
|
@ -86,6 +86,8 @@
|
|||
</dd>
|
||||
<?php endif; ?>
|
||||
</dl>
|
||||
<br />
|
||||
<a href="<?php echo $baseurl . '/users/view/me.json'; ?>" class="btn btn-inverse" download>Download user profile for data portability</a>
|
||||
</div>
|
||||
<?php
|
||||
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'view'));
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d360e6733cb4dff2cc5e8f042b9d322f339d2f83
|
||||
Subproject commit 01b05f66aa9a88e61d0303c32ae5b530926aaa83
|
|
@ -1 +1 @@
|
|||
Subproject commit c8e9155a3eba26696219127d09bcbedf95014416
|
||||
Subproject commit 3b5db95174a13e20db15eecb7837a4447a08e0dd
|
|
@ -93,13 +93,13 @@ SCHEMALOC_DICT = {
|
|||
|
||||
|
||||
def main(args):
|
||||
if len(sys.argv) < 4:
|
||||
if len(args) < 4:
|
||||
sys.exit("Invalid parameters")
|
||||
|
||||
baseURL = sys.argv[1]
|
||||
baseURL = args[1]
|
||||
if not baseURL:
|
||||
baseURL = 'https://www.misp-project.org'
|
||||
orgname = sys.argv[2]
|
||||
orgname = args[2]
|
||||
|
||||
namespace = [baseURL, orgname.replace(" ", "_")]
|
||||
namespace[1] = re.sub('[\W]+', '', namespace[1])
|
||||
|
@ -123,11 +123,11 @@ def main(args):
|
|||
stix_package = STIXPackage()
|
||||
stix_header = STIXHeader()
|
||||
|
||||
stix_header.title="Export from " + orgname + " MISP"
|
||||
stix_header.title="Export from {} MISP".format(orgname)
|
||||
stix_header.package_intents="Threat Report"
|
||||
stix_package.stix_header = stix_header
|
||||
|
||||
if sys.argv[3] == 'json':
|
||||
if args[3] == 'json':
|
||||
stix_string = stix_package.to_json()[:-1]
|
||||
stix_string += ', "related_packages": ['
|
||||
else:
|
||||
|
|
|
@ -42,7 +42,9 @@ class StixParser():
|
|||
def __init__(self):
|
||||
self.misp_event = MISPEvent()
|
||||
self.misp_event['Galaxy'] = []
|
||||
self.references = defaultdict(list)
|
||||
|
||||
# Load data from STIX document, and other usefull data
|
||||
def load(self, args, pathname):
|
||||
try:
|
||||
filename = '{}/tmp/{}'.format(pathname, args[1])
|
||||
|
@ -62,6 +64,7 @@ class StixParser():
|
|||
print(json.dumps({'success': 0, 'message': 'The temporary STIX export file could not be read'}))
|
||||
sys.exit(0)
|
||||
|
||||
# Event loading function, recursively itterating as long as namespace errors appear
|
||||
def load_event(self, filename):
|
||||
try:
|
||||
return STIXPackage.from_xml(filename)
|
||||
|
@ -75,6 +78,7 @@ class StixParser():
|
|||
else:
|
||||
return None
|
||||
|
||||
# Load the mapping dictionary for STIX object types
|
||||
def load_mapping(self):
|
||||
self.attribute_types_mapping = {
|
||||
'AddressObjectType': self.handle_address,
|
||||
|
@ -91,9 +95,13 @@ class StixParser():
|
|||
'URIObjectType': self.handle_domain_or_url,
|
||||
"WhoisObjectType": self.handle_whois,
|
||||
'WindowsRegistryKeyObjectType': self.handle_regkey,
|
||||
"WindowsExecutableFileObjectType": self.handle_pe
|
||||
"WindowsExecutableFileObjectType": self.handle_pe,
|
||||
"WindowsServiceObjectType": self.handle_windows_service
|
||||
}
|
||||
|
||||
# Define if the STIX document is from MISP or is an external one
|
||||
# and call the appropriate function to parse it.
|
||||
# Then, make references between objects
|
||||
def handler(self):
|
||||
self.outputname = '{}.json'.format(self.filename)
|
||||
if self.fromMISP:
|
||||
|
@ -102,13 +110,16 @@ class StixParser():
|
|||
else:
|
||||
# external STIX format file
|
||||
self.buildExternalDict()
|
||||
self.build_references()
|
||||
|
||||
# Build a MISP event, parsing STIX data following the structure used in our own exporter
|
||||
def buildMispDict(self):
|
||||
self.dictTimestampAndDate()
|
||||
self.eventInfo()
|
||||
for indicator in self.event.related_indicators.indicator:
|
||||
self.parse_misp_indicator(indicator)
|
||||
|
||||
# Try to parse data from external STIX documents
|
||||
def buildExternalDict(self):
|
||||
self.dictTimestampAndDate()
|
||||
self.eventInfo()
|
||||
|
@ -121,6 +132,15 @@ class StixParser():
|
|||
if self.event.courses_of_action:
|
||||
self.parse_coa(self.event.courses_of_action)
|
||||
|
||||
# Make references between objects
|
||||
def build_references(self):
|
||||
for misp_object in self.misp_event.objects:
|
||||
object_uuid = misp_object.uuid
|
||||
if object_uuid in self.references:
|
||||
for reference in self.references[object_uuid]:
|
||||
misp_object.add_reference(reference['idref'], reference['relationship'])
|
||||
|
||||
# Set timestamp & date values in the new MISP event
|
||||
def dictTimestampAndDate(self):
|
||||
if self.event.timestamp:
|
||||
stixTimestamp = self.event.timestamp
|
||||
|
@ -131,6 +151,7 @@ class StixParser():
|
|||
self.misp_event.date = date
|
||||
self.misp_event.timestamp = self.getTimestampfromDate(stixTimestamp)
|
||||
|
||||
# Translate date into timestamp
|
||||
@staticmethod
|
||||
def getTimestampfromDate(date):
|
||||
try:
|
||||
|
@ -144,6 +165,7 @@ class StixParser():
|
|||
d = int(time.mktime(date.timetuple()))
|
||||
return d
|
||||
|
||||
# Set info & title values in the new MISP event
|
||||
def eventInfo(self):
|
||||
info = "Imported from external STIX event"
|
||||
try:
|
||||
|
@ -157,6 +179,7 @@ class StixParser():
|
|||
pass
|
||||
self.misp_event.info = str(info)
|
||||
|
||||
# Parse indicators of a STIX document coming from our exporter
|
||||
def parse_misp_indicator(self, indicator):
|
||||
# define is an indicator will be imported as attribute or object
|
||||
if indicator.relationship in categories:
|
||||
|
@ -164,6 +187,7 @@ class StixParser():
|
|||
else:
|
||||
self.parse_misp_object(indicator)
|
||||
|
||||
# Parse STIX objects that we know will give MISP attributes
|
||||
def parse_misp_attribute(self, indicator):
|
||||
misp_attribute = {'category': str(indicator.relationship)}
|
||||
item = indicator.item
|
||||
|
@ -184,6 +208,7 @@ class StixParser():
|
|||
attribute_type, attribute_value = self.composite_type(attribute_dict)
|
||||
self.misp_event.add_attribute(attribute_type, attribute_value, **misp_attribute)
|
||||
|
||||
# Return type & value of a composite attribute in MISP
|
||||
@staticmethod
|
||||
def composite_type(attributes):
|
||||
if "port" in attributes:
|
||||
|
@ -200,6 +225,7 @@ class StixParser():
|
|||
ip_value = attributes["ip-dst"]
|
||||
return "domain|ip", "{}|{}".format(attributes["domain"], ip_value)
|
||||
|
||||
# Define type & value of an attribute or object in MISP
|
||||
def handle_attribute_type(self, properties, is_object=False, title=None):
|
||||
xsi_type = properties._XSI_TYPE
|
||||
try:
|
||||
|
@ -214,6 +240,7 @@ class StixParser():
|
|||
print("Unparsed type: {}".format(xsi_type))
|
||||
sys.exit(1)
|
||||
|
||||
# Return type & value of an ip address attribute
|
||||
@staticmethod
|
||||
def handle_address(properties):
|
||||
if properties.is_source:
|
||||
|
@ -222,15 +249,18 @@ class StixParser():
|
|||
ip_type = "ip-dst"
|
||||
return ip_type, properties.address_value.value, "ip"
|
||||
|
||||
# Return type & value of an attachment attribute
|
||||
@staticmethod
|
||||
def handle_attachment(properties, title):
|
||||
return eventTypes[properties._XSI_TYPE]['type'], title, properties.raw_artifact.value
|
||||
|
||||
# Return type & value of a domain or url attribute
|
||||
@staticmethod
|
||||
def handle_domain_or_url(properties):
|
||||
event_types = eventTypes[properties._XSI_TYPE]
|
||||
return event_types['type'], properties.value.value, event_types['relation']
|
||||
|
||||
# Return type & value of an email attribute
|
||||
def handle_email_attribute(self, properties):
|
||||
try:
|
||||
if properties.from_:
|
||||
|
@ -257,11 +287,13 @@ class StixParser():
|
|||
print("Unsupported Email property")
|
||||
sys.exit(1)
|
||||
|
||||
# Return type & value of an email attachment
|
||||
@staticmethod
|
||||
def handle_email_attachment(indicator_object):
|
||||
properties = indicator_object.related_objects[0].properties
|
||||
return "email-attachment", properties.file_name.value, "attachment"
|
||||
|
||||
# Return type & attributes of a file object
|
||||
def handle_file(self, properties, is_object):
|
||||
b_hash, b_file = False, False
|
||||
attributes = []
|
||||
|
@ -296,6 +328,7 @@ class StixParser():
|
|||
return self.handle_filename_object(attributes, is_object)
|
||||
return "file", self.return_attributes(attributes), ""
|
||||
|
||||
# Return the appropriate type & value when we have 1 filename & 1 hash value
|
||||
@staticmethod
|
||||
def handle_filename_object(attributes, is_object):
|
||||
for attribute in attributes:
|
||||
|
@ -313,6 +346,7 @@ class StixParser():
|
|||
# it could be malware-sample as well, but STIX is losing this information
|
||||
return "filename|{}".format(hash_type), value, ""
|
||||
|
||||
# Return type & value of a hash attribute
|
||||
@staticmethod
|
||||
def handle_hashes_attribute(properties):
|
||||
hash_type = properties.type_.value.lower()
|
||||
|
@ -322,11 +356,13 @@ class StixParser():
|
|||
hash_value = properties.fuzzy_hash_value.value
|
||||
return hash_type, hash_value, hash_type
|
||||
|
||||
# Return type & value of a hostname attribute
|
||||
@staticmethod
|
||||
def handle_hostname(properties):
|
||||
event_types = eventTypes[properties._XSI_TYPE]
|
||||
return event_types['type'], properties.hostname_value.value, event_types['relation']
|
||||
|
||||
# Return type & value of a http request attribute
|
||||
@staticmethod
|
||||
def handle_http(properties):
|
||||
client_request = properties.http_request_response[0].http_client_request
|
||||
|
@ -342,21 +378,25 @@ class StixParser():
|
|||
value = client_request.http_request_line.http_method.value
|
||||
return "http-method", value, "method"
|
||||
|
||||
# Return type & value of a mutex attribute
|
||||
@staticmethod
|
||||
def handle_mutex(properties):
|
||||
event_types = eventTypes[properties._XSI_TYPE]
|
||||
return event_types['type'], properties.name.value, event_types['relation']
|
||||
|
||||
# Return type & value of a port attribute
|
||||
@staticmethod
|
||||
def handle_port(properties):
|
||||
event_types = eventTypes[properties._XSI_TYPE]
|
||||
return event_types['type'], properties.port_value.value, event_types['relation']
|
||||
|
||||
# Return type & value of a regkey attribute
|
||||
@staticmethod
|
||||
def handle_regkey(properties):
|
||||
event_types = eventTypes[properties._XSI_TYPE]
|
||||
return event_types['type'], properties.key.value, event_types['relation']
|
||||
|
||||
# Return type & value of a composite attribute ip|port or hostname|port
|
||||
def handle_socket_address(self, properties):
|
||||
if properties.ip_address:
|
||||
type1, value1, _ = self.handle_address(properties.ip_address)
|
||||
|
@ -365,6 +405,9 @@ class StixParser():
|
|||
value1 = properties.hostname.hostname_value.value
|
||||
return "{}|port".format(type1), "{}|{}".format(value1, properties.port.port_value.value), ""
|
||||
|
||||
# Parse a whois object:
|
||||
# Return type & attributes of a whois object if we have the required fields
|
||||
# Otherwise create attributes and return type & value of the last attribute to avoid crashing the parent function
|
||||
def handle_whois(self, properties):
|
||||
required_one_of = False
|
||||
attributes = []
|
||||
|
@ -411,11 +454,19 @@ class StixParser():
|
|||
self.misp_event.add_attribute(attribute_type, attribute_value, **misp_attributes)
|
||||
return last_attribute
|
||||
|
||||
@staticmethod
|
||||
def handle_windows_service(properties):
|
||||
if properties.name:
|
||||
return "windows-service-name", properties.name.value, ""
|
||||
|
||||
# Return type & attributes of the file defining a portable executable object
|
||||
def handle_pe(self, properties):
|
||||
pe_uuid = self.parse_pe(properties)
|
||||
file_type, file_value, _ = self.handle_file(properties, False)
|
||||
return file_type, file_value, pe_uuid
|
||||
|
||||
# Parse attributes of a portable executable, create the corresponding object,
|
||||
# and return its uuid to build the reference for the file object generated at the same time
|
||||
def parse_pe(self, properties):
|
||||
misp_object = MISPObject('pe')
|
||||
filename = properties.file_name.value
|
||||
|
@ -437,14 +488,16 @@ class StixParser():
|
|||
header_object.add_attribute(**{"type": "size-in-bytes", "object_relation": "size-in-bytes",
|
||||
"value": file_header.size_of_optional_header.value})
|
||||
self.misp_event.add_object(**header_object)
|
||||
misp_object.add_reference(header_object.uuid, 'pe-section')
|
||||
misp_object.add_reference(header_object.uuid, 'included-in')
|
||||
if properties.sections:
|
||||
for section in properties.sections:
|
||||
section_uuid = self.parse_pe_section(section)
|
||||
misp_object.add_reference(section_uuid, 'pe-section')
|
||||
misp_object.add_reference(section_uuid, 'included-in')
|
||||
self.misp_event.add_object(**misp_object)
|
||||
return {"pe_uuid": misp_object.uuid}
|
||||
|
||||
# Parse attributes of a portable executable section, create the corresponding object,
|
||||
# and return its uuid to build the reference for the pe object generated at the same time
|
||||
def parse_pe_section(self, section):
|
||||
section_object = MISPObject('pe-section')
|
||||
header_hashes = section.header_hashes
|
||||
|
@ -463,6 +516,7 @@ class StixParser():
|
|||
self.misp_event.add_object(**section_object)
|
||||
return section_object.uuid
|
||||
|
||||
# Parse STIX object that we know will give MISP objects
|
||||
def parse_misp_object(self, indicator):
|
||||
object_type = str(indicator.relationship)
|
||||
if object_type == 'file':
|
||||
|
@ -477,6 +531,7 @@ class StixParser():
|
|||
if object_type != "misc":
|
||||
print("Unparsed Object type: {}".format(name))
|
||||
|
||||
# Create a MISP object, its attributes, and add it in the MISP event
|
||||
def fill_misp_object(self, item, name):
|
||||
misp_object = MISPObject(name)
|
||||
misp_object.timestamp = self.getTimestampfromDate(item.timestamp)
|
||||
|
@ -490,11 +545,13 @@ class StixParser():
|
|||
self.parse_observable(properties, misp_object)
|
||||
self.misp_event.add_object(**misp_object)
|
||||
|
||||
# Create a MISP attribute and add it in its MISP object
|
||||
def parse_observable(self, properties, misp_object):
|
||||
misp_attribute = MISPAttribute()
|
||||
misp_attribute.type, misp_attribute.value, misp_attribute.object_relation = self.handle_attribute_type(properties, is_object=True)
|
||||
misp_object.add_attribute(**misp_attribute)
|
||||
|
||||
# Parse indicators of an external STIX document
|
||||
def parse_external_indicator(self, indicators):
|
||||
for indicator in indicators:
|
||||
try:
|
||||
|
@ -514,29 +571,41 @@ class StixParser():
|
|||
# otherwise, it is a dictionary of attributes, so we build an object
|
||||
self.handle_object_case(attribute_type, attribute_value, compl_data)
|
||||
|
||||
# Parse observables of an external STIX document
|
||||
def parse_external_observable(self, observables):
|
||||
for observable in observables:
|
||||
title = observable.title
|
||||
observable_object = observable.object_
|
||||
try:
|
||||
properties = observable.object_.properties
|
||||
properties = observable_object.properties
|
||||
except:
|
||||
self.parse_description(observable)
|
||||
continue
|
||||
if properties:
|
||||
try:
|
||||
attribute_type, attribute_value, compl_data = self.handle_attribute_type(properties, title=title)
|
||||
except:
|
||||
except KeyError:
|
||||
# print("Error with an object of type: {}\n{}".format(properties._XSI_TYPE, observable.to_json()))
|
||||
continue
|
||||
object_uuid = self.fetch_uuid(observable_object.id_)
|
||||
attr_type = type(attribute_value)
|
||||
if attr_type is str or attr_type is int:
|
||||
# if the returned value is a simple value, we build an attribute
|
||||
attribute = {'to_ids': False}
|
||||
attribute = {'to_ids': False, 'uuid': object_uuid}
|
||||
# if observable_object.related_objects:
|
||||
# attribute
|
||||
self.handle_attribute_case(attribute_type, attribute_value, compl_data, attribute)
|
||||
else:
|
||||
# otherwise, it is a dictionary of attributes, so we build an object
|
||||
if attribute_value:
|
||||
self.handle_object_case(attribute_type, attribute_value, compl_data)
|
||||
self.handle_object_case(attribute_type, attribute_value, compl_data, object_uuid=object_uuid)
|
||||
if observable_object.related_objects:
|
||||
for related_object in observable_object.related_objects:
|
||||
relationship = related_object.relationship.value.inner().replace('_', '-')
|
||||
self.references[object_uuid].append({"idref": self.fetch_uuid(related_object.idref),
|
||||
"relationship": relationship})
|
||||
|
||||
# Parse description of an external indicator or observable and add it in the MISP event as an attribute
|
||||
def parse_description(self, stix_object):
|
||||
if stix_object.description:
|
||||
misp_attribute = {}
|
||||
|
@ -544,22 +613,36 @@ class StixParser():
|
|||
misp_attribute['timestamp'] = self.getTimestampfromDate(stix_object.timestamp)
|
||||
self.misp_event.add_attribute("text", stix_object.description.value, **misp_attribute)
|
||||
|
||||
# The value returned by the indicators or observables parser is of type str or int
|
||||
# Thus we can add an attribute in the MISP event with the type & value
|
||||
def handle_attribute_case(self, attribute_type, attribute_value, data, attribute):
|
||||
if attribute_type == 'attachment':
|
||||
attribute['data'] = data
|
||||
self.misp_event.add_attribute(attribute_type, attribute_value, **attribute)
|
||||
|
||||
def handle_object_case(self, attribute_type, attribute_value, compl_data):
|
||||
# The value returned by the indicators or observables parser is a list of dictionaries
|
||||
# These dictionaries are the attributes we add in an object, itself added in the MISP event
|
||||
def handle_object_case(self, attribute_type, attribute_value, compl_data, object_uuid=None):
|
||||
misp_object = MISPObject(attribute_type)
|
||||
if object_uuid:
|
||||
misp_object.uuid = object_uuid
|
||||
for attribute in attribute_value:
|
||||
misp_object.add_attribute(**attribute)
|
||||
if type(compl_data) is dict and "pe_uuid" in compl_data:
|
||||
# if some complementary data is a dictionary containing an uuid,
|
||||
# it means we are using it to add an object reference of a pe object
|
||||
# in a file object
|
||||
misp_object.add_reference(compl_data['pe_uuid'], 'pe')
|
||||
# it means we are using it to add an object reference
|
||||
misp_object.add_reference(compl_data['pe_uuid'], 'included-in')
|
||||
self.misp_event.add_object(**misp_object)
|
||||
|
||||
@staticmethod
|
||||
def fetch_uuid(object_id):
|
||||
identifier = object_id.split(':')[1]
|
||||
return_id = ""
|
||||
for part in identifier.split('-')[1:]:
|
||||
return_id += "{}-".format(part)
|
||||
return return_id[:-1]
|
||||
|
||||
# Parse the ttps field of an external STIX document
|
||||
def parse_ttps(self, ttps):
|
||||
for ttp in ttps:
|
||||
if ttp.behavior and ttp.behavior.malware_instances:
|
||||
|
@ -580,6 +663,7 @@ class StixParser():
|
|||
galaxy['GalaxyCluster'] = [cluster]
|
||||
self.misp_event['Galaxy'].append(galaxy)
|
||||
|
||||
# Parse the courses of action field of an external STIX document
|
||||
def parse_coa(self, courses_of_action):
|
||||
for coa in courses_of_action:
|
||||
misp_object = MISPObject('course-of-action')
|
||||
|
@ -626,6 +710,7 @@ class StixParser():
|
|||
misp_object.add_reference(referenced_uuid, 'observable', None, **attribute)
|
||||
self.misp_event.add_object(**misp_object)
|
||||
|
||||
# Return the attributes that will be added in a MISP object as a list of dictionaries
|
||||
@staticmethod
|
||||
def return_attributes(attributes):
|
||||
return_attributes = []
|
||||
|
@ -633,6 +718,8 @@ class StixParser():
|
|||
return_attributes.append(dict(zip(('type', 'value', 'object_relation'), attribute)))
|
||||
return return_attributes
|
||||
|
||||
# Convert the MISP event we create from the STIX document into json format
|
||||
# and write it in the output file
|
||||
def saveFile(self):
|
||||
eventDict = self.misp_event.to_json()
|
||||
with open(self.outputname, 'w') as f:
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f54d241f2b159114224db2e8447e2c8bcf36ed8e
|
||||
Subproject commit bd95487001b0415e1060f8f2a73a76e2db7b8b18
|
|
@ -79,3 +79,13 @@ line.link {
|
|||
top:-5px;
|
||||
margin-left:100px;
|
||||
}
|
||||
.fullscreen-btn-correlation {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
bottom: 2px;
|
||||
font-weight: bold;
|
||||
z-index: 1;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -133,6 +133,7 @@ label.center-in-network-header {
|
|||
z-index: 1;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.popover-content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
|
@ -1677,6 +1677,16 @@ a.discrete {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.info_container_form {
|
||||
border: 1px solid black; /*like Alexandre's heart*/
|
||||
border-radius: 7px;
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
width: 438px;
|
||||
height: 100%;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.info_container_key {
|
||||
vertical-align:text-top;
|
||||
width:100px;
|
||||
|
|
|
@ -203,8 +203,13 @@ class ActionTable {
|
|||
__add_options_to_select(select, options) {
|
||||
for(var value of options) {
|
||||
var option = document.createElement('option');
|
||||
option.value = value;
|
||||
option.innerHTML = value;
|
||||
if (Array.isArray(value)) { // array of type [value, text]
|
||||
option.value = value[1];
|
||||
option.innerHTML = value[1];
|
||||
} else { // only value, text=value
|
||||
option.value = value;
|
||||
option.innerHTML = value;
|
||||
}
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ class ContextualMenu {
|
|||
constructor(options) {
|
||||
this.options = options;
|
||||
this.trigger_container = options.trigger_container;
|
||||
this.container = options.container;
|
||||
this.bootstrap_popover = options.bootstrap_popover;
|
||||
this.right_click = options.right_click;
|
||||
this.has_been_shown_once = false;
|
||||
|
@ -90,7 +91,7 @@ class ContextualMenu {
|
|||
__toggleMenu(x, y, hide) {
|
||||
var that = this;
|
||||
if(this.__is_shown || hide) {
|
||||
that.menu.style.visibility = 'hidden';
|
||||
this.menu.style.visibility = 'hidden';
|
||||
} else {
|
||||
this.menu.style.left = x+'px';
|
||||
this.menu.style.top = y+'px';
|
||||
|
@ -103,7 +104,7 @@ class ContextualMenu {
|
|||
var div = document.createElement('div');
|
||||
div.classList.add("contextual-menu");
|
||||
div.classList.add("contextual-menu-styling");
|
||||
document.body.appendChild(div);
|
||||
this.container.appendChild(div);
|
||||
// register on click for the trigger_container
|
||||
var that = this;
|
||||
if (this.right_click) {
|
||||
|
@ -130,16 +131,17 @@ class ContextualMenu {
|
|||
__create_menu_div_bootstrap_popover() {
|
||||
var div = document.createElement('div');
|
||||
div.classList.add("contextual-menu");
|
||||
document.body.appendChild(div);
|
||||
this.container.appendChild(div);
|
||||
var that = this;
|
||||
this.trigger_container.tabIndex = 0; // required for the popover focus feature
|
||||
var additional_styling = this.options.style === undefined ? "" : this.options.style;
|
||||
$(this.trigger_container).popover({
|
||||
container: 'body',
|
||||
html: true,
|
||||
placement: "bottom",
|
||||
content: function () {return $(that.menu); }, // return contextual menu htlm
|
||||
content: function () {return $(that.menu); }, // return contextual menu html
|
||||
trigger: "manual",
|
||||
template: '<div class="popover" id="popover-contextual-menu-'+this.trigger_container.id+'" role="tooltip"><div class="arrow"></div></h3><div class="popover-content"></div></div>'
|
||||
template: '<div class="popover" id="popover-contextual-menu-'+this.trigger_container.id+'" role="tooltip" style="'+additional_styling+'"><div class="arrow"></div></h3><div class="popover-content"></div></div>'
|
||||
})
|
||||
|
||||
// Overwrite the default popover behavior: hidding cause the popover to be detached from the DOM, making impossible to fetch input values in the form
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
$(document).ready( function() {
|
||||
|
||||
var currentMousePos = { x: -1, y: -1 };
|
||||
$(document).mousemove(function(event) {
|
||||
currentMousePos.x = event.pageX;
|
||||
|
@ -11,12 +10,32 @@ $(document).ready( function() {
|
|||
height = $(window).height() - 160 - margin.top - margin.bottom;
|
||||
var menu_x_buffer_ = width - 150;
|
||||
var menu_y_buffer = height - 100;
|
||||
$('.menu-container').css('left', '200px');
|
||||
$('#hover-menu-container').css('top', '50px');
|
||||
|
||||
if ($('#graph_init').data('ajax')) {
|
||||
$('.menu-container').css('left', '20px');
|
||||
$('#hover-menu-container').css('top', '20px');
|
||||
$('#selected-menu-container').css('top', '270px');
|
||||
} else {
|
||||
$('.menu-container').css('left', '200px');
|
||||
$('#hover-menu-container').css('top', '50px');
|
||||
$('#selected-menu-container').css('top', '400px');
|
||||
}
|
||||
$('#hover-menu-container').css('z-index', 0);
|
||||
$('#selected-menu-container').css('top', '400px');
|
||||
$('#selected-menu-container').css('z-index', 1);
|
||||
|
||||
|
||||
$('#fullscreen-btn-correlation').click(function() {
|
||||
var network_div = $('#correlationgraph_div');
|
||||
var fullscreen_enabled = !network_div.data('fullscreen');
|
||||
network_div.data('fullscreen', fullscreen_enabled);
|
||||
var height_val = fullscreen_enabled == true ? "calc(100vh - 42px - 42px - 10px)" : "500px";
|
||||
|
||||
network_div.css("height", height_val);
|
||||
network_div[0].scrollIntoView({
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
|
||||
var root;
|
||||
|
||||
var highlighted;
|
||||
|
@ -162,7 +181,7 @@ $(document).ready( function() {
|
|||
}
|
||||
}
|
||||
)
|
||||
.append("xhtml:body")
|
||||
.append("xhtml:div")
|
||||
.html(function (d) {
|
||||
var result = 'fa-' + d.imgClass;
|
||||
if (d.type == 'galaxy' || d.type == 'tag') result = 'fa-2x ' + result;
|
||||
|
|
|
@ -22,6 +22,7 @@ mapping_root_id_to_type[root_id_tag] = 'tag';
|
|||
mapping_root_id_to_type[root_id_keyType] = 'keyType';
|
||||
var root_node_x_pos = 800;
|
||||
var cluster_expand_threshold = 100;
|
||||
var nodes_ask_threshold = 300;
|
||||
|
||||
/*=========
|
||||
* CLASSES
|
||||
|
@ -65,6 +66,9 @@ class EventGraph {
|
|||
this.cluster_index = 0; // use to get uniq cluster ID
|
||||
this.clusters = [];
|
||||
|
||||
this.extended_event_color_mapping = {};
|
||||
this.extended_event_points = {};
|
||||
|
||||
this.network = new vis.Network(container, data, this.network_options);
|
||||
this.add_unreferenced_root_node();
|
||||
|
||||
|
@ -89,6 +93,26 @@ class EventGraph {
|
|||
this.network.on("dragEnd", function (params) {
|
||||
eventGraph.physics_state($('#checkbox_physics_enable').prop("checked"));
|
||||
});
|
||||
|
||||
// create Hull for extending events
|
||||
this.network.on("beforeDrawing", function (ctx) {
|
||||
if (that.scope_name != "Reference" || !that.canDrawHull) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var event_id in that.extended_event_points) {
|
||||
if (that.extended_event_color_mapping[event_id] === undefined) {
|
||||
eventGraph.extended_event_color_mapping[event_id] = getRandomColor(event_id);
|
||||
}
|
||||
var chosen_color = eventGraph.extended_event_color_mapping[event_id];
|
||||
|
||||
var nodes = that.network.getPositions(that.extended_event_points[event_id]);
|
||||
nodes = $.map(nodes, function(value, index) { // object to array
|
||||
return [value];
|
||||
});
|
||||
drawExtendedEventHull(ctx, nodes, chosen_color, "Event "+event_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Util
|
||||
|
@ -124,6 +148,8 @@ class EventGraph {
|
|||
var menu_scope = new ContextualMenu({
|
||||
trigger_container: document.getElementById("network-scope"),
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventgraph_div")
|
||||
});
|
||||
menu_scope.add_select({
|
||||
id: "select_graph_scope",
|
||||
|
@ -162,7 +188,9 @@ class EventGraph {
|
|||
init_physic_menu() {
|
||||
var menu_physic = new ContextualMenu({
|
||||
trigger_container: document.getElementById("network-physic"),
|
||||
bootstrap_popover: true
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventgraph_div")
|
||||
});
|
||||
menu_physic.add_select({
|
||||
id: "select_physic_solver",
|
||||
|
@ -200,7 +228,9 @@ class EventGraph {
|
|||
init_display_menu() {
|
||||
var menu_display = new ContextualMenu({
|
||||
trigger_container: document.getElementById("network-display"),
|
||||
bootstrap_popover: true
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventgraph_div")
|
||||
});
|
||||
menu_display.add_select({
|
||||
id: "select_display_layout",
|
||||
|
@ -283,7 +313,9 @@ class EventGraph {
|
|||
init_filter_menu() {
|
||||
var menu_filter = new ContextualMenu({
|
||||
trigger_container: document.getElementById("network-filter"),
|
||||
bootstrap_popover: true
|
||||
bootstrap_popover: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventgraph_div")
|
||||
});
|
||||
menu_filter.add_action_table({
|
||||
id: "table_attr_presence",
|
||||
|
@ -308,6 +340,29 @@ class EventGraph {
|
|||
data: [],
|
||||
});
|
||||
menu_filter.create_divider(3);
|
||||
menu_filter.add_action_table({
|
||||
id: "table_tag_presence",
|
||||
container: menu_filter.menu,
|
||||
title: "Filter on Tag presence",
|
||||
header: ["Relation", "Tag"],
|
||||
control_items: [
|
||||
{
|
||||
DOMType: "select",
|
||||
item_options: {
|
||||
options: ["Contains", "Do not contain"]
|
||||
}
|
||||
},
|
||||
{
|
||||
DOMType: "select",
|
||||
item_options: {
|
||||
id: "table_control_select_tag_presence",
|
||||
options: []
|
||||
}
|
||||
},
|
||||
],
|
||||
data: [],
|
||||
});
|
||||
menu_filter.create_divider(3);
|
||||
menu_filter.add_action_table({
|
||||
id: "table_attr_value",
|
||||
container: menu_filter.menu,
|
||||
|
@ -351,7 +406,9 @@ class EventGraph {
|
|||
init_canvas_menu() {
|
||||
var menu_canvas = new ContextualMenu({
|
||||
trigger_container: document.getElementById("eventgraph_network"),
|
||||
right_click: true
|
||||
right_click: true,
|
||||
style: "z-index: 1",
|
||||
container: document.getElementById("eventgraph_div")
|
||||
});
|
||||
menu_canvas.add_button({
|
||||
label: "View/Edit",
|
||||
|
@ -402,8 +459,9 @@ class EventGraph {
|
|||
|
||||
get_filtering_rules() {
|
||||
var rules_presence = eventGraph.menu_filter.items["table_attr_presence"].get_data();
|
||||
var rules_tag_presence = eventGraph.menu_filter.items["table_tag_presence"].get_data();
|
||||
var rules_value = eventGraph.menu_filter.items["table_attr_value"].get_data();
|
||||
var rules = { presence: rules_presence, value: rules_value };
|
||||
var rules = { presence: rules_presence, tag_presence: rules_tag_presence, value: rules_value };
|
||||
return rules;
|
||||
}
|
||||
// Graph interaction
|
||||
|
@ -434,11 +492,14 @@ class EventGraph {
|
|||
this.edges.clear();
|
||||
if (hard) {
|
||||
this.backup_connection_edges = {};
|
||||
this.extended_event_points = {};
|
||||
this.extended_event_color_mapping = {};
|
||||
}
|
||||
}
|
||||
|
||||
update_graph(data) {
|
||||
setTimeout(function() { eventGraph.network_loading(true, loadingText_creating); });
|
||||
var that = this;
|
||||
that.network_loading(true, loadingText_creating);
|
||||
|
||||
// New nodes will be automatically added
|
||||
// removed references will be deleted
|
||||
|
@ -447,10 +508,17 @@ class EventGraph {
|
|||
var newNodeIDs = [];
|
||||
for(var node of data.items) {
|
||||
var group, label;
|
||||
if (node.event_id != scope_id) { // add node ids of extended event
|
||||
if (that.extended_event_points[node.event_id] === undefined) {
|
||||
that.extended_event_points[node.event_id] = [];
|
||||
}
|
||||
that.extended_event_points[node.event_id].push(node.id);
|
||||
}
|
||||
|
||||
if ( node.node_type == 'object' ) {
|
||||
var group = 'object';
|
||||
var label = dataHandler.generate_label(node);
|
||||
var striped_value = this.strip_text_value(label);
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
id: node.id,
|
||||
uuid: node.uuid,
|
||||
|
@ -462,7 +530,7 @@ class EventGraph {
|
|||
icon: {
|
||||
color: getRandomColor(),
|
||||
face: 'FontAwesome',
|
||||
code: this.get_FA_icon(node['meta-category']),
|
||||
code: that.get_FA_icon(node['meta-category']),
|
||||
}
|
||||
};
|
||||
dataHandler.mapping_value_to_nodeID.set(striped_value, node.id);
|
||||
|
@ -493,8 +561,8 @@ class EventGraph {
|
|||
dataHandler.mapping_value_to_nodeID.set(striped_value, node.id);
|
||||
} else if (node.node_type == 'keyType') {
|
||||
group = 'keyType';
|
||||
label = this.scope_keyType + ": " + node.label;
|
||||
var striped_value = this.strip_text_value(label);
|
||||
label = that.scope_keyType + ": " + node.label;
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
id: node.id,
|
||||
label: striped_value,
|
||||
|
@ -505,7 +573,7 @@ class EventGraph {
|
|||
} else {
|
||||
group = 'attribute';
|
||||
label = node.type + ': ' + node.label;
|
||||
var striped_value = this.strip_text_value(label);
|
||||
var striped_value = that.strip_text_value(label);
|
||||
node_conf = {
|
||||
id: node.id,
|
||||
uuid: node.uuid,
|
||||
|
@ -521,7 +589,7 @@ class EventGraph {
|
|||
newNodeIDs.push(node.id);
|
||||
}
|
||||
// check if nodes got deleted
|
||||
var old_node_ids = this.nodes.getIds();
|
||||
var old_node_ids = that.nodes.getIds();
|
||||
for (var old_id of old_node_ids) {
|
||||
// Ignore root node
|
||||
if (old_id == "rootNode:attribute" || old_id == "rootNode:object" || old_id == "rootNode:tag" || old_id == "rootNode:keyType") {
|
||||
|
@ -529,11 +597,11 @@ class EventGraph {
|
|||
}
|
||||
// This old node got removed
|
||||
if (newNodeIDs.indexOf(old_id) == -1) {
|
||||
this.nodes.remove(old_id);
|
||||
that.nodes.remove(old_id);
|
||||
}
|
||||
}
|
||||
|
||||
this.nodes.update(newNodes);
|
||||
that.nodes.update(newNodes);
|
||||
|
||||
// New relations will be automatically added
|
||||
// removed references will be deleted
|
||||
|
@ -554,42 +622,46 @@ class EventGraph {
|
|||
newRelationIDs.push(rel.id);
|
||||
}
|
||||
// check if nodes got deleted
|
||||
var old_rel_ids = this.edges.getIds();
|
||||
var old_rel_ids = that.edges.getIds();
|
||||
for (var old_id of old_rel_ids) {
|
||||
// This old node got removed
|
||||
if (newRelationIDs.indexOf(old_id) == -1) {
|
||||
this.edges.remove(old_id);
|
||||
that.edges.remove(old_id);
|
||||
}
|
||||
}
|
||||
|
||||
this.edges.update(newRelations);
|
||||
that.edges.update(newRelations);
|
||||
|
||||
this.remove_root_nodes();
|
||||
if (this.scope_name == 'Reference') {
|
||||
this.add_unreferenced_root_node();
|
||||
// links unreferenced attributes and object to root nodes
|
||||
if (this.first_draw) {
|
||||
this.link_not_referenced_nodes();
|
||||
this.first_draw = !this.first_draw
|
||||
}
|
||||
} else if (this.scope_name == 'Tag') {
|
||||
this.add_tag_root_node();
|
||||
// links untagged attributes and object to root nodes
|
||||
if (this.first_draw) {
|
||||
this.link_not_referenced_nodes();
|
||||
this.first_draw = !this.first_draw
|
||||
}
|
||||
} else if (this.scope_name == 'Distribution') {
|
||||
} else if (this.scope_name == 'Correlation') {
|
||||
} else {
|
||||
this.add_keyType_root_node();
|
||||
if (this.first_draw) {
|
||||
this.link_not_referenced_nodes();
|
||||
this.first_draw = !this.first_draw
|
||||
that.remove_root_nodes();
|
||||
// do not clusterize if the network is filtered
|
||||
if (!that.is_filtered) {
|
||||
if (that.scope_name == 'Reference') {
|
||||
that.add_unreferenced_root_node();
|
||||
// links unreferenced attributes and object to root nodes
|
||||
if (that.first_draw) {
|
||||
that.link_not_referenced_nodes();
|
||||
that.first_draw = !that.first_draw
|
||||
}
|
||||
} else if (that.scope_name == 'Tag') {
|
||||
that.add_tag_root_node();
|
||||
// links untagged attributes and object to root nodes
|
||||
if (that.first_draw) {
|
||||
that.link_not_referenced_nodes();
|
||||
that.first_draw = !that.first_draw
|
||||
}
|
||||
} else if (that.scope_name == 'Distribution') {
|
||||
} else if (that.scope_name == 'Correlation') {
|
||||
} else {
|
||||
that.add_keyType_root_node();
|
||||
if (that.first_draw) {
|
||||
that.link_not_referenced_nodes();
|
||||
that.first_draw = !that.first_draw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.network_loading(false, "");
|
||||
eventGraph.canDrawHull = true;
|
||||
that.network_loading(false, "");
|
||||
}
|
||||
|
||||
strip_text_value(text) {
|
||||
|
@ -1030,9 +1102,10 @@ class DataHandler {
|
|||
return label;
|
||||
}
|
||||
|
||||
update_available_object_references(available_object_references) {
|
||||
update_filtering_selectors(available_object_references, available_tags) {
|
||||
eventGraph.menu_display.add_options("select_display_object_field", available_object_references);
|
||||
eventGraph.menu_filter.items["table_attr_presence"].add_options("table_control_select_attr_presence", available_object_references);
|
||||
eventGraph.menu_filter.items["table_tag_presence"].add_options("table_control_select_tag_presence", available_tags);
|
||||
eventGraph.menu_filter.items["table_attr_value"].add_options("table_control_select_attr_value", available_object_references);
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1118,7 @@ class DataHandler {
|
|||
payload.filtering = filtering_rules;
|
||||
payload.keyType = keyType;
|
||||
var extended_text = dataHandler.extended_event ? "extended:1" : "";
|
||||
eventGraph.canDrawHull = false;
|
||||
$.ajax({
|
||||
url: "/events/"+dataHandler.get_scope_url()+"/"+scope_id+"/"+extended_text+"/event.json",
|
||||
dataType: 'json',
|
||||
|
@ -1058,10 +1132,22 @@ class DataHandler {
|
|||
eventGraph.first_draw = true;
|
||||
// update object state
|
||||
var available_object_references = Object.keys(data.existing_object_relation);
|
||||
dataHandler.update_available_object_references(available_object_references);
|
||||
var available_tags = Object.keys(data.existing_tags);
|
||||
var available_tags = $.map(data.existing_tags, function(value, index) { // object to array
|
||||
return [[index, value]];
|
||||
});
|
||||
dataHandler.update_filtering_selectors(available_object_references, available_tags);
|
||||
dataHandler.available_rotation_key = data.available_rotation_key;
|
||||
eventGraph.menu_scope.add_options("input_graph_scope_jsonkey", dataHandler.available_rotation_key);
|
||||
eventGraph.update_graph(data);
|
||||
if (data.items.length < nodes_ask_threshold) {
|
||||
eventGraph.update_graph(data);
|
||||
} else if (data.items.length > nodes_ask_threshold && confirm("The network contains a lot of nodes, displaying it may slow down your browser. Continue?")) {
|
||||
eventGraph.update_graph(data);
|
||||
} else {
|
||||
eventGraph.network_loading(false, "");
|
||||
$("#eventgraph_toggle").click();
|
||||
}
|
||||
|
||||
if ( stabilize === undefined || stabilize) {
|
||||
eventGraph.reset_view_on_stabilized();
|
||||
}
|
||||
|
@ -1139,7 +1225,6 @@ class MispInteraction {
|
|||
|
||||
add_reference(edgeData, callback) {
|
||||
var that = mispInteraction;
|
||||
//var uuid = dataHandler.mapping_attr_id_to_uuid.get(edgeData.to);
|
||||
var uuid = that.nodes.get(edgeData.to).uuid;
|
||||
if (!that.can_create_reference(edgeData.from) || !that.can_be_referenced(edgeData.to)) {
|
||||
return;
|
||||
|
@ -1233,6 +1318,84 @@ class MispInteraction {
|
|||
/*=========
|
||||
* UTILS
|
||||
* ========*/
|
||||
function drawExtendedEventHull(ctx, nodes, color, text) {
|
||||
ctx.fillStyle = color+'88';
|
||||
var hull = getHullFromPoints(nodes);
|
||||
|
||||
var start = hull[0];
|
||||
var end = hull[hull.length-1];
|
||||
var prev = start;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(start.x, start.y);
|
||||
for (var i=1; i<hull.length; i++) {
|
||||
var cur = hull[i];
|
||||
ctx.lineTo(cur.x,cur.y);
|
||||
prev = cur;
|
||||
}
|
||||
ctx.moveTo(end.x, end.y);
|
||||
var centerX = (end.x+start.x)/2;
|
||||
var centerY = (end.y+start.y)/2;
|
||||
ctx.quadraticCurveTo(centerX,centerY,start.x,start.y);
|
||||
ctx.fill();
|
||||
|
||||
var centroid = getCentroid(hull);
|
||||
ctx.beginPath();
|
||||
ctx.font="30px Verdana";
|
||||
ctx.fillStyle = getTextColour(color);
|
||||
ctx.fillText(text, centroid.x, centroid.y);
|
||||
}
|
||||
function orientation(p, q, r) {
|
||||
var val = (q.y - p.y) * (r.x - q.x) -
|
||||
(q.x - p.x) * (r.y - q.y);
|
||||
if (val == 0) {
|
||||
return 0; // collinear
|
||||
}
|
||||
return val > 0 ? 1 : 2; // clock or counterclock wise
|
||||
}
|
||||
// Implementation of Gift wrapping algorithm (jarvis march in 2D)
|
||||
// Inspired from https://www.geeksforgeeks.org/convex-hull-set-1-jarviss-algorithm-or-wrapping/
|
||||
function getHullFromPoints(points) {
|
||||
var n = points.length;
|
||||
var l = 0;
|
||||
var hull = [];
|
||||
// get leftmost point
|
||||
for (var i=0; i<n; i++) {
|
||||
l = points[l].x > points[i].x ? l : i;
|
||||
}
|
||||
|
||||
var p = l;
|
||||
var q;
|
||||
do {
|
||||
hull.push(points[p]);
|
||||
|
||||
q = (p+1) % n;
|
||||
for (var i=0; i<n; i++) {
|
||||
if (orientation(points[p], points[i], points[q]) == 2) {
|
||||
q = i;
|
||||
}
|
||||
}
|
||||
p = q;
|
||||
} while (p != l);
|
||||
return hull;
|
||||
}
|
||||
function getCentroid(coordList) {
|
||||
var cx = 0;
|
||||
var cy = 0;
|
||||
var a = 0;
|
||||
for (var i=0; i<coordList.length; i++) {
|
||||
var ci = coordList[i];
|
||||
var cj = i+1 == coordList.length ? coordList[0] : coordList[i+1]; // j = i+1 AND loop around
|
||||
var mul = (ci.x*cj.y - cj.x*ci.y);
|
||||
cx += (ci.x + cj.x)*mul;
|
||||
cy += (ci.y + cj.y)*mul;
|
||||
a += mul;
|
||||
}
|
||||
a = a / 2;
|
||||
cx = cx / (6*a);
|
||||
cy = cy / (6*a);
|
||||
return {x: cx, y: cy};
|
||||
}
|
||||
|
||||
function getRandomColor() {
|
||||
var letters = '0123456789ABCDEF';
|
||||
var color = '#';
|
||||
|
@ -1300,7 +1463,7 @@ function enable_interactive_graph() {
|
|||
html: true,
|
||||
});
|
||||
generate_background_shortcuts(shortcut_text);
|
||||
$('.fullscreen-btn').click(function() {
|
||||
$('#fullscreen-btn-eventgraph').click(function() {
|
||||
var network_div = $('#eventgraph_div');
|
||||
var fullscreen_enabled = !network_div.data('fullscreen');
|
||||
network_div.data('fullscreen', fullscreen_enabled);
|
||||
|
|
|
@ -1268,7 +1268,7 @@ function simplePopup(url) {
|
|||
error:function() {
|
||||
$(".loading").hide();
|
||||
$("#gray_out").fadeOut();
|
||||
showMessage('fail', 'Could not fetch the given GnuPG key.');
|
||||
showMessage('fail', 'Something went wrong - the queried function returned an exception. Contact your administrator for further details (the exception has been logged).');
|
||||
},
|
||||
url: url,
|
||||
});
|
||||
|
@ -3284,6 +3284,25 @@ function changeObjectReferenceSelectOption() {
|
|||
}
|
||||
}
|
||||
|
||||
function previewEventBasedOnUuids() {
|
||||
var currentValue = $("#EventExtendsUuid").val();
|
||||
if (currentValue == '') {
|
||||
$('#extended_event_preview').hide();
|
||||
} else {
|
||||
$.ajax({
|
||||
url: "/events/getEventInfoById/" + currentValue,
|
||||
type: "get",
|
||||
error: function() {
|
||||
$('#extended_event_preview').hide();
|
||||
},
|
||||
success: function(data) {
|
||||
$('#extended_event_preview').show();
|
||||
$('#extended_event_preview').html(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('.add_object_attribute_row').click(function() {
|
||||
var template_id = $(this).data('template-id');
|
||||
var object_relation = $(this).data('object-relation');
|
||||
|
|
Loading…
Reference in New Issue