From 2b6ddaff95e74f5e8f2d1c0ca93656f2f66f58fd Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 13 Jun 2017 12:00:32 +0200 Subject: [PATCH 01/77] chg: Added new fields to mysql --- INSTALL/MYSQL.sql | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index bacfc2352..da1884cf1 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -20,6 +20,8 @@ CREATE TABLE IF NOT EXISTS `admin_settings` ( CREATE TABLE IF NOT EXISTS `attributes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `event_id` int(11) NOT NULL, + `object_id` int(11) NOT NULL DEFAULT 0, + `object_relation` varchar(255) COLLATE utf8_bin, `category` varchar(255) COLLATE utf8_bin NOT NULL, `type` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, `value1` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, @@ -34,6 +36,8 @@ CREATE TABLE IF NOT EXISTS `attributes` ( `disable_correlation` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), INDEX `event_id` (`event_id`), + INDEX `object_id` (`object_id`), + INDEX `object_relation` (`object_relation`), INDEX `value1` (`value1`(255)), INDEX `value2` (`value2`(255)), INDEX `type` (`type`), @@ -370,6 +374,122 @@ CREATE TABLE IF NOT EXISTS `news` ( -- -------------------------------------------------------- +-- +-- Table structure for table `objects` +-- + +CREATE TABLE IF NOT EXISTS objects ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `version` int(11) NOT NULL, + `event_id` int(11) NOT NULL, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `timestamp` int(11) NOT NULL DEFAULT 0, + `distribution` tinyint(4) NOT NULL DEFAULT 0, + `sharing_group_id` int(11), + `comment` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`(255)), + INDEX `meta-category` (`meta-category`(255)), + INDEX `event_id` (`event_id`), + INDEX `uuid` (`uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `distribution` (`distribution`), + INDEX `sharing_group_id` (`sharing_group_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `object_attribute_references` +-- + +CREATE TABLE IF NOT EXISTS object_attribute_references ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `timestamp` int(11) NOT NULL DEFAULT 0, + `referenced_attribute_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `comment` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`), + INDEX `uuid` (`uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `referenced_attribute_uuid` (`referenced_attribute_uuid`), + INDEX `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `object_object_references` +-- + +CREATE TABLE IF NOT EXISTS object_object_references ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `timestamp` int(11) NOT NULL DEFAULT 0, + `referenced_object_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `comment` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`), + INDEX `uuid` (`uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `referenced_object_uuid` (`referenced_object_uuid`), + INDEX `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `object_templates` +-- + +CREATE TABLE IF NOT EXISTS object_templates ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `org_id` int(11) NOT NULL, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_bin, + `version` int(11) NOT NULL, + `requirements` text COLLATE utf8_bin, + PRIMARY KEY (id), + INDEX `user_id` (`user_id`), + INDEX `org_id` (`org_id`), + INDEX `uuid` (`uuid`), + INDEX `name` (`name`), + INDEX `meta-category` (`meta-category`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `object_template_elements` +-- + +CREATE TABLE IF NOT EXISTS object_template_elements ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `version` int(11) NOT NULL, + `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `frequency` int(11) NOT NULL, + `categories` text COLLATE utf8_bin, + `sane_default` text COLLATE utf8_bin, + `values_list` text COLLATE utf8_bin, + PRIMARY KEY (id), + INDEX `uuid` (`uuid`), + INDEX `in-object-name` (`in-object-name`), + INDEX `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + -- -- Table structure for table `organisations` -- From 9d7ffaea981835966284c727df23c98cc49b5d29 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 13 Jun 2017 12:01:03 +0200 Subject: [PATCH 02/77] chg: Added new tables to appmodel upgrade script --- app/Model/AppModel.php | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 7f93e3382..2e879bb98 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -627,6 +627,86 @@ class AppModel extends Model { $sqlArray[] = "ALTER TABLE `roles` ADD `perm_sighting` tinyint(1) NOT NULL DEFAULT 0;"; $sqlArray[] = 'UPDATE `roles` SET `perm_sighting` = 1 WHERE `perm_add` = 1;'; break; + case '2.4.x': + $sqlArray[] = "CREATE TABLE IF NOT EXISTS objects ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `template_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `template_version` int(11) NOT NULL, + `event_id` int(11) NOT NULL, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `timestamp` int(11) NOT NULL DEFAULT 0, + `distribution` tinyint(4) NOT NULL DEFAULT 0, + `sharing_group_id` int(11), + `comment` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`(255)), + INDEX `template_uuid` (`template_uuid`), + INDEX `template_version` (`template_version`(255)), + INDEX `meta-category` (`meta-category`(255)), + INDEX `event_id` (`event_id`), + INDEX `uuid` (`uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `distribution` (`distribution`), + INDEX `sharing_group_id` (`sharing_group_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_references ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `timestamp` int(11) NOT NULL DEFAULT 0, + `referenced_attribute_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `comment` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`), + INDEX `uuid` (`uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `referenced_attribute_uuid` (`referenced_attribute_uuid`), + INDEX `type` (`type`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_templates ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `org_id` int(11) NOT NULL, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_bin, + `version` int(11) NOT NULL, + `requirements` text COLLATE utf8_bin, + PRIMARY KEY (id), + INDEX `user_id` (`user_id`), + INDEX `org_id` (`org_id`), + INDEX `uuid` (`uuid`), + INDEX `name` (`name`), + INDEX `meta-category` (`meta-category`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_template_elements ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `version` int(11) NOT NULL, + `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `frequency` int(11) NOT NULL, + `categories` text COLLATE utf8_bin, + `sane_default` text COLLATE utf8_bin, + `values_list` text COLLATE utf8_bin, + PRIMARY KEY (id), + INDEX `uuid` (`uuid`), + INDEX `in-object-name` (`in-object-name`), + INDEX `type` (`type`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + $sqlArray[] = 'ALTER TABLE attributes CHANGE object_id object_id int(11) NOT NULL DEFAULT 0;'; + $sqlArray[] = 'ALTER TABLE attributes CHANGE object_relation object_relation varchar(255) COLLATE utf8_bin;'; + $indexArray[] = array('attributes', 'object_id'); + $indexArray[] = array('attributes', 'object_relation'); + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; From ee1c1c5de91aee58bf958e8d100c9e59e1fab117 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 2 Jul 2017 00:05:15 +0200 Subject: [PATCH 03/77] new: Further progress on the objects --- INSTALL/MYSQL.sql | 34 +-- .../ObjectTemplateElementsController.php | 23 ++ app/Controller/ObjectTemplatesController.php | 71 ++++++- app/Controller/ObjectsController.php | 53 +++-- app/Model/AppModel.php | 27 ++- app/Model/Object.php | 32 ++- app/Model/ObjectTemplate.php | 123 ++++++++++- app/Model/ObjectTemplateElement.php | 21 +- app/Model/Role.php | 3 +- .../Model/Behavior/SysLogLogableBehavior.php | 3 + app/View/Elements/global_menu.ctp | 2 + app/View/Elements/side_menu.ctp | 10 + .../ajax/view_elements.ctp | 79 +++++++ .../ObjectTemplates/ajax/object_choice.ctp | 25 +++ .../ObjectTemplates/ajax/selectObject.ctp | 0 app/View/ObjectTemplates/index.ctp | 94 +++++++++ app/View/ObjectTemplates/view.ctp | 52 +++++ app/View/Objects/add.ctp | 198 ++++++++++++++++++ app/webroot/js/misp.js | 21 ++ 19 files changed, 814 insertions(+), 57 deletions(-) create mode 100644 app/Controller/ObjectTemplateElementsController.php create mode 100644 app/View/ObjectTemplateElements/ajax/view_elements.ctp create mode 100644 app/View/ObjectTemplates/ajax/object_choice.ctp create mode 100644 app/View/ObjectTemplates/ajax/selectObject.ctp create mode 100644 app/View/ObjectTemplates/index.ctp create mode 100644 app/View/ObjectTemplates/view.ctp create mode 100644 app/View/Objects/add.ctp diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 71b6ff7c3..1391f2fc8 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -427,7 +427,8 @@ CREATE TABLE IF NOT EXISTS objects ( `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `version` int(11) NOT NULL, + `template_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `template_version` int(11) NOT NULL, `event_id` int(11) NOT NULL, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, @@ -435,8 +436,10 @@ CREATE TABLE IF NOT EXISTS objects ( `sharing_group_id` int(11), `comment` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (id), - INDEX `name` (`name`(255)), - INDEX `meta-category` (`meta-category`(255)), + INDEX `name` (`name`), + INDEX `template_uuid` (`template_uuid`), + INDEX `template_version` (`template_version`), + INDEX `meta-category` (`meta-category`), INDEX `event_id` (`event_id`), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), @@ -629,6 +632,7 @@ CREATE TABLE IF NOT EXISTS `roles` ( `perm_sharing_group` tinyint(1) NOT NULL DEFAULT 0, `perm_tag_editor` tinyint(1) NOT NULL DEFAULT 0, `perm_sighting` tinyint(1) NOT NULL DEFAULT 0, + `perm_object_template` tinyint(1) NOT NULL DEFAULT 0, `default_role` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; @@ -1177,23 +1181,23 @@ INSERT INTO `feeds` (`id`, `provider`, `name`, `url`, `distribution`, `default`, -- 7. Read Only - read -- -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`, `default_role`) -VALUES (1, 'admin', NOW(), NOW(), 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 (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`, `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); +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); -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`, `default_role`) -VALUES (3, 'User', NOW(), NOW(), 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1); +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); -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`, `default_role`) -VALUES (4, 'Publisher', NOW(), NOW(), 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 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 (4, 'Publisher', NOW(), NOW(), 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 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`, `default_role`) -VALUES (5, 'Sync user', NOW(), NOW(), 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 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 (5, 'Sync user', NOW(), NOW(), 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 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`, `default_role`) -VALUES (6, 'Read Only', NOW(), NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 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 (6, 'Read Only', NOW(), NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); -- -------------------------------------------------------- diff --git a/app/Controller/ObjectTemplateElementsController.php b/app/Controller/ObjectTemplateElementsController.php new file mode 100644 index 000000000..62b85685e --- /dev/null +++ b/app/Controller/ObjectTemplateElementsController.php @@ -0,0 +1,23 @@ + 60, + 'order' => array( + 'ObjectTemplateElement.id' => 'desc' + ), + 'recursive' => -1 + ); + + public function viewElements($id, $context = 'all') { + $this->paginate['conditions'] = array('ObjectTemplateElement.object_template_id' => $id); + $elements = $this->paginate(); + $this->set('list', $elements); + $this->layout = 'ajax'; + $this->render('ajax/view_elements'); + } +} diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index 2bc2abbf8..bc5f1a577 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -2,16 +2,21 @@ App::uses('AppController', 'Controller'); -class ObjectsController extends AppController { +class ObjectTemplatesController extends AppController { public $components = array('Security' ,'RequestHandler', 'Session'); public $paginate = array( - 'limit' => 20, + 'limit' => 60, 'order' => array( 'Object.id' => 'desc' ), + 'contain' => array( + 'Organisation' => array('fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid')) + ), + 'recursive' => -1 ); +/* public function add($eventId) { } @@ -23,13 +28,71 @@ class ObjectsController extends AppController { public function delete($id) { } +*/ + + public function objectChoice() { + $templates_raw = $this->ObjectTemplate->find('all', array( + 'recursive' => -1, + 'fields' => array('id', 'meta-category', 'name', 'description', 'org_id'), + 'contain' => array('Organisation.name') + )); + $templates = array(); + foreach ($templates_raw as $k => $template) { + unset($template['ObjectTemplate']['meta-category']); + $template['ObjectTemplate']['org_name'] = $template['Organisation']['name']; + $templates[$templates_raw[$k]['ObjectTemplate']['meta-category']][] = $template['ObjectTemplate']; + } + debug($templates); + $this->set('templates', $templates); + } public function view($id) { - + $params = array( + 'recursive' => -1, + 'contain' => array( + 'Organisation' => array('fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid')) + ), + 'conditions' => array('ObjectTemplate.id' => $id) + ); + if ($this->_isSiteAdmin()) { + $params['contain']['User']= array('fields' => array('User.id', 'User.email')); + } + $objectTemplate = $this->ObjectTemplate->find('first', $params); + if (empty($objectTemplate)) { + throw new NotFoundException('Invalid object template'); + } + if ($this->_isRest()) { + return $this->RestResponse->viewData($objectTemplate, $this->response->type()); + } else { + $this->set('id', $id); + $this->set('template', $objectTemplate); + } } + public function viewElements($id, $context = 'all') { + $elements = $this->ObjectTemplate->ObjectTemplateElement->find('all', array( + 'conditions' => array('ObjectTemplateElement.object_template_id' => $id) + )); + $this->set('list', $elements); + $this->layout = 'ajax'; + $this->render('ajax/view_elements'); + } + + public function index() { + if ($this->_isRest()) { + $rules = $this->paginate; + unset($rules['limit']); + unset($rules['order']); + $objectTemplates = $this->ObjectTemplate->find('all', $rules); + return $this->RestResponse->viewData($objectTemplates, $this->response->type()); + } else { + $objectTemplates = $this->paginate(); + $this->set('list', $objectTemplates); + } + } + public function update() { - $result = $this->ObjectTemplate->update(); + $result = $this->ObjectTemplate->update($this->Auth->user()); $this->Log = ClassRegistry::init('Log'); $fails = 0; $successes = 0; diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 83ed968de..183ccdb65 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -12,28 +12,44 @@ class ObjectsController extends AppController { ), ); + /** + * Create an object using a template + * POSTing will take the input and validate it against the template + * GETing will return the template + */ public function add($eventId, $templateId = false) { - if (!$this->userRole['perm_add']) { + if (!$this->userRole['perm_modify']) { throw new MethodNotAllowedException('You don\'t have permissions to create objects.'); } - if (Validation::uuid($eventId)) { - $lookupField = 'uuid'; - } else if (!is_numeric($eventId)) { - $lookupField = 'id'; - throw new NotFoundException('Invalid event.'); - } - $event = $this->Object->Event->find('first', array( + $eventFindParams = array( 'recursive' => -1, 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'), 'conditions' => array('Event.id' => $eventId) - )); - if (empty($event)) { + ); + + // Find the event that is to be updated + if (Validation::uuid($eventId)) { + $eventFindParams['conditions']['Event.uuid'] = $eventId; + } else if (is_numeric($eventId)) { + $eventFindParams['conditions']['Event.id'] = $eventId; + } else { + throw new NotFoundException('Invalid event.'); + } + $event = $this->Object->Event->find('first', $eventFindParams); + if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) { throw new NotFoundException('Invalid event.'); } $eventId = $event['Event']['id']; - if (!$this->_isSiteAdmin() && ($event['Event']['orgc_id'] != $this->Auth->user('org_id') || !$this->userRole['perm_modify'])) { - throw new UnauthorizedException('You do not have permission to do that.'); - } + $template = $this->Object->ObjectTemplate->find('first', array( + 'conditions' => array('ObjectTemplate.id' => $templateId), + 'recursive' => -1, + 'contain' => array( + 'ObjectTemplateElement' + ) + )); + $eventId = $event['Event']['id']; + + // If we have received a POST request if ($this->request->is('post')) { if (isset($this->request->data['request'])) { $this->request->data = $this->request->data['request']; @@ -46,8 +62,17 @@ class ObjectsController extends AppController { throw new MethodNotAllowedException('Object does not meet the template requirements'); } $this->Object->saveObject($this->request->data, $eventId, $errorBehaviour = 'halt'); - } else { + } + // In the case of a GET request or if the object could not be validated, show the form / the requirement + if ($this->_isRest()) { + return $this->RestResponse->viewData($orgs, $this->response->type()); + } else { + $template = $this->Object->prepareTemplate($template); + //debug($template); + $this->set('event', $event); + $this->set('ajax', false); + $this->set('template', $template); } } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a34a5aa3d..7e9d77850 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -42,7 +42,8 @@ class AppModel extends Model { 51 => false, 52 => false, 55 => true, 56 => true, 57 => true, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false, 63 => false, 64 => false, 65 => false, 66 => false, 67 => true, - 68 => false, 69 => false, 71 => false, 72 => false, 73 => false + 68 => false, 69 => false, 71 => false, 72 => false, 73 => false, + 76 => false ) ) ); @@ -688,7 +689,7 @@ class AppModel extends Model { $sqlArray[] = 'ALTER TABLE `servers` ADD `unpublish_event` tinyint(1) NOT NULL DEFAULT 0;'; $sqlArray[] = 'ALTER TABLE `servers` ADD `publish_without_email` tinyint(1) NOT NULL DEFAULT 0;'; break; - case '2.4.x': + case '2.4.76': $sqlArray[] = "CREATE TABLE IF NOT EXISTS objects ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, @@ -703,10 +704,10 @@ class AppModel extends Model { `sharing_group_id` int(11), `comment` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (id), - INDEX `name` (`name`(255)), + INDEX `name` (`name`), INDEX `template_uuid` (`template_uuid`), - INDEX `template_version` (`template_version`(255)), - INDEX `meta-category` (`meta-category`(255)), + INDEX `template_version` (`template_version`), + INDEX `meta-category` (`meta-category`), INDEX `event_id` (`event_id`), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), @@ -739,6 +740,7 @@ class AppModel extends Model { `description` text COLLATE utf8_bin, `version` int(11) NOT NULL, `requirements` text COLLATE utf8_bin, + `fixed` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `user_id` (`user_id`), INDEX `org_id` (`org_id`), @@ -749,8 +751,7 @@ class AppModel extends Model { $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_template_elements ( `id` int(11) NOT NULL AUTO_INCREMENT, - `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `version` int(11) NOT NULL, + `object_template_id` int(11) NOT NULL, `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `frequency` int(11) NOT NULL, @@ -758,13 +759,19 @@ class AppModel extends Model { `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, PRIMARY KEY (id), - INDEX `uuid` (`uuid`), INDEX `in-object-name` (`in-object-name`), INDEX `type` (`type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; - $sqlArray[] = 'ALTER TABLE attributes CHANGE object_id object_id int(11) NOT NULL DEFAULT 0;'; - $sqlArray[] = 'ALTER TABLE attributes CHANGE object_relation object_relation varchar(255) COLLATE utf8_bin;'; + $sqlArray[] = 'ALTER TABLE `logs` CHANGE `model` `model` VARCHAR(80) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;'; + $sqlArray[] = 'ALTER TABLE `logs` CHANGE `action` `action` VARCHAR(80) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;'; + + $sqlArray[] = 'ALTER TABLE attributes ADD object_id int(11) NOT NULL DEFAULT 0;'; + $sqlArray[] = 'ALTER TABLE attributes ADD object_relation varchar(255) COLLATE utf8_bin;'; + + $sqlArray[] = "ALTER TABLE `roles` ADD `perm_object_template` tinyint(1) NOT NULL DEFAULT 0;"; + $sqlArray[] = 'UPDATE `roles` SET `perm_object_template` = 1 WHERE `perm_site_admin` = 1;'; + $indexArray[] = array('attributes', 'object_id'); $indexArray[] = array('attributes', 'object_relation'); break; diff --git a/app/Model/Object.php b/app/Model/Object.php index 7078cdae5..b756f5de9 100644 --- a/app/Model/Object.php +++ b/app/Model/Object.php @@ -18,8 +18,14 @@ class Object extends AppModel { 'foreignKey' => 'event_id' ), 'SharingGroup' => array( - 'className' => 'SharingGroup', - 'foreignKey' => 'sharing_group_id' + 'className' => 'SharingGroup', + 'foreignKey' => 'sharing_group_id' + ), + 'ObjectTemplate' => array( + 'className' => 'ObjectTemplate', + 'foreignKey' => false, + 'dependent' => false, + 'conditions' => array('Object.template_uuid' => 'ObjectTemplate.uuid') ) ); public $hasMany = array( @@ -54,14 +60,14 @@ class Object extends AppModel { 'AND' => array( 'Event.distribution >' => 0, 'Event.distribution <' => 4, - Configure::read('MISP.unpublishedprivate') ? array('Event.published =' => 1) : array(), + Configure::read('MISP.unpublishedprivate') ? array('Event.published' => 1) : array(), ), ), array( 'AND' => array( 'Event.sharing_group_id' => $sgids, 'Event.distribution' => 4, - Configure::read('MISP.unpublishedprivate') ? array('Event.published =' => 1) : array(), + Configure::read('MISP.unpublishedprivate') ? array('Event.published' => 1) : array(), ) ) ) @@ -205,4 +211,22 @@ class Object extends AppModel { } return $results; } + + public function prepareTemplate($template) { + $temp = array(); + usort($template['ObjectTemplateElement'], function($a, $b) { + return $a['frequency'] < $b['frequency']; + }); + foreach ($template['ObjectTemplateElement'] as $k => $v) { + $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; + $template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids']; + $template['ObjectTemplateElement'][$k]['categories'] = array(); + foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { + if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { + $template['ObjectTemplateElement'][$k]['categories'][$catk] = $catk; + } + } + } + return $template; + } } diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index 0217b834b..f802fd822 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -2,7 +2,7 @@ App::uses('AppModel', 'Model'); -class Object extends AppModel { +class ObjectTemplate extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable @@ -17,8 +17,8 @@ class Object extends AppModel { 'className' => 'User', 'foreignKey' => 'user_id' ), - 'Org' => array( - 'className' => 'Org', + 'Organisation' => array( + 'className' => 'Organisation', 'foreignKey' => 'org_id' ) ); @@ -35,7 +35,21 @@ class Object extends AppModel { public $validate = array( ); - public function update() { + public function afterFind($results, $primary = false) { + foreach ($results as $k => $result) { + if (isset($results[$k]['ObjectTemplate']['requirements'])) { + $results[$k]['ObjectTemplate']['requirements'] = json_decode($results[$k]['ObjectTemplate']['requirements'], true); + } + } + return $results; + } + + public function beforeSave($options = array()) { + $this->data['ObjectTemplate']['requirements'] = empty($this->data['ObjectTemplate']['requirements']) ? '[]' : json_encode($this->data['ObjectTemplate']['requirements']); + return true; + } + + public function update($user) { $objectsDir = APP . 'files/misp-objects/objects'; $directories = glob($objectsDir . '/*', GLOB_ONLYDIR); foreach ($directories as $k => $dir) { @@ -53,12 +67,11 @@ class Object extends AppModel { if (!isset($template['version'])) $template['version'] = 1; $current = $this->find('first', array( 'conditions' => array('uuid' => $template['uuid']), - 'recursive' => -1, - 'fields' => array('version', 'uuid', 'name') + 'recursive' => -1 )); if (empty($current) || $template['version'] > $current['ObjectTemplate']['version']) { - $result = $this->__updateObjectTemplate($template, $current); - if (is_numeric($result)) { + $result = $this->__updateObjectTemplate($template, $current, $user); + if ($result === true) { $updated['success'][$result] = array('name' => $template['name'], 'new' => $template['version']); if (!empty($current)) $updated['success'][$result]['old'] = $current['ObjectTemplate']['version']; } else { @@ -69,4 +82,98 @@ class Object extends AppModel { return $updated; } + private function __updateObjectTemplate($template, $current, $user) { + $success = false; + $template['requirements'] = array(); + $requirementFields = array('required', 'requiredOneOf'); + foreach ($requirementFields as $field) { + if (isset($template[$field])) { + $template['requirements'][$field] = $template[$field]; + } + } + if (empty($current)) { + $template['user_id'] = $user['id']; + $template['org_id'] = $user['org_id']; + $template['fixed'] = 1; + $this->create(); + $result = $this->save($template); + } else { + $fieldsToUpdate = array('version', 'description', 'meta-category', 'name', 'requirements', 'fixed'); + foreach ($fieldsToUpdate as $field) { + if (isset($template[$field]) && $current['ObjectTemplate'][$field] != $template[$field]) { + $current['ObjectTemplate'][$field] = $template[$field]; + } + } + $result = $this->save($current); + } + if (!$result) { + return $this->validationErrors; + } + $id = $this->id; + $existingTemplateElementsTemp = $this->ObjectTemplateElement->find('all', array( + 'recursive' => -1, + 'conditions' => array('object_template_id' => $id) + )); + $existingTemplateElements = array(); + if (!empty($existingTemplateElementsTemp)) { + foreach ($existingTemplateElementsTemp as $k => $v) { + $existingTemplateElements[$v['ObjectTemplateElement']['in-object-name']] = $v['ObjectTemplateElement']; + } + } + unset($existingTemplateElementsTemp); + $fieldsToCompare = array('in-object-name', 'type', 'frequency', 'categories', 'sane_default', 'values_list'); + foreach ($template['attributes'] as $k => $attribute) { + $attribute['in-object-name'] = $k; + $attribute = $this->__convertJSONToElement($attribute); + if (isset($existingTemplateElements[$k])) { + $update_required = false; + foreach ($fieldsToCompare as $field) { + if (isset($attribute[$field])) { + if ($existingTemplateElements[$k][$field] != $attribute[$field]) { + $update_required = true; + } + } + } + if ($update_required) { + $attribute = $existingTemplateElements[$k]; + unset($existingTemplateElements); + $attribute['object_template_id'] = $id; + $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); + } + unset($existingTemplateElements[$k]); + } else { + $this->ObjectTemplateElement->create(); + $attribute['object_template_id'] = $id; + $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); + } + } + if (!empty($existingTemplateElements)) { + foreach ($existingTemplateElements as $k2 => $v2) { + $this->ObjectTemplateElement->delete($v2['id']); + } + } + return true; + } + + private function __convertJSONToElement($attribute) { + $result = array(); + $translation_table = array( + 'misp-usage-frequency' => 'frequency', + 'misp-attribute' => 'type', + 'description' => 'description', + 'frequency' => 'frequency', + 'type' => 'type', + 'disable_correlation' => 'disable_correlation', + 'in-object-name' => 'in-object-name', + 'categories' => 'categories', + 'sane_default' => 'sane_default', + 'values_list' => 'values_list' + ); + foreach ($translation_table as $from => $to) { + if (isset($attribute[$from])) { + $result[$to] = $attribute[$from]; + } + } + return $result; + } } diff --git a/app/Model/ObjectTemplateElement.php b/app/Model/ObjectTemplateElement.php index 71f0b4e49..4c971c12a 100644 --- a/app/Model/ObjectTemplateElement.php +++ b/app/Model/ObjectTemplateElement.php @@ -2,7 +2,7 @@ App::uses('AppModel', 'Model'); -class Object extends AppModel { +class ObjectTemplateElement extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable @@ -18,4 +18,23 @@ class Object extends AppModel { public $validate = array( ); + public function afterFind($results, $primary = false) { + foreach ($results as $k => $result) { + $results[$k]['ObjectTemplateElement']['categories'] = json_decode($results[$k]['ObjectTemplateElement']['categories'], true); + $results[$k]['ObjectTemplateElement']['values_list'] = json_decode($results[$k]['ObjectTemplateElement']['values_list'], true); + $results[$k]['ObjectTemplateElement']['sane_default'] = json_decode($results[$k]['ObjectTemplateElement']['sane_default'], true); + } + return $results; + } + + public function beforeSave($options = array()) { + if (empty($this->data['ObjectTemplateElement']['description'])) { + $this->data['ObjectTemplateElement']['description'] = ''; + } + $json_fields = array('categories', 'values_list', 'sane_default'); + foreach ($json_fields as $field) { + $this->data['ObjectTemplateElement'][$field] = empty($this->data['ObjectTemplateElement'][$field]) ? '[]' : json_encode($this->data['ObjectTemplateElement'][$field]); + } + return true; + } } diff --git a/app/Model/Role.php b/app/Model/Role.php index 6863e4544..70d0a09de 100644 --- a/app/Model/Role.php +++ b/app/Model/Role.php @@ -50,7 +50,8 @@ class Role extends AppModel { 'perm_template' => array('id' => 'RolePermTemplate', 'text' => 'Template Editor', 'readonlyenabled' => false), 'perm_sharing_group' => array('id' => 'RolePermSharingGroup', 'text' => 'Sharing Group Editor', 'readonlyenabled' => false), 'perm_delegate' => array('id' => 'RolePermDelegate', 'text' => 'Delegations Access', 'readonlyenabled' => false), - 'perm_sighting' => array('id' => 'RolePermSighting', 'text' => 'Sighting Creator', 'readonlyenabled' => true) + 'perm_sighting' => array('id' => 'RolePermSighting', 'text' => 'Sighting Creator', 'readonlyenabled' => true), + 'perm_object_template' => array('id' => 'RolePermObjectTemplate', 'text' => 'Object Template Editor', 'readonlyenabled' => false), ); public $premissionLevelName = array('Read Only', 'Manage Own Events', 'Manage Organisation Events', 'Manage and Publish Organisation Events'); diff --git a/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php b/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php index 1264edf7c..1ac306936 100644 --- a/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php +++ b/app/Plugin/SysLogLogable/Model/Behavior/SysLogLogableBehavior.php @@ -65,6 +65,9 @@ class SysLogLogableBehavior extends LogableBehavior { foreach ( $Model->data[$Model->alias] as $key => $value ) { if (isset($Model->data[$Model->alias][$Model->primaryKey]) && !empty($this->old) && isset($this->old[$Model->alias][$key])) { $old = $this->old[$Model->alias][$key]; + if (is_array($old)) { + $old = json_encode($old, true); + } } else { $old = ''; } diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 513088709..9c8600989 100644 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -91,6 +91,8 @@
  • Organisations
  • Role Permissions
  • +
  • List Object Templates
  • +
  • List Sharing Groups
  • Add Sharing Group
  • diff --git a/app/View/Elements/side_menu.ctp b/app/View/Elements/side_menu.ctp index 05d2184f7..d82e7e1f5 100644 --- a/app/View/Elements/side_menu.ctp +++ b/app/View/Elements/side_menu.ctp @@ -357,6 +357,16 @@ +
  • List Object Templates
  • + +
  • View Object Template
  • + diff --git a/app/View/ObjectTemplateElements/ajax/view_elements.ctp b/app/View/ObjectTemplateElements/ajax/view_elements.ctp new file mode 100644 index 000000000..d62212e8e --- /dev/null +++ b/app/View/ObjectTemplateElements/ajax/view_elements.ctp @@ -0,0 +1,79 @@ + + + + + + + + + + + + + $item): +?> + + + + + + + + + + + +
    Paginator->sort('in_object_name');?>Paginator->sort('type');?>Paginator->sort('frequency');?>Paginator->sort('description');?>CategoriesSane defaultsList of valid Values
         + '; + } + } + ?> +
    +

    +Paginator->counter(array('format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}'))); +?> +

    + + + +Js->writeBuffer(); ?> diff --git a/app/View/ObjectTemplates/ajax/object_choice.ctp b/app/View/ObjectTemplates/ajax/object_choice.ctp new file mode 100644 index 000000000..a888d39fb --- /dev/null +++ b/app/View/ObjectTemplates/ajax/object_choice.ctp @@ -0,0 +1,25 @@ +
    + Select Object Category +
    + + + + + + + + + +
    All Objects
    Galaxy:
    +
    +
    Cancel
    +
    + diff --git a/app/View/ObjectTemplates/ajax/selectObject.ctp b/app/View/ObjectTemplates/ajax/selectObject.ctp new file mode 100644 index 000000000..e69de29bb diff --git a/app/View/ObjectTemplates/index.ctp b/app/View/ObjectTemplates/index.ctp new file mode 100644 index 000000000..402673f33 --- /dev/null +++ b/app/View/ObjectTemplates/index.ctp @@ -0,0 +1,94 @@ +
    +

    Object Template index

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Paginator->sort('id');?>Paginator->sort('name');?>Paginator->sort('uuid');?>Paginator->sort('org_id', 'Organisation');?>Paginator->sort('version');?>Paginator->sort('meta-category');?>Paginator->sort('description');?>RequirementsActions
    >> + + + + + >>>>>> + $requirements): + ?> +
    + +   
    + +
    +

    + Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?> +

    + + +
    +element('side_menu', array('menuList' => 'objectTemplates', 'menuItem' => 'index')); diff --git a/app/View/ObjectTemplates/view.ctp b/app/View/ObjectTemplates/view.ctp new file mode 100644 index 000000000..6847710c0 --- /dev/null +++ b/app/View/ObjectTemplates/view.ctp @@ -0,0 +1,52 @@ +element('side_menu', array('menuList' => 'objectTemplates', 'menuItem' => 'view')); +?> +
    +
    +
    +

    Object Template

    +
    +
    Object Template ID
    +
    +
    Name
    +
    +
    Organisation
    +
    +
    Uuid
    +
    +
    Version
    +
    +
    Meta-category
    +
    +
    Description
    +
    +
    Requirements
    +
    + $requirements): + ?> +
    + +   
    + +
    +
    +
    +
    +
    +
    + diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp new file mode 100644 index 000000000..679a8a74b --- /dev/null +++ b/app/View/Objects/add.ctp @@ -0,0 +1,198 @@ +
    +
    + Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'])); + echo $this->Form->input('data', array( + 'style' => 'display:none;', + 'label' => false + )); + echo $this->Form->end(); + ?> +
    +

    +
    +
    +
    Object Template
    +
    +   +
    +
    Description
    +
    +   +
    + +
    Requirements
    +
    + Required: ' . h(implode(', ', $template['ObjectTemplate']['requirements']['required'])) . '
    '; + } + if (!empty($template['ObjectTemplate']['requirements']['requiredOneOf'])) { + echo 'Required one of: ' . h(implode(', ', $template['ObjectTemplate']['requirements']['requiredOneOf'])); + } + ?> +
    + +
    Meta category
    +
    +   +
    +
    + + + + + + + + + + + $element): + ?> + + + + + + + +
    NameTypeCategoryValueDescriptionTo IDSDistribution
    + + + + + + + + + + + + + + +
    +
    + +
    + + + + + + +
    + Submit + + + + Cancel +
    +
    + + + Form->button('Submit', array('class' => 'btn btn-primary')); + endif; + echo $this->Form->end(); + ?> +
    +element('side_menu', array('menuList' => 'event', 'menuItem' => 'addObject', 'event' => $event)); + } +?> + +Js->writeBuffer(); // Write cached scripts diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 85cf7ea7b..f00f65599 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -1933,6 +1933,27 @@ function freetextImportResultsSubmit(id, count) { }); } +function objectTemplateViewContent(context, id) { + var url = "/objectTemplateElements/viewElements/" + id + "/" + context; + $.ajax({ + url: url, + type:'GET', + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + error: function(){ + $('#ajaxContent').html('An error has occured, please reload the page.'); + }, + success: function(response){ + $('#ajaxContent').html(response); + }, + complete: function() { + $(".loading").hide(); + }, + }); + +} + function organisationViewContent(context, id) { organisationViewButtonHighlight(context); var action = "/organisations/landingpage/"; From d2e1a8e25966b8300ab41054d1d8a633a49b4538 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 2 Jul 2017 20:32:30 +0200 Subject: [PATCH 04/77] new: More work on the objects - mostly on adding / validating / saving objects including the UI for it --- app/Controller/ObjectsController.php | 40 +++- app/Model/Attribute.php | 22 ++ app/Model/Object.php | 60 ++++- app/Model/ObjectTemplate.php | 28 +++ app/View/Feeds/index.ctp | 6 +- app/View/Objects/add.ctp | 330 ++++++++++++++++----------- app/webroot/js/misp.js | 38 +++ 7 files changed, 374 insertions(+), 150 deletions(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 183ccdb65..7021f9dba 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -48,28 +48,50 @@ class ObjectsController extends AppController { ) )); $eventId = $event['Event']['id']; - + $error = false; // If we have received a POST request if ($this->request->is('post')) { if (isset($this->request->data['request'])) { $this->request->data = $this->request->data['request']; } - if (!isset($this->request->data['Object'])) { - $this->request->data = array('Object' => $this->request->data); + if (!isset($this->request->data['Attribute'])) { + $this->request->data = array('Attribute' => $this->request->data); } - $templateCheckResult = $this->Object->ObjectTemplate->checkTemplateConformity($templateId, $this->request->data); - if (!$templateCheckResult) { - throw new MethodNotAllowedException('Object does not meet the template requirements'); + $object = $this->Object->attributeCleanup($this->request->data); + // we pre-validate the attributes before we create an object at this point + // This allows us to stop the process and return an error (API) or return + // to the add form + foreach ($object['Attribute'] as $k => $attribute) { + $object['Attribute'][$k]['event_id'] = $eventId; + $this->Object->Event->Attribute->set($attribute); + if (!$this->Object->Event->Attribute->validates()) { + $error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->Object->Event->Attribute->validationErrors); + } + } + if (empty($error)) { + $error = $this->Object->ObjectTemplate->checkTemplateConformity($template, $object); + if ($error === true) { + $this->Object->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + } } - $this->Object->saveObject($this->request->data, $eventId, $errorBehaviour = 'halt'); } // In the case of a GET request or if the object could not be validated, show the form / the requirement if ($this->_isRest()) { - return $this->RestResponse->viewData($orgs, $this->response->type()); + if ($error) { + + } else { + return $this->RestResponse->viewData($orgs, $this->response->type()); + } } else { + if (!empty($error)) { + $this->Session->setFlash($error); + } $template = $this->Object->prepareTemplate($template); - //debug($template); + $enabledRows = array_keys($template['ObjectTemplateElement']); + $this->set('enabledRows', $enabledRows); + $distributionData = $this->Object->Event->Attribute->fetchDistributionData($this->Auth->user()); + $this->set('distributionData', $distributionData); $this->set('event', $event); $this->set('ajax', false); $this->set('template', $template); diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 80f2417ac..7e9c272ab 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -644,6 +644,10 @@ class Attribute extends AppModel { public function valueIsUnique ($fields) { if (isset($this->data['Attribute']['deleted']) && $this->data['Attribute']['deleted']) return true; + // We escape this rule for objects as we can have the same category/type/value combination in different objects + if (!empty($this->data['Attribute']['object_relation'])) { + return true; + } $value = $fields['value']; $eventId = $this->data['Attribute']['event_id']; $type = $this->data['Attribute']['type']; @@ -2538,4 +2542,22 @@ class Attribute extends AppModel { } return $cidrList; } + + public function fetchDistributionData($user) { + $initialDistribution = 5; + if (Configure::read('MISP.default_attribute_distribution') != null) { + if (Configure::read('MISP.default_attribute_distribution') === 'event') { + $initialDistribution = 5; + } else { + $initialDistribution = Configure::read('MISP.default_attribute_distribution'); + } + } + $sgs = $this->SharingGroup->fetchAllAuthorised($user, 'name', 1); + $this->set('sharingGroups', $sgs); + $distributionLevels = $this->distributionLevels; + if (empty($sgs)) { + unset($distributionLevels[4]); + } + return array('sgs' => $sgs, 'levels' => $distributionLevels, 'initial' => $initialDistribution); + } } diff --git a/app/Model/Object.php b/app/Model/Object.php index b756f5de9..c750411cd 100644 --- a/app/Model/Object.php +++ b/app/Model/Object.php @@ -42,11 +42,25 @@ class Object extends AppModel { public $validate = array( ); - public function saveObject($object, $eventId, $errorBehaviour = 'drop') { - $this->Object->create(); - $this->request->data['Object']['event_id'] = $eventId; - $this->Object->save($this->request->data); - return $this->Object->Attribute->saveAttributes($attributes, $eventId, $objectId = 0, $errorBehaviour); + public function saveObject($object, $eventId, $template, $user, $errorBehaviour = 'drop') { + $this->create(); + //id name meta-category description template_uuid template_version event_id uuid timestamp distribution sharing_group_id comment + $templateFields = array( + 'name' => 'name', + 'meta-category' => 'meta-category', + 'description' => 'description', + 'template_version' => 'version', + 'template_uuid' => 'uuid' + ); + foreach ($templateFields as $k => $v) { + $object['Object'][$k] = $template['ObjectTemplate'][$v]; + } + $object['Object']['event_id'] = $eventId; + debug($template); + debug($object); + throw new Exception(); + $this->save($object); + return $this->Attribute->saveAttributes($attributes, $eventId, $objectId = 0, $errorBehaviour); } public function buildEventConditions($user, $sgids = false) { @@ -212,6 +226,9 @@ class Object extends AppModel { return $results; } + /* + * Prepare the template form view's data, setting defaults, sorting elements + */ public function prepareTemplate($template) { $temp = array(); usort($template['ObjectTemplateElement'], function($a, $b) { @@ -220,13 +237,38 @@ class Object extends AppModel { foreach ($template['ObjectTemplateElement'] as $k => $v) { $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; $template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids']; - $template['ObjectTemplateElement'][$k]['categories'] = array(); - foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { - if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { - $template['ObjectTemplateElement'][$k]['categories'][$catk] = $catk; + if (empty($template['ObjectTemplateElement'][$k]['categories'])) { + $template['ObjectTemplateElement'][$k]['categories'] = array(); + foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { + if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { + $template['ObjectTemplateElement'][$k]['categories'][] = $catk; + } } } } return $template; } + + /* + * Clean the attribute list up from artifacts introduced by the object form + */ + public function attributeCleanup($attributes) { + foreach ($attributes['Attribute'] as $k => $attribute) { + if (isset($attribute['save']) && $attribute['save'] == 0) { + unset($attributes['Attribute'][$k]); + continue; + } + if (isset($attribute['value_select'])) { + if ($attribute['value_select'] !== 'Enter value manually') { + $attributes['Attribute'][$k]['value'] = $attribute['value_select']; + } + unset($attribute['value_select']); + } + unset($attribute['save']); + if ($attribute['distribution'] != 4) { + $attribute['sharing_group_id'] = 0; + } + } + return $attributes; + } } diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index f802fd822..c5d194335 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -176,4 +176,32 @@ class ObjectTemplate extends AppModel { } return $result; } + + public function checkTemplateConformity($template, $attributes) { + if (!empty($template['ObjectTemplate']['requirements'])) { + if (!empty($template['ObjectTemplate']['requirements']['required'])) { + foreach ($template['ObjectTemplate']['requirements']['required'] as $requiredField) { + $found = false; + foreach ($attributes['Attribute'] as $attribute) { + if ($attribute['object_relation'] == $requiredField) { + $found = true; + } + } + if (!$found) return 'Could not save the object as a required attribute is not set (' . $requiredField . ')'; + } + } + if (!empty($template['ObjectTemplate']['requirements']['requiredOneOf'])) { + $found = false; + foreach ($template['ObjectTemplate']['requirements']['requiredOneOf'] as $requiredField) { + foreach ($attributes['Attribute'] as $attribute) { + if ($attribute['object_relation'] == $requiredField) { + $found = true; + } + } + } + if (!$found) return 'Could not save the object as it requires at least one of the following attributes to be set: ' . implode(', ', $template['ObjectTemplate']['requirements']['requiredOneOf']); + } + } + return true; + } } diff --git a/app/View/Feeds/index.ctp b/app/View/Feeds/index.ctp index ac22ced6a..0b7b3ecdb 100644 --- a/app/View/Feeds/index.ctp +++ b/app/View/Feeds/index.ctp @@ -75,10 +75,10 @@ foreach ($feeds as $item): + ?> -   diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 679a8a74b..de0266b6a 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -1,20 +1,16 @@
    -
    - Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'])); - echo $this->Form->input('data', array( - 'style' => 'display:none;', - 'label' => false - )); - echo $this->Form->end(); - ?> -
    +Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'], 'enctype' => 'multipart/form-data')); +?>

    -
    +
    Object Template
    -   + +  
    Description
    @@ -41,67 +37,188 @@
     
    +
    Distribution
    +
    + Form->input('Object.distribution', array( + 'class' => 'Object_distribution_select', + 'options' => $distributionData['levels'], + 'default' => $distributionData['initial'], + 'label' => false, + 'style' => 'margin-bottom:0px;', + 'div' => false + )); + echo $this->Form->input('Object.sharing_group_id', array( + 'class' => 'Object_sharing_group_id_select', + 'options' => $distributionData['sgs'], + 'label' => false, + 'div' => false, + 'style' => 'display:none;margin-bottom:0px;' + )); + ?> +
    - - - - - - - - - - - $element): - ?> - - - - - - - -
    NameTypeCategoryValueDescriptionTo IDSDistribution
    - - - - - - - - - - - - - - -
    + + + + + + + + + + + $element): + $row_list[] = $k; +?> + + + + + + + + + + +
    SaveName :: typeCategoryValueIDSDistributionComment
    + Form->input('Attribute.' . $k . '.save', array( + 'type' => 'checkbox', + 'checked' => in_array($k, $enabledRows), + 'label' => false, + 'div' => false + )); + ?> + + Form->input('Attribute.' . $k . '.object_relation', array( + 'type' => 'hidden', + 'value' => $element['in-object-name'], + 'label' => false, + 'div' => false + )); + echo $this->Form->input('Attribute.' . $k . '.type', array( + 'type' => 'hidden', + 'value' => $element['type'], + 'label' => false, + 'div' => false + )); + echo '' . Inflector::humanize(h($element['in-object-name'])) . ''; + if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['in-object-name'], $template['ObjectTemplate']['requirements']['required'])) { + echo '' . '(*)' . ''; + } + echo ' :: ' . h($element['type']) . ''; + ?> + + Form->input('Attribute.' . $k . '.category', array( + 'options' => array_combine($element['categories'], $element['categories']), + 'default' => $element['default_category'], + 'style' => 'margin-bottom:0px;', + 'label' => false, + 'div' => false + )); + ?> + + Form->file('Attribute.' . $k . '.Attachment', array( + 'class' => 'Attribute_attachment' + )); + else: + if (empty($element['values_list']) && empty($element['sane_default'])): + echo $this->Form->input('Attribute.' . $k . '.value', array( + 'type' => 'textarea', + 'required' => false, + 'allowEmpty' => true, + 'style' => 'height:20px;width:400px;', + 'label' => false, + 'div' => false + )); + else: + if (empty($element['values_list'])) { + $list = $element['sane_default']; + $list[] = 'Enter value manually'; + } else { + $list = $element['values_list']; + } + $list = array_combine($list, $list); + ?> +
    + Form->input('Attribute.' . $k . '.value_select', array( + 'class' => 'Attribute_value_select', + 'style' => 'width:414px;margin-bottom:0px;', + 'options' => array_combine($list, $list), + 'label' => false, + 'div' => false + )); + ?> +
    + Form->input('Attribute.' . $k . '.value', array( + 'class' => 'Attribute_value', + 'type' => 'textarea', + 'required' => false, + 'allowEmpty' => true, + 'style' => 'height:20px;width:400px;display:none;', + 'label' => false, + 'div' => false + )); + ?> +
    + +
    + Form->input('Attribute.' . $k . '.to_ids', array( + 'type' => 'checkbox', + 'checked' => $element['to_ids'], + 'label' => false, + 'div' => false + )); + ?> + + Form->input('Attribute.' . $k . '.distribution', array( + 'class' => 'Attribute_distribution_select', + 'options' => $distributionData['levels'], + 'default' => $distributionData['initial'], + 'style' => 'margin-bottom:0px;', + 'label' => false, + 'div' => false + )); + ?> +
    + Form->input('Attribute.' . $k . '.sharing_group_id', array( + 'class' => 'Attribute_sharing_group_id_select', + 'options' => $distributionData['sgs'], + 'label' => false, + 'div' => false, + 'style' => 'display:none;margin-bottom:0px;', + )); + ?> +
    + Form->input('Attribute.' . $k . '.comment', array( + 'type' => 'textarea', + 'style' => 'height:20px;width:400px;', + 'required' => false, + 'allowEmpty' => true, + 'label' => false, + 'div' => false + )); + ?> +
    @@ -134,65 +251,20 @@ } ?> -Js->writeBuffer(); // Write cached scripts diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index a08ccc8bf..d047722ce 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -3069,6 +3069,44 @@ $(".cortex-json").click(function() { $("#gray_out").fadeIn(); }); +// Show $(id) if the enable parameter evaluates to true. Hide it otherwise +function checkAndEnable(id, enable) { + if (enable) { + $(id).show(); + } else { + $(id).hide(); + } +} + +// Show and enable checkbox $(id) if the enable parameter evaluates to true. Hide and disable it otherwise. +function checkAndEnableCheckbox(id, enable) { + if (enable) { + $(id).removeAttr("disabled"); + $(id).prop('checked', true); + } else { + $(id).prop('checked', false); + $(id).attr("disabled", true); + } +} + + function enableDisableObjectRows(rows) { + rows.forEach(function(i) { + if ($("#Attribute" + i + "ValueSelect").length != 0) { + checkAndEnableCheckbox("#Attribute" + i + "Save", true); + } else if ($("#Attribute" + i + "Attachment").length != 0) { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); + } else { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Value").val() != ""); + } + $("#Attribute" + i + "Value").bind('input propertychange', function() { + checkAndEnableCheckbox("#Attribute" + i + "Save", $(this).val() != ""); + }); + $("#Attribute" + i + "Attachment").on('change', function() { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); + }); + }); + } + (function(){ "use strict"; $(".datepicker").datepicker({ From df5daae664274c6bb973c207007dd30396a2ada9 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 2 Jul 2017 22:42:44 +0200 Subject: [PATCH 05/77] chg: Further work on the objects - view events with objects via the API - Further improvements to adding objects --- app/Controller/ObjectsController.php | 14 +++++- app/Lib/Tools/JSONConverterTool.php | 67 ++++++++++++++++++---------- app/Model/Attribute.php | 14 ++++++ app/Model/Event.php | 67 ++++++++++++++++++++++++++-- app/Model/Object.php | 64 ++++++++++++++++++++++---- 5 files changed, 190 insertions(+), 36 deletions(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 7021f9dba..9eca55a97 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -71,7 +71,19 @@ class ObjectsController extends AppController { if (empty($error)) { $error = $this->Object->ObjectTemplate->checkTemplateConformity($template, $object); if ($error === true) { - $this->Object->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + $result = $this->Object->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + } + if ($this->_isRest()) { + if (is_numeric($result)) { + $object = $this->Object->find('first', array( + 'recursive' => -1, + 'conditions' => array('Object.id' => $result), + 'contain' => array('Attribute') + )); + return $this->RestResponse->viewData($object, $this->response->type()); + } else { + return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); + } } } } diff --git a/app/Lib/Tools/JSONConverterTool.php b/app/Lib/Tools/JSONConverterTool.php index 83d0f36f2..582bb6c1f 100644 --- a/app/Lib/Tools/JSONConverterTool.php +++ b/app/Lib/Tools/JSONConverterTool.php @@ -10,7 +10,7 @@ class JSONConverterTool { } public function convert($event, $isSiteAdmin=false) { - $toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy'); + $toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object'); foreach ($toRearrange as $object) { if (isset($event[$object])) { $event['Event'][$object] = $event[$object]; @@ -49,29 +49,12 @@ class JSONConverterTool { } if (isset($event['Event']['Attribute'])) { - // remove value1 and value2 from the output and remove invalid utf8 characters for the xml parser - foreach ($event['Event']['Attribute'] as $key => $value) { - if (isset($value['SharingGroup']) && empty($value['SharingGroup'])) { - unset($event['Event']['Attribute'][$key]['SharingGroup']); - } - unset($event['Event']['Attribute'][$key]['value1']); - unset($event['Event']['Attribute'][$key]['value2']); - unset($event['Event']['Attribute'][$key]['category_order']); - if (isset($event['RelatedAttribute'][$value['id']])) { - $event['Event']['Attribute'][$key]['RelatedAttribute'] = $event['Event']['RelatedAttribute'][$value['id']]; - foreach ($event['Event']['Attribute'][$key]['RelatedAttribute'] as &$ra) { - $ra = array('Attribute' => $ra); - } - } - if (isset($event['Event']['Attribute'][$key]['AttributeTag'])) { - foreach ($event['Event']['Attribute'][$key]['AttributeTag'] as $atk => $tag) { - unset($tag['Tag']['org_id']); - $event['Event']['Attribute'][$key]['Tag'][$atk] = $tag['Tag']; - } - unset($event['Event']['Attribute'][$key]['AttributeTag']); - } - } + $event['Event']['Attribute'] = $this->__cleanAttributes($event['Event']['Attribute']); } + if (isset($event['Event']['Object'])) { + $event['Event']['Object'] = $this->__cleanObjects($event['Event']['Object']); + } + unset($event['Event']['RelatedAttribute']); if (isset($event['Event']['RelatedEvent'])) { foreach ($event['Event']['RelatedEvent'] as $key => $value) { @@ -87,6 +70,44 @@ class JSONConverterTool { return json_encode($result, JSON_PRETTY_PRINT); } + private function __cleanAttributes($attributes) { + // remove value1 and value2 from the output and remove invalid utf8 characters for the xml parser + foreach ($attributes as $key => $value) { + if (isset($value['SharingGroup']) && empty($value['SharingGroup'])) { + unset($attributes[$key]['SharingGroup']); + } + unset($attributes[$key]['value1']); + unset($attributes[$key]['value2']); + unset($attributes[$key]['category_order']); + if (isset($event['RelatedAttribute'][$value['id']])) { + $attributes[$key]['RelatedAttribute'] = $event['Event']['RelatedAttribute'][$value['id']]; + foreach ($attributes[$key]['RelatedAttribute'] as &$ra) { + $ra = array('Attribute' => $ra); + } + } + if (isset($attributes[$key]['AttributeTag'])) { + foreach ($attributes[$key]['AttributeTag'] as $atk => $tag) { + unset($tag['Tag']['org_id']); + $attributes[$key]['Tag'][$atk] = $tag['Tag']; + } + unset($attributes[$key]['AttributeTag']); + } + } + return $attributes; + } + + private function __cleanObjects($objects) { + foreach ($objects as $k => $object) { + if (!empty($object['Attribute'])) { + $objects[$k]['Attribute'] = $this->__cleanAttributes($object['Attribute']); + } else { + unset($objects[$k]); + } + } + $objects = array_values($objects); + return $objects; + } + public function arrayPrinter($array, $root = true) { if (is_array($array)) { $resultArray = array(); diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 7e9c272ab..22b995152 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2364,6 +2364,20 @@ class Attribute extends AppModel { else return 'Could not save changes.'; } + public function saveAttributes($attributes) { + foreach ($attributes as $k => $attribute) { + if (!empty($attribute['encrypt']) && $attribute['encrypt']) { + $result = $this->handleMaliciousBase64($attribute['event_id'], $attribute['value'], $attribute['data'], array('md5')); + $attribute['data'] = $result['data']; + $attribute['value'] = $attribute['value'] . '|' . $result['md5']; + } + unset($attribute['Attachment']); + $this->create(); + $this->save($attribute); + } + return true; + } + public function saveAndEncryptAttribute($attribute, $user) { $hashes = array('md5' => 'malware-sample', 'sha1' => 'filename|sha1', 'sha256' => 'filename|sha256'); if ($attribute['encrypt']) { diff --git a/app/Model/Event.php b/app/Model/Event.php index eda4c8ae3..3c216d9ee 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -311,6 +311,19 @@ class Event extends AppModel { 'finderQuery' => '', 'counterQuery' => '' ), + 'Object' => array( + 'className' => 'Object', + 'foreignKey' => 'event_id', + 'dependent' => true, + 'conditions' => '', + 'fields' => '', + 'order' => false, + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ), 'EventTag' => array( 'className' => 'EventTag', 'dependent' => true, @@ -1247,6 +1260,13 @@ class Event extends AppModel { $isSiteAdmin = $user['Role']['perm_site_admin']; if (isset($options['disableSiteAdmin']) && $options['disableSiteAdmin']) $isSiteAdmin = false; $conditionsAttributes = array(); + $conditionsObjects = array(); + + if (isset($options['flattenObjects']) && $options['flattenObjects']) { + $flattenObjects = true; + } else { + $flattenObjects = false; + } // restricting to non-private or same org if the user is not a site-admin. if (!$isSiteAdmin) { @@ -1290,14 +1310,28 @@ class Event extends AppModel { )), '(SELECT events.org_id FROM events WHERE events.id = Attribute.event_id)' => $user['org_id'] ); + + $conditionsObjects['AND'][0]['OR'] = array( + array('AND' => array( + 'Object.distribution >' => 0, + 'Object.distribution !=' => 4, + )), + array('AND' => array( + 'Object.distribution' => 4, + 'Object.sharing_group_id' => $sgids, + )), + '(SELECT events.org_id FROM events WHERE events.id = Object.event_id)' => $user['org_id'] + ); } if ($options['distribution']) { $conditions['AND'][] = array('Event.distribution' => $options['distribution']); $conditionsAttributes['AND'][] = array('Attribute.distribution' => $options['distribution']); + $conditionsObjects['AND'][] = array('Object.distribution' => $options['distribution']); } if ($options['sharing_group_id']) { $conditions['AND'][] = array('Event.sharing_group_id' => $options['sharing_group_id']); $conditionsAttributes['AND'][] = array('Attribute.sharing_group_id' => $options['sharing_group_id']); + $conditionsObject['AND'][] = array('Object.sharing_group_id' => $options['sharing_group_id']); } if ($options['from']) $conditions['AND'][] = array('Event.date >=' => $options['from']); if ($options['to']) $conditions['AND'][] = array('Event.date <=' => $options['to']); @@ -1311,8 +1345,17 @@ class Event extends AppModel { 'Attribute.deleted' => 0 ) ); + $conditionsObjects['AND'][] = array( + 'OR' => array( + '(SELECT events.org_id FROM events WHERE events.id = Object.event_id)' => $user['org_id'], + 'Object.deleted' => 0 + ) + ); } - } else $conditionsAttributes['AND']['Attribute.deleted'] = 0; + } else { + $conditionsAttributes['AND']['Attribute.deleted'] = 0; + $conditionsObject['AND']['Object.deleted'] = 0; + } if ($options['idList'] && !$options['tags']) { $conditions['AND'][] = array('Event.id' => $options['idList']); @@ -1346,7 +1389,8 @@ class Event extends AppModel { // do not expose all the data ... $fields = array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.date', 'Event.threat_level_id', 'Event.info', 'Event.published', 'Event.uuid', 'Event.attribute_count', 'Event.analysis', 'Event.timestamp', 'Event.distribution', 'Event.proposal_email_lock', 'Event.user_id', 'Event.locked', 'Event.publish_timestamp', 'Event.sharing_group_id', 'Event.disable_correlation'); - $fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment', 'Attribute.sharing_group_id', 'Attribute.deleted', 'Attribute.disable_correlation'); + $fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment', 'Attribute.sharing_group_id', 'Attribute.deleted', 'Attribute.disable_correlation', 'Attribute.object_id', 'Attribute.object_relation'); + $fieldsObj = array('*'); $fieldsShadowAtt = array('ShadowAttribute.id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.value', 'ShadowAttribute.to_ids', 'ShadowAttribute.uuid', 'ShadowAttribute.event_uuid', 'ShadowAttribute.event_id', 'ShadowAttribute.old_id', 'ShadowAttribute.comment', 'ShadowAttribute.org_id', 'ShadowAttribute.proposal_to_delete', 'ShadowAttribute.timestamp'); $fieldsOrg = array('id', 'name', 'uuid'); $fieldsServer = array('id', 'url', 'name'); @@ -1373,7 +1417,6 @@ class Event extends AppModel { foreach ($sharingGroupDataTemp as $k => $v) { $sharingGroupData[$v['SharingGroup']['id']] = $v; } - $params = array( 'conditions' => $conditions, 'recursive' => 0, @@ -1393,6 +1436,11 @@ class Event extends AppModel { 'order' => false ), ), + 'Object' => array( + 'fields' => $fieldsObj, + 'conditions' => $conditionsObjects, + 'order' => false + ), 'ShadowAttribute' => array( 'fields' => $fieldsShadowAtt, 'conditions' => array('deleted' => 0), @@ -1405,6 +1453,9 @@ class Event extends AppModel { ) ) ); + if ($flattenObjects) { + unset($params['contain']['Object']); + } if ($options['metadata']) { unset($params['contain']['Attribute']); unset($params['contain']['ShadowAttribute']); @@ -1547,6 +1598,16 @@ class Event extends AppModel { } } + if ($event['Attribute'][$key]['object_id'] != 0) { + if (!empty($event['Object'])) { + foreach ($event['Object'] as $objectKey => $objectValue) { + if ($event['Attribute'][$key]['object_id'] == $objectValue['id']) { + $event['Object'][$objectKey]['Attribute'][] = $event['Attribute'][$key]; + } + } + } + unset($event['Attribute'][$key]); + } } $event['Attribute'] = array_values($event['Attribute']); } diff --git a/app/Model/Object.php b/app/Model/Object.php index c750411cd..190102fa4 100644 --- a/app/Model/Object.php +++ b/app/Model/Object.php @@ -28,6 +28,7 @@ class Object extends AppModel { 'conditions' => array('Object.template_uuid' => 'ObjectTemplate.uuid') ) ); + public $hasMany = array( 'Attribute' => array( 'className' => 'Attribute', @@ -42,9 +43,28 @@ class Object extends AppModel { public $validate = array( ); + public function beforeValidate($options = array()) { + parent::beforeValidate(); + + if (empty($this->data['Object']['comment'])) { + $this->data['Object']['comment'] = ""; + } + // generate UUID if it doesn't exist + if (empty($this->data['Object']['uuid'])) { + $this->data['Object']['uuid'] = CakeText::uuid(); + } + // generate timestamp if it doesn't exist + if (empty($this->data['Object']['timestamp'])) { + $date = new DateTime(); + $this->data['Object']['timestamp'] = $date->getTimestamp(); + } + if (!isset($this->data['Object']['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; + if (!isset($this->data['Object']['distribution'])) $this->data['Object']['distribution'] = 5; + return true; + } + public function saveObject($object, $eventId, $template, $user, $errorBehaviour = 'drop') { $this->create(); - //id name meta-category description template_uuid template_version event_id uuid timestamp distribution sharing_group_id comment $templateFields = array( 'name' => 'name', 'meta-category' => 'meta-category', @@ -56,11 +76,17 @@ class Object extends AppModel { $object['Object'][$k] = $template['ObjectTemplate'][$v]; } $object['Object']['event_id'] = $eventId; - debug($template); - debug($object); - throw new Exception(); - $this->save($object); - return $this->Attribute->saveAttributes($attributes, $eventId, $objectId = 0, $errorBehaviour); + $result = false; + if ($this->save($object)) { + $id = $this->id; + foreach ($object['Attribute'] as $k => $attribute) { + $object['Attribute'][$k]['object_id'] = $id; + } + $result = $this->Attribute->saveAttributes($object['Attribute']); + } else { + $result = $this->validationErrors; + } + return $id; } public function buildEventConditions($user, $sgids = false) { @@ -262,11 +288,31 @@ class Object extends AppModel { if ($attribute['value_select'] !== 'Enter value manually') { $attributes['Attribute'][$k]['value'] = $attribute['value_select']; } - unset($attribute['value_select']); + unset($attributes['Attribute'][$k]['value_select']); } - unset($attribute['save']); + if (isset($attribute['Attachment'])) { + // Check if there were problems with the file upload + // only keep the last part of the filename, this should prevent directory attacks + $filename = basename($attribute['Attachment']['name']); + $tmpfile = new File($attribute['Attachment']['tmp_name']); + if ((isset($attribute['Attachment']['error']) && $attribute['Attachment']['error'] == 0) || + (!empty($attribute['Attachment']['tmp_name']) && $attribute['Attachment']['tmp_name'] != 'none') + ) { + if (!is_uploaded_file($tmpfile->path)) + throw new InternalErrorException('PHP says file was not uploaded. Are you attacking me?'); + } else { + return 'Issues with the file attachment for the ' . $attribute['object_relation'] . ' attribute. The error code returned is ' . $attribute['Attachment']['error']; + } + $attributes['Attribute'][$k]['value'] = $attribute['Attachment']['name']; + unset($attributes['Attribute'][$k]['Attachment']); + $attributes['Attribute'][$k]['encrypt'] = $attribute['type'] == 'malware-sample' ? 1 : 0; + $attributes['Attribute'][$k]['data'] = base64_encode($tmpfile->read()); + $tmpfile->delete(); + $tmpfile->close(); + } + unset($attributes['Attribute'][$k]['save']); if ($attribute['distribution'] != 4) { - $attribute['sharing_group_id'] = 0; + $attributes['Attribute'][$k]['sharing_group_id'] = 0; } } return $attributes; From be740e608c1356126cf87d020ab1c7324c3e9a62 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 3 Jul 2017 00:29:03 +0200 Subject: [PATCH 06/77] new: Further progress with the objects - added option to populate event with an object to the side menu - multiselect popup for objects added - redirect after adding object fixed --- app/Controller/ObjectTemplatesController.php | 11 +++-- app/Controller/ObjectsController.php | 3 ++ app/View/Elements/side_menu.ctp | 5 +- .../ObjectTemplates/ajax/object_choice.ctp | 46 ++++++++++++++++--- app/View/Objects/add.ctp | 17 ++++++- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index bc5f1a577..f8bc8f131 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -30,20 +30,25 @@ class ObjectTemplatesController extends AppController { } */ - public function objectChoice() { + public function objectChoice($event_id) { $templates_raw = $this->ObjectTemplate->find('all', array( 'recursive' => -1, 'fields' => array('id', 'meta-category', 'name', 'description', 'org_id'), 'contain' => array('Organisation.name') )); - $templates = array(); + $templates = array('all' => array()); foreach ($templates_raw as $k => $template) { unset($template['ObjectTemplate']['meta-category']); $template['ObjectTemplate']['org_name'] = $template['Organisation']['name']; $templates[$templates_raw[$k]['ObjectTemplate']['meta-category']][] = $template['ObjectTemplate']; + $templates['all'][] = $template['ObjectTemplate']; } - debug($templates); + $template_categories = array_keys($templates); + $this->layout = false; + $this->set('template_categories', $template_categories); + $this->set('eventId', $event_id); $this->set('templates', $templates); + $this->render('ajax/object_choice'); } public function view($id) { diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 9eca55a97..cf3d60ffa 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -84,6 +84,9 @@ class ObjectsController extends AppController { } else { return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); } + } else { + $this->Session->setFlash('Object saved.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $eventId)); } } } diff --git a/app/View/Elements/side_menu.ctp b/app/View/Elements/side_menu.ctp index d82e7e1f5..b87bd1fcd 100644 --- a/app/View/Elements/side_menu.ctp +++ b/app/View/Elements/side_menu.ctp @@ -6,7 +6,9 @@ ?> Edit Event
  • Form->postLink('Delete Event', array('action' => 'delete', h($event['Event']['id'])), null, __('Are you sure you want to delete # %s?', h($event['Event']['id']))); ?>
  • Add Attribute
  • +
  • Add Object
  • Add Attachment
  • Populate from...
  • diff --git a/app/View/ObjectTemplates/ajax/object_choice.ctp b/app/View/ObjectTemplates/ajax/object_choice.ctp index a888d39fb..6b44dc68b 100644 --- a/app/View/ObjectTemplates/ajax/object_choice.ctp +++ b/app/View/ObjectTemplates/ajax/object_choice.ctp @@ -1,25 +1,57 @@
    Select Object Category
    -
    +
    - + - - - - -
    All ObjectsAll Objects
    Galaxy:
    Cancel
    diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index de0266b6a..1541a0d0b 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -45,7 +45,7 @@ 'options' => $distributionData['levels'], 'default' => $distributionData['initial'], 'label' => false, - 'style' => 'margin-bottom:0px;', + 'style' => 'margin-bottom:5px;', 'div' => false )); echo $this->Form->input('Object.sharing_group_id', array( @@ -53,7 +53,20 @@ 'options' => $distributionData['sgs'], 'label' => false, 'div' => false, - 'style' => 'display:none;margin-bottom:0px;' + 'style' => 'display:none;margin-bottom:5px;' + )); + ?> + +
    Comment
    +
    + Form->input('Object.comment', array( + 'type' => 'textarea', + 'style' => 'height:20px;width:400px;', + 'required' => false, + 'allowEmpty' => true, + 'label' => false, + 'div' => false )); ?>
    From 092b2247da1a2a6e6574b20f7804e7c6e28eacd6 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 08:43:17 +0200 Subject: [PATCH 07/77] fix: Add object functions to ACL --- app/Controller/Component/ACLComponent.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 768d504e8..aa74bb81d 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -105,9 +105,11 @@ class ACLComponent extends Component { 'freeTextImport' => array('perm_add'), 'hids' => array('*'), 'index' => array('*'), + 'massDelete' => array('perm_site_admin'), 'nids' => array('*'), 'proposalEventIndex' => array('*'), 'publish' => array('perm_publish'), + 'pushEventToZMQ' => array('perm_site_admin'), 'pushProposals' => array('perm_sync'), 'queryEnrichment' => array('perm_add'), 'removePivot' => array('*'), @@ -189,6 +191,25 @@ class ACLComponent extends Component { 'delete' => array(), 'index' => array('*'), ), + 'objects' => array( + 'add' => array('perm_add'), + 'delete' => array('perm_add'), + 'edit' => array('perm_add'), + 'view' => array('*'), + ), + 'objectTemplates' => array( + 'add' => array('perm_object_template'), + 'edit' => array('perm_object_template'), + 'delete' => array('perm_object_template'), + 'objectChoice' => array('*'), + 'view' => array('*'), + 'viewElements' => array('*'), + 'index' => array('*'), + 'update' => array('perm_site_admin') + ), + 'objectTemplateElements' => array( + 'viewElements' => array('*') + ), 'orgBlacklists' => array( 'add' => array(), 'delete' => array(), @@ -215,6 +236,7 @@ class ACLComponent extends Component { 'add' => array('*'), 'delete' => array('*'), 'edit' => array('*'), + 'pushMessageToZMQ' => array('perm_site_admin') ), 'regexp' => array( 'admin_add' => array('perm_regexp_access'), @@ -243,6 +265,7 @@ class ACLComponent extends Component { 'fetchServersForSG' => array('*'), 'filterEventIndex' => array(), 'getGit' => array(), + 'getInstanceUUID' => array('perm_sync'), 'getPyMISPVersion' => array('*'), 'getVersion' => array('*'), 'index' => array('OR' => array('perm_sync', 'perm_admin')), From 4b825ed51e5cf084b10df5e91ca82aac27c01bb4 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 09:02:16 +0200 Subject: [PATCH 08/77] chg: changed frequency to ui-priority --- app/Model/AppModel.php | 2 +- app/Model/Object.php | 4 ++-- app/Model/ObjectTemplate.php | 4 ++-- app/View/ObjectTemplateElements/ajax/view_elements.ctp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 93aae2d6c..4f6a493d4 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -764,7 +764,7 @@ class AppModel extends Model { `object_template_id` int(11) NOT NULL, `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `frequency` int(11) NOT NULL, + `ui-priority` int(11) NOT NULL, `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, diff --git a/app/Model/Object.php b/app/Model/Object.php index 190102fa4..7525285a0 100644 --- a/app/Model/Object.php +++ b/app/Model/Object.php @@ -28,7 +28,7 @@ class Object extends AppModel { 'conditions' => array('Object.template_uuid' => 'ObjectTemplate.uuid') ) ); - + public $hasMany = array( 'Attribute' => array( 'className' => 'Attribute', @@ -258,7 +258,7 @@ class Object extends AppModel { public function prepareTemplate($template) { $temp = array(); usort($template['ObjectTemplateElement'], function($a, $b) { - return $a['frequency'] < $b['frequency']; + return $a['ui-priority'] < $b['ui-priority']; }); foreach ($template['ObjectTemplateElement'] as $k => $v) { $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index c5d194335..50aaa6ee5 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -121,7 +121,7 @@ class ObjectTemplate extends AppModel { } } unset($existingTemplateElementsTemp); - $fieldsToCompare = array('in-object-name', 'type', 'frequency', 'categories', 'sane_default', 'values_list'); + $fieldsToCompare = array('in-object-name', 'type', 'ui-priority', 'categories', 'sane_default', 'values_list'); foreach ($template['attributes'] as $k => $attribute) { $attribute['in-object-name'] = $k; $attribute = $this->__convertJSONToElement($attribute); @@ -161,7 +161,7 @@ class ObjectTemplate extends AppModel { 'misp-usage-frequency' => 'frequency', 'misp-attribute' => 'type', 'description' => 'description', - 'frequency' => 'frequency', + 'ui-priority' => 'ui-priority', 'type' => 'type', 'disable_correlation' => 'disable_correlation', 'in-object-name' => 'in-object-name', diff --git a/app/View/ObjectTemplateElements/ajax/view_elements.ctp b/app/View/ObjectTemplateElements/ajax/view_elements.ctp index d62212e8e..0a09aa67b 100644 --- a/app/View/ObjectTemplateElements/ajax/view_elements.ctp +++ b/app/View/ObjectTemplateElements/ajax/view_elements.ctp @@ -18,7 +18,7 @@ Paginator->sort('in_object_name');?> Paginator->sort('type');?> - Paginator->sort('frequency');?> + Paginator->sort('ui-priority');?> Paginator->sort('description');?> Categories Sane defaults @@ -32,7 +32,7 @@     -   +     Date: Wed, 5 Jul 2017 10:36:45 +0200 Subject: [PATCH 09/77] fix: Fixed object references table --- INSTALL/MYSQL.sql | 12 ++++++------ app/Model/AppModel.php | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 2848d68cf..fb4270781 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -474,19 +474,19 @@ CREATE TABLE IF NOT EXISTS object_attribute_references ( -- Table structure for table `object_object_references` -- -CREATE TABLE IF NOT EXISTS object_object_references ( +CREATE TABLE IF NOT EXISTS object_references ( `id` int(11) NOT NULL AUTO_INCREMENT, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, - `referenced_object_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `referenced_type` int(11) NOT NULL DEFAULT 0, + `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (id), - INDEX `name` (`name`), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), - INDEX `referenced_object_uuid` (`referenced_object_uuid`), - INDEX `type` (`type`) + INDEX `referenced_uuid` (`referenced_uuid`), + INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 4f6a493d4..d47a1ac8e 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -729,15 +729,15 @@ class AppModel extends Model { `id` int(11) NOT NULL AUTO_INCREMENT, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, - `referenced_attribute_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `referenced_type` int(11) NOT NULL DEFAULT 0, + `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (id), - INDEX `name` (`name`), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), - INDEX `referenced_attribute_uuid` (`referenced_attribute_uuid`), - INDEX `type` (`type`) + INDEX `referenced_uuid` (`referenced_uuid`), + INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_templates ( From 38d02692df2744d80d3557b1f02088a29cedeb90 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 10:55:07 +0200 Subject: [PATCH 10/77] fix: Removed obsolete table --- INSTALL/MYSQL.sql | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index fb4270781..f77123ae9 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -449,27 +449,6 @@ CREATE TABLE IF NOT EXISTS objects ( -- -------------------------------------------------------- --- --- Table structure for table `object_attribute_references` --- - -CREATE TABLE IF NOT EXISTS object_attribute_references ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `timestamp` int(11) NOT NULL DEFAULT 0, - `referenced_attribute_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `comment` text COLLATE utf8_bin NOT NULL, - PRIMARY KEY (id), - INDEX `name` (`name`), - INDEX `uuid` (`uuid`), - INDEX `timestamp` (`timestamp`), - INDEX `referenced_attribute_uuid` (`referenced_attribute_uuid`), - INDEX `type` (`type`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- -------------------------------------------------------- - -- -- Table structure for table `object_object_references` -- From 02464da6f2cf342d192b302a731dec7d06eac688 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 11:25:11 +0200 Subject: [PATCH 11/77] fix: cakephp updated --- app/Lib/cakephp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Lib/cakephp b/app/Lib/cakephp index cfcbd6ea3..1d1a762a3 160000 --- a/app/Lib/cakephp +++ b/app/Lib/cakephp @@ -1 +1 @@ -Subproject commit cfcbd6ea31cd6b6216fb14ecf47b166a19a6c703 +Subproject commit 1d1a762a37298f41c2ffc2706db6d36ccbfba939 From 3a5b2f01571778bc54efd6ad11f59c8d6eaa7c67 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 14:25:09 +0200 Subject: [PATCH 12/77] chg: Changed Object to MispObject internally --- app/Controller/ObjectsController.php | 30 +++++++++--------- app/Model/Event.php | 2 +- app/Model/{Object.php => MispObject.php} | 40 ++++++++++++++---------- app/Model/ObjectReference.php | 7 ++--- app/Model/ObjectTemplate.php | 7 ++--- app/Model/ObjectTemplateElement.php | 7 ++--- app/View/Elements/side_menu.ctp | 5 +++ app/View/Objects/add.ctp | 2 +- 8 files changed, 55 insertions(+), 45 deletions(-) rename app/Model/{Object.php => MispObject.php} (90%) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index cf3d60ffa..396bf34de 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -3,6 +3,8 @@ App::uses('AppController', 'Controller'); class ObjectsController extends AppController { + var $uses = 'MispObject'; + public $components = array('Security' ,'RequestHandler', 'Session'); public $paginate = array( @@ -35,12 +37,12 @@ class ObjectsController extends AppController { } else { throw new NotFoundException('Invalid event.'); } - $event = $this->Object->Event->find('first', $eventFindParams); + $event = $this->MispObject->Event->find('first', $eventFindParams); if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) { throw new NotFoundException('Invalid event.'); } $eventId = $event['Event']['id']; - $template = $this->Object->ObjectTemplate->find('first', array( + $template = $this->MispObject->ObjectTemplate->find('first', array( 'conditions' => array('ObjectTemplate.id' => $templateId), 'recursive' => -1, 'contain' => array( @@ -57,25 +59,25 @@ class ObjectsController extends AppController { if (!isset($this->request->data['Attribute'])) { $this->request->data = array('Attribute' => $this->request->data); } - $object = $this->Object->attributeCleanup($this->request->data); + $object = $this->MispObject->attributeCleanup($this->request->data); // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return // to the add form foreach ($object['Attribute'] as $k => $attribute) { $object['Attribute'][$k]['event_id'] = $eventId; - $this->Object->Event->Attribute->set($attribute); - if (!$this->Object->Event->Attribute->validates()) { - $error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->Object->Event->Attribute->validationErrors); + $this->MispObject->Event->Attribute->set($attribute); + if (!$this->MispObject->Event->Attribute->validates()) { + $error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->MispObject->Event->Attribute->validationErrors); } } if (empty($error)) { - $error = $this->Object->ObjectTemplate->checkTemplateConformity($template, $object); + $error = $this->MispObject->ObjectTemplate->checkTemplateConformity($template, $object); if ($error === true) { - $result = $this->Object->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); } if ($this->_isRest()) { if (is_numeric($result)) { - $object = $this->Object->find('first', array( + $object = $this->MispObject->find('first', array( 'recursive' => -1, 'conditions' => array('Object.id' => $result), 'contain' => array('Attribute') @@ -102,10 +104,10 @@ class ObjectsController extends AppController { if (!empty($error)) { $this->Session->setFlash($error); } - $template = $this->Object->prepareTemplate($template); + $template = $this->MispObject->prepareTemplate($template); $enabledRows = array_keys($template['ObjectTemplateElement']); $this->set('enabledRows', $enabledRows); - $distributionData = $this->Object->Event->Attribute->fetchDistributionData($this->Auth->user()); + $distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user()); $this->set('distributionData', $distributionData); $this->set('event', $event); $this->set('ajax', false); @@ -127,7 +129,7 @@ class ObjectsController extends AppController { $lookupField = 'id'; throw new NotFoundException('Invalid event.'); } - $event = $this->Object->Event->find('first', array( + $event = $this->MispObject->Event->find('first', array( 'recursive' => -1, 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'), 'conditions' => array('Event.id' => $eventId) @@ -139,12 +141,12 @@ class ObjectsController extends AppController { if (!$this->_isSiteAdmin() && ($event['Event']['orgc_id'] != $this->Auth->user('org_id') || !$this->userRole['perm_modify'])) { throw new UnauthorizedException('You do not have permission to do that.'); } - $this->Object->delete($id); + $this->MispObject->delete($id); } public function view($id) { if ($this->_isRest()) { - $objects = $this->Object->fetchObjects($this->Auth->user(), array('conditions' => array('Object.id' => $id))); + $objects = $this->MispObject->fetchObjects($this->Auth->user(), array('conditions' => array('Object.id' => $id))); if (!empty($objects)) { return $this->RestResponse->viewData($objects, $this->response->type()); } diff --git a/app/Model/Event.php b/app/Model/Event.php index 3c216d9ee..7dfd1e4bc 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -312,7 +312,7 @@ class Event extends AppModel { 'counterQuery' => '' ), 'Object' => array( - 'className' => 'Object', + 'className' => 'MispObject', 'foreignKey' => 'event_id', 'dependent' => true, 'conditions' => '', diff --git a/app/Model/Object.php b/app/Model/MispObject.php similarity index 90% rename from app/Model/Object.php rename to app/Model/MispObject.php index 7525285a0..3c0b7399a 100644 --- a/app/Model/Object.php +++ b/app/Model/MispObject.php @@ -2,14 +2,16 @@ App::uses('AppModel', 'Model'); -class Object extends AppModel { +class MispObject extends AppModel { + + public $useTable = 'objects'; + public $actsAs = array( 'Containable', - 'SysLogLogable.SysLogLogable' => array( - 'roleModel' => 'Object', - 'roleKey' => 'object_id', - 'change' => 'full' - ), + 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full'), ); public $belongsTo = array( @@ -25,7 +27,7 @@ class Object extends AppModel { 'className' => 'ObjectTemplate', 'foreignKey' => false, 'dependent' => false, - 'conditions' => array('Object.template_uuid' => 'ObjectTemplate.uuid') + 'conditions' => array('MispObject.template_uuid' => 'ObjectTemplate.uuid') ) ); @@ -45,21 +47,25 @@ class Object extends AppModel { public function beforeValidate($options = array()) { parent::beforeValidate(); + if (isset($this->data['Object'])) { + $this->data['MispObject'] = $this->data['Object']; + unset($this->data['Object']); + } - if (empty($this->data['Object']['comment'])) { - $this->data['Object']['comment'] = ""; + if (empty($this->data['MispObject']['comment'])) { + $this->data['MispObject']['comment'] = ""; } // generate UUID if it doesn't exist - if (empty($this->data['Object']['uuid'])) { - $this->data['Object']['uuid'] = CakeText::uuid(); + if (empty($this->data['MispObject']['uuid'])) { + $this->data['MispObject']['uuid'] = CakeText::uuid(); } // generate timestamp if it doesn't exist - if (empty($this->data['Object']['timestamp'])) { + if (empty($this->data['MispObject']['timestamp'])) { $date = new DateTime(); - $this->data['Object']['timestamp'] = $date->getTimestamp(); + $this->data['MispObject']['timestamp'] = $date->getTimestamp(); } - if (!isset($this->data['Object']['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; - if (!isset($this->data['Object']['distribution'])) $this->data['Object']['distribution'] = 5; + if (!isset($this->data['MispObject']['distribution']) || $this->data['MispObject']['distribution'] != 4) $this->data['MispObject']['sharing_group_id'] = 0; + if (!isset($this->data['MispObject']['distribution'])) $this->data['MispObject']['distribution'] = 5; return true; } @@ -73,9 +79,9 @@ class Object extends AppModel { 'template_uuid' => 'uuid' ); foreach ($templateFields as $k => $v) { - $object['Object'][$k] = $template['ObjectTemplate'][$v]; + $object['MispObject'][$k] = $template['ObjectTemplate'][$v]; } - $object['Object']['event_id'] = $eventId; + $object['MispObject']['event_id'] = $eventId; $result = false; if ($this->save($object)) { $id = $this->id; diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 71f0b4e49..9f8c511e0 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -6,10 +6,9 @@ class Object extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable - 'roleModel' => 'Object', - 'roleKey' => 'object_id', - 'change' => 'full' - ), + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full'), ); public $belongsTo = array( diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index 50aaa6ee5..fd0266507 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -6,10 +6,9 @@ class ObjectTemplate extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable - 'roleModel' => 'Object', - 'roleKey' => 'object_id', - 'change' => 'full' - ), + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full'), ); public $belongsTo = array( diff --git a/app/Model/ObjectTemplateElement.php b/app/Model/ObjectTemplateElement.php index 4c971c12a..bc8d6eec6 100644 --- a/app/Model/ObjectTemplateElement.php +++ b/app/Model/ObjectTemplateElement.php @@ -6,10 +6,9 @@ class ObjectTemplateElement extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable - 'roleModel' => 'Object', - 'roleKey' => 'object_id', - 'change' => 'full' - ), + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full'), ); public $belongsTo = array( diff --git a/app/View/Elements/side_menu.ctp b/app/View/Elements/side_menu.ctp index b87bd1fcd..890e273df 100644 --- a/app/View/Elements/side_menu.ctp +++ b/app/View/Elements/side_menu.ctp @@ -364,6 +364,11 @@ ?>
  • List Object Templates
  • +
  • Form->postLink('Update Objects', '/objectTemplates/update'); ?>
  • +
  • View Object Template
  • diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 1541a0d0b..920814161 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -1,6 +1,6 @@
    Form->create('Object', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'], 'enctype' => 'multipart/form-data')); + echo $this->Form->create('MispObject', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'], 'enctype' => 'multipart/form-data')); ?>

    From 988e312c3cdc44c41a7ca61b9c70644550bfa51f Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 14:33:54 +0200 Subject: [PATCH 13/77] new: Added objects submodule --- .gitignore | 2 ++ .gitmodules | 3 +++ app/files/misp-objects | 1 + 3 files changed, 6 insertions(+) create mode 160000 app/files/misp-objects diff --git a/.gitignore b/.gitignore index 4365542ed..62f9afc10 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ !/app/files/warninglists/* !/app/files/misp-galaxy !/app/files/misp-galaxy/* +!/app/files/misp-objects +!/app/files/misp-objects/* /app/files/scripts/python-stix/ /app/files/scripts/python-cybox/ /app/files/scripts/mixbox/ diff --git a/.gitmodules b/.gitmodules index db318e1f2..9b44b1e92 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,3 +21,6 @@ [submodule "app/files/misp-galaxy"] path = app/files/misp-galaxy url = https://github.com/MISP/misp-galaxy +[submodule "app/files/misp-objects"] + path = app/files/misp-objects + url = https://github.com/MISP/misp-objects diff --git a/app/files/misp-objects b/app/files/misp-objects new file mode 160000 index 000000000..92fbb3861 --- /dev/null +++ b/app/files/misp-objects @@ -0,0 +1 @@ +Subproject commit 92fbb386168f6dea9e1e885f5f1f585a11925b86 From 89dd4fc8cae048bd4ed64fc2a03e7f30bd099243 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 5 Jul 2017 14:41:55 +0200 Subject: [PATCH 14/77] fix: Object renamed to MispObject in form --- app/View/Objects/add.ctp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 920814161..f0265e35b 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -40,7 +40,7 @@
    Distribution
    Form->input('Object.distribution', array( + echo $this->Form->input('MispObject.distribution', array( 'class' => 'Object_distribution_select', 'options' => $distributionData['levels'], 'default' => $distributionData['initial'], @@ -48,7 +48,7 @@ 'style' => 'margin-bottom:5px;', 'div' => false )); - echo $this->Form->input('Object.sharing_group_id', array( + echo $this->Form->input('MispObject.sharing_group_id', array( 'class' => 'Object_sharing_group_id_select', 'options' => $distributionData['sgs'], 'label' => false, @@ -60,7 +60,7 @@
    Comment
    Form->input('Object.comment', array( + echo $this->Form->input('MispObject.comment', array( 'type' => 'textarea', 'style' => 'height:20px;width:400px;', 'required' => false, From a5d582750e86e20a4971f780ab06a517e312d2bd Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 6 Jul 2017 15:02:19 +0200 Subject: [PATCH 15/77] fix: Updated fields --- INSTALL/MYSQL.sql | 4 +++- app/Model/AppModel.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index f77123ae9..1e7eeb18b 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -504,10 +504,12 @@ CREATE TABLE IF NOT EXISTS object_template_elements ( `version` int(11) NOT NULL, `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `frequency` int(11) NOT NULL, + `ui-priority` int(11) NOT NULL, `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, + `disable_correlation` tinyint(1) NOT NULL DEFAULT 0, + `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `uuid` (`uuid`), INDEX `in-object-name` (`in-object-name`), diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index d47a1ac8e..f3febaadd 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -768,6 +768,8 @@ class AppModel extends Model { `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, + `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, + `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `in-object-name` (`in-object-name`), INDEX `type` (`type`) From dd27f118f3b18e7335decc7af4c5faa189bc167a Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 6 Jul 2017 15:04:01 +0200 Subject: [PATCH 16/77] new: WIP - change to model aliasing to solve the reserved class name - Internal name is now MispObject for the model, but it is used Aliased, removing the need to do any data massaging - Added WIP edit function --- app/Controller/ObjectsController.php | 104 ++++++++++++++++++++++++++- app/Model/MispObject.php | 72 +++++++++++++++---- app/View/Objects/add.ctp | 41 +++++++---- 3 files changed, 188 insertions(+), 29 deletions(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 396bf34de..7d8c102c6 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -49,7 +49,6 @@ class ObjectsController extends AppController { 'ObjectTemplateElement' ) )); - $eventId = $event['Event']['id']; $error = false; // If we have received a POST request if ($this->request->is('post')) { @@ -111,12 +110,115 @@ class ObjectsController extends AppController { $this->set('distributionData', $distributionData); $this->set('event', $event); $this->set('ajax', false); + $this->set('action', 'add'); $this->set('template', $template); } } public function edit($id) { + if (!$this->userRole['perm_modify']) { + throw new MethodNotAllowedException('You don\'t have permissions to create objects.'); + } + $object = $this->MispObject->find('first', array( + 'conditions' => array('Object.id' => $id), + 'recursive' => -1, + 'contain' => array( + 'Attribute' => array( + 'conditions' => array( + 'Attribute.deleted' => 0 + ) + ) + ) + )); + if (empty($object)) { + throw new NotFoundException('Invalid object.'); + } + $eventFindParams = array( + 'recursive' => -1, + 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'), + 'conditions' => array('Event.id' => $object['Object']['event_id']) + ); + $event = $this->MispObject->Event->find('first', $eventFindParams); + if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) { + throw new NotFoundException('Invalid object.'); + } + $template = $this->MispObject->ObjectTemplate->find('first', array( + 'conditions' => array( + 'ObjectTemplate.uuid' => $object['Object']['template_uuid'], + 'ObjectTemplate.version' => $object['Object']['template_version'], + ), + 'recursive' => -1, + 'contain' => array( + 'ObjectTemplateElement' + ) + )); + $template = $this->MispObject->prepareTemplate($template); + $enabledRows = false; + + if ($this->request->is('post') || $this->request->is('put')) { + if (isset($this->request->data['request'])) { + $this->request->data = $this->request->data['request']; + } + if (!isset($this->request->data['Attribute'])) { + $this->request->data = array('Attribute' => $this->request->data); + } + $objectToSave = $this->MispObject->attributeCleanup($this->request->data); + $objectToSave = $this->MispObject->deltaMerge($object, $objectToSave); + // we pre-validate the attributes before we create an object at this point + // This allows us to stop the process and return an error (API) or return + // to the add form + if (empty($error)) { + if ($this->_isRest()) { + if (is_numeric($result)) { + $objectToSave = $this->MispObject->find('first', array( + 'recursive' => -1, + 'conditions' => array('Object.id' => $result), + 'contain' => array('Attribute') + )); + return $this->RestResponse->viewData($objectToSave, $this->response->type()); + } else { + return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); + } + } else { + $this->Session->setFlash('Object saved.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['id'])); + } + } + } else { + $enabledRows = array(); + $this->request->data['Object'] = $object['Object']; + foreach ($template['ObjectTemplateElement'] as $k => $element) { + foreach ($object['Attribute'] as $k2 => $attribute) { + if ($attribute['object_relation'] == $element['in-object-name']) { + $enabledRows[] = $k; + $this->request->data['Attribute'][$k] = $attribute; + if (!empty($element['values_list'])) { + $this->request->data['Attribute'][$k]['value_select'] = $attribute['value']; + } else { + if (!empty($element['sane_default'])) { + if (in_array($attribute['value'], $element['sane_default'])) { + $this->request->data['Attribute'][$k]['value_select'] = $attribute['value']; + } else { + $this->request->data['Attribute'][$k]['value_select'] = 'Enter value manually'; + } + } + } + } + } + } + + } + + $this->set('enabledRows', $enabledRows); + $distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user()); + $this->set('distributionData', $distributionData); + $this->set('event', $event); + $this->set('ajax', false); + $this->set('template', $template); + $this->set('action', 'edit'); + $this->set('object', $object); + $this->render('add'); } public function delete($id) { diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 3c0b7399a..78189f03a 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -4,6 +4,9 @@ App::uses('AppModel', 'Model'); class MispObject extends AppModel { + public $name = 'Object'; + public $alias = 'Object'; + public $useTable = 'objects'; public $actsAs = array( @@ -47,25 +50,20 @@ class MispObject extends AppModel { public function beforeValidate($options = array()) { parent::beforeValidate(); - if (isset($this->data['Object'])) { - $this->data['MispObject'] = $this->data['Object']; - unset($this->data['Object']); - } - - if (empty($this->data['MispObject']['comment'])) { - $this->data['MispObject']['comment'] = ""; + if (empty($this->data['Object']['comment'])) { + $this->data['Object']['comment'] = ""; } // generate UUID if it doesn't exist - if (empty($this->data['MispObject']['uuid'])) { - $this->data['MispObject']['uuid'] = CakeText::uuid(); + if (empty($this->data['Object']['uuid'])) { + $this->data['Object']['uuid'] = CakeText::uuid(); } // generate timestamp if it doesn't exist - if (empty($this->data['MispObject']['timestamp'])) { + if (empty($this->data['Object']['timestamp'])) { $date = new DateTime(); - $this->data['MispObject']['timestamp'] = $date->getTimestamp(); + $this->data['Object']['timestamp'] = $date->getTimestamp(); } - if (!isset($this->data['MispObject']['distribution']) || $this->data['MispObject']['distribution'] != 4) $this->data['MispObject']['sharing_group_id'] = 0; - if (!isset($this->data['MispObject']['distribution'])) $this->data['MispObject']['distribution'] = 5; + if (!isset($this->data['Object']['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; + if (!isset($this->data['Object']['distribution'])) $this->data['Object']['distribution'] = 5; return true; } @@ -79,9 +77,9 @@ class MispObject extends AppModel { 'template_uuid' => 'uuid' ); foreach ($templateFields as $k => $v) { - $object['MispObject'][$k] = $template['ObjectTemplate'][$v]; + $object['Object'][$k] = $template['ObjectTemplate'][$v]; } - $object['MispObject']['event_id'] = $eventId; + $object['Object']['event_id'] = $eventId; $result = false; if ($this->save($object)) { $id = $this->id; @@ -323,4 +321,48 @@ class MispObject extends AppModel { } return $attributes; } + + public function deltaMerge($object, $objectToSave) { + $object['Object']['comment'] = $objectToSave['Object']['comment']; + $object['Object']['distribution'] = $objectToSave['Object']['distribution']; + $object['Object']['sharing_group_id'] = $objectToSave['Object']['sharing_group_id']; + $date = new DateTime(); + $object['Object']['timestamp'] = $date->getTimestamp(); + $this->save($object); + foreach ($objectToSave['Attribute'] as $newKey => $newAttribute) { + foreach ($object['Attribute'] as $origKey => $originalAttribute) { + if (!empty($newAttribute['uuid'])) { + if ($newAttribute['uuid'] == $originalAttribute['uuid']) { + $newAttribute['id'] = $originalAttribute['id']; + $newAttribute['event_id'] = $object['Object']['event_id']; + $newAttribute['object_id'] = $object['Object']['id']; + $newAttribute['timestamp'] = $date->getTimestamp(); + $this->Event->Attribute->save($newAttribute, array( + 'category', + 'value', + 'to_ids', + 'distribution', + 'sharing_group_id', + 'comment', + 'timestamp', + 'object_id', + 'event_id' + )); + unset($object['Attribute'][$origKey]); + continue 2; + } + } + } + $this->Event->Attribute->create(); + $newAttribute['event_id'] = $object['Object']['event_id']; + $newAttribute['object_id'] = $object['Object']['id']; + $this->Event->Attribute->save($newAttribute); + $attributeArrays['add'][] = $newAttribute; + unset($objectToSave['Attribute'][$newKey]); + } + foreach ($object['Attribute'] as $origKey => $originalAttribute) { + $originalAttribute['deleted'] = 1; + $this->Event->Attribute->save($originalAttribute); + } + } } diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index f0265e35b..89af17665 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -1,8 +1,9 @@
    Form->create('MispObject', array('id', 'url' => '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'], 'enctype' => 'multipart/form-data')); + $url = ($action == 'add') ? '/objects/add/' . $event['Event']['id'] . '/' . $template['ObjectTemplate']['id'] : '/objects/edit/' . $object['Object']['id']; + echo $this->Form->create('Object', array('id', 'url' => $url, 'enctype' => 'multipart/form-data')); ?> -

    +

    Object Template
    @@ -40,7 +41,7 @@
    Distribution
    Form->input('MispObject.distribution', array( + echo $this->Form->input('Object.distribution', array( 'class' => 'Object_distribution_select', 'options' => $distributionData['levels'], 'default' => $distributionData['initial'], @@ -48,7 +49,7 @@ 'style' => 'margin-bottom:5px;', 'div' => false )); - echo $this->Form->input('MispObject.sharing_group_id', array( + echo $this->Form->input('Object.sharing_group_id', array( 'class' => 'Object_sharing_group_id_select', 'options' => $distributionData['sgs'], 'label' => false, @@ -60,7 +61,7 @@
    Comment
    Form->input('MispObject.comment', array( + echo $this->Form->input('Object.comment', array( 'type' => 'textarea', 'style' => 'height:20px;width:400px;', 'required' => false, @@ -100,18 +101,29 @@ Form->input('Attribute.' . $k . '.object_relation', array( + $formSettings = array( 'type' => 'hidden', 'value' => $element['in-object-name'], 'label' => false, 'div' => false - )); - echo $this->Form->input('Attribute.' . $k . '.type', array( + ); + if ($action == 'edit') unset($formSettings['value']); + echo $this->Form->input('Attribute.' . $k . '.object_relation', $formSettings); + if ($action == 'edit') { + echo $this->Form->input('Attribute.' . $k . '.uuid', array( + 'type' => 'hidden', + 'label' => false, + 'div' => false + )); + } + $formSettings = array( 'type' => 'hidden', 'value' => $element['type'], 'label' => false, 'div' => false - )); + ); + if ($action == 'edit') unset($formSettings['value']); + echo $this->Form->input('Attribute.' . $k . '.type', $formSettings); echo '' . Inflector::humanize(h($element['in-object-name'])) . ''; if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['in-object-name'], $template['ObjectTemplate']['requirements']['required'])) { echo '' . '(*)' . ''; @@ -121,13 +133,15 @@ Form->input('Attribute.' . $k . '.category', array( + $formSettings = array( 'options' => array_combine($element['categories'], $element['categories']), 'default' => $element['default_category'], 'style' => 'margin-bottom:0px;', 'label' => false, 'div' => false - )); + ); + if ($action == 'edit') unset($formSettings['value']); + echo $this->Form->input('Attribute.' . $k . '.category', $formSettings); ?> @@ -267,7 +281,9 @@ var rows = ; $(document).ready(function() { enableDisableObjectRows(rows); - + $(".Attribute_value_select").each(function() { + checkAndEnable($(this).parent().find('.Attribute_value'), $(this).val() == 'Enter value manually'); + }); $(".Attribute_distribution_select").change(function() { checkAndEnable($(this).parent().find('.Attribute_sharing_group_id_select'), $(this).val() == 4); }); @@ -275,7 +291,6 @@ $(".Object_distribution_select").change(function() { checkAndEnable($(this).parent().find('.Object_sharing_group_id_select'), $(this).val() == 4); }); - $(".Attribute_value_select").change(function() { checkAndEnable($(this).parent().find('.Attribute_value'), $(this).val() == 'Enter value manually'); }); From 8cd31dd76c68a474ab3282ddfc7643669fded85c Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 2 Aug 2017 23:01:12 +0200 Subject: [PATCH 17/77] new: Further work on the object UI - refactoring - added objects fields to object rows - nested rows within the object - massive cleanup --- app/Controller/ObjectsController.php | 1 - app/Model/Event.php | 222 ++++++--- app/Model/Warninglist.php | 17 + .../Elements/Events/View/row_attribute.ctp | 256 +++++++++++ app/View/Elements/Events/View/row_object.ctp | 60 +++ .../Elements/Events/View/row_proposal.ctp | 316 +++++++++++++ .../Events/View/row_proposal_delete.ctp | 60 +++ .../Elements/Events/View/sighting_field.ctp | 35 ++ app/View/Elements/Events/View/value_field.ctp | 49 ++ app/View/Elements/ajaxTags.ctp | 2 +- app/View/Elements/eventattribute.ctp | 434 +----------------- app/webroot/css/main.css | 138 +++++- 12 files changed, 1113 insertions(+), 477 deletions(-) create mode 100644 app/View/Elements/Events/View/row_attribute.ctp create mode 100644 app/View/Elements/Events/View/row_object.ctp create mode 100644 app/View/Elements/Events/View/row_proposal.ctp create mode 100644 app/View/Elements/Events/View/row_proposal_delete.ctp create mode 100644 app/View/Elements/Events/View/sighting_field.ctp create mode 100644 app/View/Elements/Events/View/value_field.ctp diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 7d8c102c6..30fd38b87 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -209,7 +209,6 @@ class ObjectsController extends AppController { } } - $this->set('enabledRows', $enabledRows); $distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user()); $this->set('distributionData', $distributionData); diff --git a/app/Model/Event.php b/app/Model/Event.php index 7dfd1e4bc..b37f1b551 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -53,6 +53,8 @@ class Event extends AppModel { 0 => 'Your organisation only', 1 => 'This community only', 2 => 'Connected communities', 3 => 'All communities', 4 => 'Sharing group' ); + private $__fTool = false; + public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group'); public $export_types = array( @@ -3114,8 +3116,128 @@ class Event extends AppModel { return time() - ($delta * $multiplier); } + private function __prepareAttributeForView( + $attribute, + $correlatedAttributes, + $correlatedShadowAttributes, + $filterType = false, + $eventWarnings, + $warningLists + ) { + $attribute['objectType'] = 'attribute'; + $include = true; + if ($filterType && !in_array($filterType, array('proposal', 'correlation', 'warning'))) { + if (!in_array($attribute['type'], $this->Attribute->typeGroupings[$filterType])) { + $include = false; + } + } + if ($filterType === 'proposal' && empty($attribute['ShadowAttribute'])) { + $include = false; + } + if ($filterType === 'correlation' && !in_array($attribute['id'], $correlatedAttributes)) { + $include = false; + } + if (!empty($attribute['ShadowAttribute'])) { + $temp = array(); + foreach ($attribute['ShadowAttribute'] as $k => $proposal) { + $result = $this->__prepareProposalForView( + $proposal, + $correlatedShadowAttributes, + false, + $eventWarnings, + $warningLists + ); + $temp[] = $result['data']; + } + $attribute['ShadowAttribute'] = $temp; + } + $attribute = $this->__prepareGenericForView($attribute, $eventWarnings, $warningLists); + return array('include' => $include, 'data' => $attribute); + } + + private function __prepareProposalForView( + $proposal, + $correlatedShadowAttributes, + $filterType = false, + $eventWarnings, + $warningLists + ) { + if ($proposal['proposal_to_delete']) { + $proposal['objectType'] = 'proposal_delete'; + } else { + $proposal['objectType'] = 'proposal'; + } + + $include = true; + if ($filterType === 'correlation' && !in_array($proposal['id'], $correlatedShadowAttributes)) { + $include = false; + } + if ($filterType && !in_array($filterType, array('proposal', 'correlation', 'warning'))) { + if (!in_array($attribute['type'], $this->Attribute->typeGroupings[$filterType])) { + $include = false; + } + } + $proposal = $this->__prepareGenericForView($proposal, $eventWarnings, $warningLists); + return array('include' => $include, 'data' => $proposal); + } + + private function __prepareObjectForView( + $object, + $correlatedAttributes, + $correlatedShadowAttributes, + $filterType = false, + $eventWarnings, + $warningLists + ) { + $object['category'] = $object['meta-category']; + $proposal['objectType'] = 'object'; + // filters depend on child objects + $include = empty($filterType) || $filterType == 'object'; + if (!empty($object['Attribute'])) { + $temp = array(); + foreach ($object['Attribute'] as $k => $proposal) { + $result = $this->__prepareAttributeForView( + $proposal, + $correlatedAttributes, + $correlatedShadowAttributes, + $filterType, + $eventWarnings, + $warningLists + ); + $include = $include || $object['include']; + $temp[] = $result['data']; + } + $object['Attribute'] = $temp; + } + return array('include' => $include, 'data' => $object); + } + + private function __prepareGenericForView( + $object, + $eventWarnings, + $warningLists + ) { + if (!$this->__fTool) { + $this->__fTool = new FinancialTool(); + } + if ($object['type'] == 'attachment' && preg_match('/.*\.(jpg|png|jpeg|gif)$/i', $object['value'])) { + $object['image'] = $this->Attribute->base64EncodeAttachment($object); + } + if (isset($object['distribution']) && $object['distribution'] != 4) unset($object['SharingGroup']); + if ($object['objectType'] !== 'object') { + if ($object['category'] === 'Financial fraud') { + if (!$this->__fTool->validateRouter($object['type'], $object['value'])) { + $object['validationIssue'] = true; + } + } + } + $this->Warninglist->checkForWarning($object, $eventWarnings, $warningLists); + return $object; + } + public function rearrangeEventForView(&$event, $passedArgs = array(), $all = false) { - $fTool = new FinancialTool(); + $this->Warninglist = ClassRegistry::init('Warninglist'); + $warningLists = $this->Warninglist->fetchForEventView(); foreach ($event['Event'] as $k => $v) { if (is_array($v)) { $event[$k] = $v; @@ -3131,79 +3253,55 @@ class Event extends AppModel { } } $eventArray = array(); + $eventWarnings = array(); $correlatedAttributes = isset($event['RelatedAttribute']) ? array_keys($event['RelatedAttribute']) : array(); $correlatedShadowAttributes = isset($event['RelatedShadowAttribute']) ? array_keys($event['RelatedShadowAttribute']) : array(); - $totalElements = count($event['Attribute']); foreach ($event['Attribute'] as $attribute) { - $totalElements += isset($attribute['ShadowAttribute']) ? count($attribute['ShadowAttribute']) : 0; - if ($filterType && !in_array($filterType, array('proposal', 'correlation', 'warning'))) if (!in_array($attribute['type'], $this->Attribute->typeGroupings[$filterType])) continue; - if (isset($attribute['distribution']) && $attribute['distribution'] != 4) unset($attribute['SharingGroup']); - $attribute['objectType'] = 0; - if (!empty($attribute['ShadowAttribute'])) { - $attribute['hasChildren'] = 1; - } else { - $attribute['hasChildren'] = 0; - } - if ($filterType === 'proposal' && $attribute['hasChildren'] == 0) continue; - if ($filterType === 'correlation' && !in_array($attribute['id'], $correlatedAttributes)) continue; - if ($attribute['type'] == 'attachment' && preg_match('/.*\.(jpg|png|jpeg|gif)$/i', $attribute['value'])) { - $attribute['image'] = $this->Attribute->base64EncodeAttachment($attribute); - } - $eventArray[] = $attribute; + $result = $this->__prepareAttributeForView( + $attribute, + $correlatedAttributes, + $correlatedShadowAttributes, + $filterType, + $eventWarnings, + $warningLists + ); + $event['objects'][] = $result['data']; } unset($event['Attribute']); - if (isset($event['ShadowAttribute'])) { - $totalElements += count($event['ShadowAttribute']); - foreach ($event['ShadowAttribute'] as $shadowAttribute) { - if ($filterType === 'correlation' && !in_array($shadowAttribute['id'], $correlatedShadowAttributes)) continue; - if ($filterType && !in_array($filterType, array('proposal', 'correlation', 'warning'))) if (!in_array($attribute['type'], $this->Attribute->typeGroupings[$filterType])) continue; - $shadowAttribute['objectType'] = 2; - if ($shadowAttribute['type'] == 'attachment' && preg_match('/.*\.(jpg|png|jpeg|gif)$/i', $shadowAttribute['value'])) { - $shadowAttribute['image'] = $this->ShadowAttribute->base64EncodeAttachment($attribute); - } - $eventArray[] = $shadowAttribute; + if (!empty($event['ShadowAttribute'])) { + foreach ($event['ShadowAttribute'] as $proposal) { + $result = $this->__prepareProposalForView( + $proposal, + $correlatedShadowAttributes, + $filterType, + $eventWarnings, + $warningLists + ); + $event['objects'][] = $result['data']; } + unset($event['ShadowAttribute']); + } + if (!empty($event['Object'])) { + foreach ($event['Object'] as $object) { + $object['objectType'] = 'object'; + $result = $this->__prepareObjectForView( + $object, + $correlatedAttributes, + $correlatedShadowAttributes, + false, + $eventWarnings, + $warningLists + ); + $event['objects'][] = $result['data']; + } + unset($event['Object']); } unset($event['ShadowAttribute']); App::uses('CustomPaginationTool', 'Tools'); $customPagination = new CustomPaginationTool(); if ($all) $passedArgs['page'] = 0; - $eventArrayWithProposals = array(); - foreach ($eventArray as $k => &$object) { - if ($object['category'] === 'Financial fraud') { - if (!$fTool->validateRouter($object['type'], $object['value'])) { - $object['validationIssue'] = true; - } - } - if ($object['objectType'] == 0) { - if (isset($object['ShadowAttribute'])) { - $shadowAttributeTemp = $object['ShadowAttribute']; - unset($object['ShadowAttribute']); - $eventArrayWithProposals[] = $object; - foreach ($shadowAttributeTemp as $kk => $shadowAttribute) { - $shadowAttribute['objectType'] = 1; - if ($kk == 0) $shadowAttribute['firstChild'] = true; - if (($kk + 1) == count($shadowAttributeTemp)) $shadowAttribute['lastChild'] = true; - $eventArrayWithProposals[] = $shadowAttribute; - } - } else { - $eventArrayWithProposals[] = $object; - } - } else { - $eventArrayWithProposals[] = $object; - } - unset($eventArray[$k]); - } - $event['objects'] = $eventArrayWithProposals; - $this->Warninglist = ClassRegistry::init('Warninglist'); - $warningLists = $this->Warninglist->fetchForEventView(); - if (!empty($warningLists)) $event = $this->Warninglist->setWarnings($event, $warningLists); - if ($filterType && $filterType == 'warning') { - foreach ($event['objects'] as $k => &$object) if (empty($object['warnings'])) unset($event['objects'][$k]); - $event['objects'] = array_values($event['objects']); - } $params = $customPagination->applyRulesOnArray($event['objects'], $passedArgs, 'events', 'category'); - $params['total_elements'] = $totalElements; + $params['total_elements'] = count($event['objects']); return $params; } diff --git a/app/Model/Warninglist.php b/app/Model/Warninglist.php index 691ece79d..4ea65e62c 100644 --- a/app/Model/Warninglist.php +++ b/app/Model/Warninglist.php @@ -232,6 +232,23 @@ class Warninglist extends AppModel{ return $warninglists; } + public function checkForWarning($object, &$eventWarnings, $warningLists) { + if ($object['to_ids']) { + foreach ($warningLists as $list) { + if (in_array('ALL', $list['types']) || in_array($object['type'], $list['types'])) { + $result = $this->__checkValue($list['values'], $object['value'], $object['type'], $list['Warninglist']['type']); + if (!empty($result)) { + $object['warnings'][$result][] = $list['Warninglist']['name']; + if (!in_array($list['Warninglist']['name'], $eventWarnings)) { + $eventWarnings[$list['Warninglist']['id']] = $list['Warninglist']['name']; + } + } + } + } + } + return $object; + } + public function setWarnings(&$event, &$warninglists) { if (empty($event['objects'])) return $event; $eventWarnings = array(); diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp new file mode 100644 index 000000000..b0f3fcf0d --- /dev/null +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + +   + + +
    +
    + +
    + + +
    +
    + +
    + + +
    + +
    > + > + element('/Events/View/value_field', array('object' => $object)); + ?> + + 0, 2 => 1); + $valueParts = explode('|', $object['value']); + foreach ($components as $component => $valuePart) { + if (isset($object['warnings'][$component]) && isset($valueParts[$valuePart])) { + foreach ($object['warnings'][$component] as $warning) $temp .= '' . h($valueParts[$valuePart]) . ': ' . h($warning) . '
    '; + } + } + echo '  '; + } + ?> +
    + + +
    + element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['AttributeTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']) )); ?> +
    + + +
    +
    +   +
    + + + + > + + + +
      + $relatedAttribute['info'], 'Correlating Value' => $relatedAttribute['value'], 'date' => isset($relatedAttribute['date']) ? $relatedAttribute['date'] : 'N/A'); + $popover = ''; + foreach ($relatedData as $k => $v) { + $popover .= '' . h($k) . ': ' . h($v) . '
      '; + } + echo '
    • '; + if ($relatedAttribute['org_id'] == $me['org_id']) { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => 'red')); + } else { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => $otherColour)); + } + echo "
    • "; + echo ' '; + } + } + ?> +
    + + +
      + $v): + if ($k == 'id') continue; + $popover .= '' . Inflector::humanize(h($k)) . ': ' . h($v) . '
      '; + endforeach; + ?> +
    • + Html->link($feed['id'], array('controller' => 'feeds', 'action' => 'previewIndex', $feed['id']), array('style' => 'margin-right:3px;')); + else: + ?> + + +
    • + +
    + + +
    +
    + +
    + + + +
    +
    class="inline-field-solid" ondblclick="activateField('Attribute', '', 'distribution', );"> + + + +
    + + element('/Events/View/sighting_field', array( + 'object' => $object, + 'tr_class' => $tr_class, + 'page' => $page + )); + endif; + ?> + + + + + +   + + + + + + +   + +   + + C + + + + + + + $proposal) { + echo $this->element('/Events/View/row_' . $proposal['objectType'], array( + 'object' => $proposal, + 'mayModify' => $mayModify, + 'mayChangeCorrelation' => $mayChangeCorrelation, + 'page' => $page, + 'fieldCount' => $fieldCount, + 'child' => $propKey == $lastElement ? 'last' : true, + 'objectContainer' => $child + )); + } + } +?> diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp new file mode 100644 index 000000000..44d45846e --- /dev/null +++ b/app/View/Elements/Events/View/row_object.ctp @@ -0,0 +1,60 @@ + + + + + + + + + + Name:
    + Meta-category:
    + Description:
    + Tempate: + + + +
    +
    class="inline-field-solid" ondblclick="activateField('', '', 'distribution', );"> + + +   +
    + +   +   + + + + $attribute) { + echo $this->element('/Events/View/row_' . $attribute['objectType'], array( + 'object' => $attribute, + 'mayModify' => $mayModify, + 'mayChangeCorrelation' => $mayChangeCorrelation, + 'page' => $page, + 'fieldCount' => $fieldCount, + 'child' => $attrKey == $lastElement ? 'last' : true + )); + } + } +?> diff --git a/app/View/Elements/Events/View/row_proposal.ctp b/app/View/Elements/Events/View/row_proposal.ctp new file mode 100644 index 000000000..9ebb557bd --- /dev/null +++ b/app/View/Elements/Events/View/row_proposal.ctp @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + +
    + +
    + + + Html->image('orgs/' . h($object['Org']['name']) . '.png', array('alt' => h($object['Org']['name']), 'title' => h($object['Org']['name']), 'style' => 'width:24px; height:24px')); + else echo h($object['Org']['name']); + } + } else { ?> +   + + + +
    +
    + +
    + + +
    +
    + +
    + + +
    + +
    > + > + '; + } else { + $t = ($object['objectType'] == 0 ? 'attributes' : 'shadow_attributes'); + $filenameHash = explode('|', nl2br(h($object['value']))); + if (strrpos($filenameHash[0], '\\')) { + $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); + $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); + echo h($filepath); + echo '' . h($filename) . ''; + } else { + echo '' . h($filenameHash[0]) . ''; + } + if (isset($filenameHash[1])) echo '
    ' . $filenameHash[1]; + } + } else if (strpos($object['type'], '|') !== false) { + $filenameHash = explode('|', $object['value']); + echo h($filenameHash[0]); + if (isset($filenameHash[1])) { + $separator = '
    '; + if (in_array($object['type'], array('ip-dst|port', 'ip-src|port'))) { + $separator = ':'; + } + echo $separator . h($filenameHash[1]); + } + } else if ('vulnerability' == $object['type']) { + if (! is_null(Configure::read('MISP.cveurl'))) { + $cveUrl = Configure::read('MISP.cveurl'); + } else { + $cveUrl = "http://www.google.com/search?q="; + } + echo $this->Html->link($sigDisplay, $cveUrl . $sigDisplay, array('target' => '_blank', 'class' => $linkClass)); + } else if ('link' == $object['type']) { + echo $this->Html->link($sigDisplay, $sigDisplay, array('class' => $linkClass)); + } else if ('cortex' == $object['type']) { + echo '
    Cortex object
    '; + } else if ('text' == $object['type']) { + if ($object['category'] == 'External analysis' && preg_match('/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i', $object['value'])) { + echo '' . h($object['value']) . ''; + } else { + $sigDisplay = str_replace("\r", '', h($sigDisplay)); + $sigDisplay = str_replace(" ", ' ', $sigDisplay); + echo nl2br($sigDisplay); + } + } else if ('hex' == $object['type']) { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo '' . nl2br(h($sigDisplay)) . ' '; + } else { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo nl2br(h($sigDisplay)); + } + if (isset($object['validationIssue'])) echo '  '; + ?> +
    + 0, 2 => 1); + $valueParts = explode('|', $object['value']); + foreach ($components as $component => $valuePart) { + if (isset($object['warnings'][$component]) && isset($valueParts[$valuePart])) { + foreach ($object['warnings'][$component] as $warning) $temp .= '' . h($valueParts[$valuePart]) . ': ' . h($warning) . '
    '; + } + } + echo '  '; + } + ?> +
    + + + +
    +   +
    + +   + + + +
    +
    +   +
    + +   + +
      + $relatedAttribute['info'], 'Correlating Value' => $relatedAttribute['value'], 'date' => isset($relatedAttribute['date']) ? $relatedAttribute['date'] : 'N/A'); + $popover = ''; + foreach ($relatedData as $k => $v) { + $popover .= '' . h($k) . ': ' . h($v) . '
      '; + } + echo '
    • '; + if ($relatedAttribute['org_id'] == $me['org_id']) { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => 'red')); + } else { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => $otherColour)); + } + echo "
    • "; + echo ' '; + } + } + ?> +
    + + +
      + $v): + if ($k == 'id') continue; + $popover .= '' . Inflector::humanize(h($k)) . ': ' . h($v) . '
      '; + endforeach; + ?> +
    • + Html->link($feed['id'], array('controller' => 'feeds', 'action' => 'previewIndex', $feed['id']), array('style' => 'margin-right:3px;')); + else: + ?> + + +
    • + +
    + + +
    +
    + +
    + +   + + + + Form->create('Sighting', array('id' => 'Sighting_' . $object['id'], 'url' => '/sightings/add/' . $object['id'], 'style' => 'display:none;')); + echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $object['id'] . '_type')); + echo $this->Form->end(); + ?> + + +   +   +   + + + + + ' . h($s) . '/' . h($f) . '/' . h($e) . ')'; ?> + + + + + element('sparkline', array('id' => $object['id'], 'csv' => $temp)); + } + ?> + + + + Form->create('Shadow_Attribute', array('id' => 'ShadowAttribute_' . $object['id'] . '_accept', 'url' => '/shadow_attributes/accept/' . $object['id'], 'style' => 'display:none;')); + echo $this->Form->end(); + ?> + + + + + + diff --git a/app/View/Elements/Events/View/row_proposal_delete.ctp b/app/View/Elements/Events/View/row_proposal_delete.ctp new file mode 100644 index 000000000..694fe84a6 --- /dev/null +++ b/app/View/Elements/Events/View/row_proposal_delete.ctp @@ -0,0 +1,60 @@ + + + + + + + + + + + + DELETE + + Form->create('Shadow_Attribute', array('id' => 'ShadowAttribute_' . $object['id'] . '_accept', 'url' => '/shadow_attributes/accept/' . $object['id'], 'style' => 'display:none;')); + echo $this->Form->end(); + ?> + + + + + + diff --git a/app/View/Elements/Events/View/sighting_field.ctp b/app/View/Elements/Events/View/sighting_field.ctp new file mode 100644 index 000000000..c4380fcff --- /dev/null +++ b/app/View/Elements/Events/View/sighting_field.ctp @@ -0,0 +1,35 @@ + + + Form->create('Sighting', array('id' => 'Sighting_' . $object['id'], 'url' => '/sightings/add/' . $object['id'], 'style' => 'display:none;')); + echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $object['id'] . '_type')); + echo $this->Form->end(); + ?> + + +   +   +   + + + + + ' . h($s) . '/' . h($f) . '/' . h($e) . ')'; ?> + + + + element('sparkline', array('id' => $object['id'], 'csv' => $temp)); + } + ?> + diff --git a/app/View/Elements/Events/View/value_field.ctp b/app/View/Elements/Events/View/value_field.ctp new file mode 100644 index 000000000..d03e2e911 --- /dev/null +++ b/app/View/Elements/Events/View/value_field.ctp @@ -0,0 +1,49 @@ +'; + } else { + $filenameHash = explode('|', nl2br(h($object['value']))); + if (strrpos($filenameHash[0], '\\')) { + $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); + $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); + echo h($filepath); + echo '' . h($filename) . ''; + } else { + echo '' . h($filenameHash[0]) . ''; + } + if (isset($filenameHash[1])) echo '
    ' . $filenameHash[1]; + } + } else if (strpos($object['type'], '|') !== false) { + $separator = in_array($object['type'], array('ip-dst|port', 'ip-src|port')) ? ':' : '
    '; + $separator_pos = strpos('|', $object['value']); + $final_value = h($object['value']); + echo substr_replace(h($object['value']), $separator, $separator_pos, strlen($separator)); + } else if ('vulnerability' == $object['type']) { + $cveUrl = (is_null(Configure::read('MISP.cveurl'))) ? "http://www.google.com/search?q=" : Configure::read('MISP.cveurl'); + echo $this->Html->link($sigDisplay, $cveUrl . $sigDisplay, array('target' => '_blank', 'class' => $linkClass)); + } else if ('link' == $object['type']) { + echo $this->Html->link($sigDisplay, $sigDisplay, array('class' => $linkClass)); + } else if ('cortex' == $object['type']) { + echo '
    Cortex object
    '; + } else if ('text' == $object['type']) { + if ($object['category'] == 'External analysis' && preg_match('/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i', $object['value'])) { + echo '' . h($object['value']) . ''; + } else { + $sigDisplay = str_replace("\r", '', h($sigDisplay)); + $sigDisplay = str_replace(" ", ' ', $sigDisplay); + echo nl2br($sigDisplay); + } + } else if ('hex' == $object['type']) { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo '' . nl2br(h($sigDisplay)) . ' '; + } else { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo nl2br(h($sigDisplay)); + } + if (isset($object['validationIssue'])) echo '  '; +?> diff --git a/app/View/Elements/ajaxTags.ctp b/app/View/Elements/ajaxTags.ctp index aa5fa34c2..90c229ac5 100644 --- a/app/View/Elements/ajaxTags.ctp +++ b/app/View/Elements/ajaxTags.ctp @@ -1,4 +1,4 @@ -
    +
    $data) { $sightingsData['data'][$aid]['html'] = ''; @@ -174,419 +175,28 @@ Actions $object): - $extra = ''; - $extra2 = ''; - $extra3 = ''; - $linkClass = 'white'; - $currentType = 'denyForm'; - if ($object['objectType'] == 0 ) { - $currentType = 'Attribute'; - if ($object['hasChildren'] == 1) { - $extra = 'highlight1'; - $extra3 = 'highlightBlueSides highlightBlueTop'; - } else { - $linkClass = ''; - } - if (!$mayModify) $currentType = 'ShadowAttribute'; - } else { - if (isset($object['proposal_to_delete']) && $object['proposal_to_delete']) { - $extra = 'highlight3'; - unset($object['type']); - } else $extra = 'highlight2'; - + $elements = array( + 0 => 'attribute', + 1 => 'proposal', + 2 => 'proposal_delete', + 3 => 'object' + ); + foreach ($event['objects'] as $k => $object) { + $element_type = 0; + if ($object['objectType'] == 1 && !empty($object['proposal_to_delete'])) { + $element_type = 1; + } else if ($object['objectType'] == 3) { + $element_type = 2; } - if ($object['objectType'] == 1) { - $extra2 = '1'; - $extra3 = 'highlightBlueSides'; - if (isset($object['firstChild'])) { - $extra3 .= ' highlightBlueTop'; - } - if (isset($object['lastChild'])) { - $extra3 .= ' highlightBlueBottom'; - } - } - if (isset($object['deleted']) && $object['deleted']) { - $extra .= ' background-light-red'; - } - $extra .= (isset($object['deleted']) && $object['deleted']) ? ' background-light-red' : ''; - ?> - - - - - - - - - - - - - - - -
    - -
    - - - Html->image('orgs/' . h($object['Org']['name']) . '.png', array('alt' => h($object['Org']['name']), 'title' => h($object['Org']['name']), 'style' => 'width:24px; height:24px')); - else echo h($object['Org']['name']); - } - } else { ?> -   - - - -
    -
    - -
    - - -
    -
    - -
    - - -
    - -
    > - > - '; - } else { - $t = ($object['objectType'] == 0 ? 'attributes' : 'shadow_attributes'); - $filenameHash = explode('|', nl2br(h($object['value']))); - if (strrpos($filenameHash[0], '\\')) { - $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); - $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); - echo h($filepath); - echo '' . h($filename) . ''; - } else { - echo '' . h($filenameHash[0]) . ''; - } - if (isset($filenameHash[1])) echo '
    ' . $filenameHash[1]; - } - } else if (strpos($object['type'], '|') !== false) { - $filenameHash = explode('|', $object['value']); - echo h($filenameHash[0]); - if (isset($filenameHash[1])) { - $separator = '
    '; - if (in_array($object['type'], array('ip-dst|port', 'ip-src|port'))) { - $separator = ':'; - } - echo $separator . h($filenameHash[1]); - } - } else if ('vulnerability' == $object['type']) { - if (! is_null(Configure::read('MISP.cveurl'))) { - $cveUrl = Configure::read('MISP.cveurl'); - } else { - $cveUrl = "http://www.google.com/search?q="; - } - echo $this->Html->link($sigDisplay, $cveUrl . $sigDisplay, array('target' => '_blank', 'class' => $linkClass)); - } else if ('link' == $object['type']) { - echo $this->Html->link($sigDisplay, $sigDisplay, array('class' => $linkClass)); - } else if ('cortex' == $object['type']) { - echo '
    Cortex object
    '; - } else if ('text' == $object['type']) { - if ($object['category'] == 'External analysis' && preg_match('/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i', $object['value'])) { - echo '' . h($object['value']) . ''; - } else { - $sigDisplay = str_replace("\r", '', h($sigDisplay)); - $sigDisplay = str_replace(" ", ' ', $sigDisplay); - echo nl2br($sigDisplay); - } - } else if ('hex' == $object['type']) { - $sigDisplay = str_replace("\r", '', $sigDisplay); - echo '' . nl2br(h($sigDisplay)) . ' '; - } else { - $sigDisplay = str_replace("\r", '', $sigDisplay); - echo nl2br(h($sigDisplay)); - } - if (isset($object['validationIssue'])) echo '  '; - ?> -
    - 0, 2 => 1); - $valueParts = explode('|', $object['value']); - foreach ($components as $component => $valuePart) { - if (isset($object['warnings'][$component]) && isset($valueParts[$valuePart])) { - foreach ($object['warnings'][$component] as $warning) $temp .= '' . h($valueParts[$valuePart]) . ': ' . h($warning) . '
    '; - } - } - echo '  '; - } - ?> -
    - - - -
    - element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['AttributeTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']) )); ?> -
    - -   - - - -
    -
    -   -
    - - - - > - - -   - - -
      - $relatedAttribute['info'], 'Correlating Value' => $relatedAttribute['value'], 'date' => isset($relatedAttribute['date']) ? $relatedAttribute['date'] : 'N/A'); - $popover = ''; - foreach ($relatedData as $k => $v) { - $popover .= '' . h($k) . ': ' . h($v) . '
      '; - } - echo '
    • '; - if ($relatedAttribute['org_id'] == $me['org_id']) { - echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array ('class' => 'red')); - } else { - echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array ('class' => $otherColour)); - } - echo "
    • "; - echo ' '; - } - } - ?> -
    - - -
      - $v): - if ($k == 'id') continue; - $popover .= '' . Inflector::humanize(h($k)) . ': ' . h($v) . '
      '; - endforeach; - ?> -
    • - Html->link($feed['id'], array('controller' => 'feeds', 'action' => 'previewIndex', $feed['id']), array('style' => 'margin-right:3px;')); - else: - ?> - - -
    • - -
    - - -
    -
    - -
    - - - -
    -
    class="inline-field-solid" ondblclick="activateField('', '', 'distribution', );"> - - -   -
    - - - - - Form->create('Sighting', array('id' => 'Sighting_' . $object['id'], 'url' => '/sightings/add/' . $object['id'], 'style' => 'display:none;')); - echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $object['id'] . '_type')); - echo $this->Form->end(); - ?> - - -   -   -   - - - - - ' . h($s) . '/' . h($f) . '/' . h($e) . ')'; ?> - - - - - element('sparkline', array('id' => $object['id'], 'csv' => $temp)); - } - ?> - - - - - - - -   - - - - - - -   - -   - - C - - - - Form->create('Shadow_Attribute', array('id' => 'ShadowAttribute_' . $object['id'] . '_accept', 'url' => '/shadow_attributes/accept/' . $object['id'], 'style' => 'display:none;')); - echo $this->Form->end(); - ?> - - - - - - - element('/Events/View/row_' . $object['objectType'], array( + 'object' => $object, + 'k' => $k, + 'mayModify' => $mayModify, + 'mayChangeCorrelation' => $mayChangeCorrelation, + 'page' => $page, + 'fieldCount' => $fieldCount + )); + } ?>
    diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index ae7795519..4a176ba8a 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -272,7 +272,6 @@ td.highlight1 { td.highlight2 { background-color: #747170 !important; color: #ffffff !important; - } td.highlight3 { @@ -1714,6 +1713,143 @@ table.table.table-striped tr.deleted_row td { background-color:#f4f4f4; } +tr.tableInsetOrangeFirst td:first-child { + box-shadow: + inset 0px 2px 0px 0px #f57900, + inset 3px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeFirst td { + box-shadow: + inset 0px 2px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeFirst td:last-child { + box-shadow: + inset 0px 2px 0px 0px #f57900, + inset -2px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeMiddle td:first-child { + box-shadow: + inset 3px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeMiddle td:last-child { + box-shadow: + inset -3px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeLast td:first-child { + box-shadow: + inset 0px -3px 0px 0px #f57900, + inset 3px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeLast td { + box-shadow: + inset 0px -3px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrangeLast td:last-child { + box-shadow: + inset 0px -3px 0px 0px #f57900, + inset -3px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrange td:first-child { + box-shadow: + inset 0px 2px 0px 0px #f57900, + inset 0px -3px 0px 0px #f57900, + inset 2px 0px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrange td { + box-shadow: + inset 0px 2px 0px 0px #f57900, + inset 0px -3px 0px 0px #f57900; + border-top:0px !important; +} + +tr.tableInsetOrange td:last-child { + box-shadow: + inset 0px 2px 0px 0px #f57900, + inset 0px -3px 0px 0px #f57900, + inset -2px 0px 0px 0px #f57900; + border-top:0px !important; +} + +.tableHighlightBorder { + border-radius: 3px !important; + border-width: 3px; + border-style: solid; +} + +.tableHighlightBorderTop { + border-radius: 3px !important; + border-width: 3px; + border-style: solid; + border-bottom: 0px; +} + +.tableHighlightBorderCenter { + border-radius: 3px !important; + border-width: 3px; + border-style: solid; + border-bottom: 0px; + border-top: 0px; +} + +.tableHighlightBorderBottom { + border-radius: 3px !important; + border-width: 3px; + border-style:solid; + border-top: 0px; +} + +tr.tableHighlightBorderBottom td { + border-top:0px !important; +} + +.borderRed { + border-color:#cc0000; +} + +.borderOrange { + border-color:#f57900; +} + +.borderBlue { + border-color:#3465a4; +} + +tr.darkOrangeRow td, .darkOrangeElement { + background-color: #ce5c00 !important; + color: #ffffff; + border-top:0px !important; +} + +tr.blueRow td, .blueElement { + background-color: #3465a4 !important; + color: #ffffff; + border-top:0px !important; +} + +tr.redRow td, .redElement { + background-color: #cc0000 !important; + color: #ffffff; + border-top:0px !important; +} + @-webkit-keyframes rotation { from {-webkit-transform: rotate(0deg);} to {-webkit-transform: rotate(359deg);} From dd20e9853623cf999f52a2130faf39846ebe2253 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 13:46:23 +0200 Subject: [PATCH 18/77] chg: Updated object definitions --- app/files/misp-objects | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/misp-objects b/app/files/misp-objects index 92fbb3861..113eb9e5a 160000 --- a/app/files/misp-objects +++ b/app/files/misp-objects @@ -1 +1 @@ -Subproject commit 92fbb386168f6dea9e1e885f5f1f585a11925b86 +Subproject commit 113eb9e5a027b5cb00ad42ee87fd3f5c4d226599 From 04ffe988a51313d12e7c29bccd25255ad26d0a88 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 14:23:35 +0200 Subject: [PATCH 19/77] fix: Missing field in object template elements added to match upgrade script --- INSTALL/MYSQL.sql | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 9ed491667..f6b19badd 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -498,22 +498,23 @@ CREATE TABLE IF NOT EXISTS object_templates ( -- Table structure for table `object_template_elements` -- -CREATE TABLE IF NOT EXISTS object_template_elements ( +CREATE TABLE IF NOT EXISTS object_templates ( `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `org_id` int(11) NOT NULL, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_bin, `version` int(11) NOT NULL, - `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `ui-priority` int(11) NOT NULL, - `categories` text COLLATE utf8_bin, - `sane_default` text COLLATE utf8_bin, - `values_list` text COLLATE utf8_bin, - `disable_correlation` tinyint(1) NOT NULL DEFAULT 0, - `multiple` tinyint(1) NOT NULL DEFAULT 0, + `requirements` text COLLATE utf8_bin, + `fixed` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), + INDEX `user_id` (`user_id`), + INDEX `org_id` (`org_id`), INDEX `uuid` (`uuid`), - INDEX `in-object-name` (`in-object-name`), - INDEX `type` (`type`) + INDEX `name` (`name`), + INDEX `meta-category` (`meta-category`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- From 1bf51908ea0e2d50db29383771a52bfb09ef7068 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 14:26:11 +0200 Subject: [PATCH 20/77] fix: Fixed previous commit --- INSTALL/MYSQL.sql | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index f6b19badd..bd7922e6f 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -498,23 +498,20 @@ CREATE TABLE IF NOT EXISTS object_templates ( -- Table structure for table `object_template_elements` -- -CREATE TABLE IF NOT EXISTS object_templates ( +CREATE TABLE IF NOT EXISTS object_template_elements ( `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) NOT NULL, - `org_id` int(11) NOT NULL, - `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `meta-category` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `description` text COLLATE utf8_bin, - `version` int(11) NOT NULL, - `requirements` text COLLATE utf8_bin, - `fixed` tinyint(1) NOT NULL DEFAULT 0, + `object_template_id` int(11) NOT NULL, + `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `ui-priority` int(11) NOT NULL, + `categories` text COLLATE utf8_bin, + `sane_default` text COLLATE utf8_bin, + `values_list` text COLLATE utf8_bin, + `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, + `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `user_id` (`user_id`), - INDEX `org_id` (`org_id`), - INDEX `uuid` (`uuid`), - INDEX `name` (`name`), - INDEX `meta-category` (`meta-category`) + INDEX `in-object-name` (`in-object-name`), + INDEX `type` (`type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -------------------------------------------------------- From 48423fa1810ec0d631a1faae44ab3efd609e9aba Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 14:44:33 +0200 Subject: [PATCH 21/77] fix: Added description field to object template elements --- INSTALL/MYSQL.sql | 1 + app/Model/AppModel.php | 1 + 2 files changed, 2 insertions(+) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index bd7922e6f..9430c87d8 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -507,6 +507,7 @@ CREATE TABLE IF NOT EXISTS object_template_elements ( `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, + `description` text COLLATE utf8_bin, `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 6ca263238..e720d21bf 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -771,6 +771,7 @@ class AppModel extends Model { `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, `values_list` text COLLATE utf8_bin, + `description` text COLLATE utf8_bin, `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), From af4589547753f08adcdd544ab4fcf9b066f8046f Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 14:55:58 +0200 Subject: [PATCH 22/77] fix: Fixed event view issue for empty events --- app/Model/Event.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index ab80d4433..0baca83e7 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3261,6 +3261,7 @@ class Event extends AppModel { $eventWarnings = array(); $correlatedAttributes = isset($event['RelatedAttribute']) ? array_keys($event['RelatedAttribute']) : array(); $correlatedShadowAttributes = isset($event['RelatedShadowAttribute']) ? array_keys($event['RelatedShadowAttribute']) : array(); + $event['objects'] = array(); foreach ($event['Attribute'] as $attribute) { $result = $this->__prepareAttributeForView( $attribute, @@ -3284,7 +3285,6 @@ class Event extends AppModel { ); $event['objects'][] = $result['data']; } - unset($event['ShadowAttribute']); } if (!empty($event['Object'])) { foreach ($event['Object'] as $object) { @@ -3299,8 +3299,8 @@ class Event extends AppModel { ); $event['objects'][] = $result['data']; } - unset($event['Object']); } + unset($event['Object']); unset($event['ShadowAttribute']); App::uses('CustomPaginationTool', 'Tools'); $customPagination = new CustomPaginationTool(); From 33c5a80fd58b83cc4eae06e0759fe11b80ad5621 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 15:20:15 +0200 Subject: [PATCH 23/77] chg: Added empty row after each object / attribute-proposal block --- app/View/Elements/eventattribute.ctp | 15 +++++++++------ app/webroot/css/main.css | 9 +++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp index 20967cb1a..9bf4b4e81 100644 --- a/app/View/Elements/eventattribute.ctp +++ b/app/View/Elements/eventattribute.ctp @@ -182,12 +182,7 @@ 3 => 'object' ); foreach ($event['objects'] as $k => $object) { - $element_type = 0; - if ($object['objectType'] == 1 && !empty($object['proposal_to_delete'])) { - $element_type = 1; - } else if ($object['objectType'] == 3) { - $element_type = 2; - } + $insertBlank = false; echo $this->element('/Events/View/row_' . $object['objectType'], array( 'object' => $object, 'k' => $k, @@ -196,6 +191,14 @@ 'page' => $page, 'fieldCount' => $fieldCount )); + if ( + ($object['objectType'] == 'attribute' && !empty($object['ShadowAttribute'])) || + $object['objectType'] == 'object' + ): + ?> + + diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 157b3fefd..99a86c643 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1818,6 +1818,7 @@ tr.tableInsetOrange td:last-child { tr.tableHighlightBorderBottom td { border-top:0px !important; + margin-bottom:10px; } .borderRed { @@ -1850,6 +1851,14 @@ tr.redRow td, .redElement { border-top:0px !important; } +.blank_table_row { + height: 10px; +} + +tr.blank_table_row td { + background-color: #ffffff !important; +} + @-webkit-keyframes rotation { from {-webkit-transform: rotate(0deg);} to {-webkit-transform: rotate(359deg);} From 6f7529ef9272afe1b745a0d1b386f283c8ccdbd2 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 3 Aug 2017 17:56:55 +0200 Subject: [PATCH 24/77] new: Collapsible object metadata --- app/View/Elements/Events/View/row_object.ctp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp index 44d45846e..b69b3842d 100644 --- a/app/View/Elements/Events/View/row_object.ctp +++ b/app/View/Elements/Events/View/row_object.ctp @@ -4,7 +4,7 @@ $currentType = 'denyForm'; $tr_class = 'tableHighlightBorderTop borderBlue blueRow'; ?> - + @@ -12,10 +12,14 @@ - Name:
    - Meta-category:
    - Description:
    - Tempate: + Name: + +
    +
    + Meta-category:
    + Description:
    + Tempate: +
    Date: Sun, 6 Aug 2017 18:23:24 +0200 Subject: [PATCH 25/77] new: Progress on the Objects - Fixed UI elements in the event view - Added object-aware filtering to the event view - Objects can now be deleted and viewed once deleted - object sanitisation if the setting is set is implemented - Edit objects directly from the interface (if the template exists) - Various other fixes --- INSTALL/MYSQL.sql | 5 +- app/Controller/ObjectsController.php | 160 ++++++++++++++++-- app/Model/AppModel.php | 5 +- app/Model/Event.php | 95 +++++++++-- app/Model/MispObject.php | 1 + app/Model/ObjectReference.php | 2 +- .../Elements/Events/View/row_attribute.ctp | 8 +- app/View/Elements/Events/View/row_object.ctp | 32 +++- .../Elements/Events/View/row_proposal.ctp | 129 ++------------ .../Events/View/row_proposal_delete.ctp | 31 ++-- app/View/Elements/eventattribute.ctp | 2 +- app/View/Objects/ajax/delete.ctp | 25 +++ app/webroot/css/main.css | 9 + app/webroot/js/misp.js | 2 +- 14 files changed, 336 insertions(+), 170 deletions(-) create mode 100644 app/View/Objects/ajax/delete.ctp diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 9430c87d8..eda1269ff 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -435,6 +435,7 @@ CREATE TABLE IF NOT EXISTS objects ( `distribution` tinyint(4) NOT NULL DEFAULT 0, `sharing_group_id` int(11), `comment` text COLLATE utf8_bin NOT NULL, + `deleted` TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `name` (`name`), INDEX `template_uuid` (`template_uuid`), @@ -457,10 +458,12 @@ CREATE TABLE IF NOT EXISTS object_references ( `id` int(11) NOT NULL AUTO_INCREMENT, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, - `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `object_id` int(11) NOT NULL, + `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, + `deleted` TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 30fd38b87..1fb9a6a5f 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -117,7 +117,7 @@ class ObjectsController extends AppController { public function edit($id) { if (!$this->userRole['perm_modify']) { - throw new MethodNotAllowedException('You don\'t have permissions to create objects.'); + throw new MethodNotAllowedException('You don\'t have permissions to edit objects.'); } $object = $this->MispObject->find('first', array( 'conditions' => array('Object.id' => $id), @@ -182,7 +182,7 @@ class ObjectsController extends AppController { } } else { $this->Session->setFlash('Object saved.'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['id'])); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['event_id'])); } } } else { @@ -220,31 +220,165 @@ class ObjectsController extends AppController { $this->render('add'); } - public function delete($id) { + public function delete($id, $hard = false) { if (!$this->userRole['perm_modify']) { throw new MethodNotAllowedException('You don\'t have permissions to delete objects.'); } - if (Validation::uuid($eventId)) { + if (Validation::uuid($id)) { $lookupField = 'uuid'; - } else if (!is_numeric($eventId)) { + } else if (!is_numeric($id)) { $lookupField = 'id'; - throw new NotFoundException('Invalid event.'); + throw new NotFoundException('Invalid object.'); } - $event = $this->MispObject->Event->find('first', array( + $object = $this->MispObject->find('first', array( 'recursive' => -1, - 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'), - 'conditions' => array('Event.id' => $eventId) + 'fields' => array('Object.id', 'Object.event_id', 'Event.id', 'Event.uuid', 'Event.orgc_id'), + 'conditions' => array('Object.id' => $id), + 'contain' => array( + 'Event' + ) )); - if (empty($event)) { + if (empty($object)) { throw new NotFoundException('Invalid event.'); } - $eventId = $event['Event']['id']; - if (!$this->_isSiteAdmin() && ($event['Event']['orgc_id'] != $this->Auth->user('org_id') || !$this->userRole['perm_modify'])) { + $eventId = $object['Event']['id']; + if (!$this->_isSiteAdmin() && ($object['Event']['orgc_id'] != $this->Auth->user('org_id') || !$this->userRole['perm_modify'])) { throw new UnauthorizedException('You do not have permission to do that.'); } - $this->MispObject->delete($id); + if ($this->request->is('post')) { + if ($this->__delete($id, $hard)) { + $message = 'Object deleted.'; + if ($this->request->is('ajax')) { + return new CakeResponse( + array( + 'body'=> json_encode( + array( + 'saved' => true, + 'success' => $message + ) + ), + 'status'=>200 + ) + ); + } else if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse( + 'Objects', + 'delete', + $id, + $this->response->type() + ); + } else { + $this->Session->setFlash($message); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Event']['id'])); + } + } else { + $message = 'Object could not be deleted.'; + if ($this->request->is('ajax')) { + return new CakeResponse( + array( + 'body'=> json_encode( + array( + 'saved' => false, + 'errors' => $message + ) + ), + 'status'=>200) + ); + } else if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse( + 'Objects', + 'delete', + false, + $this->MispObject->validationErrors, + $this->response->type() + ); + } else { + $this->Session->setFlash($message); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Event']['id'])); + } + } + } else { + if ($this->request->is('ajax') && $this->request->is('get')) { + $this->set('hard', $hard); + $this->set('id', $id); + $this->set('event_id', $object['Event']['id']); + $this->render('ajax/delete'); + } + } } + private function __delete($id, $hard) { + $this->MispObject->id = $id; + if (!$this->MispObject->exists()) { + return false; + } + $object = $this->MispObject->find('first', array( + 'conditions' => array('Object.id' => $id), + 'fields' => array('Object.*'), + 'contain' => array( + 'Event' => array( + 'fields' => array('Event.*') + ), + 'Attribute' => array( + 'fields' => array('Attribute.*') + ) + ), + )); + if (empty($object)) throw new MethodNotAllowedException('Object not found or not authorised.'); + + // check for permissions + if (!$this->_isSiteAdmin()) { + if ($object['Event']['locked']) { + if ($this->Auth->user('org_id') != $object['Event']['org_id'] || !$this->userRole['perm_sync']) { + throw new MethodNotAllowedException('Object not found or not authorised.'); + } + } else { + if ($this->Auth->user('org_id') != $object['Event']['orgc_id']) { + throw new MethodNotAllowedException('Object not found or not authorised.'); + } + } + } + $date = new DateTime(); + if ($hard) { + // For a hard delete, simply run the delete, it will cascade + $this->MispObject->delete($id); + return true; + } else { + // For soft deletes, sanitise the object first if the setting is enabled + if (Configure::read('Security.sanitise_attribute_on_delete')) { + $object['Object']['name'] = 'N/A'; + $object['Object']['category'] = 'N/A'; + $object['Object']['description'] = 'N/A'; + $object['Object']['template_uuid'] = 'N/A'; + $object['Object']['template_version'] = 0; + $object['Object']['comment'] = ''; + } + $object['Object']['deleted'] = 1; + $object['Object']['timestamp'] = $date->getTimestamp(); + $this->MispObject->save($object); + foreach ($object['Attribute'] as $attribute) { + if (Configure::read('Security.sanitise_attribute_on_delete')) { + $attribute['category'] = 'Other'; + $attribute['type'] = 'comment'; + $attribute['value'] = 'deleted'; + $attribute['comment'] = ''; + $attribute['to_ids'] = 0; + } + $attribute['deleted'] = 1; + $attribute['timestamp'] = $date->getTimestamp(); + $this->MispObject->Attribute->save(array('Attribute' => $attribute)); + $this->MispObject->Event->ShadowAttribute->deleteAll( + array('ShadowAttribute.old_id' => $attribute['id']), + false + ); + } + $object['Event']['timestamp'] = $date->getTimestamp(); + $object['Event']['published'] = 0; + $this->MispObject->Event->save($object, array('fieldList' => array('published', 'timestamp', 'info'))); + return true; + } + } + public function view($id) { if ($this->_isRest()) { $objects = $this->MispObject->fetchObjects($this->Auth->user(), array('conditions' => array('Object.id' => $id))); diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index e720d21bf..03c75d457 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -716,6 +716,7 @@ class AppModel extends Model { `distribution` tinyint(4) NOT NULL DEFAULT 0, `sharing_group_id` int(11), `comment` text COLLATE utf8_bin NOT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `name` (`name`), INDEX `template_uuid` (`template_uuid`), @@ -732,10 +733,12 @@ class AppModel extends Model { `id` int(11) NOT NULL AUTO_INCREMENT, `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, - `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `object_id` int(11) NOT NULL, + `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, + `deleted` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), diff --git a/app/Model/Event.php b/app/Model/Event.php index 0baca83e7..97a183f55 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1338,7 +1338,7 @@ class Event extends AppModel { if ($options['sharing_group_id']) { $conditions['AND'][] = array('Event.sharing_group_id' => $options['sharing_group_id']); $conditionsAttributes['AND'][] = array('Attribute.sharing_group_id' => $options['sharing_group_id']); - $conditionsObject['AND'][] = array('Object.sharing_group_id' => $options['sharing_group_id']); + $conditionsObjects['AND'][] = array('Object.sharing_group_id' => $options['sharing_group_id']); } if ($options['from']) $conditions['AND'][] = array('Event.date >=' => $options['from']); if ($options['to']) $conditions['AND'][] = array('Event.date <=' => $options['to']); @@ -1361,7 +1361,7 @@ class Event extends AppModel { } } else { $conditionsAttributes['AND']['Attribute.deleted'] = 0; - $conditionsObject['AND']['Object.deleted'] = 0; + $conditionsObjects['AND']['Object.deleted'] = 0; } if ($options['idList'] && !$options['tags']) { @@ -3126,7 +3126,7 @@ class Event extends AppModel { $correlatedAttributes, $correlatedShadowAttributes, $filterType = false, - $eventWarnings, + &$eventWarnings, $warningLists ) { $attribute['objectType'] = 'attribute'; @@ -3148,15 +3148,18 @@ class Event extends AppModel { $result = $this->__prepareProposalForView( $proposal, $correlatedShadowAttributes, - false, + $filterType, $eventWarnings, $warningLists ); - $temp[] = $result['data']; + if ($result['include']) $temp[] = $result['data']; } $attribute['ShadowAttribute'] = $temp; } $attribute = $this->__prepareGenericForView($attribute, $eventWarnings, $warningLists); + if ($filterType === 'warning') { + if (empty($attribute['warnings'])) $include = false; + } return array('include' => $include, 'data' => $attribute); } @@ -3164,7 +3167,7 @@ class Event extends AppModel { $proposal, $correlatedShadowAttributes, $filterType = false, - $eventWarnings, + &$eventWarnings, $warningLists ) { if ($proposal['proposal_to_delete']) { @@ -3178,11 +3181,14 @@ class Event extends AppModel { $include = false; } if ($filterType && !in_array($filterType, array('proposal', 'correlation', 'warning'))) { - if (!in_array($attribute['type'], $this->Attribute->typeGroupings[$filterType])) { + if (!in_array($proposal['type'], $this->Attribute->typeGroupings[$filterType])) { $include = false; } } $proposal = $this->__prepareGenericForView($proposal, $eventWarnings, $warningLists); + if ($filterType === 'warning') { + if (empty($proposal['warnings'])) $include = false; + } return array('include' => $include, 'data' => $proposal); } @@ -3191,13 +3197,16 @@ class Event extends AppModel { $correlatedAttributes, $correlatedShadowAttributes, $filterType = false, - $eventWarnings, + &$eventWarnings, $warningLists ) { $object['category'] = $object['meta-category']; $proposal['objectType'] = 'object'; // filters depend on child objects - $include = empty($filterType) || $filterType == 'object'; + $include = empty($filterType) || $filterType == 'object' || $object['meta-category'] === $filterType; + if ($filterType === 'correlation' || $filterType === 'proposal') { + $include = $this->__checkObjectByFilter($object, $filterType, $correlatedAttributes, $correlatedShadowAttributes); + } if (!empty($object['Attribute'])) { $temp = array(); foreach ($object['Attribute'] as $k => $proposal) { @@ -3205,21 +3214,72 @@ class Event extends AppModel { $proposal, $correlatedAttributes, $correlatedShadowAttributes, - $filterType, + false, $eventWarnings, $warningLists ); - $include = $include || $object['include']; - $temp[] = $result['data']; + if ($result['include']) $temp[] = $result['data']; } $object['Attribute'] = $temp; } + if ($filterType === 'warning') { + $include = $this->__checkObjectByFilter($object, $filterType, $correlatedAttributes, $correlatedShadowAttributes); + } return array('include' => $include, 'data' => $object); } + private function __checkObjectByFilter($object, $filterType, $correlatedAttributes, $correlatedShadowAttributes) { + $include = false; + switch($filterType) { + case 'warning': + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $k => $attribute) { + if (!empty($attribute['warnings'])) { + $include = true; + } + if (!empty($attribute['ShadowAttribute'])) { + foreach ($attribute['ShadowAttribute'] as $shadowAttribute) { + if (!empty($shadowAttribute['warnings'])) { + $include = true; + } + } + } + } + } + break; + case 'correlation': + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $k => $attribute) { + if (in_array($attribute['id'], $correlatedAttributes)) { + $include = true; + } else { + if (!empty($attribute['ShadowAttribute'])) { + foreach ($attribute['ShadowAttribute'] as $k => $shadowAttribute) { + if (in_array($shadowAttribute['id'], $correlatedShadowAttributes)) { + $include = true; + } + } + } + } + } + } + break; + case 'proposal': + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $k => $attribute) { + if (!empty($attribute['ShadowAttribute'])) { + $include = true; + } + } + } + break; + } + return $include; + } + private function __prepareGenericForView( $object, - $eventWarnings, + &$eventWarnings, $warningLists ) { if (!$this->__fTool) { @@ -3236,7 +3296,7 @@ class Event extends AppModel { } } } - $this->Warninglist->checkForWarning($object, $eventWarnings, $warningLists); + $object = $this->Warninglist->checkForWarning($object, $eventWarnings, $warningLists); return $object; } @@ -3271,7 +3331,7 @@ class Event extends AppModel { $eventWarnings, $warningLists ); - $event['objects'][] = $result['data']; + if ($result['include']) $event['objects'][] = $result['data']; } unset($event['Attribute']); if (!empty($event['ShadowAttribute'])) { @@ -3293,11 +3353,11 @@ class Event extends AppModel { $object, $correlatedAttributes, $correlatedShadowAttributes, - false, + $filterType, $eventWarnings, $warningLists ); - $event['objects'][] = $result['data']; + if ($result['include']) $event['objects'][] = $result['data']; } } unset($event['Object']); @@ -3307,6 +3367,7 @@ class Event extends AppModel { if ($all) $passedArgs['page'] = 0; $params = $customPagination->applyRulesOnArray($event['objects'], $passedArgs, 'events', 'category'); $params['total_elements'] = count($event['objects']); + $event['Event']['warnings'] = $eventWarnings; return $params; } diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 78189f03a..8a565de48 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -42,6 +42,7 @@ class MispObject extends AppModel { 'ObjectReference' => array( 'className' => 'ObjectReference', 'dependent' => true, + 'foreignKey' => 'object_id' ), ); diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 9f8c511e0..1a2b2a3e3 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -2,7 +2,7 @@ App::uses('AppModel', 'Model'); -class Object extends AppModel { +class ObjectReference extends AppModel { public $actsAs = array( 'Containable', 'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index b0f3fcf0d..45f4df1ef 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -2,9 +2,6 @@ $tr_class = ''; $linkClass = 'white'; $otherColour = 'blue'; - if (isset($object['deleted']) && $object['deleted']) { - $tr_class .= ' background-light-red'; - } if (!empty($child)) { if ($child === 'last' && empty($object['ShadowAttribute'])) { $tr_class .= ' tableHighlightBorderBottom borderBlue'; @@ -20,6 +17,9 @@ $tr_class .= ' tableHighlightBorderTop borderOrange'; } } + if (!empty($object['deleted'])) { + $tr_class .= ' deleted-attribute'; + } ?> _value_solid" class="inline-field-solid" > > element('/Events/View/value_field', array('object' => $object)); + echo $this->element('/Events/View/value_field', array('object' => $object, 'linkClass' => $linkClass)); ?> - - + + + + + + + + + + @@ -44,6 +58,18 @@     + + + + + +
    > > - '; - } else { - $t = ($object['objectType'] == 0 ? 'attributes' : 'shadow_attributes'); - $filenameHash = explode('|', nl2br(h($object['value']))); - if (strrpos($filenameHash[0], '\\')) { - $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); - $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); - echo h($filepath); - echo '' . h($filename) . ''; - } else { - echo '' . h($filenameHash[0]) . ''; - } - if (isset($filenameHash[1])) echo '
    ' . $filenameHash[1]; - } - } else if (strpos($object['type'], '|') !== false) { - $filenameHash = explode('|', $object['value']); - echo h($filenameHash[0]); - if (isset($filenameHash[1])) { - $separator = '
    '; - if (in_array($object['type'], array('ip-dst|port', 'ip-src|port'))) { - $separator = ':'; - } - echo $separator . h($filenameHash[1]); - } - } else if ('vulnerability' == $object['type']) { - if (! is_null(Configure::read('MISP.cveurl'))) { - $cveUrl = Configure::read('MISP.cveurl'); - } else { - $cveUrl = "http://www.google.com/search?q="; - } - echo $this->Html->link($sigDisplay, $cveUrl . $sigDisplay, array('target' => '_blank', 'class' => $linkClass)); - } else if ('link' == $object['type']) { - echo $this->Html->link($sigDisplay, $sigDisplay, array('class' => $linkClass)); - } else if ('cortex' == $object['type']) { - echo '
    Cortex object
    '; - } else if ('text' == $object['type']) { - if ($object['category'] == 'External analysis' && preg_match('/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i', $object['value'])) { - echo '' . h($object['value']) . ''; - } else { - $sigDisplay = str_replace("\r", '', h($sigDisplay)); - $sigDisplay = str_replace(" ", ' ', $sigDisplay); - echo nl2br($sigDisplay); - } - } else if ('hex' == $object['type']) { - $sigDisplay = str_replace("\r", '', $sigDisplay); - echo '' . nl2br(h($sigDisplay)) . ' '; - } else { - $sigDisplay = str_replace("\r", '', $sigDisplay); - echo nl2br(h($sigDisplay)); - } - if (isset($object['validationIssue'])) echo '  '; - ?> + element('/Events/View/value_field', array('object' => $object, 'linkClass' => $linkClass)); + ?>
    ' . h($valueParts[$valuePart]) . ': ' . h($warning) . '
    '; } } - echo '  '; + echo '  '; } ?>
    @@ -185,28 +129,16 @@
      $relatedAttribute['info'], 'Correlating Value' => $relatedAttribute['value'], 'date' => isset($relatedAttribute['date']) ? $relatedAttribute['date'] : 'N/A'); $popover = ''; foreach ($relatedData as $k => $v) { $popover .= '' . h($k) . ': ' . h($v) . '
      '; } echo '
    • '; - if ($relatedAttribute['org_id'] == $me['org_id']) { - echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => 'red')); - } else { - echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => $otherColour)); - } + $correlationClass = 'white' . ($relatedAttribute['org_id'] == $me['org_id'] ? ' bold' : ''); + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => $correlationClass)); echo "
    • "; echo ' '; } @@ -255,45 +187,8 @@ - - - Form->create('Sighting', array('id' => 'Sighting_' . $object['id'], 'url' => '/sightings/add/' . $object['id'], 'style' => 'display:none;')); - echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $object['id'] . '_type')); - echo $this->Form->end(); - ?> - - -   -   -   - - - - - ' . h($s) . '/' . h($f) . '/' . h($e) . ')'; ?> - - - - - element('sparkline', array('id' => $object['id'], 'csv' => $temp)); - } - ?> - +   +   @@ -303,12 +198,12 @@ echo $this->Form->create('Shadow_Attribute', array('id' => 'ShadowAttribute_' . $object['id'] . '_accept', 'url' => '/shadow_attributes/accept/' . $object['id'], 'style' => 'display:none;')); echo $this->Form->end(); ?> - + - + diff --git a/app/View/Elements/Events/View/row_proposal_delete.ctp b/app/View/Elements/Events/View/row_proposal_delete.ctp index 694fe84a6..729ddd8b8 100644 --- a/app/View/Elements/Events/View/row_proposal_delete.ctp +++ b/app/View/Elements/Events/View/row_proposal_delete.ctp @@ -30,16 +30,25 @@ } ?> - - - - - - - - - + + + + + + + + + + + + + + DELETE Form->create('Shadow_Attribute', array('id' => 'ShadowAttribute_' . $object['id'] . '_accept', 'url' => '/shadow_attributes/accept/' . $object['id'], 'style' => 'display:none;')); echo $this->Form->end(); ?> - + - + diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp index 9bf4b4e81..23d154bb8 100644 --- a/app/View/Elements/eventattribute.ctp +++ b/app/View/Elements/eventattribute.ctp @@ -119,7 +119,7 @@
    Filters: -
    All (Paginator->params()['total_elements']); ?>)
    +
    All
    diff --git a/app/View/Objects/ajax/delete.ctp b/app/View/Objects/ajax/delete.ctp new file mode 100644 index 000000000..7f6e4c87e --- /dev/null +++ b/app/View/Objects/ajax/delete.ctp @@ -0,0 +1,25 @@ +
    +Form->create('Object', array('style' => 'margin:0px;', 'id' => 'PromptForm')); + if ($hard) $hard = '/true'; +?> +Object Deletion +
    +

    Are you sure you want to delete Object #?

    + + + + + + +
    + Yes + + + No +
    +
    +Form->end(); +?> +
    diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 99a86c643..131da0462 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1511,6 +1511,10 @@ a.proposal_link_red:hover { color: white !important; } +tr.deleted-attribute td { + background-color: #d3d7cf !important; +} + .background-deleted { background: repeating-linear-gradient( 45deg, @@ -1845,6 +1849,11 @@ tr.blueRow td, .blueElement { border-top:0px !important; } +tr.lightBlueRow td, .lightBlueElement { + background-color: #729fcf !important; + border-top:0px !important; +} + tr.redRow td, .redElement { background-color: #cc0000 !important; color: #ffffff; diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index fedb01ed7..0ed493e0d 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -4,7 +4,7 @@ String.prototype.ucfirst = function() { function deleteObject(type, action, id, event) { var destination = 'attributes'; - var alternateDestinations = ['shadow_attributes', 'template_elements', 'taxonomies']; + var alternateDestinations = ['shadow_attributes', 'template_elements', 'taxonomies', 'objects']; if (alternateDestinations.indexOf(type) > -1) destination = type; url = "/" + destination + "/" + action + "/" + id; $.get(url, function(data) { From 94fbfed48fa1862391f60b202d62e09eec7f8d50 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 6 Aug 2017 20:53:21 +0200 Subject: [PATCH 26/77] fix: Ommit object template elements with invalid attribute types - and warn users - shout out to all C-level managers at SHA2017 --- app/Model/MispObject.php | 19 ++++++++++++------- app/View/Objects/add.ctp | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 8a565de48..829aeed4c 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -266,15 +266,20 @@ class MispObject extends AppModel { return $a['ui-priority'] < $b['ui-priority']; }); foreach ($template['ObjectTemplateElement'] as $k => $v) { - $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; - $template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids']; - if (empty($template['ObjectTemplateElement'][$k]['categories'])) { - $template['ObjectTemplateElement'][$k]['categories'] = array(); - foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { - if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { - $template['ObjectTemplateElement'][$k]['categories'][] = $catk; + if (isset($this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']])) { + $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; + $template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids']; + if (empty($template['ObjectTemplateElement'][$k]['categories'])) { + $template['ObjectTemplateElement'][$k]['categories'] = array(); + foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { + if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { + $template['ObjectTemplateElement'][$k]['categories'][] = $catk; + } } } + } else { + $template['warnings'][] = 'Missing attribute type "' . $template['ObjectTemplateElement'][$k]['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['in-object-name'] . '") that would not pass validation due to this.'; + unset($template['ObjectTemplateElement'][$k]); } } return $template; diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 89af17665..bf9ba5a45 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -73,6 +73,20 @@
    + + Warning, issues found with the template: +
    + '; + } + ?> +
    + From 0e7dd2eddcf08acbce98392f8a4323548e3ab9e9 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 9 Aug 2017 17:53:25 +0200 Subject: [PATCH 27/77] new: Added first iteration of object references and other changes - various fixes - rework of the pagination library --- INSTALL/MYSQL.sql | 1 + app/Controller/EventsController.php | 14 ++ app/Controller/ObjectReferencesController.php | 156 ++++++++++++++++++ app/Controller/ObjectsController.php | 1 + app/Lib/Tools/CustomPaginationTool.php | 31 +++- app/Model/AppModel.php | 1 + app/Model/Event.php | 59 +++++-- app/Model/ObjectReference.php | 32 ++++ .../Elements/Events/View/row_attribute.ctp | 7 +- app/View/Elements/Events/View/row_object.ctp | 27 ++- .../Elements/Events/View/row_proposal.ctp | 3 +- .../Events/View/row_proposal_delete.ctp | 3 +- app/View/Elements/eventattribute.ctp | 20 ++- app/View/ObjectReferences/ajax/add.ctp | 93 +++++++++++ app/View/Objects/add.ctp | 5 +- app/webroot/css/main.css | 14 ++ app/webroot/js/misp.js | 106 ++++++++++-- 17 files changed, 521 insertions(+), 52 deletions(-) create mode 100644 app/Controller/ObjectReferencesController.php create mode 100644 app/View/ObjectReferences/ajax/add.ctp diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index eda1269ff..d1adb9fb7 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -459,6 +459,7 @@ CREATE TABLE IF NOT EXISTS object_references ( `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, `object_id` int(11) NOT NULL, + `event_id` int(11) NOT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 6b723af69..a3b73f84b 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -716,6 +716,9 @@ class EventsController extends AppController { } public function viewEventAttributes($id, $all = false) { + if (isset($this->params['named']['focus'])) { + $this->set('focus', $this->params['named']['focus']); + } $conditions = array('eventid' => $id); if (isset($this->params['named']['deleted']) && $this->params['named']['deleted']) { $conditions['deleted'] = 1; @@ -774,6 +777,16 @@ class EventsController extends AppController { $this->disableCache(); $this->layout = 'ajax'; $this->loadModel('Sighting'); + $uriArray = explode('/', $this->params->here); + foreach ($uriArray as $k => $v) { + if (strpos($v, ':')) { + $temp = explode(':', $v); + if ($temp[0] == 'focus') { + unset($uriArray[$k]); + } + } + $this->params->here = implode('/', $uriArray); + } $this->set('sightingTypes', $this->Sighting->type); $this->set('currentUri', $this->params->here); $this->render('/Elements/eventattribute'); @@ -894,6 +907,7 @@ class EventsController extends AppController { $this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings)); $this->loadModel('Sighting'); $this->set('sightingTypes', $this->Sighting->type); + $this->set('currentUri', '/events/viewEventAttributes/' . $event['Event']['id']); } public function view($id = null, $continue=false, $fromEvent=null) { diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php new file mode 100644 index 000000000..1bca62964 --- /dev/null +++ b/app/Controller/ObjectReferencesController.php @@ -0,0 +1,156 @@ + 20, + 'order' => array( + 'ObjectReference.id' => 'desc' + ), + ); + + public function add($objectId, $targetId = false, $targetType = false) { + if (Validation::uuid($objectId)) { + $temp = $this->ObjectReference->MispObject->find('first', array( + 'recursive' => -1, + 'fields' => array('Object.id'), + 'conditions' => array('Object.uuid' => $id) + )); + if (empty($temp)) throw new NotFoundException('Invalid Object'); + $objectId = $temp['Object']['id']; + } else if (!is_numeric($objectId)) { + throw new NotFoundException(__('Invalid object')); + } + $object = $this->ObjectReference->MispObject->find('first', array( + 'conditions' => array('Object.id' => $objectId), + 'recursive' => -1, + 'contain' => array( + 'Event' => array( + 'fields' => array('Event.id', 'Event.orgc_id') + ) + ) + )); + if (!$this->userRole['perm_add']) { + throw new MethodNotAllowedException('You don\'t have the required permissions to add object reference.'); + } + if (empty($object) || (!$this->_isSiteAdmin() && $object['Event']['orgc_id'] != $this->Auth->user('orgc_id'))) { + throw new MethodNotAllowedException('Invalid object.'); + } + $this->set('objectId', $objectId); + if ($this->request->is('post')) { + $data = array(); + if (!isset($this->request->data['ObjectReference'])) { + $this->request->data['ObjectReference'] = $this->request->data; + } + $referenced_type = 1; + $target_object = $this->ObjectReference->MispObject->find('first', array( + 'conditions' => array('Object.uuid' => $this->request->data['ObjectReference']['uuid']), + 'recursive' => -1, + 'fields' => array('Object.id', 'Object.uuid', 'Object.event_id') + )); + if (!empty($target_object)) { + $referenced_id = $target_object['Object']['id']; + if ($target_object['Object']['event_id'] != $object['Event']['id']) { + throw new NotFoundException('Invalid target. Target has to be within the same event.'); + } + } else { + $target_attribute = $this->ObjectReference->MispObject->Attribute->find('first', array( + 'conditions' => array('Attribute.uuid' => $this->request->data['ObjectReference']['uuid']), + 'recursive' => -1, + 'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.event_id') + )); + if (empty($target_attribute)) { + throw new NotFoundException('Invalid target.'); + } + if ($target_attribute['Attribute']['event_id'] != $object['Event']['id']) { + throw new NotFoundException('Invalid target. Target has to be within the same event.'); + } + $referenced_id = $target_attribute['Attribute']['id']; + $referenced_type = 0; + } + $data = array( + 'referenced_type' => $referenced_type, + 'referenced_id' => $referenced_id, + 'uuid' => $this->request->data['ObjectReference']['uuid'], + 'relationship_type' => !empty($this->request->data['ObjectReference']['relationship_type']) ? $this->request->data['ObjectReference']['relationship_type'] : '', + 'comment' => !empty($this->request->data['ObjectReference']['comment']) ? $this->request->data['ObjectReference']['comment'] : '', + 'event_id' => $object['Event']['id'], + 'object_id' => $objectId + ); + $data['referenced_type'] = $referenced_type; + $data['relationship_type'] = $this->request->data['ObjectReference']['relationship_type']; + $data['uuid'] = $this->request->data['ObjectReference']['uuid']; + $this->ObjectReference->create(); + $result = $this->ObjectReference->save(array('ObjectReference' => $data)); + if ($result) { + if ($this->_isRest()) { + $object = $this->ObjectReference->find("first", array( + 'recursive' => -1, + 'conditions' => array('ObjectReference' => $this->ObjectReference->id) + )); + return $this->RestResponse->viewData($object, $this->response->type()); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Object reference added.')),'status'=>200)); + } + } else { + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('ObjectReferences', 'add', false, $this->ObjectReference->validationErrors, $this->response->type()); + } else if ($this->request->is('ajax')) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Object reference could not be added.')),'status'=>200)); + } + } + } else { + if ($this->_isRest()) { + return $this->RestResponse->describe('ObjectReferences', 'add', false, $this->response->type()); + } else { + $event = $this->ObjectReference->MispObject->Event->find('first', array( + 'conditions' => array('Event.id' => $object['Event']['id']), + 'recursive' => -1, + 'fields' => array('Event.id'), + 'contain' => array( + 'Attribute' => array( + 'conditions' => array('Attribute.deleted' => 0, 'Attribute.object_id' => 0), + 'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids') + ), + 'Object' => array( + 'conditions' => array('Object.deleted' => 0), + 'conditions' => array('NOT' => array('Object.id' => $objectId)), + 'fields' => array('Object.id', 'Object.uuid', 'Object.name', 'Object.meta-category'), + 'Attribute' => array( + 'conditions' => array('Attribute.deleted' => 0), + 'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids') + ) + ) + ) + )); + $toRearrange = array('Attribute', 'Object'); + foreach ($toRearrange as $d) { + if (!empty($event[$d])) { + $temp = array(); + foreach ($event[$d] as $data) { + $temp[$data['uuid']] = $data; + } + $event[$d] = $temp; + } + } + $this->set('event', $event); + $this->set('objectId', $objectId); + $this->layout = 'ajax'; + $this->render('ajax/add'); + } + } + + } + + public function delete($id, $hard = false) { + + } + + public function view($id) { + + } +} diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 1fb9a6a5f..fc1e362a0 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -165,6 +165,7 @@ class ObjectsController extends AppController { } $objectToSave = $this->MispObject->attributeCleanup($this->request->data); $objectToSave = $this->MispObject->deltaMerge($object, $objectToSave); + // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return // to the add form diff --git a/app/Lib/Tools/CustomPaginationTool.php b/app/Lib/Tools/CustomPaginationTool.php index 4554f4153..bf62f4018 100644 --- a/app/Lib/Tools/CustomPaginationTool.php +++ b/app/Lib/Tools/CustomPaginationTool.php @@ -1,7 +1,7 @@ $model, 'current' => 1, @@ -16,7 +16,7 @@ class CustomPaginationTool { 'options' => array( ), ); - $validOptions = array('sort', 'direction', 'page'); + $validOptions = array('sort', 'direction', 'page', 'focus'); if ($model == 'events') $validOptions[] = 'attributeFilter'; foreach ($validOptions as $v) { if (isset($options[$v])) { @@ -40,23 +40,38 @@ class CustomPaginationTool { function truncateByPagination(&$items, $params) { if (empty($items)) return; - $items = array_slice($items, $params['current'] - 1, $params['current'] + $params['limit']); + $items = array_slice($items, $params['current'] - 1, $params['limit']); } - function applyRulesOnArray(&$items, $options, $model, $sort = 'id') { - $params = $this->createPaginationRules($items, $options, $model, $sort); + function applyRulesOnArray(&$items, $options, $model, $sort = 'id', $focusKey = 'uuid') { + $params = $this->createPaginationRules($items, $options, $model, $sort, $focusKey); if (isset($params['sort'])) { $sortArray = array(); foreach ($items as $k => $item) { - $sortArray[$k] = $item[$params['sort']]; + $sortArray[$k] = empty($item[$params['sort']]) ? '' : $item[$params['sort']]; } - asort($sortArray); + if (empty($params['options']['direction']) || $params['options']['direction'] == 'asc') { + asort($sortArray); + } else { + arsort($sortArray); + } + foreach ($sortArray as $k => $sortedElement) { $sortArray[$k] = $items[$k]; } $items = array(); $items = $sortArray; - //$items = Set::sort($items, '{n}.' . $params['sort'], $params['direction']); + } + $items = array_values($items); + if (!empty($params['options']['focus'])) { + foreach ($items as $k => $item) { + if ($item[$focusKey] == $params['options']['focus']) { + $params['page'] = 1 + intval(floor($k / $params['limit'])); + $params['current'] = 1 + ($params['page'] - 1) * 60; + continue; + } + } + unset($params['options']['focus']); } array_unshift($items, 'dummy'); unset($items[0]); diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 03c75d457..37ed73160 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -734,6 +734,7 @@ class AppModel extends Model { `uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `timestamp` int(11) NOT NULL DEFAULT 0, `object_id` int(11) NOT NULL, + `event_id` int(11) NOT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, diff --git a/app/Model/Event.php b/app/Model/Event.php index 97a183f55..6690aec9e 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1268,6 +1268,7 @@ class Event extends AppModel { if (isset($options['disableSiteAdmin']) && $options['disableSiteAdmin']) $isSiteAdmin = false; $conditionsAttributes = array(); $conditionsObjects = array(); + $conditionsObjectReferences = array(); if (isset($options['flattenObjects']) && $options['flattenObjects']) { $flattenObjects = true; @@ -1344,26 +1345,24 @@ class Event extends AppModel { if ($options['to']) $conditions['AND'][] = array('Event.date <=' => $options['to']); if ($options['last']) $conditions['AND'][] = array('Event.publish_timestamp >=' => $options['last']); if ($options['event_uuid']) $conditions['AND'][] = array('Event.uuid' => $options['event_uuid']); + + $softDeletables = array('Attribute', 'Object', 'ObjectReference'); if (isset($options['deleted']) && $options['deleted']) { if (!$user['Role']['perm_sync']) { - $conditionsAttributes['AND'][] = array( - 'OR' => array( - '(SELECT events.org_id FROM events WHERE events.id = Attribute.event_id)' => $user['org_id'], - 'Attribute.deleted' => 0 - ) - ); - $conditionsObjects['AND'][] = array( - 'OR' => array( - '(SELECT events.org_id FROM events WHERE events.id = Object.event_id)' => $user['org_id'], - 'Object.deleted' => 0 - ) - ); + foreach ($softDeletables as $softDeletable) { + ${'conditions' . $softDeletable . 's'}['AND'][] = array( + 'OR' => array( + '(SELECT events.org_id FROM events WHERE events.id = ' . $softDeletable . '.event_id)' => $user['org_id'], + $softDeletable . '.deleted' => 0 + ) + ); + } } } else { - $conditionsAttributes['AND']['Attribute.deleted'] = 0; - $conditionsObjects['AND']['Object.deleted'] = 0; + foreach ($softDeletables as $softDeletable) { + ${'conditions' . $softDeletable . 's'}['AND'][$softDeletable . '.deleted'] = 0; + } } - if ($options['idList'] && !$options['tags']) { $conditions['AND'][] = array('Event.id' => $options['idList']); } @@ -1446,7 +1445,11 @@ class Event extends AppModel { 'Object' => array( 'fields' => $fieldsObj, 'conditions' => $conditionsObjects, - 'order' => false + 'order' => false, + 'ObjectReference' => array( + 'conditions' => $conditionsObjectReferences, + 'order' => false + ) ), 'ShadowAttribute' => array( 'fields' => $fieldsShadowAtt, @@ -1472,6 +1475,7 @@ class Event extends AppModel { } $results = $this->find('all', $params); if (empty($results)) return array(); + // Do some refactoring with the event $sgsids = $this->SharingGroup->fetchAllAuthorised($user); if (Configure::read('Plugin.Sightings_enable') !== false) { @@ -1479,6 +1483,29 @@ class Event extends AppModel { } $userEmails = array(); foreach ($results as $eventKey => &$event) { + if (!empty($event['Object'])) { + foreach ($event['Object'] as $k => $object) { + if (!empty($object['ObjectReference'])) { + foreach ($object['ObjectReference'] as $k2 => $reference) { + $type = array('Attribute', 'Object')[$reference['referenced_type']]; + $temp = $this->{$type}->find('first', array( + 'recursive' => -1, + 'fields' => array('distribution', 'sharing_group_id', 'uuid'), + 'conditions' => array('id' => $reference['referenced_id']) + )); + if (!empty($temp)) { + if (!$isSiteAdmin && $user['User']['org_id'] != $event['Event']['orgc_id']) { + if ($temp[$type]['distribution'] == 0 || ($temp[$type]['distribution'] == 4 && !in_array($temp[$type]['sharing_group_id'], $sgsids))) { + unset($object['ObjectReference'][$k2]); + continue; + } + } + $event['Object'][$k]['ObjectReference'][$k2][$type] = $temp[$type]; + } + } + } + } + } if (!$options['sgReferenceOnly'] && $event['Event']['sharing_group_id']) { $event['SharingGroup'] = $sharingGroupData[$event['Event']['sharing_group_id']]['SharingGroup']; } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 1a2b2a3e3..0c9ef746b 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -12,9 +12,41 @@ class ObjectReference extends AppModel { ); public $belongsTo = array( + 'MispObject' => array( + 'className' => 'MispObject', + 'foreignKey' => 'object_id' + ), + 'ReferencedObject' => array( + 'className' => 'MispObject', + 'foreignKey' => false, + 'conditions' => array( + 'ReferencedObject.id' => 'ObjectReference.referenced_id', + 1 => 'ObjectReference.referenced_type' + ), + ), + 'ReferencedAttribute' => array( + 'className' => 'Attribute', + 'foreignKey' => false, + 'conditions' => array( + 'ReferencedAttribute.id' => 'ObjectReference.referenced_id', + 0 => 'ObjectReference.referenced_type' + ), + ) ); + public $validate = array( ); + + public function beforeValidate($options = array()) { + parent::beforeValidate(); + if (empty($this->data['ObjectReference']['uuid'])) { + $this->data['ObjectReference']['uuid'] = CakeText::uuid(); + } + $date = date('Y-m-d H:i:s'); + $this->data['Organisation']['timestamp'] = $date; + return true; + } + } diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index 45f4df1ef..15034c158 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -1,6 +1,6 @@ - + diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp index be9a8bd38..01cce0c07 100644 --- a/app/View/Elements/Events/View/row_object.ctp +++ b/app/View/Elements/Events/View/row_object.ctp @@ -5,8 +5,11 @@ $tr_class = 'tableHighlightBorderTop borderBlue'; if ($object['deleted']) $tr_class .= ' lightBlueRow'; else $tr_class .= ' blueRow'; + if (!empty($k)) { + $tr_class .= ' row_' . h($k); + } ?> - + @@ -27,13 +30,33 @@ +> +> diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp index 23d154bb8..1f8f31d13 100644 --- a/app/View/Elements/eventattribute.ctp +++ b/app/View/Elements/eventattribute.ctp @@ -37,7 +37,9 @@
    Save
    Name: - +
    Meta-category:
    Description:
    Tempate:
    + References: + + + + +
    + +   
    + +
    -
    diff --git a/app/View/Elements/Events/View/row_proposal_delete.ctp b/app/View/Elements/Events/View/row_proposal_delete.ctp index 729ddd8b8..26ead3d82 100644 --- a/app/View/Elements/Events/View/row_proposal_delete.ctp +++ b/app/View/Elements/Events/View/row_proposal_delete.ctp @@ -28,8 +28,9 @@ $tr_class .= ' tableHighlightBorder borderOrange'; } } + $identifier = (empty($k)) ? '' : ' id="row_' . h($k) . '" tabindex="0"'; ?> -
    + + + + + +
    + Submit +   + Cancel +
    +
    + Form->end(); + ?> +
    + +
    +
    + +Js->writeBuffer(); // Write cached scripts diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index bf9ba5a45..e74a33bdd 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -74,7 +74,7 @@ Warning, issues found with the template:
    @@ -121,7 +121,6 @@ 'label' => false, 'div' => false ); - if ($action == 'edit') unset($formSettings['value']); echo $this->Form->input('Attribute.' . $k . '.object_relation', $formSettings); if ($action == 'edit') { echo $this->Form->input('Attribute.' . $k . '.uuid', array( @@ -136,7 +135,6 @@ 'label' => false, 'div' => false ); - if ($action == 'edit') unset($formSettings['value']); echo $this->Form->input('Attribute.' . $k . '.type', $formSettings); echo '' . Inflector::humanize(h($element['in-object-name'])) . ''; if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['in-object-name'], $template['ObjectTemplate']['requirements']['required'])) { @@ -154,7 +152,6 @@ 'label' => false, 'div' => false ); - if ($action == 'edit') unset($formSettings['value']); echo $this->Form->input('Attribute.' . $k . '.category', $formSettings); ?> diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 131da0462..ec5a2b933 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1610,6 +1610,20 @@ a.discrete { border-radius:5px; } +.redHighlightedBlock { + border:1px solid red; + background-color:white; + padding:5px; + border-radius:5px; + height:228px; + overflow-y:scroll; + line-height:12px; +} + +.indent { + text-indent: 2em; +} + .tooltip-inner { white-space:pre-wrap !important; } diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 0ed493e0d..8f153be2f 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -877,6 +877,9 @@ function submitPopoverForm(context_id, referer, update_context_id) { url = "/sightings/add/" + context_id; closePopover = false; break; + case 'addObjectReference': + url = "/objectReferences/add/" + context_id; + break; } if (url !== null) { $.ajax({ @@ -902,7 +905,7 @@ function submitPopoverForm(context_id, referer, update_context_id) { $('#sightingsListAllToggle').removeClass('btn-inverse'); $('#sightingsListAllToggle').addClass('btn-primary'); } - if (context == 'event' && (referer == 'add' || referer == 'massEdit' || referer == 'replaceAttributes')) eventUnpublish(); + if (context == 'event' && (referer == 'add' || referer == 'massEdit' || referer == 'replaceAttributes' || referer == 'addObjectReference')) eventUnpublish(); $(".loading").hide(); }, type:"post", @@ -2560,6 +2563,25 @@ function filterAttributes(filter, id) { }); } +function pivotObjectReferences(url, uuid) { + url += '/focus:' + uuid; + console.log(url); + $.ajax({ + type:"get", + url:url, + beforeSend: function (XMLHttpRequest) { + $(".loading").show(); + }, + success:function (data) { + $("#attributes_div").html(data); + $(".loading").hide(); + }, + error:function() { + showMessage('fail', 'Something went wrong - could not fetch attributes.'); + } + }); +} + function toggleDeletedAttributes(url) { url = url.replace(/view\//i, 'viewEventAttributes/'); if (url.indexOf('deleted:') > -1) { @@ -3090,23 +3112,75 @@ function checkAndEnableCheckbox(id, enable) { } } - function enableDisableObjectRows(rows) { - rows.forEach(function(i) { - if ($("#Attribute" + i + "ValueSelect").length != 0) { - checkAndEnableCheckbox("#Attribute" + i + "Save", true); - } else if ($("#Attribute" + i + "Attachment").length != 0) { - checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); - } else { - checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Value").val() != ""); - } - $("#Attribute" + i + "Value").bind('input propertychange', function() { - checkAndEnableCheckbox("#Attribute" + i + "Save", $(this).val() != ""); - }); - $("#Attribute" + i + "Attachment").on('change', function() { - checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); - }); +function enableDisableObjectRows(rows) { + rows.forEach(function(i) { + if ($("#Attribute" + i + "ValueSelect").length != 0) { + checkAndEnableCheckbox("#Attribute" + i + "Save", true); + } else if ($("#Attribute" + i + "Attachment").length != 0) { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); + } else { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Value").val() != ""); + } + $("#Attribute" + i + "Value").bind('input propertychange', function() { + checkAndEnableCheckbox("#Attribute" + i + "Save", $(this).val() != ""); }); + $("#Attribute" + i + "Attachment").on('change', function() { + checkAndEnableCheckbox("#Attribute" + i + "Save", $("#Attribute" + i + "Attachment").val() != ""); + }); + }); +} + +function objectReferenceInput() { + var types = ["Attribute", "Object"]; + for (var type in types) { + for (var k in targetEvent[types[type]]) { + if (targetEvent[types[type]][k]['uuid'] == $('#ObjectReferenceUuid').val()) { + $('#targetSelect').val($('#ObjectReferenceUuid').val()); + changeObjectReferenceSelectOption(); + } + } } +} + +function changeObjectReferenceSelectOption() { + var object = $('#targetSelect option:selected'); + var uuid = $(object).val(); + $('#ObjectReferenceUuid').val(uuid); + var type = $(object).data('type'); + if (type == "Attribute") { + $('#targetData').html(""); + for (var k in targetEvent[type][uuid]) { + if ($.inArray(k, ['uuid', 'category', 'type', 'value', 'to_ids']) !== -1) { + $('#targetData').append('
    :
    '); + $('#' + uuid + '_' + k + '_key').text(k); + $('#' + uuid + '_' + k + '_data').text(targetEvent[type][uuid][k]); + } + } + } else { + $('#targetData').html(""); + for (var k in targetEvent[type][uuid]) { + if (k == 'Attribute') { + $('#targetData').append('
    Attributes:'); + for (attribute in targetEvent[type][uuid]['Attribute']) { + for (k2 in targetEvent[type][uuid]['Attribute'][attribute]) { + if ($.inArray(k2, ['category', 'type', 'value', 'to_ids']) !== -1) { + $('#targetData').append('
    :
    '); + $('#' + targetEvent[type][uuid]['Attribute'][attribute]['uuid'] + '_' + k2 + '_key').text(k2); + $('#' + targetEvent[type][uuid]['Attribute'][attribute]['uuid'] + '_' + k2 + '_data').text(targetEvent[type][uuid]['Attribute'][attribute][k2]); + } + } + $('#targetData').append('
    '); + } + } else { + if ($.inArray(k, ['name', 'uuid', 'meta-category']) !== -1) { + $('#targetData').append('
    :
    '); + $('#' + uuid + '_' + k + '_key').text(k); + $('#' + uuid + '_' + k + '_data').text(targetEvent[type][uuid][k]); + } + } + } + } +} (function(){ "use strict"; From e9caf3c180e05376c6594e9a2b9ff8dad184d535 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 10 Aug 2017 09:12:21 +0200 Subject: [PATCH 28/77] fix: Fixed an outdated index pointing to a now non-existant field --- INSTALL/MYSQL.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index d1adb9fb7..7a6ef267f 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -468,7 +468,7 @@ CREATE TABLE IF NOT EXISTS object_references ( PRIMARY KEY (id), INDEX `uuid` (`uuid`), INDEX `timestamp` (`timestamp`), - INDEX `referenced_uuid` (`referenced_uuid`), + INDEX `referenced_id` (`referenced_id`), INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; From ead2b9e1fd9a7f784e1315127ae7cefa2a865539 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 10 Aug 2017 11:11:33 +0200 Subject: [PATCH 29/77] fix: Various fixes --- app/Controller/AttributesController.php | 34 ++++++++++--- app/Controller/EventsController.php | 8 +++- app/Controller/ObjectsController.php | 10 ++-- app/Controller/ShadowAttributesController.php | 24 +++++++++- app/View/Attributes/edit.ctp | 48 ++++++++++++------- app/View/Events/view.ctp | 2 +- app/View/ShadowAttributes/edit.ctp | 12 +++-- 7 files changed, 104 insertions(+), 34 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 44552bacb..58d9a9522 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -729,7 +729,12 @@ class AttributesController extends AppController { // enabling / disabling the distribution field in the edit view based on whether user's org == orgc in the event $this->Event->read(); - if ($this->Attribute->save($this->request->data)) { + if ($this->Attribute->data['Attribute']['object_id']) { + $result = $this->Attribute->save($this->request->data, array('Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.comment', 'Attribute.distribution', 'Attribute.sharing_group_id')); + } else { + $result = $this->Attribute->save($this->request->data); + } + if ($result) { $this->Session->setFlash(__('The attribute has been saved')); // remove the published flag from the event $this->Event->set('timestamp', $date->getTimestamp()); @@ -760,7 +765,11 @@ class AttributesController extends AppController { $this->request->data = $this->Attribute->read(null, $id); } $this->set('attribute', $this->request->data); - + if ($this->request->data['Attribute']['object_id']) { + $this->set('objectAttribute', true); + } else { + $this->set('objectAttribute', false); + } // enabling / disabling the distribution field in the edit view based on whether user's org == orgc in the event $this->loadModel('Event'); $this->Event->id = $eventId; @@ -777,9 +786,6 @@ class AttributesController extends AppController { $types = $this->_arrayToValuesIndexArray($types); $this->set('types', $types); // combobox for categories - $categories = array_keys($this->Attribute->categoryDefinitions); - $categories = $this->_arrayToValuesIndexArray($categories); - $this->set('categories', $categories); $this->set('currentDist', $this->Event->data['Event']['distribution']); $this->loadModel('SharingGroup'); @@ -802,7 +808,23 @@ class AttributesController extends AppController { $this->set('info', $info); $this->set('attrDescriptions', $this->Attribute->fieldDescriptions); $this->set('typeDefinitions', $this->Attribute->typeDefinitions); - $this->set('categoryDefinitions', $this->Attribute->categoryDefinitions); + $categoryDefinitions = $this->Attribute->categoryDefinitions; + $categories = array_keys($this->Attribute->categoryDefinitions); + $categories = $this->_arrayToValuesIndexArray($categories); + if ($this->request->data['Attribute']['object_id']) { + foreach ($categoryDefinitions as $k => $v) { + if (!in_array($this->request->data['Attribute']['type'], $v['types'])) { + unset($categoryDefinitions[$k]); + } + } + foreach ($categories as $k => $v) { + if (!isset($categoryDefinitions[$k])) { + unset($categories[$k]); + } + } + } + $this->set('categories', $categories); + $this->set('categoryDefinitions', $categoryDefinitions); } // ajax edit - post a single edited field and this method will attempt to save it and return a json with the validation errors if they occur. diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 570bbf654..96795e31d 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -907,7 +907,13 @@ class EventsController extends AppController { $this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings)); $this->loadModel('Sighting'); $this->set('sightingTypes', $this->Sighting->type); - $this->set('currentUri', '/events/viewEventAttributes/' . $event['Event']['id']); + $attributeUri = '/events/viewEventAttributes/' . $event['Event']['id']; + foreach ($this->params->named as $k => $v) { + if (!is_numeric($k)) { + $attributeUri .= '/' . $v; + } + } + $this->set('currentUri', $attributeUri); } public function view($id = null, $continue=false, $fromEvent=null) { diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index fc1e362a0..b2128e79a 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -71,8 +71,10 @@ class ObjectsController extends AppController { } if (empty($error)) { $error = $this->MispObject->ObjectTemplate->checkTemplateConformity($template, $object); - if ($error === true) { + if ($error === false) { $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + } else { + $result = false; } if ($this->_isRest()) { if (is_numeric($result)) { @@ -86,8 +88,10 @@ class ObjectsController extends AppController { return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); } } else { - $this->Session->setFlash('Object saved.'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $eventId)); + if (is_numeric($result)) { + $this->Session->setFlash('Object saved.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $eventId)); + } } } } diff --git a/app/Controller/ShadowAttributesController.php b/app/Controller/ShadowAttributesController.php index e8f4bd3e7..5c8ede0e2 100644 --- a/app/Controller/ShadowAttributesController.php +++ b/app/Controller/ShadowAttributesController.php @@ -662,6 +662,10 @@ class ShadowAttributesController extends AppController { 'static' => array('old_id' => 'Attribute.id', 'uuid' => 'Attribute.uuid', 'event_id' => 'Attribute.event_id', 'event_uuid' => 'Event.uuid', 'event_org_id' => 'Event.orgc_id'), 'optional' => array('category', 'type', 'value', 'to_ids', 'comment') ); + if ($existingAttribute['object_id']) { + unset($fields['optional']['type']); + $fields['static']['type'] = 'Attribute.type'; + } } foreach ($fields['static'] as $k => $v) { $v = explode('.', $v); @@ -724,18 +728,36 @@ class ShadowAttributesController extends AppController { unset($types[$key]); } } + if ($existingAttribute['Attribute']['object_id']) { + $this->set('objectAttribute', true); + } else { + $this->set('objectAttribute', false); + } $types = $this->_arrayToValuesIndexArray($types); $this->set('types', $types); // combobox for categories $categories = $this->_arrayToValuesIndexArray(array_keys($this->ShadowAttribute->Event->Attribute->categoryDefinitions)); $categories = $this->_arrayToValuesIndexArray($categories); - $this->set('categories', $categories); foreach ($this->ShadowAttribute->Event->Attribute->categoryDefinitions as $key => $value) { $info['category'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']); } foreach ($this->ShadowAttribute->Event->Attribute->typeDefinitions as $key => $value) { $info['type'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']); } + + if ($existingAttribute['Attribute']['object_id']) { + foreach ($categoryDefinitions as $k => $v) { + if (!in_array($existingAttribute['Attribute']['type'], $v['types'])) { + unset($categoryDefinitions[$k]); + } + } + foreach ($categories as $k => $v) { + if (!isset($categoryDefinitions[$k])) { + unset($categories[$k]); + } + } + } + $this->set('categories', $categories); $this->set('info', $info); $this->set('attrDescriptions', $this->ShadowAttribute->fieldDescriptions); $this->set('typeDefinitions', $this->ShadowAttribute->typeDefinitions); diff --git a/app/View/Attributes/edit.ctp b/app/View/Attributes/edit.ctp index 2b8bfc414..555749ba3 100644 --- a/app/View/Attributes/edit.ctp +++ b/app/View/Attributes/edit.ctp @@ -8,10 +8,14 @@ 'empty' => '(choose one)', 'label' => 'Category ' . $this->element('formInfo', array('type' => 'category')) )); - echo $this->Form->input('type', array( + $typeInputData = array( 'empty' => '(first choose category)', - 'label' => 'Type ' . $this->element('formInfo', array('type' => 'type')) - )); + 'label' => 'Type ' . $this->element('formInfo', array('type' => 'type')), + ); + if ($objectAttribute) { + $typeInputData[] = 'disabled'; + } + echo $this->Form->input('type', $typeInputData); ?>
    Form->input('to_ids', array( 'label' => 'for Intrusion Detection System', )); - echo $this->Form->input('batch_import', array( - 'type' => 'checkbox', - )); + if (!$objectAttribute) { + echo $this->Form->input('batch_import', array( + 'type' => 'checkbox', + )); + } ?>

    " id="warning-message">Warning: You are about to share data that is of a sensitive nature (Attribution / targeting data). Make sure that you are authorised to share this.

    @@ -103,18 +109,24 @@ $(document).ready(function() { else $('#SGContainer').hide(); }); - $("#AttributeCategory").on('change', function(e) { - formCategoryChanged('Attribute'); - if ($(this).val() === 'Attribution' || $(this).val() === 'Targeting data') { - $("#warning-message").show(); - } else { - $("#warning-message").hide(); - } - if ($(this).val() === 'Internal reference') { - $("#AttributeDistribution").val('0'); - $('#SGContainer').hide(); - } - }); + + $("#AttributeCategory").on('change', function(e) { + formCategoryChanged('Attribute'); + if ($(this).val() === 'Attribution' || $(this).val() === 'Targeting data') { + $("#warning-message").show(); + } else { + $("#warning-message").hide(); + } + if ($(this).val() === 'Internal reference') { + $("#AttributeDistribution").val('0'); + $('#SGContainer').hide(); + } + }); + $("#AttributeCategory, #AttributeType, #AttributeDistribution").change(function() { initPopoverContent('Attribute'); diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index ed100e368..15f4088cf 100644 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -177,7 +177,7 @@
    Activity
    element('sparkline', array('id' => $event['Event']['id'], 'csv' => $sightingsData['csv']['event'])); } else { echo ' '; diff --git a/app/View/ShadowAttributes/edit.ctp b/app/View/ShadowAttributes/edit.ctp index 674cb89a8..edb5c7f43 100644 --- a/app/View/ShadowAttributes/edit.ctp +++ b/app/View/ShadowAttributes/edit.ctp @@ -9,11 +9,15 @@ 'div' => 'input', 'label' => 'Category ' . $this->element('formInfo', array('type' => 'category')), )); + $typeInputData = array( + 'empty' => '(first choose category)', + 'label' => 'Type ' . $this->element('formInfo', array('type' => 'type')), + ); + if ($objectAttribute) { + $typeInputData[] = 'disabled'; + } if (!$attachment) { - echo $this->Form->input('type', array( - 'empty' => '(first choose category)', - 'label' => 'Type ' . $this->element('formInfo', array('type' => 'type')), - )); + echo $this->Form->input('type', $typeInputData); } ?>
    From 5d9cfe5d6c8b5439a403ee00a7accc4c5008800d Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 10 Aug 2017 14:12:47 +0200 Subject: [PATCH 30/77] fix: Updated the new ajax methods to follow the new JSON rules --- app/Controller/ObjectReferencesController.php | 6 +++--- app/Controller/ObjectsController.php | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 1bca62964..77e9fa1e2 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -93,14 +93,14 @@ class ObjectReferencesController extends AppController { 'conditions' => array('ObjectReference' => $this->ObjectReference->id) )); return $this->RestResponse->viewData($object, $this->response->type()); - } else { - return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Object reference added.')),'status'=>200)); + } else if ($this->request->is('ajax')) { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Object reference added.')),'status'=>200, 'type' => 'json')); } } else { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('ObjectReferences', 'add', false, $this->ObjectReference->validationErrors, $this->response->type()); } else if ($this->request->is('ajax')) { - return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Object reference could not be added.')),'status'=>200)); + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Object reference could not be added.')),'status'=>200, 'type' => 'json')); } } } else { diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index b2128e79a..ef34acb5d 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -262,7 +262,8 @@ class ObjectsController extends AppController { 'success' => $message ) ), - 'status'=>200 + 'status'=>200, + 'type' => 'json' ) ); } else if ($this->_isRest()) { @@ -281,13 +282,15 @@ class ObjectsController extends AppController { if ($this->request->is('ajax')) { return new CakeResponse( array( - 'body'=> json_encode( - array( - 'saved' => false, - 'errors' => $message - ) - ), - 'status'=>200) + 'body'=> json_encode( + array( + 'saved' => false, + 'errors' => $message + ) + ), + 'status'=>200, + 'type' => 'json' + ) ); } else if ($this->_isRest()) { return $this->RestResponse->saveFailResponse( From fb799fc61a2df8c1e5072a6237eb8b2800e10c18 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 10 Aug 2017 16:19:46 +0200 Subject: [PATCH 31/77] fix: Fixed the saving of objects --- app/Controller/ObjectsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index ef34acb5d..a98026819 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -71,7 +71,7 @@ class ObjectsController extends AppController { } if (empty($error)) { $error = $this->MispObject->ObjectTemplate->checkTemplateConformity($template, $object); - if ($error === false) { + if ($error === true) { $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); } else { $result = false; From fb357a8a785c35abacb13d3958e948af5f280539 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 11 Aug 2017 15:57:08 +0200 Subject: [PATCH 32/77] chg: Sane defaults set by pre-validation script as a fallback (attributes) --- app/Model/Attribute.php | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index b516bfdfe..6471c4c53 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -537,8 +537,6 @@ class Attribute extends AppModel { } } - if ($this->data['Attribute']['distribution'] != 4) $this->data['Attribute']['sharing_group_id'] = 0; - // update correlation... (only needed here if there's an update) if ($this->id || !empty($this->data['Attribute']['id'])) { $this->__beforeSaveCorrelation($this->data['Attribute']); @@ -634,9 +632,24 @@ class Attribute extends AppModel { $this->data['Attribute']['value'] = $result; } - // TODO: add explanatory comment - if (!isset($this->data['Attribute']['distribution']) || $this->data['Attribute']['distribution'] != 4) $this->data['Attribute']['sharing_group_id'] = 0; - if (!isset($this->data['Attribute']['distribution'])) $this->data['Attribute']['distribution'] = 5; + // Set defaults for when some of the mandatory fields don't have defaults + // These fields all have sane defaults either based on another field, or due to server settings + if (empty($this->data['Attribute']['distribution'])) { + $this->data['Attribute']['distribution'] = Configure::read('MISP.default_attribute_distribution'); + if ($this->data['Attribute']['distribution'] == 'event') { + $this->data['Attribute']['distribution'] = 5; + } + } + + if (!empty($this->data['Attribute']['type']) && empty($this->data['Attribute']['category'])) { + $this->data['Attribute']['category'] = $this->typeDefinitions[$this->data['Attribute']['type']]['default_category']; + } + + if (!isset($this->data['Attribute']['to_ids'])) { + $this->data['Attribute']['to_ids'] = $this->typeDefinitions[$this->data['Attribute']['type']]['to_ids']; + } + + if ($this->data['Attribute']['distribution'] != 4) $this->data['Attribute']['sharing_group_id'] = 0; // return true, otherwise the object cannot be saved return true; From 7b8e11379dfd272cacc6ae8d77ca70eed0995e85 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 11 Aug 2017 15:58:06 +0200 Subject: [PATCH 33/77] chg: Removed default distribution for attributes in object - tkaen over by the pre-validation script --- app/Model/MispObject.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 829aeed4c..220dcc7c7 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -321,9 +321,6 @@ class MispObject extends AppModel { $tmpfile->close(); } unset($attributes['Attribute'][$k]['save']); - if ($attribute['distribution'] != 4) { - $attributes['Attribute'][$k]['sharing_group_id'] = 0; - } } return $attributes; } From 859d6e1c469fe7e4d00c9950080c6e97cc86db13 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 21 Aug 2017 10:13:19 +0200 Subject: [PATCH 34/77] new: Added object relations --- INSTALL/MYSQL.sql | 16 ++++++ app/Controller/ObjectReferencesController.php | 21 ++++++- app/Controller/ObjectTemplatesController.php | 3 +- app/Controller/ObjectsController.php | 2 +- app/Model/AppModel.php | 11 ++++ app/Model/Event.php | 31 ++++++++++ app/Model/ObjectRelationship.php | 57 +++++++++++++++++++ app/Model/ObjectTemplate.php | 5 +- app/View/ObjectReferences/ajax/add.ctp | 21 ++++++- app/webroot/js/misp.js | 10 ++++ 10 files changed, 166 insertions(+), 11 deletions(-) create mode 100644 app/Model/ObjectRelationship.php diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 7a6ef267f..0dbfaaa30 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -474,6 +474,22 @@ CREATE TABLE IF NOT EXISTS object_references ( -- -------------------------------------------------------- +-- +-- Table structure for table `object_relationships` +-- + +CREATE TABLE IF NOT EXISTS object_relationships ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `version` int(11) NOT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_bin NOT NULL, + `format` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + -- -- Table structure for table `object_templates` -- diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 77e9fa1e2..480b50dda 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -13,7 +13,7 @@ class ObjectReferencesController extends AppController { ), ); - public function add($objectId, $targetId = false, $targetType = false) { + public function add($objectId) { if (Validation::uuid($objectId)) { $temp = $this->ObjectReference->MispObject->find('first', array( 'recursive' => -1, @@ -72,17 +72,20 @@ class ObjectReferencesController extends AppController { $referenced_id = $target_attribute['Attribute']['id']; $referenced_type = 0; } + $relationship_type = empty($this->request->data['ObjectReference']['relationship_type']) ? '' : $this->request->data['ObjectReference']['relationship_type']; + if (!empty($this->request->data['ObjectReference']['relationship_type_select']) && $this->request->data['ObjectReference']['relationship_type_select'] !== 'custom') { + $relationship_type = $this->request->data['ObjectReference']['relationship_type_select']; + } $data = array( 'referenced_type' => $referenced_type, 'referenced_id' => $referenced_id, 'uuid' => $this->request->data['ObjectReference']['uuid'], - 'relationship_type' => !empty($this->request->data['ObjectReference']['relationship_type']) ? $this->request->data['ObjectReference']['relationship_type'] : '', + 'relationship_type' => $relationship_type, 'comment' => !empty($this->request->data['ObjectReference']['comment']) ? $this->request->data['ObjectReference']['comment'] : '', 'event_id' => $object['Event']['id'], 'object_id' => $objectId ); $data['referenced_type'] = $referenced_type; - $data['relationship_type'] = $this->request->data['ObjectReference']['relationship_type']; $data['uuid'] = $this->request->data['ObjectReference']['uuid']; $this->ObjectReference->create(); $result = $this->ObjectReference->save(array('ObjectReference' => $data)); @@ -137,6 +140,18 @@ class ObjectReferencesController extends AppController { $event[$d] = $temp; } } + $this->loadModel('ObjectRelationship'); + $relationshipsTemp = $this->ObjectRelationship->find('all', array( + 'recursive' => -1 + )); + $relationships = array(); + $relationshipMetadata = array(); + foreach ($relationshipsTemp as $k => $v) { + $relationshipMetadata[$v['ObjectRelationship']['name']] = $v; + $relationships[$v['ObjectRelationship']['name']] = $v['ObjectRelationship']['name']; + } + $relationships['custom'] = 'custom'; + $this->set('relationships', $relationships); $this->set('event', $event); $this->set('objectId', $objectId); $this->layout = 'ajax'; diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index f8bc8f131..6f711cbad 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -98,6 +98,8 @@ class ObjectTemplatesController extends AppController { public function update() { $result = $this->ObjectTemplate->update($this->Auth->user()); + $this->loadModel('ObjectRelationship'); + $result2 = $this->ObjectRelationship->update(); $this->Log = ClassRegistry::init('Log'); $fails = 0; $successes = 0; @@ -157,6 +159,5 @@ class ObjectTemplatesController extends AppController { $this->Session->setFlash($message); } $this->redirect(array('controller' => 'ObjectTemplates', 'action' => 'index')); - } } diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index a98026819..8c072f37c 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -72,7 +72,7 @@ class ObjectsController extends AppController { if (empty($error)) { $error = $this->MispObject->ObjectTemplate->checkTemplateConformity($template, $object); if ($error === true) { - $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); } else { $result = false; } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 37ed73160..19f2cabd2 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -747,6 +747,17 @@ class AppModel extends Model { INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_relationships ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `version` int(11) NOT NULL, + `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `description` text COLLATE utf8_bin NOT NULL, + `format` text COLLATE utf8_bin NOT NULL, + PRIMARY KEY (id), + INDEX `name` (`name`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; + + $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_templates ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, diff --git a/app/Model/Event.php b/app/Model/Event.php index d33de9bd5..a780ecd04 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3675,4 +3675,35 @@ class Event extends AppModel { if (!empty($subcondition)) array_push ($conditions['AND'], $subcondition); return $conditions; } + + public function prepareEventForView() { + // workaround to get the event dates in to the attribute relations + $relatedDates = array(); + if (!empty($event['RelatedEvent'])) { + foreach ($event['RelatedEvent'] as $relation) { + $relatedDates[$relation['Event']['id']] = $relation['Event']['date']; + } + if (!empty($event['RelatedAttribute'])) { + foreach ($event['RelatedAttribute'] as $key => $relatedAttribute) { + foreach ($relatedAttribute as $key2 => $relation) { + $event['RelatedAttribute'][$key][$key2]['date'] = $relatedDates[$relation['id']]; + } + } + } + } + $dataForView = array( + 'Attribute' => array('attrDescriptions', 'typeDefinitions', 'categoryDefinitions', 'distributionDescriptions', 'distributionLevels', 'shortDist'), + 'Event' => array('fieldDescriptions') + ); + foreach ($dataForView as $m => $variables) { + if ($m === 'Event') { + $currentModel = $this->Event; + } else if ($m === 'Attribute') { + $currentModel = $this->Event->Attribute; + } + foreach ($variables as $alias => $variable) { + $this->set($alias, $currentModel->{$variable}); + } + } + } } diff --git a/app/Model/ObjectRelationship.php b/app/Model/ObjectRelationship.php new file mode 100644 index 000000000..ba48393b6 --- /dev/null +++ b/app/Model/ObjectRelationship.php @@ -0,0 +1,57 @@ + array( // TODO Audit, logable + 'userModel' => 'User', + 'userKey' => 'user_id', + 'change' => 'full'), + ); + + public $validate = array( + 'name' => array( + 'unique' => array( + 'rule' => 'isUnique', + 'message' => 'A relationship with this name already exists.' + ), + 'valueNotEmpty' => array( + 'rule' => array('valueNotEmpty'), + ), + ), + ); + + + public function beforeValidate($options = array()) { + parent::beforeValidate(); + return true; + } + + public function afterFind($results, $primary = false) { + foreach ($results as $k => $result) { + if (!empty($results[$k]['ObjectRelationship']['format'])) { + $results[$k]['ObjectRelationship']['format'] = json_decode($results[$k]['ObjectRelationship']['format'], true); + } + } + return $results; + } + + public function update() { + $relationsFile = APP . 'files/misp-objects/relationships/definition.json'; + if (file_exists($relationsFile)) { + $file = new File($relationsFile); + $relations = json_decode($file->read(), true); + if (!isset($relations['version'])) $relations['version'] = 1; + $this->deleteAll(array('version <' => $relations['version'])); + foreach ($relations['values'] as $k => $relation) { + $relation['format'] = json_encode($relation['format'], true); + $relation['version'] = $relations['version']; + $this->create(); + $this->save($relation); + } + } + return true; + } +} diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index fd0266507..f17b871c7 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -29,7 +29,7 @@ class ObjectTemplate extends AppModel { 'ObjectTemplateElement' => array( 'className' => 'ObjectTemplateElement', 'dependent' => true, - ), + ) ); public $validate = array( ); @@ -135,11 +135,10 @@ class ObjectTemplate extends AppModel { } if ($update_required) { $attribute = $existingTemplateElements[$k]; - unset($existingTemplateElements); $attribute['object_template_id'] = $id; $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); } - unset($existingTemplateElements[$k]); + if (isset($existingTemplateElements[$k])) unset($existingTemplateElements[$k]); } else { $this->ObjectTemplateElement->create(); $attribute['object_template_id'] = $id; diff --git a/app/View/ObjectReferences/ajax/add.ctp b/app/View/ObjectReferences/ajax/add.ctp index 5e5280497..a400604af 100644 --- a/app/View/ObjectReferences/ajax/add.ctp +++ b/app/View/ObjectReferences/ajax/add.ctp @@ -6,11 +6,23 @@
    Form->input('relationship_type', array( + echo $this->Form->input('relationship_type_select', array( 'label' => 'Relationship type', - 'style' => 'width:320px;' + 'options' => $relationships, + 'style' => 'width:334px;', + 'div' => false )); - ?> + ?> +
    Js->writeBuffer(); // Write cached scripts diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index d3fb080b6..ad7633952 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -3142,6 +3142,16 @@ function objectReferenceInput() { } } +function objectReferenceCheckForCustomRelationship() { + var relationship_type_field = $('#ObjectReferenceRelationshipTypeSelect option:selected'); + var relationship_type = $(relationship_type_field).val(); + if (relationship_type == 'custom') { + $('#ObjectReferenceRelationshipType').parent().removeClass('hidden'); + } else { + $('#ObjectReferenceRelationshipType').parent().addClass('hidden'); + } +} + function changeObjectReferenceSelectOption() { var object = $('#targetSelect option:selected'); var uuid = $(object).val(); From b4fa196fa35265700265cb9c6a4f897a7adf5026 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 21 Aug 2017 10:14:17 +0200 Subject: [PATCH 35/77] fix: Added missing object row change --- app/View/Elements/Events/View/row_object.ctp | 2 +- app/files/misp-objects | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp index 01cce0c07..2497f3ab4 100644 --- a/app/View/Elements/Events/View/row_object.ctp +++ b/app/View/Elements/Events/View/row_object.ctp @@ -52,7 +52,7 @@ foreach ($object['ObjectReference'] as $reference): $uuid = empty($reference['Object']) ? $reference['Attribute']['uuid'] : $reference['Object']['uuid']; ?> -   
    +   
    diff --git a/app/files/misp-objects b/app/files/misp-objects index 113eb9e5a..01a23c205 160000 --- a/app/files/misp-objects +++ b/app/files/misp-objects @@ -1 +1 @@ -Subproject commit 113eb9e5a027b5cb00ad42ee87fd3f5c4d226599 +Subproject commit 01a23c205cf2a27a2351ae3fcc0f3735a8b436b1 From 412b2bcfb1b5075f01cb36ae094bfe1bf94dea25 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 22 Aug 2017 10:59:39 +0200 Subject: [PATCH 36/77] fix: Fixed invalid lookup for adding object references --- app/Controller/ObjectReferencesController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 480b50dda..888495db5 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -93,7 +93,7 @@ class ObjectReferencesController extends AppController { if ($this->_isRest()) { $object = $this->ObjectReference->find("first", array( 'recursive' => -1, - 'conditions' => array('ObjectReference' => $this->ObjectReference->id) + 'conditions' => array('ObjectReference.id' => $this->ObjectReference->id) )); return $this->RestResponse->viewData($object, $this->response->type()); } else if ($this->request->is('ajax')) { From d3d6566b1616a3ac70de3d3fd59c17b8749eda76 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 23 Aug 2017 11:57:40 +0200 Subject: [PATCH 37/77] new: Various new features for the objects --- app/Controller/AppController.php | 2 +- app/Controller/EventsController.php | 8 +++ app/Controller/ObjectReferencesController.php | 54 ++++++++++++++++--- app/Controller/ShadowAttributesController.php | 4 +- app/Model/Attribute.php | 24 ++++++++- app/Model/Event.php | 7 ++- app/Model/ObjectReference.php | 18 ++++++- app/View/Elements/Events/View/row_object.ctp | 29 +++++++++- .../Elements/Events/View/row_proposal.ctp | 6 +-- .../Events/View/row_proposal_delete.ctp | 6 +-- app/webroot/css/main.css | 10 ++-- app/webroot/js/misp.js | 3 +- 12 files changed, 146 insertions(+), 25 deletions(-) diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 87b861897..47284941a 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -46,7 +46,7 @@ class AppController extends Controller { public $helpers = array('Utility'); - private $__queryVersion = '17'; + private $__queryVersion = '18'; public $pyMispVersion = '2.4.77'; public $phpmin = '5.6.5'; public $phprec = '7.0.16'; diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 4db64a0be..e12086a79 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -863,6 +863,14 @@ class EventsController extends AppController { unset($event['EventTag'][$k]); } } + if (!empty($event['Object'])) { + $objectReferences = array('Attribute' => array(), 'Object' => array()); + foreach ($event['Object'] as $objectKey => $object) { + if (!empty($object['ObjectReference'])) { + foreach ($object['ObjectReference'] as $refKey => $reference); + } + } + } $params = $this->Event->rearrangeEventForView($event); $this->params->params['paging'] = array($this->modelClass => $params); $this->set('event', $event); diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 888495db5..5e8194449 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -15,7 +15,7 @@ class ObjectReferencesController extends AppController { public function add($objectId) { if (Validation::uuid($objectId)) { - $temp = $this->ObjectReference->MispObject->find('first', array( + $temp = $this->ObjectReference->Object->find('first', array( 'recursive' => -1, 'fields' => array('Object.id'), 'conditions' => array('Object.uuid' => $id) @@ -25,7 +25,7 @@ class ObjectReferencesController extends AppController { } else if (!is_numeric($objectId)) { throw new NotFoundException(__('Invalid object')); } - $object = $this->ObjectReference->MispObject->find('first', array( + $object = $this->ObjectReference->Object->find('first', array( 'conditions' => array('Object.id' => $objectId), 'recursive' => -1, 'contain' => array( @@ -47,7 +47,7 @@ class ObjectReferencesController extends AppController { $this->request->data['ObjectReference'] = $this->request->data; } $referenced_type = 1; - $target_object = $this->ObjectReference->MispObject->find('first', array( + $target_object = $this->ObjectReference->Object->find('first', array( 'conditions' => array('Object.uuid' => $this->request->data['ObjectReference']['uuid']), 'recursive' => -1, 'fields' => array('Object.id', 'Object.uuid', 'Object.event_id') @@ -58,7 +58,7 @@ class ObjectReferencesController extends AppController { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } } else { - $target_attribute = $this->ObjectReference->MispObject->Attribute->find('first', array( + $target_attribute = $this->ObjectReference->Object->Attribute->find('first', array( 'conditions' => array('Attribute.uuid' => $this->request->data['ObjectReference']['uuid']), 'recursive' => -1, 'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.event_id') @@ -110,7 +110,7 @@ class ObjectReferencesController extends AppController { if ($this->_isRest()) { return $this->RestResponse->describe('ObjectReferences', 'add', false, $this->response->type()); } else { - $event = $this->ObjectReference->MispObject->Event->find('first', array( + $event = $this->ObjectReference->Object->Event->find('first', array( 'conditions' => array('Event.id' => $object['Event']['id']), 'recursive' => -1, 'fields' => array('Event.id'), @@ -162,7 +162,49 @@ class ObjectReferencesController extends AppController { } public function delete($id, $hard = false) { - + if (Validation::uuid($id)) { + $temp = $this->ObjectReference->find('first', array( + 'recursive' => -1, + 'fields' => array('ObjectReference.id'), + 'conditions' => array('ObjectReference.uuid' => $id) + )); + if (empty($temp)) throw new NotFoundException('Invalid object reference'); + $id = $temp['ObjectReference']['id']; + } else if (!is_numeric($id)) { + throw new NotFoundException(__('Invalid object reference')); + } + $objectReference = $this->ObjectReference->find('first', array( + 'conditions' => array('ObjectReference.id' => $id), + 'recursive' => -1, + 'contain' => array('Object' => array('Event')) + )); + if (empty($objectReference)) { + throw new MethodNotAllowedException('Invalid object reference.'); + } + if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != $objectReference['Object']['Event']['orgc_id']) { + throw new MethodNotAllowedException('Invalid object reference.'); + } + if ($this->request->is('post') || $this->request->is('put') || $this->request->is('delete')) { + $result = $this->ObjectReference->smartDelete($objectReference['ObjectReference']['id'], $hard); + if ($result === true) { + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('ObjectReferences', 'delete', $id, $this->response->type()); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Object reference deleted.')), 'status'=>200, 'type' => 'json')); + } + } else { + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('ObjectReferences', 'delete', $id, $result, $this->response->type()); + } else { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Object reference was not deleted.')), 'status'=>200, 'type' => 'json')); + } + } + } else { + $this->set('hard', $hard); + $this->set('id', $id); + $this->set('event_id', $objectReference['Object']['Event']['id']); + $this->render('ajax/delete'); + } } public function view($id) { diff --git a/app/Controller/ShadowAttributesController.php b/app/Controller/ShadowAttributesController.php index 5c8ede0e2..d18c573bc 100644 --- a/app/Controller/ShadowAttributesController.php +++ b/app/Controller/ShadowAttributesController.php @@ -662,7 +662,7 @@ class ShadowAttributesController extends AppController { 'static' => array('old_id' => 'Attribute.id', 'uuid' => 'Attribute.uuid', 'event_id' => 'Attribute.event_id', 'event_uuid' => 'Event.uuid', 'event_org_id' => 'Event.orgc_id'), 'optional' => array('category', 'type', 'value', 'to_ids', 'comment') ); - if ($existingAttribute['object_id']) { + if ($existingAttribute['Attribute']['object_id']) { unset($fields['optional']['type']); $fields['static']['type'] = 'Attribute.type'; } @@ -744,7 +744,7 @@ class ShadowAttributesController extends AppController { foreach ($this->ShadowAttribute->Event->Attribute->typeDefinitions as $key => $value) { $info['type'][$key] = array('key' => $key, 'desc' => isset($value['formdesc'])? $value['formdesc'] : $value['desc']); } - + $categoryDefinitions = $this->ShadowAttribute->Event->Attribute->categoryDefinitions; if ($existingAttribute['Attribute']['object_id']) { foreach ($categoryDefinitions as $k => $v) { if (!in_array($existingAttribute['Attribute']['type'], $v['types'])) { diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 9a04729d0..469e33480 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2240,6 +2240,28 @@ class Attribute extends AppModel { return $this->find('list', $params); } + public function fetchAttributesSimple($user, $options = array()) { + $params = array( + 'conditions' => $this->buildConditions($user), + 'fields' => array(), + 'recursive' => -1 + ); + if (isset($options['conditions'])) { + $params['conditions']['AND'][] = $options['conditions']; + } + if (isset($options['fields'])) { + $params['fields'] = $options['fields']; + } + $results = $this->find('all', array( + 'conditions' => $params['conditions'], + 'recursive' => -1, + 'fields' => $params['fields'], + 'sort' => false + )); + return $results; + + } + // Method that fetches all attributes for the various exports // very flexible, it's basically a replacement for find, with the addition that it restricts access based on user // options: @@ -2256,7 +2278,7 @@ class Attribute extends AppModel { 'Event' => array( 'fields' => array('id', 'info', 'org_id', 'orgc_id', 'uuid'), ), - ), + ) ); $params['contain']['AttributeTag'] = array('Tag' => array('conditions' => array())); if (empty($options['includeAllTags'])) $params['contain']['AttributeTag']['Tag']['conditions']['exportable'] = 1; diff --git a/app/Model/Event.php b/app/Model/Event.php index a780ecd04..8343eb220 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1482,6 +1482,11 @@ class Event extends AppModel { $this->Sighting = ClassRegistry::init('Sighting'); } $userEmails = array(); + $fields = array( + 'common' => array('distribution', 'sharing_group_id', 'uuid'), + 'Attribute' => array('value', 'type', 'category', 'to_ids'), + 'Object' => array('name', 'meta-category') + ); foreach ($results as $eventKey => &$event) { if (!empty($event['Object'])) { foreach ($event['Object'] as $k => $object) { @@ -1490,7 +1495,7 @@ class Event extends AppModel { $type = array('Attribute', 'Object')[$reference['referenced_type']]; $temp = $this->{$type}->find('first', array( 'recursive' => -1, - 'fields' => array('distribution', 'sharing_group_id', 'uuid'), + 'fields' => array_merge($fields['common'], $fields[array('Attribute', 'Object')[$reference['referenced_type']]]), 'conditions' => array('id' => $reference['referenced_id']) )); if (!empty($temp)) { diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 0c9ef746b..4d7d8be6b 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -12,7 +12,7 @@ class ObjectReference extends AppModel { ); public $belongsTo = array( - 'MispObject' => array( + 'Object' => array( 'className' => 'MispObject', 'foreignKey' => 'object_id' ), @@ -38,7 +38,6 @@ class ObjectReference extends AppModel { public $validate = array( ); - public function beforeValidate($options = array()) { parent::beforeValidate(); if (empty($this->data['ObjectReference']['uuid'])) { @@ -49,4 +48,19 @@ class ObjectReference extends AppModel { return true; } + public function smartDelete($id, $hard = false) { + if ($hard) { + return $this->delete($id); + } else { + $reference = $this->find('first', array( + 'conditions' => array('ObjectReference.id' => $id), + 'recursive' => -1 + )); + if (empty($reference)) return array('Invalid object reference.'); + $reference['ObjectReference']['deleted'] = 1; + $result = $this->save($reference); + if ($result) return true; + return $this->validationErrors; + } + } } diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp index 2497f3ab4..183d5af9f 100644 --- a/app/View/Elements/Events/View/row_object.ctp +++ b/app/View/Elements/Events/View/row_object.ctp @@ -39,7 +39,22 @@
    References: ' . $temp[1] . ''; + } + } + echo $refCount; if (!empty($object['ObjectReference'])): ?> @@ -50,9 +65,19 @@
    -   
    +    + +
    diff --git a/app/View/Elements/Events/View/row_proposal.ctp b/app/View/Elements/Events/View/row_proposal.ctp index 5d4759dec..f8fe4c30d 100644 --- a/app/View/Elements/Events/View/row_proposal.ctp +++ b/app/View/Elements/Events/View/row_proposal.ctp @@ -12,10 +12,10 @@ } else { $tr_class .= ' tableInsetOrange'; } - if ($objectContainer === true) { - $tr_class .= ' tableHighlightBorderCenter borderBlue'; - } else { + if ($child === 'last') { $tr_class .= ' tableHighlightBorderBottom borderBlue'; + } else { + $tr_class .= ' tableHighlightBorderCenter borderBlue'; } } else { if (!empty($child)) { diff --git a/app/View/Elements/Events/View/row_proposal_delete.ctp b/app/View/Elements/Events/View/row_proposal_delete.ctp index 26ead3d82..0550f0248 100644 --- a/app/View/Elements/Events/View/row_proposal_delete.ctp +++ b/app/View/Elements/Events/View/row_proposal_delete.ctp @@ -12,10 +12,10 @@ } else { $tr_class .= ' tableInsetOrange'; } - if ($objectContainer === true) { - $tr_class .= ' tableHighlightBorderCenter borderBlue'; - } else { + if ($child === 'last') { $tr_class .= ' tableHighlightBorderBottom borderBlue'; + } else { + $tr_class .= ' tableHighlightBorderCenter borderBlue'; } } else { if (!empty($child)) { diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index ec5a2b933..c8a3d9ee3 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1,5 +1,5 @@ /* bootstrap changes */ -body{ +body { font-size: 12px; } @@ -7,11 +7,11 @@ input, button, select, textarea{ } -input[type="file"]{ +input[type="file"] { margin-bottom: 10px; } -button .full-width{ +button .full-width { width:100%; } @@ -1882,6 +1882,10 @@ tr.blank_table_row td { background-color: #ffffff !important; } +.strikethrough { + text-decoration: line-through; +} + @-webkit-keyframes rotation { from {-webkit-transform: rotate(0deg);} to {-webkit-transform: rotate(359deg);} diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index e3aca37b4..3fb56eee8 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -4,8 +4,9 @@ String.prototype.ucfirst = function() { function deleteObject(type, action, id, event) { var destination = 'attributes'; - var alternateDestinations = ['shadow_attributes', 'template_elements', 'taxonomies', 'objects']; + var alternateDestinations = ['shadow_attributes', 'template_elements', 'taxonomies', 'objects', 'object_references']; if (alternateDestinations.indexOf(type) > -1) destination = type; + else destination = type; url = "/" + destination + "/" + action + "/" + id; $.get(url, function(data) { openPopup("#confirmation_box"); From 62ecb49f586d3eb237792a43cd4bd694fa7062c2 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 23 Aug 2017 14:05:34 +0200 Subject: [PATCH 38/77] fix: Fixed invalid variable name --- app/Controller/ObjectReferencesController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 5e8194449..91bbb1450 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -18,7 +18,7 @@ class ObjectReferencesController extends AppController { $temp = $this->ObjectReference->Object->find('first', array( 'recursive' => -1, 'fields' => array('Object.id'), - 'conditions' => array('Object.uuid' => $id) + 'conditions' => array('Object.uuid' => $objectId) )); if (empty($temp)) throw new NotFoundException('Invalid Object'); $objectId = $temp['Object']['id']; From c1ef6fe2b60ae87822db6f9b17f623c3adc43e89 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 23 Aug 2017 14:28:24 +0200 Subject: [PATCH 39/77] fix: Fixed array level mess-up derp --- app/Controller/ObjectsController.php | 9 ++++++++- app/Model/MispObject.php | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 8c072f37c..4b62fea74 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -58,6 +58,13 @@ class ObjectsController extends AppController { if (!isset($this->request->data['Attribute'])) { $this->request->data = array('Attribute' => $this->request->data); } + if (!isset($this->request->data['Object'])) { + $attributeTemp = $this->request->data['Attribute']; + unset($this->request->data['Attribute']); + $this->request->data = array('Object' => $this->request->data); + $this->request->data['Attribute'] = $attributeTemp; + unset($attributeTemp); + } $object = $this->MispObject->attributeCleanup($this->request->data); // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return @@ -85,7 +92,7 @@ class ObjectsController extends AppController { )); return $this->RestResponse->viewData($object, $this->response->type()); } else { - return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); + return $this->RestResponse->saveFailResponse('Objects', 'add', false, $error, $this->response->type()); } } else { if (is_numeric($result)) { diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 220dcc7c7..dd7e0d9f0 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -289,6 +289,7 @@ class MispObject extends AppModel { * Clean the attribute list up from artifacts introduced by the object form */ public function attributeCleanup($attributes) { + if (empty($attributes['Attribute'])) return 'No attribute data found'; foreach ($attributes['Attribute'] as $k => $attribute) { if (isset($attribute['save']) && $attribute['save'] == 0) { unset($attributes['Attribute'][$k]); From 78f49e5e62bca6531fdfb1cf0c0115ec62b45a22 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 24 Aug 2017 07:49:11 +0200 Subject: [PATCH 40/77] new: Added back referencing from a referenced object - also fixed some view file issues --- app/Controller/EventsController.php | 8 --- app/Model/Event.php | 19 +++++++ app/View/Elements/Events/View/row_object.ctp | 53 ++++--------------- .../Events/View/row_object_reference.ctp | 50 +++++++++++++++++ .../Events/View/row_object_referenced_by.ctp | 32 +++++++++++ app/View/ObjectReferences/ajax/delete.ctp | 29 ++++++++++ 6 files changed, 140 insertions(+), 51 deletions(-) create mode 100644 app/View/Elements/Events/View/row_object_reference.ctp create mode 100644 app/View/Elements/Events/View/row_object_referenced_by.ctp create mode 100644 app/View/ObjectReferences/ajax/delete.ctp diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index e12086a79..4db64a0be 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -863,14 +863,6 @@ class EventsController extends AppController { unset($event['EventTag'][$k]); } } - if (!empty($event['Object'])) { - $objectReferences = array('Attribute' => array(), 'Object' => array()); - foreach ($event['Object'] as $objectKey => $object) { - if (!empty($object['ObjectReference'])) { - foreach ($object['ObjectReference'] as $refKey => $reference); - } - } - } $params = $this->Event->rearrangeEventForView($event); $this->params->params['paging'] = array($this->modelClass => $params); $this->set('event', $event); diff --git a/app/Model/Event.php b/app/Model/Event.php index 8343eb220..98a63f04b 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3395,6 +3395,25 @@ class Event extends AppModel { } unset($event['Object']); unset($event['ShadowAttribute']); + $referencedObjectFields = array('meta-category', 'name', 'uuid', 'id'); + foreach ($event['objects'] as $object) { + if (!in_array($object['objectType'], array('attribute', 'object'))) continue; + if (!empty($object['ObjectReference'])) { + foreach ($object['ObjectReference'] as $reference) { + foreach ($event['objects'] as $k => $v) { + $referencedType = isset($reference['Attribute']) ? 'attribute' : 'object'; + if ($v['objectType'] == $referencedType && $reference['referenced_id'] == $v['id']) { + $temp = array(); + foreach ($referencedObjectFields as $field) { + $temp[$field] = $object[$field]; + } + $temp['relationship_type'] = $reference['relationship_type']; + $event['objects'][$k]['referenced_by'][$object['objectType']][] = $temp; + } + } + } + } + } App::uses('CustomPaginationTool', 'Tools'); $customPagination = new CustomPaginationTool(); if ($all) $passedArgs['page'] = 0; diff --git a/app/View/Elements/Events/View/row_object.ctp b/app/View/Elements/Events/View/row_object.ctp index 183d5af9f..90bf015fc 100644 --- a/app/View/Elements/Events/View/row_object.ctp +++ b/app/View/Elements/Events/View/row_object.ctp @@ -35,53 +35,20 @@
    Meta-category:
    Description:
    - Tempate: + Template:
    - References: ' . $temp[1] . ''; - } + echo $this->element('/Events/View/row_object_reference', array( + 'deleted' => $deleted, + 'object' => $object + )); + if (!empty($object['referenced_by'])) { + echo $this->element('/Events/View/row_object_referenced_by', array( + 'deleted' => $deleted, + 'object' => $object + )); } - echo $refCount; - if (!empty($object['ObjectReference'])): ?> - - - -
    - -    - -
    - -
    References: +' . $temp[1] . ''; + } + } + echo $refCount . ' '; + if (!empty($object['ObjectReference'])): +?> + + + +
    + +    + + + + +
    + +
    diff --git a/app/View/Elements/Events/View/row_object_referenced_by.ctp b/app/View/Elements/Events/View/row_object_referenced_by.ctp new file mode 100644 index 000000000..e0801b3fe --- /dev/null +++ b/app/View/Elements/Events/View/row_object_referenced_by.ctp @@ -0,0 +1,32 @@ +Referenced by: + + + +
    + $reference): + foreach ($reference as $ref): + if ($type == 'object') { + $uuid = $ref['uuid']; + $output = ' (' . $ref['meta-category'] . ': ' . $ref['name'] . ')'; + } else { + $uuid = $ref['uuid']; + $output = ' (' . $ref['category'] . '/' . $ref['type'] . ': "' . $ref['value'] . '")'; + } +?> +    + + + +
    + +
    diff --git a/app/View/ObjectReferences/ajax/delete.ctp b/app/View/ObjectReferences/ajax/delete.ctp new file mode 100644 index 000000000..f8eaa76a3 --- /dev/null +++ b/app/View/ObjectReferences/ajax/delete.ctp @@ -0,0 +1,29 @@ +
    +Form->create('ObjectReference', array('style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => $url)); + if ($hard) $hard = '/true'; +?> +Object reference Deletion +
    +

    Are you sure you want to delete Object reference #?

    + + + + + + +
    + Yes + + + No +
    +
    +Form->end(); +?> +
    From c8b0be9431c5c95fa72109118f63636ca16903a7 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 28 Aug 2017 09:18:40 +0200 Subject: [PATCH 41/77] new: Added first iteration of new add attachment functionality - still WIP --- app/Controller/AttributesController.php | 61 +++++++------- app/Model/Attribute.php | 79 ++++++++++++++++++- app/Model/MispObject.php | 45 +++++++++-- app/Model/ObjectReference.php | 42 ++++++++++ app/View/Attributes/add_attachment.ctp | 13 ++- .../Elements/Events/View/row_attribute.ctp | 8 ++ app/files/scripts/generate_file_objects.py | 66 ++++++++++++++++ 7 files changed, 270 insertions(+), 44 deletions(-) create mode 100644 app/files/scripts/generate_file_objects.py diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 22c3f5ff6..a5e3dfd2a 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -345,7 +345,6 @@ class AttributesController extends AppController { $success = 0; foreach ($this->request->data['Attribute']['values'] as $k => $value) { - // Check if there were problems with the file upload // only keep the last part of the filename, this should prevent directory attacks $filename = basename($value['name']); @@ -361,36 +360,34 @@ class AttributesController extends AppController { } if ($this->request->data['Attribute']['malware']) { - $result = $this->Event->Attribute->handleMaliciousBase64($this->request->data['Attribute']['event_id'], $filename, base64_encode($tmpfile->read()), array_keys($hashes)); - if (!$result['success']) { - $this->Session->setFlash(__('There was a problem to upload the file.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['Attribute']['event_id'])); - } - foreach ($hashes as $hash => $typeName) { - if (!$result[$hash]) continue; - $attribute = array( - 'Attribute' => array( - 'value' => $filename . '|' . $result[$hash], - 'category' => $this->request->data['Attribute']['category'], - 'type' => $typeName, - 'event_id' => $this->request->data['Attribute']['event_id'], - 'comment' => $this->request->data['Attribute']['comment'], - 'to_ids' => 1, - 'distribution' => $this->request->data['Attribute']['distribution'], - 'sharing_group_id' => isset($this->request->data['Attribute']['sharing_group_id']) ? $this->request->data['Attribute']['sharing_group_id'] : 0, - ) - ); - if ($hash == 'md5') $attribute['Attribute']['data'] = $result['data']; - $this->Attribute->create(); - $r = $this->Attribute->save($attribute); - if ($r == false) { - if ($hash == 'md5') { - $fails[] = $filename; - } else { - $partialFails[] = '[' . $typeName . ']' . $filename; + if ($this->request->data['Attribute']['advanced']) { + $execRetval = ''; + $execOutput = array(); + $result = shell_exec('python ' . APP . 'files/scripts/generate_file_objects.py -p ' . $tmpfile->path); + if (!empty($result)) { + $result = json_decode($result, true); + if (isset($result['objects'])) { + $result['Object'] = $result['objects']; + unset($result['objects']); } - } else { - if ($hash == 'md5') $success++; + } + } else { + $result = $this->Attribute->simpleAddMalwareSample( + $eventId, + $this->request->data['Attribute']['category'], + $this->request->data['Attribute']['distribution'], + $this->request->data['Attribute']['distribution'] == 4 ? $this->request->data['Attribute']['sharing_group_id'] : 0, + $this->request->data['Attribute']['comment'], + $filename, + $tmpfile + ); + } + if (!empty($result)) { + foreach ($result['Object'] as $object) { + foreach ($object['Attribute'] as $k => $attribute) { + if ($attribute['value'] == $tmpfile->name) $object['Attribute'][$k]['value'] = $value['name']; + } + $result = $this->Attribute->Object->captureObject($eventId, array('Object' => $object), $this->Auth->user()); } } } else { @@ -413,9 +410,9 @@ class AttributesController extends AppController { else $success++; } } - +throw new Exception(); $message = 'The attachment(s) have been uploaded.'; - if (!empty($partialFails)) $message .= ' Some of the hashes however could not be generated.'; + if (!empty($partialFails)) $message .= ' Some of the attributes however could not be created.'; if (!empty($fails)) $message = 'Some of the attachments failed to upload. The failed files were: ' . implode(', ', $fails) . ' - This can be caused by the attachments already existing in the event.'; if (empty($success)) { if (empty($fails)) $message = 'The attachment(s) could not be saved. please contact your administrator.'; diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 469e33480..eefe13d99 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -487,6 +487,10 @@ class Attribute extends AppModel { 'SharingGroup' => array( 'className' => 'SharingGroup', 'foreignKey' => 'sharing_group_id' + ), + 'Object' => array( + 'className' => 'MispObject', + 'foreignKey' => 'object_id' ) ); @@ -2466,7 +2470,7 @@ class Attribute extends AppModel { return true; } - public function saveAndEncryptAttribute($attribute, $user) { + public function saveAndEncryptAttribute($attribute, $user = false) { $hashes = array('md5' => 'malware-sample', 'sha1' => 'filename|sha1', 'sha256' => 'filename|sha256'); if ($attribute['encrypt']) { $result = $this->handleMaliciousBase64($attribute['event_id'], $attribute['value'], $attribute['data'], array_keys($hashes)); @@ -2662,4 +2666,77 @@ class Attribute extends AppModel { } return array('sgs' => $sgs, 'levels' => $distributionLevels, 'initial' => $initialDistribution); } + + public function simpleAddMalwareSample($event_id, $category, $distribution, $sharing_group_id, $comment, $filename, $tmpfile) { + $attributes = array( + 'malware-sample' => array('type' => 'malware-sample', 'data' => 1, 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), + 'filename' => array('type' => 'filename', 'category' => '', 'to_ids' => 0, 'disable_correlation' => 0), + 'md5' => array('type' => 'md5', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), + 'sha1' => array('type' => 'sha1', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), + 'sha256' => array('type' => 'sha256', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), + 'size-in-bytes' => array('type' => 'size-in-bytes', 'category' => '', 'to_ids' => 0, 'disable_correlation' => 1) + ); + $hashes = array('md5', 'sha1', 'sha256'); + $this->Object = ClassRegistry::init('Object'); + $this->ObjectTemplate = ClassRegistry::init('ObjectTemplate'); + $object_template = $this->ObjectTemplate->find('first', array( + 'conditions' => array( + 'ObjectTemplate.uuid' => '688c46fb-5edb-40a3-8273-1af7923e2215' + ), + 'recursive' => -1 + )); + if (empty($object_template)) { + $object_template = array( + 'ObjectTemplate' => array( + 'meta-category' => 'file', + 'name' => 'file', + 'template_uuid' => '688c46fb-5edb-40a3-8273-1af7923e2215', + 'version' => 1, + 'description' => 'File object describing a file with meta-information' + ) + ); + } + $object = array( + 'distribution' => $distribution, + 'sharing_group_id' => $sharing_group_id, + 'meta-category' => $object_template['ObjectTemplate']['meta-category'], + 'name' => $object_template['ObjectTemplate']['name'], + 'template_version' => $object_template['ObjectTemplate']['version'], + 'description' => $object_template['ObjectTemplate']['description'], + 'template_uuid' => $object_template['ObjectTemplate']['uuid'], + 'event_id' => $event_id, + 'comment' => $comment + ); + foreach ($attributes as $k => $v) { + $result = $this->Event->Attribute->handleMaliciousBase64($event_id, $filename, base64_encode($tmpfile->read()), $hashes); + $attribute = array( + 'distribution' => 5, + 'category' => $category, + 'type' => $v['type'], + 'to_ids' => $v['to_ids'], + 'disable_correlation' => $v['disable_correlation'], + 'object_id' => $this->Object->id, + 'event_id' => $event_id + ); + if (isset($v['data'])) { + $attribute['data'] = $result['data']; + } + if ($k == 'malware-sample') { + $attribute['value'] = $filename . '|' . $result['md5']; + } else if ($k == 'size-in-bytes') { + $attribute['value'] = 0; + } else if ($k == 'filename') { + $attribute['value'] = $filename; + } else { + $attribute['value'] = $result[$v['type']]; + } + $object['Attribute'][] = $attribute; + } + return array('Object' => $object); + } + + public function advancedAddMalwareSample() { + + } + } diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index dd7e0d9f0..b02ed2deb 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -51,20 +51,23 @@ class MispObject extends AppModel { public function beforeValidate($options = array()) { parent::beforeValidate(); - if (empty($this->data['Object']['comment'])) { - $this->data['Object']['comment'] = ""; + if (empty($this->data[$this->alias]['comment'])) { + $this->data[$this->alias]['comment'] = ""; } // generate UUID if it doesn't exist - if (empty($this->data['Object']['uuid'])) { - $this->data['Object']['uuid'] = CakeText::uuid(); + if (empty($this->data[$this->alias]['uuid'])) { + $this->data[$this->alias]['uuid'] = CakeText::uuid(); } // generate timestamp if it doesn't exist - if (empty($this->data['Object']['timestamp'])) { + if (empty($this->data[$this->alias]['timestamp'])) { $date = new DateTime(); - $this->data['Object']['timestamp'] = $date->getTimestamp(); + $this->data[$this->alias]['timestamp'] = $date->getTimestamp(); } - if (!isset($this->data['Object']['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; - if (!isset($this->data['Object']['distribution'])) $this->data['Object']['distribution'] = 5; + if (empty($this->data[$this->alias]['template_version'])) { + $this->data[$this->alias]['template_version'] = 1; + } + if (!isset($this->data[$this->alias]['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; + if (!isset($this->data[$this->alias]['distribution'])) $this->data['Object']['distribution'] = 5; return true; } @@ -369,4 +372,30 @@ class MispObject extends AppModel { $this->Event->Attribute->save($originalAttribute); } } + + public function captureObject($eventId, $object, $user) { + $this->create(); + $object['Object']['event_id'] = $eventId; + if ($this->save($object)) { + $objectId = $this->id; + $partialFails = array(); + foreach ($object['Object']['Attribute'] as $attribute) { + if (isset($attribute['encrypt'])) { + $result = $this->Attribute->handleMaliciousBase64($eventId, $attribute['value'], $attribute['data'], array('md5')); + $attribute['data'] = $result['data']; + $attribute['value'] = $attribute['value'] . '|' . $result['md5']; + } + $attribute['event_id'] = $eventId; + $attribute['object_id'] = $objectId; + $this->Attribute->create(); + $result = $this->Attribute->save(array('Attribute' => $attribute)); + if (!$result) { + $partialFails[] = $attribute['type']; + } + } + if (!empty($partialFails)) return $partialFails; + return true; + } + return 'fail'; + } } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 4d7d8be6b..e1c105bad 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -63,4 +63,46 @@ class ObjectReference extends AppModel { return $this->validationErrors; } } + + public function smartSave($objectReference, $eventId) { + $sides = array('source', 'referenced'); + foreach ($sides as $side) { + $data[$side] = $this->Object->find('first', array( + 'conditions' => array( + 'Object.uuid' => $objectReference[$side . '_uuid'], + 'Object.event_id' => $eventId + ), + 'recursive' => -1, + 'fields' => array('Object.id') + )); + if (empty($data[$side]) && $side == 'referenced') { + $data[$side] = $this->Attribute->find('first', array( + 'conditions' => array( + 'Attribute.uuid' => $objectReference[$side . '_uuid'], + 'Attribute.event_id' => $eventId + ), + 'recursive' => -1, + 'fields' => array('Attribute.id') + )); + $referenced_id = $data[$side]['Attribute']['id']; + $referenced_type = 0; + } else if (!empty($data[$side]) && $side == 'referenced') { + $referenced_id = $data[$side]['Object']['id']; + $referenced_type = 1; + } else if (!empty($data[$side]) && $side = 'source') { + $object_id = $data[$side]['Object']['id']; + } else { + return 'Invalid ' . $side . ' uuid'; + } + } + $this->create(); + $objectReference['referenced_type'] = $referenced_type; + $objectReference['referenced_id'] = $referenced_id; + $objectReference['object_id'] = $object_id; + $result = $this->save(array('ObjectReference' => $ojectReference)); + if (!$result) { + return $this->validationErrors; + } + return true; + } } diff --git a/app/View/Attributes/add_attachment.ctp b/app/View/Attributes/add_attachment.ctp index 97e0ef3ff..9dc1e9e83 100644 --- a/app/View/Attributes/add_attachment.ctp +++ b/app/View/Attributes/add_attachment.ctp @@ -61,9 +61,16 @@ 'type' => 'checkbox', 'checked' => false, 'data-content' => isset($attrDescriptions['signature']['formdesc']) ? $attrDescriptions['signature']['formdesc'] : $attrDescriptions['signature']['desc'], - 'label' => 'IDS (encrypt and hash)', - // 'after' => $this->Html->div('forminfo', 'Tick this box to neutralize the sample. Every malware sample will be zipped with the password "infected"', ''), - //'after' => '
    Tick this box to neutralize the sample. Every malware sample will be zipped with the password "infected"', + 'label' => 'IDS (encrypt and hash)' + )); + ?> +
    + Form->input('advanced', array( + 'type' => 'checkbox', + 'checked' => false, + 'data-content' => isset($attrDescriptions['signature']['formdesc']) ? $attrDescriptions['signature']['formdesc'] : $attrDescriptions['signature']['desc'], + 'label' => 'Advanced extraction (if installed)', )); ?> diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index 15034c158..da5ad27c6 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -53,6 +53,14 @@
    + +
    :
    + +
    diff --git a/app/files/scripts/generate_file_objects.py b/app/files/scripts/generate_file_objects.py new file mode 100644 index 000000000..850825bef --- /dev/null +++ b/app/files/scripts/generate_file_objects.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import argparse +import json + +try: + from pymisp import MISPEncode + from pymisp.tools import make_binary_objects +except ImportError: + pass + +def check(): + missing_dependencies = {'pydeep': False, 'lief': False, 'magic': False, 'pymisp': False} + try: + import pymisp # noqa + except ImportError: + missing_dependencies['pymisp'] = 'Please install pydeep: pip install pymisp' + try: + import pydeep # noqa + except ImportError: + missing_dependencies['pydeep'] = 'Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git' + #try: + import lief # noqa + #except ImportError: + # missing_dependencies['lief'] = 'Please install lief, documentation here: https://github.com/lief-project/LIEF' + try: + import magic # noqa + except ImportError: + missing_dependencies['magic'] = 'Please install python-magic: pip install python-magic.' + return json.dumps(missing_dependencies) + + +def make_objects(path): + to_return = {'objects': [], 'references': []} + fo, peo, seos = make_binary_objects(path) + + if seos: + for s in seos: + to_return['objects'].append(s) + if s.references: + to_return['references'] += s.references + + if peo: + to_return['objects'].append(peo) + if peo.references: + to_return['references'] += peo.references + + if fo: + to_return['objects'].append(fo) + if fo.references: + to_return['references'] += fo.references + return json.dumps(to_return, cls=MISPEncode) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Extract indicators out of binaries and returns MISP objects.') + group = parser.add_mutually_exclusive_group() + group.add_argument("-p", "--path", help="Path to process.") + group.add_argument("-c", "--check", action='store_true', help="Check the dependencies.") + args = parser.parse_args() + + if args.check: + print(check()) + if args.path: + obj = make_objects(args.path) + print(obj) From 5552d7c030bc9acda0fa3563420e9de2535e1d26 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 29 Aug 2017 18:28:18 +0200 Subject: [PATCH 42/77] new: Several new features - added multiple flag among other things --- INSTALL/MYSQL.sql | 7 +- app/Controller/ObjectsController.php | 87 +++++++- app/Model/AppModel.php | 4 +- app/Model/Attribute.php | 2 +- app/Model/MispObject.php | 100 ++++++--- app/Model/ObjectTemplate.php | 34 +++- .../Objects/object_add_attributes.ctp | 114 +++++++++++ .../Elements/Objects/object_value_field.ctp | 56 +++++ .../ajax/view_elements.ctp | 10 +- app/View/Objects/add.ctp | 192 ++++-------------- app/View/Objects/get_row.ctp | 10 + app/View/Objects/revise_object.ctp | 56 +++++ app/files/misp-objects | 2 +- app/webroot/css/main.css | 13 ++ app/webroot/js/misp.js | 12 ++ 15 files changed, 494 insertions(+), 205 deletions(-) create mode 100644 app/View/Elements/Objects/object_add_attributes.ctp create mode 100644 app/View/Elements/Objects/object_value_field.ctp create mode 100644 app/View/Objects/get_row.ctp create mode 100644 app/View/Objects/revise_object.ctp diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 92e98b24b..c879b1526 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -508,6 +508,7 @@ CREATE TABLE IF NOT EXISTS object_templates ( `description` text COLLATE utf8_bin, `version` int(11) NOT NULL, `requirements` text COLLATE utf8_bin, + `fixed` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `user_id` (`user_id`), INDEX `org_id` (`org_id`), @@ -525,8 +526,8 @@ CREATE TABLE IF NOT EXISTS object_templates ( CREATE TABLE IF NOT EXISTS object_template_elements ( `id` int(11) NOT NULL AUTO_INCREMENT, `object_template_id` int(11) NOT NULL, - `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, - `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `object_relation` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin, + `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin, `ui-priority` int(11) NOT NULL, `categories` text COLLATE utf8_bin, `sane_default` text COLLATE utf8_bin, @@ -535,7 +536,7 @@ CREATE TABLE IF NOT EXISTS object_template_elements ( `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `in-object-name` (`in-object-name`), + INDEX `object_relation` (`object_relation`), INDEX `type` (`type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 4b62fea74..a9678f4f6 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -14,6 +14,51 @@ class ObjectsController extends AppController { ), ); + public function beforeFilter() { + parent::beforeFilter(); + $this->Security->unlockedActions = array('revise_object', 'get_row'); + } + + public function revise_object($action, $event_id, $template_id, $object_id = false) { + if (!$this->request->is('post') && !$this->request->is('put')) { + throw new MethodNotAllowedException('This action can only be reached via POST requests'); + } + $this->request->data = $this->MispObject->attributeCleanup($this->request->data); + $eventFindParams = array( + 'recursive' => -1, + 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id'), + 'conditions' => array('Event.id' => $event_id) + ); + $template = $this->MispObject->ObjectTemplate->find('first', array( + 'conditions' => array('ObjectTemplate.id' => $template_id), + 'recursive' => -1, + 'contain' => array( + 'ObjectTemplateElement' + ) + )); + $event = $this->MispObject->Event->find('first', $eventFindParams); + if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) { + throw new NotFoundException('Invalid event.'); + } + if ($this->request->data['Object']['distribution'] == 4) { + $sg = $this->MispObject->SharingGroup->find('first', array( + 'conditions' => array('SharingGroup.id' => $this->request->data['Object']['sharing_group_id']), + 'recursive' => -1, + 'fields' => array('SharingGroup.id', 'SharingGroup.name'), + 'order' => false + )); + if (empty($sg)) throw new NotFoundException('Invalid sharing group.'); + $this->set('sg', $sg); + } + $this->set('distributionLevels', $this->MispObject->Attribute->distributionLevels); + $this->set('action', $action); + $this->set('template', $template); + $this->set('object_id', $object_id); + $this->set('event', $event); + $this->set('data', $this->request->data); + } + + /** * Create an object using a template * POSTing will take the input and validate it against the template @@ -55,6 +100,9 @@ class ObjectsController extends AppController { if (isset($this->request->data['request'])) { $this->request->data = $this->request->data['request']; } + if (isset($this->request->data['Object']['data'])) { + $this->request->data = json_decode($this->request->data['Object']['data'], true); + } if (!isset($this->request->data['Attribute'])) { $this->request->data = array('Attribute' => $this->request->data); } @@ -64,7 +112,7 @@ class ObjectsController extends AppController { $this->request->data = array('Object' => $this->request->data); $this->request->data['Attribute'] = $attributeTemp; unset($attributeTemp); - } + } $object = $this->MispObject->attributeCleanup($this->request->data); // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return @@ -114,7 +162,7 @@ class ObjectsController extends AppController { if (!empty($error)) { $this->Session->setFlash($error); } - $template = $this->MispObject->prepareTemplate($template); + $template = $this->MispObject->prepareTemplate($template, $this->request->data); $enabledRows = array_keys($template['ObjectTemplateElement']); $this->set('enabledRows', $enabledRows); $distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user()); @@ -126,6 +174,28 @@ class ObjectsController extends AppController { } } + public function get_row($template_id, $object_relation, $k) { + $template = $this->MispObject->ObjectTemplate->find('first', array( + 'conditions' => array('ObjectTemplate.id' => $template_id), + 'recursive' => -1, + 'contain' => array( + 'ObjectTemplateElement' + ) + )); + $template = $this->MispObject->prepareTemplate($template); + $element = array(); + foreach ($template['ObjectTemplateElement'] as $templateElement) { + if ($templateElement['object_relation'] == $object_relation) { + $element = $templateElement; + } + } + $distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user()); + $this->layout = false; + $this->set('distributionData', $distributionData); + $this->set('k', $k); + $this->set('element', $element); + } + public function edit($id) { if (!$this->userRole['perm_modify']) { throw new MethodNotAllowedException('You don\'t have permissions to edit objects.'); @@ -164,19 +234,22 @@ class ObjectsController extends AppController { 'ObjectTemplateElement' ) )); - $template = $this->MispObject->prepareTemplate($template); + $template = $this->MispObject->prepareTemplate($template, $object); $enabledRows = false; if ($this->request->is('post') || $this->request->is('put')) { if (isset($this->request->data['request'])) { $this->request->data = $this->request->data['request']; } + if (isset($this->request->data['Object']['data'])) { + $this->request->data = json_decode($this->request->data['Object']['data'], true); + } if (!isset($this->request->data['Attribute'])) { $this->request->data = array('Attribute' => $this->request->data); } $objectToSave = $this->MispObject->attributeCleanup($this->request->data); $objectToSave = $this->MispObject->deltaMerge($object, $objectToSave); - + // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return // to the add form @@ -202,7 +275,7 @@ class ObjectsController extends AppController { $this->request->data['Object'] = $object['Object']; foreach ($template['ObjectTemplateElement'] as $k => $element) { foreach ($object['Attribute'] as $k2 => $attribute) { - if ($attribute['object_relation'] == $element['in-object-name']) { + if ($attribute['object_relation'] == $element['object_relation']) { $enabledRows[] = $k; $this->request->data['Attribute'][$k] = $attribute; if (!empty($element['values_list'])) { @@ -232,6 +305,10 @@ class ObjectsController extends AppController { $this->render('add'); } + public function addValueField() { + + } + public function delete($id, $hard = false) { if (!$this->userRole['perm_modify']) { throw new MethodNotAllowedException('You don\'t have permissions to delete objects.'); diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 72010a1a0..60c33578d 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -786,7 +786,7 @@ class AppModel extends Model { $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_template_elements ( `id` int(11) NOT NULL AUTO_INCREMENT, `object_template_id` int(11) NOT NULL, - `in-object-name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, + `object_relation` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `ui-priority` int(11) NOT NULL, `categories` text COLLATE utf8_bin, @@ -796,7 +796,7 @@ class AppModel extends Model { `disable_correlations` tinyint(1) NOT NULL DEFAULT 0, `multiple` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `in-object-name` (`in-object-name`), + INDEX `object_relation` (`object_relation`), INDEX `type` (`type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index eefe13d99..57e6d5efc 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -638,7 +638,7 @@ class Attribute extends AppModel { // Set defaults for when some of the mandatory fields don't have defaults // These fields all have sane defaults either based on another field, or due to server settings - if (empty($this->data['Attribute']['distribution'])) { + if (!isset($this->data['Attribute']['distribution'])) { $this->data['Attribute']['distribution'] = Configure::read('MISP.default_attribute_distribution'); if ($this->data['Attribute']['distribution'] == 'event') { $this->data['Attribute']['distribution'] = 5; diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index b02ed2deb..9da003263 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -263,26 +263,58 @@ class MispObject extends AppModel { /* * Prepare the template form view's data, setting defaults, sorting elements */ - public function prepareTemplate($template) { + public function prepareTemplate($template, $request = array()) { $temp = array(); usort($template['ObjectTemplateElement'], function($a, $b) { return $a['ui-priority'] < $b['ui-priority']; }); - foreach ($template['ObjectTemplateElement'] as $k => $v) { - if (isset($this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']])) { - $template['ObjectTemplateElement'][$k]['default_category'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['default_category']; - $template['ObjectTemplateElement'][$k]['to_ids'] = $this->Event->Attribute->typeDefinitions[$template['ObjectTemplateElement'][$k]['type']]['to_ids']; - if (empty($template['ObjectTemplateElement'][$k]['categories'])) { - $template['ObjectTemplateElement'][$k]['categories'] = array(); - foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { - if (in_array($template['ObjectTemplateElement'][$k]['type'], $catv['types'])) { - $template['ObjectTemplateElement'][$k]['categories'][] = $catk; + $request_rearranged = array(); + $template_object_elements = $template['ObjectTemplateElement']; + unset($template['ObjectTemplateElement']); + if (!empty($request['Attribute'])) { + foreach ($request['Attribute'] as $attribute) { + $request_rearranged[$attribute['object_relation']][] = $attribute; + } + } + foreach ($template_object_elements as $k => $v) { + if (empty($request_rearranged[$v['object_relation']])) { + if (isset($this->Event->Attribute->typeDefinitions[$v['type']])) { + $v['default_category'] = $this->Event->Attribute->typeDefinitions[$v['type']]['default_category']; + $v['to_ids'] = $this->Event->Attribute->typeDefinitions[$v['type']]['to_ids']; + if (empty($v['categories'])) { + $v['categories'] = array(); + foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { + if (in_array($v['type'], $catv['types'])) { + $v['categories'][] = $catk; + } } } + $template['ObjectTemplateElement'][] = $v; + } else { + $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['object_relation'] . '") that would not pass validation due to this.'; } } else { - $template['warnings'][] = 'Missing attribute type "' . $template['ObjectTemplateElement'][$k]['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['in-object-name'] . '") that would not pass validation due to this.'; - unset($template['ObjectTemplateElement'][$k]); + foreach($request_rearranged[$v['object_relation']] as $request_item) { + if (isset($this->Event->Attribute->typeDefinitions[$v['type']])) { + $v['default_category'] = $request_item['category']; + $v['value'] = $request_item['value']; + $v['to_ids'] = $request_item['to_ids']; + $v['comment'] = $request_item['comment']; + $v['uuid'] = $request_item['uuid']; + if (isset($request_item['data'])) $v['data'] = $request_item['data']; + if (empty($v['categories'])) { + $v['categories'] = array(); + foreach ($this->Event->Attribute->categoryDefinitions as $catk => $catv) { + if (in_array($v['type'], $catv['types'])) { + $v['categories'][] = $catk; + } + } + } + $template['ObjectTemplateElement'][] = $v; + } else { + $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['object_relation'] . '") that would not pass validation due to this.'; + } + } } } return $template; @@ -332,29 +364,41 @@ class MispObject extends AppModel { public function deltaMerge($object, $objectToSave) { $object['Object']['comment'] = $objectToSave['Object']['comment']; $object['Object']['distribution'] = $objectToSave['Object']['distribution']; - $object['Object']['sharing_group_id'] = $objectToSave['Object']['sharing_group_id']; + if ($object['Object']['distribution'] == 4) { + $object['Object']['sharing_group_id'] = $objectToSave['Object']['sharing_group_id']; + } $date = new DateTime(); $object['Object']['timestamp'] = $date->getTimestamp(); $this->save($object); + $checkFields = array('category', 'value', 'to_ids', 'distribution', 'sharing_group_id', 'comment'); foreach ($objectToSave['Attribute'] as $newKey => $newAttribute) { foreach ($object['Attribute'] as $origKey => $originalAttribute) { if (!empty($newAttribute['uuid'])) { if ($newAttribute['uuid'] == $originalAttribute['uuid']) { - $newAttribute['id'] = $originalAttribute['id']; - $newAttribute['event_id'] = $object['Object']['event_id']; - $newAttribute['object_id'] = $object['Object']['id']; - $newAttribute['timestamp'] = $date->getTimestamp(); - $this->Event->Attribute->save($newAttribute, array( - 'category', - 'value', - 'to_ids', - 'distribution', - 'sharing_group_id', - 'comment', - 'timestamp', - 'object_id', - 'event_id' - )); + $different = false; + foreach ($checkFields as $f) { + if ($f == 'sharing_group_id' && empty($newAttribute[$f])) { + $newAttribute[$f] = 0; + } + if ($newAttribute[$f] != $originalAttribute[$f]) $different = true; + } + if ($different) { + $newAttribute['id'] = $originalAttribute['id']; + $newAttribute['event_id'] = $object['Object']['event_id']; + $newAttribute['object_id'] = $object['Object']['id']; + $newAttribute['timestamp'] = $date->getTimestamp(); + $result = $this->Event->Attribute->save(array('Attribute' => $newAttribute), array( + 'category', + 'value', + 'to_ids', + 'distribution', + 'sharing_group_id', + 'comment', + 'timestamp', + 'object_id', + 'event_id' + )); + } unset($object['Attribute'][$origKey]); continue 2; } diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index f17b871c7..24f8cdfe0 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -116,13 +116,13 @@ class ObjectTemplate extends AppModel { $existingTemplateElements = array(); if (!empty($existingTemplateElementsTemp)) { foreach ($existingTemplateElementsTemp as $k => $v) { - $existingTemplateElements[$v['ObjectTemplateElement']['in-object-name']] = $v['ObjectTemplateElement']; + $existingTemplateElements[$v['ObjectTemplateElement']['object_relation']] = $v['ObjectTemplateElement']; } } unset($existingTemplateElementsTemp); - $fieldsToCompare = array('in-object-name', 'type', 'ui-priority', 'categories', 'sane_default', 'values_list'); + $fieldsToCompare = array('object_relation', 'type', 'ui-priority', 'categories', 'sane_default', 'values_list', 'multiple'); foreach ($template['attributes'] as $k => $attribute) { - $attribute['in-object-name'] = $k; + $attribute['object_relation'] = $k; $attribute = $this->__convertJSONToElement($attribute); if (isset($existingTemplateElements[$k])) { $update_required = false; @@ -134,15 +134,15 @@ class ObjectTemplate extends AppModel { } } if ($update_required) { - $attribute = $existingTemplateElements[$k]; + $attribute['id'] = $existingTemplateElements[$k]['id']; $attribute['object_template_id'] = $id; - $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); + $result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); } if (isset($existingTemplateElements[$k])) unset($existingTemplateElements[$k]); } else { $this->ObjectTemplateElement->create(); $attribute['object_template_id'] = $id; - $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); + $result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); } } if (!empty($existingTemplateElements)) { @@ -162,10 +162,11 @@ class ObjectTemplate extends AppModel { 'ui-priority' => 'ui-priority', 'type' => 'type', 'disable_correlation' => 'disable_correlation', - 'in-object-name' => 'in-object-name', + 'object_relation' => 'object_relation', 'categories' => 'categories', 'sane_default' => 'sane_default', - 'values_list' => 'values_list' + 'values_list' => 'values_list', + 'multiple' => 'multiple' ); foreach ($translation_table as $from => $to) { if (isset($attribute[$from])) { @@ -177,6 +178,7 @@ class ObjectTemplate extends AppModel { public function checkTemplateConformity($template, $attributes) { if (!empty($template['ObjectTemplate']['requirements'])) { + // check for all required attributes if (!empty($template['ObjectTemplate']['requirements']['required'])) { foreach ($template['ObjectTemplate']['requirements']['required'] as $requiredField) { $found = false; @@ -188,6 +190,7 @@ class ObjectTemplate extends AppModel { if (!$found) return 'Could not save the object as a required attribute is not set (' . $requiredField . ')'; } } + // check for all required one of attributes if (!empty($template['ObjectTemplate']['requirements']['requiredOneOf'])) { $found = false; foreach ($template['ObjectTemplate']['requirements']['requiredOneOf'] as $requiredField) { @@ -200,6 +203,21 @@ class ObjectTemplate extends AppModel { if (!$found) return 'Could not save the object as it requires at least one of the following attributes to be set: ' . implode(', ', $template['ObjectTemplate']['requirements']['requiredOneOf']); } } + // check the multiple flag is adhered to + foreach ($template['ObjectTemplateElement'] as $template_attribute) { + if ($template_attribute['multiple'] !== true) { + $found_relations = array(); + foreach ($attributes['Attribute'] as $attribute) { + if ($attribute['object_relation'] == $template_attribute['object_relation']) { + if (!isset($found_relations[$attribute['object_relation']])) { + $found_relations[$attribute['object_relation']] = true; + } else { + return 'Could not save the object as a unique relationship within the object was assigned to more than one attribute. This is only allowed if the multiple flag is set in the object template.'; + } + } + } + } + } return true; } } diff --git a/app/View/Elements/Objects/object_add_attributes.ctp b/app/View/Elements/Objects/object_add_attributes.ctp new file mode 100644 index 000000000..d1fd07911 --- /dev/null +++ b/app/View/Elements/Objects/object_add_attributes.ctp @@ -0,0 +1,114 @@ + + + Form->input('Attribute.' . $k . '.save', array( + 'type' => 'checkbox', + 'checked' => in_array($k, $enabledRows), + 'label' => false, + 'div' => false + )); + ?> + + + 'hidden', + 'value' => $element['object_relation'], + 'label' => false, + 'div' => false + ); + echo $this->Form->input('Attribute.' . $k . '.object_relation', $formSettings); + if ($action == 'edit') { + echo $this->Form->input('Attribute.' . $k . '.uuid', array( + 'type' => 'hidden', + 'label' => false, + 'div' => false, + 'value' => !empty($element['uuid']) ? $element['uuid'] : '' + )); + } + $formSettings = array( + 'type' => 'hidden', + 'value' => $element['type'], + 'label' => false, + 'div' => false + ); + echo $this->Form->input('Attribute.' . $k . '.type', $formSettings); + echo '' . Inflector::humanize(h($element['object_relation'])) . ''; + if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['object_relation'], $template['ObjectTemplate']['requirements']['required'])) { + echo '' . '(*)' . ''; + } + echo ' :: ' . h($element['type']) . ''; + ?> + + + array_combine($element['categories'], $element['categories']), + 'default' => $element['default_category'], + 'style' => 'margin-bottom:0px;', + 'label' => false, + 'div' => false + ); + echo $this->Form->input('Attribute.' . $k . '.category', $formSettings); + ?> + + + element( + 'Objects/object_value_field', + array( + 'element' => $element, + 'k' => $k + ) + ); + ?> + + + Form->input('Attribute.' . $k . '.to_ids', array( + 'type' => 'checkbox', + 'checked' => $element['to_ids'], + 'label' => false, + 'div' => false + )); + ?> + + + Form->input('Attribute.' . $k . '.distribution', array( + 'class' => 'Attribute_distribution_select', + 'options' => $distributionData['levels'], + 'default' => !empty($element['distribution']) ? $element['distribution'] : $distributionData['initial'], + 'style' => 'margin-bottom:0px;', + 'label' => false, + 'div' => false + )); + ?> +
    + Form->input('Attribute.' . $k . '.sharing_group_id', array( + 'class' => 'Attribute_sharing_group_id_select', + 'options' => $distributionData['sgs'], + 'default' => !empty($element['sharing_group_id']) ? $element['sharing_group_id'] : false, + 'label' => false, + 'div' => false, + 'style' => 'display:none;margin-bottom:0px;', + )); + ?> + + + Form->input('Attribute.' . $k . '.comment', array( + 'type' => 'textarea', + 'style' => 'height:20px;width:400px;', + 'required' => false, + 'allowEmpty' => true, + 'label' => false, + 'div' => false, + 'value' => empty($element['comment']) ? '' : $element['comment'] + )); + ?> + + diff --git a/app/View/Elements/Objects/object_value_field.ctp b/app/View/Elements/Objects/object_value_field.ctp new file mode 100644 index 000000000..081969e1c --- /dev/null +++ b/app/View/Elements/Objects/object_value_field.ctp @@ -0,0 +1,56 @@ +
    + Form->file('Attribute.' . $k . '.Attachment', array( + 'class' => 'Attribute_attachment' + )); + else: + if (empty($element['values_list']) && empty($element['sane_default'])): + echo $this->Form->input('Attribute.' . $k . '.value', array( + 'type' => 'textarea', + 'required' => false, + 'allowEmpty' => true, + 'style' => 'height:20px;width:400px;', + 'label' => false, + 'div' => false, + 'value' => empty($element['value']) ? '' : $element['value'] + )); + else: + if (empty($element['values_list'])) { + $list = $element['sane_default']; + $list[] = 'Enter value manually'; + } else { + $list = $element['values_list']; + } + $list = array_combine($list, $list); + ?> +
    + Form->input('Attribute.' . $k . '.value_select', array( + 'class' => 'Attribute_value_select', + 'style' => 'width:414px;margin-bottom:0px;', + 'options' => array_combine($list, $list), + 'label' => false, + 'div' => false, + 'value' => empty($element['value']) ? '' : $element['value'] + )); + ?> +
    + Form->input('Attribute.' . $k . '.value', array( + 'class' => 'Attribute_value', + 'type' => 'textarea', + 'required' => false, + 'allowEmpty' => true, + 'style' => 'height:20px;width:400px;display:none;', + 'label' => false, + 'div' => false, + 'value' => empty($element['value']) ? '' : $element['value'] + )); + ?> +
    + +
    diff --git a/app/View/ObjectTemplateElements/ajax/view_elements.ctp b/app/View/ObjectTemplateElements/ajax/view_elements.ctp index 0a09aa67b..fffd44f62 100644 --- a/app/View/ObjectTemplateElements/ajax/view_elements.ctp +++ b/app/View/ObjectTemplateElements/ajax/view_elements.ctp @@ -16,9 +16,10 @@
    - + - + + @@ -30,10 +31,11 @@ foreach ($list as $k => $item): ?> - + + - + diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index e74a33bdd..2319a8690 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -1,6 +1,6 @@
    Form->create('Object', array('id', 'url' => $url, 'enctype' => 'multipart/form-data')); ?>

    @@ -67,7 +67,8 @@ 'required' => false, 'allowEmpty' => true, 'label' => false, - 'div' => false + 'div' => false, + 'value' => empty($template['Object']['comment']) ? '' : $template['Object']['comment'] )); ?> @@ -101,162 +102,42 @@ $row_list = array(); foreach ($template['ObjectTemplateElement'] as $k => $element): $row_list[] = $k; -?> -
    - - - - - - - - + } + if ($lastOfType): + ?> + + + +
    Paginator->sort('in_object_name');?>Paginator->sort('object_relation');?> Paginator->sort('type');?>Paginator->sort('ui-priority');?>Paginator->sort('multiple');?>Paginator->sort('ui-priority', 'UI-priority');?> Paginator->sort('description');?> Categories Sane defaults
            
    - Form->input('Attribute.' . $k . '.save', array( - 'type' => 'checkbox', - 'checked' => in_array($k, $enabledRows), - 'label' => false, - 'div' => false - )); - ?> - - 'hidden', - 'value' => $element['in-object-name'], - 'label' => false, - 'div' => false - ); - echo $this->Form->input('Attribute.' . $k . '.object_relation', $formSettings); - if ($action == 'edit') { - echo $this->Form->input('Attribute.' . $k . '.uuid', array( - 'type' => 'hidden', - 'label' => false, - 'div' => false - )); + echo $this->element( + 'Objects/object_add_attributes', + array( + 'element' => $element, + 'k' => $k, + 'action' => $action, + 'enabledRows' => $enabledRows + ) + ); + if ($element['multiple']): + $lastOfType = true; + $lookAheadArray = array_slice($template['ObjectTemplateElement'], $k, count($template['ObjectTemplateElement']), true); + if (count($lookAheadArray) > 1) { + foreach ($lookAheadArray as $k2 => $temp) { + if ($k2 == $k) continue; + if ($temp['object_relation'] == $element['object_relation']) { + $lastOfType = false; + } } - $formSettings = array( - 'type' => 'hidden', - 'value' => $element['type'], - 'label' => false, - 'div' => false - ); - echo $this->Form->input('Attribute.' . $k . '.type', $formSettings); - echo '' . Inflector::humanize(h($element['in-object-name'])) . ''; - if (!empty($template['ObjectTemplate']['requirements']['required']) && in_array($element['in-object-name'], $template['ObjectTemplate']['requirements']['required'])) { - echo '' . '(*)' . ''; - } - echo ' :: ' . h($element['type']) . ''; - ?> - - array_combine($element['categories'], $element['categories']), - 'default' => $element['default_category'], - 'style' => 'margin-bottom:0px;', - 'label' => false, - 'div' => false - ); - echo $this->Form->input('Attribute.' . $k . '.category', $formSettings); - ?> - - Form->file('Attribute.' . $k . '.Attachment', array( - 'class' => 'Attribute_attachment' - )); - else: - if (empty($element['values_list']) && empty($element['sane_default'])): - echo $this->Form->input('Attribute.' . $k . '.value', array( - 'type' => 'textarea', - 'required' => false, - 'allowEmpty' => true, - 'style' => 'height:20px;width:400px;', - 'label' => false, - 'div' => false - )); - else: - if (empty($element['values_list'])) { - $list = $element['sane_default']; - $list[] = 'Enter value manually'; - } else { - $list = $element['values_list']; - } - $list = array_combine($list, $list); - ?> -
    - Form->input('Attribute.' . $k . '.value_select', array( - 'class' => 'Attribute_value_select', - 'style' => 'width:414px;margin-bottom:0px;', - 'options' => array_combine($list, $list), - 'label' => false, - 'div' => false - )); - ?> -
    - Form->input('Attribute.' . $k . '.value', array( - 'class' => 'Attribute_value', - 'type' => 'textarea', - 'required' => false, - 'allowEmpty' => true, - 'style' => 'height:20px;width:400px;display:none;', - 'label' => false, - 'div' => false - )); - ?> -
    - -
    - Form->input('Attribute.' . $k . '.to_ids', array( - 'type' => 'checkbox', - 'checked' => $element['to_ids'], - 'label' => false, - 'div' => false - )); - ?> - - Form->input('Attribute.' . $k . '.distribution', array( - 'class' => 'Attribute_distribution_select', - 'options' => $distributionData['levels'], - 'default' => $distributionData['initial'], - 'style' => 'margin-bottom:0px;', - 'label' => false, - 'div' => false - )); - ?> -
    - Form->input('Attribute.' . $k . '.sharing_group_id', array( - 'class' => 'Attribute_sharing_group_id_select', - 'options' => $distributionData['sgs'], - 'label' => false, - 'div' => false, - 'style' => 'display:none;margin-bottom:0px;', - )); - ?> -
    - Form->input('Attribute.' . $k . '.comment', array( - 'type' => 'textarea', - 'style' => 'height:20px;width:400px;', - 'required' => false, - 'allowEmpty' => true, - 'label' => false, - 'div' => false - )); - ?> -
    + +
    +
    @@ -305,5 +186,10 @@ $(".Attribute_value_select").change(function() { checkAndEnable($(this).parent().find('.Attribute_value'), $(this).val() == 'Enter value manually'); }); + $('.add_attribute_row').click(function() { + var selector = $(this).data('target'); + var count = $(this).parent().children(selector).length; + $(this).parent().children(selector).first().clone().appendTo($(this).parent()).insertBefore($('.add_unlocked_field')); + }); }); diff --git a/app/View/Objects/get_row.ctp b/app/View/Objects/get_row.ctp new file mode 100644 index 000000000..ef4de00e3 --- /dev/null +++ b/app/View/Objects/get_row.ctp @@ -0,0 +1,10 @@ +element( + 'Objects/object_add_attributes', + array( + 'element' => $element, + 'k' => $k, + 'appendValue' => '0' + ) + ); +?> diff --git a/app/View/Objects/revise_object.ctp b/app/View/Objects/revise_object.ctp new file mode 100644 index 000000000..8e61e1f71 --- /dev/null +++ b/app/View/Objects/revise_object.ctp @@ -0,0 +1,56 @@ +
    +

    Object pre-save review

    +

    Make sure that the below Object reflects your expectation before submiting it.

    + Form->create('Object', array('id', 'url' => $url)); + $formSettings = array( + 'type' => 'hidden', + 'value' => json_encode($data, true), + 'label' => false, + 'div' => false + ); + echo $this->Form->input('data', $formSettings); + ?> +
    + Name:
    + Meta-category:
    + Distribution: +
    + Comment:
    + Attributes:
    + ' . h($attribute['object_relation']) . ':
    '; + foreach ($attributeFields as $field): + if ($field == 'to_ids') $attribute[$field] = $attribute[$field] ? 'Yes' : 'No'; + if (isset($attribute[$field])): + ?> + :
    + +
    + Form->button('Submit', array('class' => 'btn btn-primary')); + ?> + Cancel + Form->end(); + ?> + +
    +element('side_menu', array('menuList' => 'event', 'menuItem' => 'addObject', 'event' => $event)); +?> diff --git a/app/files/misp-objects b/app/files/misp-objects index 01a23c205..d34dd5fb6 160000 --- a/app/files/misp-objects +++ b/app/files/misp-objects @@ -1 +1 @@ -Subproject commit 01a23c205cf2a27a2351ae3fcc0f3735a8b436b1 +Subproject commit d34dd5fb606f1c4d882733d16c16103fe429991c diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index c8a3d9ee3..24448f8af 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1882,6 +1882,19 @@ tr.blank_table_row td { background-color: #ffffff !important; } +.down-expand-button { + text-align:center !important; + border:0px; + padding:0px !important; + width:100%; + cursor: pointer; + cursor: hand; +} + +table tr:hover .down-expand-button { + background: rgb(221, 221, 221) !important; +} + .strikethrough { text-decoration: line-through; } diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 3fb56eee8..92d513782 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -3198,6 +3198,18 @@ function changeObjectReferenceSelectOption() { } } +$('.add_object_attribute_row').click(function() { + var template_id = $(this).data('template-id'); + var object_relation = $(this).data('object-relation'); + var k = $('#last-row').data('last-row'); + var k = k+1; + $('#last-row').data('last-row', k); + url = "/objects/get_row/" + template_id + "/" + object_relation + "/" + k; + $.get(url, function(data) { + $('#row_' + object_relation + '_expand').before($(data).fadeIn()).html(); + }); +}); + (function(){ "use strict"; $(".datepicker").datepicker({ From c09741ebfc25bcb2034031488d2c7ea32d0b4e37 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 1 Sep 2017 17:42:54 +0200 Subject: [PATCH 43/77] new: Further work on the objects - uuids of both sides saved in references - attachment adding fixed --- INSTALL/MYSQL.sql | 6 ++- app/Controller/AttributesController.php | 18 +++----- app/Controller/ObjectReferencesController.php | 11 +++-- app/Model/AppModel.php | 12 ++++-- app/Model/Attribute.php | 41 +++++++++++++------ app/Model/ObjectReference.php | 19 +++++---- app/files/scripts/generate_file_objects.py | 21 +++++----- 7 files changed, 75 insertions(+), 53 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index c879b1526..c2f7d760c 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -464,14 +464,18 @@ CREATE TABLE IF NOT EXISTS object_references ( `timestamp` int(11) NOT NULL DEFAULT 0, `object_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, + `source_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `destination_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, `deleted` TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `uuid` (`uuid`), + INDEX `source_uuid` (`source_uuid`), + INDEX `destination_uuid` (`destination_uuid`), INDEX `timestamp` (`timestamp`), + INDEX `object_id` (`object_id`), INDEX `referenced_id` (`referenced_id`), INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index a5e3dfd2a..070dec255 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -361,16 +361,7 @@ class AttributesController extends AppController { if ($this->request->data['Attribute']['malware']) { if ($this->request->data['Attribute']['advanced']) { - $execRetval = ''; - $execOutput = array(); - $result = shell_exec('python ' . APP . 'files/scripts/generate_file_objects.py -p ' . $tmpfile->path); - if (!empty($result)) { - $result = json_decode($result, true); - if (isset($result['objects'])) { - $result['Object'] = $result['objects']; - unset($result['objects']); - } - } + $result = $this->Attribute->advancedAddMalwareSample($tmpfile); } else { $result = $this->Attribute->simpleAddMalwareSample( $eventId, @@ -387,7 +378,11 @@ class AttributesController extends AppController { foreach ($object['Attribute'] as $k => $attribute) { if ($attribute['value'] == $tmpfile->name) $object['Attribute'][$k]['value'] = $value['name']; } - $result = $this->Attribute->Object->captureObject($eventId, array('Object' => $object), $this->Auth->user()); + $this->loadModel('MispObject'); + $result = $this->MispObject->captureObject($eventId, array('Object' => $object), $this->Auth->user()); + } + foreach ($result['ObjectReference'] as $reference) { + $result = $this->MispObject->ObjectReference->smartSave($reference, $eventId); } } } else { @@ -410,7 +405,6 @@ class AttributesController extends AppController { else $success++; } } -throw new Exception(); $message = 'The attachment(s) have been uploaded.'; if (!empty($partialFails)) $message .= ' Some of the attributes however could not be created.'; if (!empty($fails)) $message = 'Some of the attachments failed to upload. The failed files were: ' . implode(', ', $fails) . ' - This can be caused by the attachments already existing in the event.'; diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 91bbb1450..c62efa5a4 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -54,6 +54,7 @@ class ObjectReferencesController extends AppController { )); if (!empty($target_object)) { $referenced_id = $target_object['Object']['id']; + $target_uuid = $target_attribute['Object']['uuid']; if ($target_object['Object']['event_id'] != $object['Event']['id']) { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } @@ -70,6 +71,7 @@ class ObjectReferencesController extends AppController { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } $referenced_id = $target_attribute['Attribute']['id']; + $destination_uuid = $target_attribute['Attribute']['uuid']; $referenced_type = 0; } $relationship_type = empty($this->request->data['ObjectReference']['relationship_type']) ? '' : $this->request->data['ObjectReference']['relationship_type']; @@ -79,14 +81,15 @@ class ObjectReferencesController extends AppController { $data = array( 'referenced_type' => $referenced_type, 'referenced_id' => $referenced_id, - 'uuid' => $this->request->data['ObjectReference']['uuid'], + 'destination_uuid' => $destination_uuid, 'relationship_type' => $relationship_type, 'comment' => !empty($this->request->data['ObjectReference']['comment']) ? $this->request->data['ObjectReference']['comment'] : '', 'event_id' => $object['Event']['id'], - 'object_id' => $objectId + 'source_uuid' => $object['Object']['uuid'], + 'object_id' => $objectId, + 'referenced_type' => $referenced_type, + 'uuid' => CakeText::uuid() ); - $data['referenced_type'] = $referenced_type; - $data['uuid'] = $this->request->data['ObjectReference']['uuid']; $this->ObjectReference->create(); $result = $this->ObjectReference->save(array('ObjectReference' => $data)); if ($result) { diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 60c33578d..6eafc8495 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -741,16 +741,20 @@ class AppModel extends Model { `timestamp` int(11) NOT NULL DEFAULT 0, `object_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, + `source_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `destination_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, `deleted` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `uuid` (`uuid`), - INDEX `timestamp` (`timestamp`), - INDEX `referenced_uuid` (`referenced_uuid`), - INDEX `relationship_type` (`relationship_type`) + INDEX `source_uuid` (`source_uuid`), + INDEX `destination_uuid` (`destination_uuid`), + INDEX `timestamp` (`timestamp`), + INDEX `object_id` (`object_id`), + INDEX `referenced_id` (`referenced_id`), + INDEX `relationship_type` (`relationship_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"; $sqlArray[] = "CREATE TABLE IF NOT EXISTS object_relationships ( diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 57e6d5efc..398305595 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2669,12 +2669,12 @@ class Attribute extends AppModel { public function simpleAddMalwareSample($event_id, $category, $distribution, $sharing_group_id, $comment, $filename, $tmpfile) { $attributes = array( - 'malware-sample' => array('type' => 'malware-sample', 'data' => 1, 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), - 'filename' => array('type' => 'filename', 'category' => '', 'to_ids' => 0, 'disable_correlation' => 0), - 'md5' => array('type' => 'md5', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), - 'sha1' => array('type' => 'sha1', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), - 'sha256' => array('type' => 'sha256', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0), - 'size-in-bytes' => array('type' => 'size-in-bytes', 'category' => '', 'to_ids' => 0, 'disable_correlation' => 1) + 'malware-sample' => array('type' => 'malware-sample', 'data' => 1, 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0, 'object_relation' => 'malware-sample'), + 'filename' => array('type' => 'filename', 'category' => '', 'to_ids' => 0, 'disable_correlation' => 0, 'object_relation' => 'filename'), + 'md5' => array('type' => 'md5', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0, 'object_relation' => 'md5'), + 'sha1' => array('type' => 'sha1', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0, 'object_relation' => 'sha1'), + 'sha256' => array('type' => 'sha256', 'category' => '', 'to_ids' => 1, 'disable_correlation' => 0, 'object_relation' => 'sha256'), + 'size-in-bytes' => array('type' => 'size-in-bytes', 'category' => 'Other', 'to_ids' => 0, 'disable_correlation' => 1, 'object_relation' => 'filesize') ); $hashes = array('md5', 'sha1', 'sha256'); $this->Object = ClassRegistry::init('Object'); @@ -2707,16 +2707,17 @@ class Attribute extends AppModel { 'event_id' => $event_id, 'comment' => $comment ); + $result = $this->Event->Attribute->handleMaliciousBase64($event_id, $filename, base64_encode($tmpfile->read()), $hashes); foreach ($attributes as $k => $v) { - $result = $this->Event->Attribute->handleMaliciousBase64($event_id, $filename, base64_encode($tmpfile->read()), $hashes); $attribute = array( 'distribution' => 5, - 'category' => $category, + 'category' => empty($v['category']) ? $category : $v['category'], 'type' => $v['type'], 'to_ids' => $v['to_ids'], 'disable_correlation' => $v['disable_correlation'], 'object_id' => $this->Object->id, - 'event_id' => $event_id + 'event_id' => $event_id, + 'object_relation' => $v['object_relation'] ); if (isset($v['data'])) { $attribute['data'] = $result['data']; @@ -2724,7 +2725,7 @@ class Attribute extends AppModel { if ($k == 'malware-sample') { $attribute['value'] = $filename . '|' . $result['md5']; } else if ($k == 'size-in-bytes') { - $attribute['value'] = 0; + $attribute['value'] = $tmpfile->size(); } else if ($k == 'filename') { $attribute['value'] = $filename; } else { @@ -2732,11 +2733,25 @@ class Attribute extends AppModel { } $object['Attribute'][] = $attribute; } - return array('Object' => $object); + return array('Object' => array($object)); } - public function advancedAddMalwareSample() { - + public function advancedAddMalwareSample($tmpfile) { + $execRetval = ''; + $execOutput = array(); + $result = shell_exec('python ' . APP . 'files/scripts/generate_file_objects.py -p ' . $tmpfile->path); + if (!empty($result)) { + $result = json_decode($result, true); + if (isset($result['objects'])) { + $result['Object'] = $result['objects']; + unset($result['objects']); + } + if (isset($result['references'])) { + $result['ObjectReference'] = $result['references']; + unset($result['references']); + } + } + return $result; } } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index e1c105bad..02425d601 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -65,7 +65,8 @@ class ObjectReference extends AppModel { } public function smartSave($objectReference, $eventId) { - $sides = array('source', 'referenced'); + $sides = array('source', 'destination'); + $data = array(); foreach ($sides as $side) { $data[$side] = $this->Object->find('first', array( 'conditions' => array( @@ -75,7 +76,7 @@ class ObjectReference extends AppModel { 'recursive' => -1, 'fields' => array('Object.id') )); - if (empty($data[$side]) && $side == 'referenced') { + if (empty($data[$side]) && $side == 'destination') { $data[$side] = $this->Attribute->find('first', array( 'conditions' => array( 'Attribute.uuid' => $objectReference[$side . '_uuid'], @@ -84,11 +85,11 @@ class ObjectReference extends AppModel { 'recursive' => -1, 'fields' => array('Attribute.id') )); - $referenced_id = $data[$side]['Attribute']['id']; - $referenced_type = 0; - } else if (!empty($data[$side]) && $side == 'referenced') { - $referenced_id = $data[$side]['Object']['id']; - $referenced_type = 1; + $destination_id = $data[$side]['Attribute']['id']; + $destination_type = 0; + } else if (!empty($data[$side]) && $side == 'destination') { + $destination_id = $data[$side]['Object']['id']; + $destination_type = 1; } else if (!empty($data[$side]) && $side = 'source') { $object_id = $data[$side]['Object']['id']; } else { @@ -96,8 +97,8 @@ class ObjectReference extends AppModel { } } $this->create(); - $objectReference['referenced_type'] = $referenced_type; - $objectReference['referenced_id'] = $referenced_id; + $objectReference['destination_type'] = $destination_type; + $objectReference['destination_id'] = $destination_id; $objectReference['object_id'] = $object_id; $result = $this->save(array('ObjectReference' => $ojectReference)); if (!$result) { diff --git a/app/files/scripts/generate_file_objects.py b/app/files/scripts/generate_file_objects.py index 850825bef..326984537 100644 --- a/app/files/scripts/generate_file_objects.py +++ b/app/files/scripts/generate_file_objects.py @@ -10,6 +10,7 @@ try: except ImportError: pass + def check(): missing_dependencies = {'pydeep': False, 'lief': False, 'magic': False, 'pymisp': False} try: @@ -20,10 +21,10 @@ def check(): import pydeep # noqa except ImportError: missing_dependencies['pydeep'] = 'Please install pydeep: pip install git+https://github.com/kbandla/pydeep.git' - #try: - import lief # noqa - #except ImportError: - # missing_dependencies['lief'] = 'Please install lief, documentation here: https://github.com/lief-project/LIEF' + try: + import lief # noqa + except ImportError: + missing_dependencies['lief'] = 'Please install lief, documentation here: https://github.com/lief-project/LIEF' try: import magic # noqa except ImportError: @@ -38,18 +39,18 @@ def make_objects(path): if seos: for s in seos: to_return['objects'].append(s) - if s.references: - to_return['references'] += s.references + if s.ObjectReference: + to_return['references'] += s.ObjectReference if peo: to_return['objects'].append(peo) - if peo.references: - to_return['references'] += peo.references + if peo.ObjectReference: + to_return['references'] += peo.ObjectReference if fo: to_return['objects'].append(fo) - if fo.references: - to_return['references'] += fo.references + if fo.ObjectReference: + to_return['references'] += fo.ObjectReference return json.dumps(to_return, cls=MISPEncode) if __name__ == '__main__': From 55c55d65ed1f0f57bcdebaab8a748752d20a74cf Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 1 Sep 2017 17:51:18 +0200 Subject: [PATCH 44/77] fix: Some realignment on the attribute add view --- app/View/Elements/Objects/object_add_attributes.ctp | 9 ++++++--- app/View/Objects/add.ctp | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/View/Elements/Objects/object_add_attributes.ctp b/app/View/Elements/Objects/object_add_attributes.ctp index d1fd07911..400cbcd5d 100644 --- a/app/View/Elements/Objects/object_add_attributes.ctp +++ b/app/View/Elements/Objects/object_add_attributes.ctp @@ -11,7 +11,7 @@ )); ?> - + - - + From 58bfabfa739c9bb47fe66000009fda84e4fcdf46 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2017 17:26:45 +0200 Subject: [PATCH 45/77] new: Sync with objects wip - add/edit of full events now capture all object related structures - restructuring of the edit/add functionalities into clearly divided subsections --- app/Controller/AttributesController.php | 22 +- app/Model/Attribute.php | 173 +++++++++++++++ app/Model/Event.php | 277 +++++++++--------------- app/Model/MispObject.php | 109 ++++++++-- app/Model/ObjectReference.php | 81 +++++++ 5 files changed, 466 insertions(+), 196 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 070dec255..87abaef1c 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -375,14 +375,18 @@ class AttributesController extends AppController { } if (!empty($result)) { foreach ($result['Object'] as $object) { - foreach ($object['Attribute'] as $k => $attribute) { - if ($attribute['value'] == $tmpfile->name) $object['Attribute'][$k]['value'] = $value['name']; + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $k => $attribute) { + if ($attribute['value'] == $tmpfile->name) $object['Attribute'][$k]['value'] = $value['name']; + } } $this->loadModel('MispObject'); $result = $this->MispObject->captureObject($eventId, array('Object' => $object), $this->Auth->user()); } - foreach ($result['ObjectReference'] as $reference) { - $result = $this->MispObject->ObjectReference->smartSave($reference, $eventId); + if (!empty($result['ObjectReference'])) { + foreach ($result['ObjectReference'] as $reference) { + $result = $this->MispObject->ObjectReference->smartSave($reference, $eventId); + } } } } else { @@ -731,6 +735,16 @@ class AttributesController extends AppController { $this->Event->set('timestamp', $date->getTimestamp()); $this->Event->set('published', 0); $this->Event->save($this->Event->data, array('fieldList' => array('published', 'timestamp', 'info'))); + if (!empty($this->Attribute->data['Attribute']['object_id'])) { + $object = $this->Attribute->Object->find('first', array( + 'recursive' => -1, + 'conditions' => array('Object.id' => $this->Attribute->data['Attribute']['object_id']) + )); + if (!empty($object)) { + $object['Object']['timestamp'] = $date->getTimestamp(); + $this->Attribute->Object->save($object); + } + } if ($this->_isRest() || $this->response->type() === 'application/json') { $saved_attribute = $this->Attribute->find('first', array( 'conditions' => array('id' => $this->Attribute->id), diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 398305595..cec55e654 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2754,4 +2754,177 @@ class Attribute extends AppModel { return $result; } + // gets an attribute, saves it + // handles encryption, attaching to event/object, logging of issues, tag capturing + public function captureAttribute($attribute, $eventId, $user, $objectId = false, $log = false) { + if ($log == false) { + $log = ClassRegistry::init('Log'); + } + $attribute['event_id'] = $eventId; + $attribute['object_id'] = $objectId ? $objectId : 0; + unset($attribute['id']); + if (isset($attribute['encrypt'])) { + $result = $this->handleMaliciousBase64($eventId, $attribute['value'], $attribute['data'], array('md5')); + $attribute['data'] = $result['data']; + $attribute['value'] = $attribute['value'] . '|' . $result['md5']; + } + $fieldList = array( + 'event_id', + 'category', + 'type', + 'value', + 'value1', + 'value2', + 'to_ids', + 'uuid', + 'timestamp', + 'distribution', + 'comment', + 'sharing_group_id', + 'deleted', + 'disable_correlation', + 'object_id', + 'object_relation' + ); + $this->create(); + if (!$this->save($attribute, array('fieldList' => $fieldList))) { + $validationErrors['Attribute'][$k] = $this->validationErrors; + $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); + $log->create(); + $log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Attribute', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'add', + 'user_id' => $user['id'], + 'title' => 'Attribute dropped due to validation for Event ' . $eventId . ' failed: ' . $attribute_short, + 'change' => 'Validation errors: ' . json_encode($this->validationErrors) . ' Full Attribute: ' . json_encode($attribute), + )); + } else { + if (isset($attribute['AttributeTag'])) { + foreach ($attribute['AttributeTag'] as $at) { + unset($at['id']); + $this->AttributeTag->create(); + $at['attribute_id'] = $this->id; + $at['event_id'] = $eventId; + $this->AttributeTag->save($at); + } + } + } + return $attribute; + } + + public function editAttribute($attribute, $eventId, $user, $objectId, $log = false) { + $attribute['event_id'] = $eventId; + $attribute['object_id'] = $objectId; + if (isset($attribute['encrypt'])) { + $result = $this->handleMaliciousBase64($eventId, $attribute['value'], $attribute['data'], array('md5')); + $attribute['data'] = $result['data']; + $attribute['value'] = $attribute['value'] . '|' . $result['md5']; + } + if (isset($attribute['uuid'])) { + $existingAttribute = $this->find('first', array( + 'conditions' => array('Attribute.uuid' => $attribute['uuid']), + 'recursive' => -1 + )); + if (count($existingAttribute)) { + if ($existingAttribute['Attribute']['event_id'] != $eventId || $existingAttribute['Attribute']['object_id'] != $objectId) { + $result = $this->Log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Attribute', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'edit', + 'user_id' => $user['id'], + 'title' => 'Duplicate UUID found in attribute', + 'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'] . '. This can also be due to the same attribute (or an attribute with the same UUID) existing in a different event / object)', + )); + return true; + } + // If a field is not set in the request, just reuse the old value + $recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id', 'object_id', 'object_relation'); + foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $attribute[$rF] = $existingAttribute['Attribute'][$rF]; + $attribute['id'] = $existingAttribute['Attribute']['id']; + // Check if the attribute's timestamp is bigger than the one that already exists. + // If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute. + // Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event. + if (isset($attribute['timestamp'])) { + if ($attribute['timestamp'] <= $existingAttribute['Attribute']['timestamp']) { + return true; + } + } else { + $attribute['timestamp'] = $date; + } + } else { + $this->create(); + } + } else { + $this->create(); + } + $attribute['event_id'] = $eventId; + if ($attribute['distribution'] == 4) { + $attribute['sharing_group_id'] = $this->SharingGroup->captureSG($attribute['SharingGroup'], $user); + } + $fieldList = array( + 'event_id', + 'category', + 'type', + 'value', + 'value1', + 'value2', + 'to_ids', + 'uuid', + 'revision', + 'distribution', + 'timestamp', + 'comment', + 'sharing_group_id', + 'deleted', + 'disable_correlation' + ); + if (!$this->save($attribute, array('fieldList' => $fieldList))) { + $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); + $this->Log->create(); + $this->Log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Attribute', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'edit', + 'user_id' => $user['id'], + 'title' => 'Attribute dropped due to validation for Event ' . $eventId . ' failed: ' . $attribute_short, + 'change' => 'Validation errors: ' . json_encode($this->validationErrors) . ' Full Attribute: ' . json_encode($attribute), + )); + return $this->validationErrors; + } else { + if (isset($attribute['Tag']) && $user['Role']['perm_tagger']) { + foreach ($attribute['Tag'] as $tag) { + $tag_id = $this->AttributeTag->Tag->captureTag($tag, $user); + if ($tag_id) { + // fix the IDs here + $this->AttributeTag->attachTagToAttribute($this->id, $this->id, $tag_id); + } else { + // If we couldn't attach the tag it is most likely because we couldn't create it - which could have many reasons + // However, if a tag couldn't be added, it could also be that the user is a tagger but not a tag editor + // In which case if no matching tag is found, no tag ID is returned. Logging these is pointless as it is the correct behaviour. + if ($user['Role']['perm_tag_editor']) { + $this->Log->create(); + $this->Log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Attrubute', + 'model_id' => $this->id, + 'email' => $user['email'], + 'action' => 'edit', + 'user_id' => $user['id'], + 'title' => 'Failed create or attach Tag ' . $tag['name'] . ' to the attribute.', + 'change' => '' + )); + } + } + } + } + } + return true; + } } diff --git a/app/Model/Event.php b/app/Model/Event.php index 98a63f04b..cef071d9d 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -738,7 +738,7 @@ class Event extends AppModel { * modifies the original data. */ public function cleanupEventArrayFromXML(&$data) { - $objects = array('Attribute', 'ShadowAttribute'); + $objects = array('Attribute', 'ShadowAttribute', 'Object'); foreach ($objects as $object) { // Workaround for different structure in XML/array than what CakePHP expects if (isset($data['Event'][$object]) && is_array($data['Event'][$object]) && count($data['Event'][$object])) { @@ -2238,17 +2238,71 @@ class Event extends AppModel { } else { if ($fromXml) $data = $this->__captureObjects($data, $user); } - // FIXME chri: validatebut the necessity for all these fields...impact on security ! $fieldList = array( - 'Event' => array('org_id', 'orgc_id', 'date', 'threat_level_id', 'analysis', 'info', 'user_id', 'published', 'uuid', 'timestamp', 'distribution', 'sharing_group_id', 'locked', 'disable_correlation'), - 'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'timestamp', 'distribution', 'comment', 'sharing_group_id', 'deleted', 'disable_correlation'), + 'Event' => array( + 'org_id', + 'orgc_id', + 'date', + 'threat_level_id', + 'analysis', + 'info', + 'user_id', + 'published', + 'uuid', + 'timestamp', + 'distribution', + 'sharing_group_id', + 'locked', + 'disable_correlation' + ), + 'Attribute' => array( + 'event_id', + 'category', + 'type', + 'value', + 'to_ids', + 'uuid', + 'timestamp', + 'distribution', + 'comment', + 'sharing_group_id', + 'deleted', + 'disable_correlation', + 'object_id', + 'object_relation' + ), + 'Object' => array( + 'name', + 'meta-category', + 'description', + 'template_uuid', + 'template_version', + 'event_id', + 'uuid', + 'timestamp', + 'distribution', + 'sharing_group_id', + 'comment', + 'deleted' + ), + 'ObjectRelation' => array() ); $saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList['Event'])); $this->Log = ClassRegistry::init('Log'); if ($saveResult) { if ($passAlong) { $this->Server = ClassRegistry::init('Server'); - $server = $this->Server->find('first', array('conditions' => array('Server.id' => $passAlong), 'recursive' => -1, 'fields' => array('Server.name', 'Server.id', 'Server.unpublish_event'))); + $server = $this->Server->find('first', array( + 'conditions' => array( + 'Server.id' => $passAlong + ), + 'recursive' => -1, + 'fields' => array( + 'Server.name', + 'Server.id', + 'Server.unpublish_event' + ) + )); $this->Log->create(); $this->Log->save(array( 'org' => $user['Organisation']['name'], @@ -2269,60 +2323,18 @@ class Event extends AppModel { } } if (isset($data['Event']['Attribute']) && !empty($data['Event']['Attribute'])) { - foreach ($data['Event']['Attribute'] as $k => &$attribute) { - $attribute['event_id'] = $this->id; - unset($attribute['id']); - if (isset($attribute['encrypt'])) { - $saveResult = $this->Attribute->saveAndEncryptAttribute($attribute, $user); - if ($saveResult !== true) { - $validationErrors['Attribute'][$k] = $saveResult; - $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'add', - 'user_id' => $user['id'], - 'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short, - 'change' => json_encode($saveResult ? $saveResult : array()), - )); - } else { - if (isset($attribute['AttributeTag'])) { - foreach ($attribute['AttributeTag'] as $at) { - $this->Attribute->AttributeTag->create(); - $at['attribute_id'] = $this->Attribute->id; - $at['event_id'] = $this->id; - $this->Attribute->AttributeTag->save($at); - } - } - } - } else { - $this->Attribute->create(); - if (!$this->Attribute->save($attribute, array('fieldList' => $fieldList['Attribute']))) { - $validationErrors['Attribute'][$k] = $this->Attribute->validationErrors; - $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'add', - 'user_id' => $user['id'], - 'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short, - 'change' => 'Validation errors: ' . json_encode($this->Attribute->validationErrors) . ' Full Attribute: ' . json_encode($attribute), - )); - } else { - if (isset($attribute['AttributeTag'])) { - foreach ($attribute['AttributeTag'] as $at) { - $this->Attribute->AttributeTag->create(); - $at['attribute_id'] = $this->Attribute->id; - $at['event_id'] = $this->id; - $this->Attribute->AttributeTag->save($at); - } - } + foreach ($data['Event']['Attribute'] as $k => $attribute) { + $data['Event']['Attribute'][$k] = $this->Attribute->captureAttribute($attribute, $this->id, $user, 0, $this->Log); + } + } + if (!empty($data['Event']['Object'])) { + foreach ($data['Event']['Object'] as $object) { + $result = $this->Object->captureObject($object, $this->id, $user, $this->Log); + } + foreach ($data['Event']['Object'] as $object) { + if (isset($object['ObjectReference'])) { + foreach ($object['ObjectReference'] as $objectRef) { + $result = $this->Object->ObjectReference->captureReference($objectRef, $this->id, $user, $this->Log); } } } @@ -2395,136 +2407,45 @@ class Event extends AppModel { } else { return (array('error' => 'Event could not be saved: Could not find the local event.')); } - if (isset($data['Event']['published']) && $data['Event']['published'] && !$user['Role']['perm_publish']) $data['Event']['published'] = 0; + if (!empty($data['Event']['published']) && !$user['Role']['perm_publish']) $data['Event']['published'] = 0; if (!isset($data['Event']['published'])) $data['Event']['published'] = 0; $fieldList = array( - 'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'distribution', 'timestamp', 'sharing_group_id', 'disable_correlation'), - 'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment', 'sharing_group_id', 'deleted', 'disable_correlation') + 'date', + 'threat_level_id', + 'analysis', + 'info', + 'published', + 'uuid', + 'distribution', + 'timestamp', + 'sharing_group_id', + 'disable_correlation' ); - $saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList['Event'])); + $saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList)); $this->Log = ClassRegistry::init('Log'); if ($saveResult) { $validationErrors = array(); if (isset($data['Event']['Attribute'])) { $data['Event']['Attribute'] = array_values($data['Event']['Attribute']); foreach ($data['Event']['Attribute'] as $k => $attribute) { - $attribute['event_id'] = $existingEvent['Event']['id']; - if (isset($attribute['encrypt'])) { - if (isset($attribute['uuid'])) { - $existingAttribute = $this->Attribute->findByUuid($attribute['uuid']); - if (!empty($existingAttribute)) { - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'add', - 'user_id' => $user['id'], - 'title' => 'Attribute dropped because the encrypt parameter was passed along an attribute that already exists', - 'change' => '', - )); - } - continue; - } - $saveResult = $this->Attribute->saveAndEncryptAttribute($attribute, $user); - if ($saveResult !== true) { - $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'add', - 'user_id' => $user['id'], - 'title' => 'Attribute dropped due to validation for Event ' . $id . ' failed: ' . $attribute_short, - 'change' => json_encode($saveResult ? $saveResult : array()), - )); - } - } else { - if (isset($attribute['uuid'])) { - $existingAttribute = $this->Attribute->findByUuid($attribute['uuid']); - if (count($existingAttribute)) { - if ($existingAttribute['Attribute']['event_id'] != $id) { - $result = $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'edit', - 'user_id' => $user['id'], - 'title' => 'Duplicate UUID found in attribute', - 'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'], - )); - unset($data['Event']['Attribute'][$k]); - } else { - // If a field is not set in the request, just reuse the old value - $recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id'); - foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $data['Event']['Attribute'][$k][$rF] = $existingAttribute['Attribute'][$rF]; - $data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id']; - // Check if the attribute's timestamp is bigger than the one that already exists. - // If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute. - // Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event. - if (isset($data['Event']['Attribute'][$k]['timestamp'])) { - if ($data['Event']['Attribute'][$k]['timestamp'] <= $existingAttribute['Attribute']['timestamp']) { - unset($data['Event']['Attribute'][$k]); - continue; - } - } else { - $data['Event']['Attribute'][$k]['timestamp'] = $date; - } - } - } else { - $this->Attribute->create(); - } - } else { - $this->Attribute->create(); - } + $result = $this->Attribute->editAttribute($attribute, $this->id, $user, 0, $this->Log); + if ($result !== true) { + $validationErrors['Attribute'][] = $result; } - $data['Event']['Attribute'][$k]['event_id'] = $this->id; - if ($data['Event']['Attribute'][$k]['distribution'] == 4) { - $data['Event']['Attribute'][$k]['sharing_group_id'] = $this->SharingGroup->captureSG($data['Event']['Attribute'][$k]['SharingGroup'], $user); + } + } + if (isset($data['Event']['Object'])) { + $data['Event']['Object'] = array_values($data['Event']['Object']); + foreach ($data['Event']['Object'] as $k => $object) { + $result = $this->Object->editObject($object, $this->id, $user, $this->Log); + if ($result !== true) { + $validationErrors['Object'][] = $result; } - if (!$this->Attribute->save($data['Event']['Attribute'][$k], array('fieldList' => $fieldList['Attribute']))) { - $validationErrors['Attribute'][$k] = $this->Attribute->validationErrors; - $attribute_short = (isset($data['Event']['Attribute'][$k]['category']) ? $data['Event']['Attribute'][$k]['category'] : 'N/A') . '/' . (isset($data['Event']['Attribute'][$k]['type']) ? $data['Event']['Attribute'][$k]['type'] : 'N/A') . ' ' . (isset($data['Event']['Attribute'][$k]['value']) ? $data['Event']['Attribute'][$k]['value'] : 'N/A'); - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attribute', - 'model_id' => 0, - 'email' => $user['email'], - 'action' => 'edit', - 'user_id' => $user['id'], - 'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short, - 'change' => 'Validation errors: ' . json_encode($this->Attribute->validationErrors) . ' Full Attribute: ' . json_encode($attribute), - )); - } else { - if (isset($data['Event']['Attribute'][$k]['Tag']) && $user['Role']['perm_tagger']) { - foreach ($data['Event']['Attribute'][$k]['Tag'] as $tag) { - $tag_id = $this->Attribute->AttributeTag->Tag->captureTag($tag, $user); - if ($tag_id) { - $this->Attribute->AttributeTag->attachTagToAttribute($this->Attribute->id, $this->id, $tag_id); - } else { - // If we couldn't attach the tag it is most likely because we couldn't create it - which could have many reasons - // However, if a tag couldn't be added, it could also be that the user is a tagger but not a tag editor - // In which case if no matching tag is found, no tag ID is returned. Logging these is pointless as it is the correct behaviour. - if ($user['Role']['perm_tag_editor']) { - $this->Log->create(); - $this->Log->save(array( - 'org' => $user['Organisation']['name'], - 'model' => 'Attrubute', - 'model_id' => $this->Attribute->id, - 'email' => $user['email'], - 'action' => 'edit', - 'user_id' => $user['id'], - 'title' => 'Failed create or attach Tag ' . $tag['name'] . ' to the attribute.', - 'change' => '' - )); - } - } - } + } + foreach ($data['Event']['Object'] as $object) { + if (isset($object['ObjectReference'])) { + foreach ($object['ObjectReference'] as $objectRef) { + $result = $this->Object->ObjectReference->captureReference($objectRef, $this->id, $user, $this->Log); } } } diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 9da003263..bdd5f5978 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -417,29 +417,110 @@ class MispObject extends AppModel { } } - public function captureObject($eventId, $object, $user) { + public function captureObject($object, $eventId, $user, $log = false) { $this->create(); + if (!isset($object['Object'])) { + $object = array('Object' => $object); + } + if (empty($log)) { + $log = ClassRegistry::init('Log'); + } $object['Object']['event_id'] = $eventId; if ($this->save($object)) { $objectId = $this->id; $partialFails = array(); foreach ($object['Object']['Attribute'] as $attribute) { - if (isset($attribute['encrypt'])) { - $result = $this->Attribute->handleMaliciousBase64($eventId, $attribute['value'], $attribute['data'], array('md5')); - $attribute['data'] = $result['data']; - $attribute['value'] = $attribute['value'] . '|' . $result['md5']; - } - $attribute['event_id'] = $eventId; - $attribute['object_id'] = $objectId; - $this->Attribute->create(); - $result = $this->Attribute->save(array('Attribute' => $attribute)); - if (!$result) { - $partialFails[] = $attribute['type']; - } + $this->Attribute->captureAttribute($attribute, $eventId, $user, $objectId, $log); } - if (!empty($partialFails)) return $partialFails; return true; + } else { + $log->create(); + $log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Object', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'add', + 'user_id' => $user['id'], + 'title' => 'Object dropped due to validation for Event ' . $eventId . ' failed: ' . $object['Object']['name'], + 'change' => 'Validation errors: ' . json_encode($this->validationErrors) . ' Full Object: ' . json_encode($attribute), + )); } return 'fail'; } + + public function editObject($object, $eventId, $user, $log) { + $object['event_id'] = $eventId; + if (isset($object['uuid'])) { + $existingObject = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('Object.uuid' => $object['uuid']) + )); + if ($existingObject['Object']['event_id'] != $eventId) { + $this->Log->create(); + $this->Log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Object', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'edit', + 'user_id' => $user['id'], + 'title' => 'Duplicate UUID found in object', + 'change' => 'An object was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $object['uuid'] . '. This can also be due to the same object (or an object with the same UUID) existing in a different event)', + )); + return true; + } + if (empty($existingObject)) { + return $this->captureObject($object, $eventId, $user, $log); + } else { + if (isset($object['timestamp'])) { + if ($existingObject['Object']['timestamp'] >= $object['timestamp']) { + return true; + } + } else { + $object['timestamp'] = $dateObj->getTimestamp(); + } + } + } else { + return $this->captureObject($object, $eventId, $user, $log); + } + // At this point we have an existingObject that we can edit + $recoverFields = array( + 'name', + 'meta-category', + 'description', + 'template_uuid', + 'template_version', + 'distribution', + 'sharing_group_id', + 'comment', + 'deleted' + ); + foreach ($recoverFields as $rF) if (!isset($object[$rF])) $object[$rF] = $existingObject['Object'][$rF]; + $object['id'] = $existingObject['Object']['id']; + $object['uuid'] = $existingObject['Object']['uuid']; + $object['event_id'] = $eventId; + if ($object['distribution'] == 4) { + $object['sharing_group_id'] = $this->SharingGroup->captureSG($object['SharingGroup'], $user); + } + if (!$this->save($object)) { + $this->Log->create(); + $this->Log->save(array( + 'org' => $user['Organisation']['name'], + 'model' => 'Object', + 'model_id' => 0, + 'email' => $user['email'], + 'action' => 'edit', + 'user_id' => $user['id'], + 'title' => 'Attribute dropped due to validation for Event ' . $eventId . ' failed: ' . $object['Object']['name'], + 'change' => 'Validation errors: ' . json_encode($this->validationErrors) . ' Full Object: ' . json_encode($attribute), + )); + return $this->validationErrors; + } + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $attribute) { + $result = $this->Attribute->editAttribute($attribute, $eventId, $user, $this->id, $log); + } + } + } } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 02425d601..33dddd534 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -106,4 +106,85 @@ class ObjectReference extends AppModel { } return true; } + + public function captureReference($reference, $eventId, $user, $log = false) { + if ($log == false) { + $log = ClassRegistry::init('Log'); + } + if (isset($reference['uuid'])) { + $existingReference = $this->find('first', array( + 'conditions' => array('ObjectReference.uuid' => $reference['uuid']) + )); + if (empty($reference)) { + return true; + } + // ObjectReference not newer than existing one + if (isset($reference['timestamp']) && $reference['timestamp'] <= $existingReference['ObjectReference']['timestamp']) { + return true; + } + $fieldsToUpdate = array('timestamp', 'relationship_type', 'comment', 'deleted'); + foreach ($fieldsToUpdate as $field) { + if (isset($reference[$field])) $existingReference['ObjectReference'][$field] = $reference[$field]; + } + $result = $this->save($existingReference); + if ($result) { + return true; + } else { + return $this->validationErrors; + } + } else { + if (isset($reference['source_uuid'])) { + $conditions = array('Object.uuid' => $reference['source_uuid']); + } else if (isset($reference['object_id'])) { + $conditions = array('Object.id' => $reference['object_id']); + } else { + return true; + } + $sourceObject = $this->Object->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions + )); + if (isset($reference['destination_uuid'])) { + $conditions[0] = array('Attribute.uuid' => $reference['destination_uuid']); + $conditions[1] = array('Object.uuid' => $reference['destination_uuid']); + } else if (isset($reference['object_id'])) { + if ($reference['object_type'] == 1) { + $conditions[0] = array('Attribute.id' => $reference['object_id']); + $conditions[1] = array('Object.id' => $reference['object_id']); + } else { + $conditions = false; + } + } else { + return true; + } + if ($conditions) { + $destinationObject = $this->Object->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions[1] + )); + } + if (!isset($destinationObject)) { + $destinationObject = $this->Attribute->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions[0] + )); + if (empty($destinationObject)) return true; + $object_type = 0; + } else { + $object_type = 1; + } + $objectTypes = array('Attribute', 'Object'); + if ($sourceObject['Object']['event_id'] != $eventId) return true; + if ($destinationObject[$objectTypes[$object_type]]['event_id'] != $eventId) return true; + $this->create(); + unset($reference['id']); + $reference['referenced_type'] = $object_type; + $reference['object_id'] = $sourceObject['Object']['id']; + $reference['referenced_id'] = $destinationObject[$objectTypes[$object_type]]['id']; + $reference['destination_uuid'] = $destinationObject[$objectTypes[$object_type]]['uuid']; + $reference['source_uuid'] = $sourceObject['Object']['uuid']; + $this->save(array('ObjectReference' => $reference)); + return true; + } + } } From 634ce56c7165af1e7417036412ea8386f99f25c3 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2017 22:18:14 +0200 Subject: [PATCH 46/77] fix: Fixed an invalid user field lookup --- app/Model/Event.php | 66 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index 5b9fa2ee7..18071c23b 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1496,7 +1496,7 @@ class Event extends AppModel { 'conditions' => array('id' => $reference['referenced_id']) )); if (!empty($temp)) { - if (!$isSiteAdmin && $user['User']['org_id'] != $event['Event']['orgc_id']) { + if (!$isSiteAdmin && $user['org_id'] != $event['Event']['orgc_id']) { if ($temp[$type]['distribution'] == 0 || ($temp[$type]['distribution'] == 4 && !in_array($temp[$type]['sharing_group_id'], $sgsids))) { unset($object['ObjectReference'][$k2]); continue; @@ -2553,47 +2553,41 @@ class Event extends AppModel { // Uploads this specific event to all remote servers public function uploadEventToServersRouter($id, $passAlong = null) { // make sure we have all the data of the Event + $orgContain = array( + 'fields' => array('id', 'uuid', 'name') + ); + $sharingGroupContain = array( + 'Organisation' => $orgContain, + 'SharingGroupOrg' => array( + 'fields' => array('id', 'org_id', 'extend'), + 'Organisation' => $orgContain + ), + 'SharingGroupServer' => array( + 'fields' => array('id', 'server_id', 'all_orgs'), + 'Server' => array( + 'fields' => array('id', 'url', 'name') + ) + ) + ); + $attributeContain = array( + 'SharingGroup' => $sharingGroupContain, + 'AttributeTag' => array('Tag') + ); $event = $this->find('first', array( 'conditions' => array('Event.id' => $id), 'recursive' => -1, 'contain' => array( - 'Attribute' => array( - 'SharingGroup' => array( - 'SharingGroupOrg' => array( - 'fields' => array('id', 'org_id', 'extend'), - 'Organisation' => array( - 'fields' => array('id', 'uuid', 'name') - ) - ), - 'SharingGroupServer' => array( - 'fields' => array('id', 'server_id', 'all_orgs'), - 'Server' => array( - 'fields' => array('id', 'url', 'name') - ) - ), - ), - 'AttributeTag' => array('Tag') + 'Attribute' => $attributeContain, + 'Object' => array( + 'fields' => array(), + 'SharingGroup' => $sharingGroupContain, + 'ObjectReference' => array(), + 'Attribute' => $attributeContain ), 'EventTag' => array('Tag'), - 'Org' => array('fields' => array('id', 'uuid', 'name', 'local')), - 'Orgc' => array('fields' => array('id', 'uuid', 'name', 'local')), - 'SharingGroup' => array( - 'Organisation' => array( - 'fields' => array('id', 'uuid', 'name', 'local'), - ), - 'SharingGroupOrg' => array( - 'fields' => array('id', 'org_id', 'extend'), - 'Organisation' => array( - 'fields' => array('id', 'uuid', 'name') - ) - ), - 'SharingGroupServer' => array( - 'fields' => array('id', 'server_id', 'all_orgs'), - 'Server' => array( - 'fields' => array('id', 'url', 'name') - ) - ), - ), + 'Org' => $orgContain, + 'Orgc' => $orgContain, + 'SharingGroup' => $sharingGroupContain ), )); if (empty($event)) return true; From 80a00ea0545684828d242cb064aa6138130322dc Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2017 22:36:35 +0200 Subject: [PATCH 47/77] fix: Fixing various issues with the pull --- app/Controller/ObjectsController.php | 11 ++++++----- app/Model/Event.php | 12 ++++++++++++ app/Model/MispObject.php | 7 +++++++ app/Model/ObjectReference.php | 5 +++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index a9678f4f6..696b3bd08 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -249,25 +249,26 @@ class ObjectsController extends AppController { } $objectToSave = $this->MispObject->attributeCleanup($this->request->data); $objectToSave = $this->MispObject->deltaMerge($object, $objectToSave); - // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return // to the add form if (empty($error)) { if ($this->_isRest()) { - if (is_numeric($result)) { + if (is_numeric($objectToSave)) { $objectToSave = $this->MispObject->find('first', array( 'recursive' => -1, 'conditions' => array('Object.id' => $result), 'contain' => array('Attribute') )); + $this->MispObject->Event->unpublishEvent($object['Object']['event_id']); return $this->RestResponse->viewData($objectToSave, $this->response->type()); } else { - return $this->RestResponse->saveFailResponse('Attributes', 'add', false, $result, $this->response->type()); + return $this->RestResponse->saveFailResponse('Objects', 'add', false, $result, $this->response->type()); } } else { - $this->Session->setFlash('Object saved.'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['event_id'])); + $this->MispObject->Event->unpublishEvent($object['Object']['event_id']); + $this->Session->setFlash('Object saved.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['event_id'])); } } } else { diff --git a/app/Model/Event.php b/app/Model/Event.php index 18071c23b..64553241e 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3731,4 +3731,16 @@ class Event extends AppModel { private function __destroyCaches() { $this->__assetCache = array(); } + + public function unpublishEvent($id) { + $event = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('Event.id' => $id) + )); + if (empty($event)) return false; + $event['Event']['published'] = 0; + $date = new DateTime(); + $event['Event']['timestamp'] = $date->getTimestamp(); + return $this->save($event); + } } diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index bdd5f5978..73b083df0 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -66,6 +66,9 @@ class MispObject extends AppModel { if (empty($this->data[$this->alias]['template_version'])) { $this->data[$this->alias]['template_version'] = 1; } + if (isset($this->data[$this->alias]['deleted']) && empty($this->data[$this->alias]['deleted'])) { + $this->data[$this->alias]['deleted'] = 0; + } if (!isset($this->data[$this->alias]['distribution']) || $this->data['Object']['distribution'] != 4) $this->data['Object']['sharing_group_id'] = 0; if (!isset($this->data[$this->alias]['distribution'])) $this->data['Object']['distribution'] = 5; return true; @@ -415,6 +418,7 @@ class MispObject extends AppModel { $originalAttribute['deleted'] = 1; $this->Event->Attribute->save($originalAttribute); } + return $this->id; } public function captureObject($object, $eventId, $user, $log = false) { @@ -427,6 +431,7 @@ class MispObject extends AppModel { } $object['Object']['event_id'] = $eventId; if ($this->save($object)) { + $this->Event->unpublishEvent($eventId); $objectId = $this->id; $partialFails = array(); foreach ($object['Object']['Attribute'] as $attribute) { @@ -516,6 +521,8 @@ class MispObject extends AppModel { 'change' => 'Validation errors: ' . json_encode($this->validationErrors) . ' Full Object: ' . json_encode($attribute), )); return $this->validationErrors; + } else { + $this->Event->unpublishEvent($eventId); } if (!empty($object['Attribute'])) { foreach ($object['Attribute'] as $attribute) { diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 33dddd534..2008b1269 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -113,9 +113,10 @@ class ObjectReference extends AppModel { } if (isset($reference['uuid'])) { $existingReference = $this->find('first', array( - 'conditions' => array('ObjectReference.uuid' => $reference['uuid']) + 'conditions' => array('ObjectReference.uuid' => $reference['uuid']), + 'recursive' => -1 )); - if (empty($reference)) { + if (empty($existingReference)) { return true; } // ObjectReference not newer than existing one From 4bf01000dd8017b546fa20a26d21799e76ad0641 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 4 Sep 2017 23:12:46 +0200 Subject: [PATCH 48/77] fix: Various fixes with object reference editing --- app/Controller/ObjectReferencesController.php | 3 +- app/Model/MispObject.php | 10 ++ app/Model/ObjectReference.php | 162 ++++++++++-------- 3 files changed, 105 insertions(+), 70 deletions(-) diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index c62efa5a4..8511f091f 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -54,7 +54,7 @@ class ObjectReferencesController extends AppController { )); if (!empty($target_object)) { $referenced_id = $target_object['Object']['id']; - $target_uuid = $target_attribute['Object']['uuid']; + $destination_uuid = $target_object['Object']['uuid']; if ($target_object['Object']['event_id'] != $object['Event']['id']) { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } @@ -93,6 +93,7 @@ class ObjectReferencesController extends AppController { $this->ObjectReference->create(); $result = $this->ObjectReference->save(array('ObjectReference' => $data)); if ($result) { + $this->ObjectReference->updateTimestamps($this->id, $data); if ($this->_isRest()) { $object = $this->ObjectReference->find("first", array( 'recursive' => -1, diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 73b083df0..567e5b822 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -530,4 +530,14 @@ class MispObject extends AppModel { } } } + + public function updateTimestamp($id) { + $date = new DateTime(); + $object = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('Object.id' => $id) + )); + $object['Object']['timestamp'] = $date->getTimestamp(); + return $this->save($object); + } } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 2008b1269..3939d0736 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -48,9 +48,29 @@ class ObjectReference extends AppModel { return true; } + public function updateTimestamps($id, $objectReference = false) { + if (!$objectReference) { + $objectReference = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('ObjectReference.id' => $id), + 'fields' => array('event_id', 'object_id') + )); + } + if (empty($objectReference)) return false; + if (!isset($objectReference['ObjectReference'])) { + $objectReference = array('ObjectReference' => $objectReference); + } + $this->Object->updateTimestamp($objectReference['ObjectReference']['object_id']); + $this->Object->Event->unpublishEvent($objectReference['ObjectReference']['event_id']); + } + public function smartDelete($id, $hard = false) { if ($hard) { - return $this->delete($id); + $result = $this->delete($id); + if ($result) { + $this->updateTimestamps($id); + } + return $result; } else { $reference = $this->find('first', array( 'conditions' => array('ObjectReference.id' => $id), @@ -59,7 +79,10 @@ class ObjectReference extends AppModel { if (empty($reference)) return array('Invalid object reference.'); $reference['ObjectReference']['deleted'] = 1; $result = $this->save($reference); - if ($result) return true; + if ($result) { + $this->updateTimestamps($id); + return true; + } return $this->validationErrors; } } @@ -103,6 +126,8 @@ class ObjectReference extends AppModel { $result = $this->save(array('ObjectReference' => $ojectReference)); if (!$result) { return $this->validationErrors; + } else { + $this->updateTimestamps($this->id, $objectReference); } return true; } @@ -116,76 +141,75 @@ class ObjectReference extends AppModel { 'conditions' => array('ObjectReference.uuid' => $reference['uuid']), 'recursive' => -1 )); - if (empty($existingReference)) { - return true; - } - // ObjectReference not newer than existing one - if (isset($reference['timestamp']) && $reference['timestamp'] <= $existingReference['ObjectReference']['timestamp']) { - return true; - } - $fieldsToUpdate = array('timestamp', 'relationship_type', 'comment', 'deleted'); - foreach ($fieldsToUpdate as $field) { - if (isset($reference[$field])) $existingReference['ObjectReference'][$field] = $reference[$field]; - } - $result = $this->save($existingReference); - if ($result) { - return true; - } else { - return $this->validationErrors; - } - } else { - if (isset($reference['source_uuid'])) { - $conditions = array('Object.uuid' => $reference['source_uuid']); - } else if (isset($reference['object_id'])) { - $conditions = array('Object.id' => $reference['object_id']); - } else { - return true; - } - $sourceObject = $this->Object->find('first', array( - 'recursive' => -1, - 'conditions' => $conditions - )); - if (isset($reference['destination_uuid'])) { - $conditions[0] = array('Attribute.uuid' => $reference['destination_uuid']); - $conditions[1] = array('Object.uuid' => $reference['destination_uuid']); - } else if (isset($reference['object_id'])) { - if ($reference['object_type'] == 1) { - $conditions[0] = array('Attribute.id' => $reference['object_id']); - $conditions[1] = array('Object.id' => $reference['object_id']); - } else { - $conditions = false; + if (!empty($existingReference)) { + // ObjectReference not newer than existing one + if (isset($reference['timestamp']) && $reference['timestamp'] <= $existingReference['ObjectReference']['timestamp']) { + return true; + } + $fieldsToUpdate = array('timestamp', 'relationship_type', 'comment', 'deleted'); + foreach ($fieldsToUpdate as $field) { + if (isset($reference[$field])) $existingReference['ObjectReference'][$field] = $reference[$field]; + } + $result = $this->save($existingReference); + if ($result) { + return true; + } else { + return $this->validationErrors; } - } else { - return true; } - if ($conditions) { - $destinationObject = $this->Object->find('first', array( - 'recursive' => -1, - 'conditions' => $conditions[1] - )); - } - if (!isset($destinationObject)) { - $destinationObject = $this->Attribute->find('first', array( - 'recursive' => -1, - 'conditions' => $conditions[0] - )); - if (empty($destinationObject)) return true; - $object_type = 0; - } else { - $object_type = 1; - } - $objectTypes = array('Attribute', 'Object'); - if ($sourceObject['Object']['event_id'] != $eventId) return true; - if ($destinationObject[$objectTypes[$object_type]]['event_id'] != $eventId) return true; - $this->create(); - unset($reference['id']); - $reference['referenced_type'] = $object_type; - $reference['object_id'] = $sourceObject['Object']['id']; - $reference['referenced_id'] = $destinationObject[$objectTypes[$object_type]]['id']; - $reference['destination_uuid'] = $destinationObject[$objectTypes[$object_type]]['uuid']; - $reference['source_uuid'] = $sourceObject['Object']['uuid']; - $this->save(array('ObjectReference' => $reference)); + } + if (isset($reference['source_uuid'])) { + $conditions = array('Object.uuid' => $reference['source_uuid']); + } else if (isset($reference['object_id'])) { + $conditions = array('Object.id' => $reference['object_id']); + } else { return true; } + $sourceObject = $this->Object->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions + )); + if (isset($reference['destination_uuid'])) { + $conditions[0] = array('Attribute.uuid' => $reference['destination_uuid']); + $conditions[1] = array('Object.uuid' => $reference['destination_uuid']); + } else if (isset($reference['object_id'])) { + if ($reference['referenced_type'] == 1) { + $conditions[0] = array('Attribute.id' => $reference['referenced_id']); + $conditions[1] = array('Object.id' => $reference['referenced_id']); + } else { + $conditions = false; + } + } else { + return true; + } + if ($conditions) { + $destinationObject = $this->Object->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions[1] + )); + } + if (!isset($destinationObject)) { + $destinationObject = $this->Attribute->find('first', array( + 'recursive' => -1, + 'conditions' => $conditions[0] + )); + if (empty($destinationObject)) return true; + $referenced_type = 0; + } else { + $referenced_type = 1; + } + $objectTypes = array('Attribute', 'Object'); + if ($sourceObject['Object']['event_id'] != $eventId) return true; + if ($destinationObject[$objectTypes[$referenced_type]]['event_id'] != $eventId) return true; + $this->create(); + unset($reference['id']); + $reference['referenced_type'] = $referenced_type; + $reference['object_id'] = $sourceObject['Object']['id']; + $reference['referenced_id'] = $destinationObject[$objectTypes[$referenced_type]]['id']; + $reference['destination_uuid'] = $destinationObject[$objectTypes[$referenced_type]]['uuid']; + $reference['source_uuid'] = $sourceObject['Object']['uuid']; + $reference['event_id'] = $eventId; + $this->save(array('ObjectReference' => $reference)); + return true; } } From 89bc6d1690088808887440e0df2a9e002867e3c5 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 5 Sep 2017 10:41:55 +0200 Subject: [PATCH 49/77] fix: Fixed the empty event warning if an event only has objects but no attributes --- app/Controller/EventsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index c1ddd7968..17e21c30a 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -792,7 +792,7 @@ class EventsController extends AppController { } private function __viewUI($event, $continue, $fromEvent) { - $emptyEvent = (!isset($event['Attribute']) || empty($event['Attribute'])); + $emptyEvent = (empty($event['Object']) && empty($event['Attribute'])); $this->set('emptyEvent', $emptyEvent); $attributeCount = isset($event['Attribute']) ? count($event['Attribute']) : 0; $this->set('attribute_count', $attributeCount); From b442a273fcc742dd7ac58108eb9bf5f8a62d2fe3 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 12:20:20 +0200 Subject: [PATCH 50/77] new: Further progress on the synchronisation --- app/Controller/AttributesController.php | 2 + app/Controller/EventsController.php | 11 +- app/Controller/ObjectTemplatesController.php | 15 +- app/Model/Event.php | 217 +++++++++++-------- app/Model/MispObject.php | 2 +- app/Model/ObjectTemplate.php | 12 + 6 files changed, 149 insertions(+), 110 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 87abaef1c..38c5e65c5 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -726,12 +726,14 @@ class AttributesController extends AppController { $this->Event->read(); if ($this->Attribute->data['Attribute']['object_id']) { $result = $this->Attribute->save($this->request->data, array('Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.comment', 'Attribute.distribution', 'Attribute.sharing_group_id')); + $this->Attribute->Object->updateTimestamp($id); } else { $result = $this->Attribute->save($this->request->data); } if ($result) { $this->Session->setFlash(__('The attribute has been saved')); // remove the published flag from the event + $this->Event->unpublishEvent($eventId); $this->Event->set('timestamp', $date->getTimestamp()); $this->Event->set('published', 0); $this->Event->save($this->Event->data, array('fieldList' => array('published', 'timestamp', 'info'))); diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 17e21c30a..cbe527866 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -725,7 +725,7 @@ class EventsController extends AppController { $results = $this->Event->fetchEvent($this->Auth->user(), $conditions); if (empty($results)) throw new NotFoundException('Invalid event'); $event = $results[0]; - $emptyEvent = (!isset($event['Attribute']) || empty($event['Attribute'])); + $emptyEvent = (empty($event['Object']) && empty($event['Attribute'])); $this->set('emptyEvent', $emptyEvent); $params = $this->Event->rearrangeEventForView($event, $this->passedArgs, $all); $this->params->params['paging'] = array($this->modelClass => $params); @@ -2650,7 +2650,7 @@ class EventsController extends AppController { if (isset($searchall) && ($searchall == 1 || $searchall === true || $searchall == 'true')) { $eventIds = $this->__quickFilter($value); } else { - $parameters = array('value', 'type', 'category', 'org', 'eventid', 'uuid'); + $parameters = array('value', 'type', 'category', 'org', 'uuid', 'eventid'); $attributeLevelFilters = array('value', 'type', 'category', 'uuid'); $preFilterLevel = 'event'; foreach ($parameters as $k => $param) { @@ -2658,7 +2658,10 @@ class EventsController extends AppController { if (in_array($param, $attributeLevelFilters)) { $preFilterLevel = 'attribute'; } - $conditions = $this->Event->setSimpleConditions($parameters[$k], ${$parameters[$k]}, $conditions); + if ($param == 'eventid') { + $restrictScopeToEvents = true; + } + $conditions = $this->Event->setSimpleConditions($parameters[$k], ${$parameters[$k]}, $conditions, !empty($restrictScopeToEvents)); } } // If we sent any tags along, load the associated tag names for each attribute @@ -2673,7 +2676,7 @@ class EventsController extends AppController { $params = array( 'conditions' => $conditions ); - $eventIds = $this->Event->fetchSipleEventIds($this->Auth->user(), $params); + $eventIds = $this->Event->fetchSimpleEventIds($this->Auth->user(), $params); } else { $params = array( 'conditions' => $conditions, diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index 6f711cbad..d1e949f9a 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -16,21 +16,8 @@ class ObjectTemplatesController extends AppController { 'recursive' => -1 ); -/* - public function add($eventId) { - - } - - public function edit($id) { - - } - - public function delete($id) { - - } -*/ - public function objectChoice($event_id) { + $this->ObjectTemplate->populateIfEmpty($this->Auth->user()); $templates_raw = $this->ObjectTemplate->find('all', array( 'recursive' => -1, 'fields' => array('id', 'meta-category', 'name', 'description', 'org_id'), diff --git a/app/Model/Event.php b/app/Model/Event.php index 64553241e..f501c8ae3 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -978,7 +978,7 @@ class Event extends AppModel { private function __updateEventForSync($event, $server) { // rearrange things to be compatible with the Xml::fromArray() - $objectsToRearrange = array('Attribute', 'Orgc', 'SharingGroup', 'EventTag', 'Org', 'ShadowAttribute'); + $objectsToRearrange = array('Attribute', 'Object', 'Orgc', 'SharingGroup', 'EventTag', 'Org', 'ShadowAttribute'); foreach ($objectsToRearrange as $o) { if (isset($event[$o])) { $event['Event'][$o] = $event[$o]; @@ -1006,57 +1006,20 @@ class Event extends AppModel { } } } - // remove value1 and value2 from the output - if (isset($event['Event']['Attribute'])) { - foreach ($event['Event']['Attribute'] as $key => &$attribute) { - // do not keep attributes that are private, nor cluster - if (!$server['Server']['internal'] && $attribute['distribution'] < 2) { - unset($event['Event']['Attribute'][$key]); - continue; // stop processing this - } - // Downgrade the attribute from connected communities to community only - if (!$server['Server']['internal'] && $attribute['distribution'] == 2) { - $attribute['distribution'] = 1; - } - // If the attribute has a sharing group attached, make sure it can be transfered - if ($attribute['distribution'] == 4) { - if (!$server['Server']['internal'] && $this->checkDistributionForPush(array('Attribute' => $attribute), $server, 'Attribute') === false) { - unset($event['Event']['Attribute'][$key]); - continue; - } - // Add the local server to the list of instances in the SG - if (isset($attribute['SharingGroup']['SharingGroupServer'])) { - foreach ($attribute['SharingGroup']['SharingGroupServer'] as &$s) { - if ($s['server_id'] == 0) { - $s['Server'] = array('id' => 0, 'url' => Configure::read('MISP.baseurl')); - } - } - } - } - foreach ($attribute['AttributeTag'] as $kt => $tag) { - if (!$tag['Tag']['exportable']) { - unset($attribute['AttributeTag'][$kt]); - } else { - unset($tag['Tag']['org_id']); - $attribute['Tag'][] = $tag['Tag']; - } - } - unset($attribute['AttributeTag']); + // prepare attribute for sync + if (!empty($event['Event']['Attribute'])) { + foreach ($event['Event']['Attribute'] as $key => $attribute) { + $event['Event']['Attribute'][$key] = $this->__updateAttributeForSync($attribute, $server); + if (empty($event['Event']['Attribute'][$key])) unset($event['Event']['Attribute'][$key]); + } + } - // remove value1 and value2 from the output - unset($attribute['value1']); - unset($attribute['value2']); - // also add the encoded attachment - if ($this->Attribute->typeIsAttachment($attribute['type'])) { - $encodedFile = $this->Attribute->base64EncodeAttachment($attribute); - $attribute['data'] = $encodedFile; - } - // Passing the attribute ID together with the attribute could cause the deletion of attributes after a publish/push - // Basically, if the attribute count differed between two instances, and the instance with the lower attribute - // count pushed, the old attributes with the same ID got overwritten. Unsetting the ID before pushing it - // solves the issue and a new attribute is always created. - unset($attribute['id']); + // prepare Object for sync + if (!empty($event['Event']['Object'])) { + foreach ($event['Event']['Object'] as $key => $object) { + $event['Event']['Object'][$key] = $this->__updateObjectForSync($object, $server); + if (empty($event['Event']['Object'][$key])) unset($event['Event']['Object'][$key]); } } @@ -1064,10 +1027,92 @@ class Event extends AppModel { if (!$server['Server']['internal'] && $event['Event']['distribution'] == 2) { $event['Event']['distribution'] = 1; } - $event['Event']['Attribute'] = array_values($event['Event']['Attribute']); + if (!empty($event['Event']['Attribute'])) $event['Event']['Attribute'] = array_values($event['Event']['Attribute']); + if (!empty($event['Event']['Object'])) $event['Event']['Object'] = array_values($event['Event']['Object']); return $event; } + private function __updateObjectForSync($object, $server) { + if (!$server['Server']['internal'] && $object['distribution'] < 2) { + return false; + } + // Downgrade the object from connected communities to community only + if (!$server['Server']['internal'] && $object['distribution'] == 2) { + $object['distribution'] = 1; + } + // If the object has a sharing group attached, make sure it can be transfered + if ($object['distribution'] == 4) { + if (!$server['Server']['internal'] && $this->checkDistributionForPush(array('Object' => $object), $server, 'Object') === false) { + return false; + } + // Add the local server to the list of instances in the SG + if (isset($object['SharingGroup']['SharingGroupServer'])) { + foreach ($object['SharingGroup']['SharingGroupServer'] as &$s) { + if ($s['server_id'] == 0) { + $s['Server'] = array('id' => 0, 'url' => Configure::read('MISP.baseurl')); + } + } + } + } + if (!empty($object['Attribute'])) { + foreach ($object['Attribute'] as $key => $attribute) { + $object['Attribute'][$key] = $this->__updateAttributeForSync($attribute, $server); + if (empty($object['Attribute'][$key])) unset($object['Attribute'][$key]); + } + } + return $object; + } + + private function __updateAttributeForSync($attribute, $server) { + // do not keep attributes that are private, nor cluster + if (!$server['Server']['internal'] && $attribute['distribution'] < 2) { + return false; + } + // Downgrade the attribute from connected communities to community only + if (!$server['Server']['internal'] && $attribute['distribution'] == 2) { + $attribute['distribution'] = 1; + } + + // If the attribute has a sharing group attached, make sure it can be transfered + if ($attribute['distribution'] == 4) { + if (!$server['Server']['internal'] && $this->checkDistributionForPush(array('Attribute' => $attribute), $server, 'Attribute') === false) { + return false; + } + // Add the local server to the list of instances in the SG + if (isset($attribute['SharingGroup']['SharingGroupServer'])) { + foreach ($attribute['SharingGroup']['SharingGroupServer'] as &$s) { + if ($s['server_id'] == 0) { + $s['Server'] = array('id' => 0, 'url' => Configure::read('MISP.baseurl')); + } + } + } + } + foreach ($attribute['AttributeTag'] as $kt => $tag) { + if (!$tag['Tag']['exportable']) { + unset($attribute['AttributeTag'][$kt]); + } else { + unset($tag['Tag']['org_id']); + $attribute['Tag'][] = $tag['Tag']; + } + } + unset($attribute['AttributeTag']); + + // remove value1 and value2 from the output + unset($attribute['value1']); + unset($attribute['value2']); + // also add the encoded attachment + if ($this->Attribute->typeIsAttachment($attribute['type'])) { + $encodedFile = $this->Attribute->base64EncodeAttachment($attribute); + $attribute['data'] = $encodedFile; + } + // Passing the attribute ID together with the attribute could cause the deletion of attributes after a publish/push + // Basically, if the attribute count differed between two instances, and the instance with the lower attribute + // count pushed, the old attributes with the same ID got overwritten. Unsetting the ID before pushing it + // solves the issue and a new attribute is always created. + unset($attribute['id']); + return $attribute; + } + public function downloadEventFromServer($eventId, $server, $HttpSocket=null) { $url = $server['Server']['url']; $authkey = $server['Server']['authkey']; @@ -2552,45 +2597,27 @@ class Event extends AppModel { // Uploads this specific event to all remote servers public function uploadEventToServersRouter($id, $passAlong = null) { - // make sure we have all the data of the Event - $orgContain = array( - 'fields' => array('id', 'uuid', 'name') - ); - $sharingGroupContain = array( - 'Organisation' => $orgContain, - 'SharingGroupOrg' => array( - 'fields' => array('id', 'org_id', 'extend'), - 'Organisation' => $orgContain - ), - 'SharingGroupServer' => array( - 'fields' => array('id', 'server_id', 'all_orgs'), - 'Server' => array( - 'fields' => array('id', 'url', 'name') - ) - ) - ); - $attributeContain = array( - 'SharingGroup' => $sharingGroupContain, - 'AttributeTag' => array('Tag') - ); - $event = $this->find('first', array( - 'conditions' => array('Event.id' => $id), - 'recursive' => -1, - 'contain' => array( - 'Attribute' => $attributeContain, - 'Object' => array( - 'fields' => array(), - 'SharingGroup' => $sharingGroupContain, - 'ObjectReference' => array(), - 'Attribute' => $attributeContain - ), - 'EventTag' => array('Tag'), - 'Org' => $orgContain, - 'Orgc' => $orgContain, - 'SharingGroup' => $sharingGroupContain - ), + $eventOrgcId = $this->find('first', array( + 'conditions' => array('Event.id' => $id), + 'recursive' => -1, + 'fields' => array('Event.orgc_id') )); + // we create a fake site admin user object to fetch the event with everything included + // This replaces the old method of manually just fetching everything, staying consistent + // with the fetchEvent() output + $elevatedUser = array( + 'Role' => array( + 'perm_site_admin' => 1, + 'perm_sync' => 1 + ), + 'org_id' => $eventOrgcId['Event']['orgc_id'] + ); + $elevatedUser['Role']['perm_site_admin'] = 1; + $elevatedUser['Role']['perm_sync'] = 1; + $elevatedUser['Role']['perm_audit'] = 0; + $event = $this->fetchEvent($elevatedUser, array('eventid' => $id, 'includeAttachments' => true, 'includeAllTags' => true)); if (empty($event)) return true; + $event = $event[0]; $event['Event']['locked'] = 1; // get a list of the servers $this->Server = ClassRegistry::init('Server'); @@ -3545,7 +3572,7 @@ class Event extends AppModel { return array('data' => array(), 'csv' => array()); } - public function setSimpleConditions($parameterKey, $parameterValue, $conditions) { + public function setSimpleConditions($parameterKey, $parameterValue, $conditions, $restrictScopeToEvents = false) { if (is_array($parameterValue)) { $elements = $parameterValue; } else { @@ -3573,7 +3600,11 @@ class Event extends AppModel { $subcondition['AND'][] = array('Event.orgc_id !=' => $o['Org']['id']); } } else if ($parameterKey === 'eventid') { - $subcondition['AND'][] = array('Attribute.event_id !=' => substr($v, 1)); + if ($restrictScopeToEvents) { + $subcondition['AND'][] = array('Event.id !=' => substr($v, 1)); + } else { + $subcondition['AND'][] = array('Attribute.event_id !=' => substr($v, 1)); + } } else if ($parameterKey === 'uuid') { $subcondition['AND'][] = array('Event.uuid !=' => substr($v, 1)); $subcondition['AND'][] = array('Attribute.uuid !=' => substr($v, 1)); @@ -3598,7 +3629,11 @@ class Event extends AppModel { $subcondition['OR'][] = array('Event.orgc_id' => $o['Org']['id']); } } else if ($parameterKey === 'eventid') { - $subcondition['OR'][] = array('Attribute.event_id' => $v); + if ($restrictScopeToEvents) { + $subcondition['OR'][] = array('Event.id' => $v); + } else { + $subcondition['OR'][] = array('Attribute.event_id' => $v); + } } else if ($parameterKey === 'uuid') { $subcondition['OR'][] = array('Attribute.uuid' => $v); $subcondition['OR'][] = array('Event.uuid' => $v); diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 567e5b822..53847c4ee 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -303,7 +303,7 @@ class MispObject extends AppModel { $v['value'] = $request_item['value']; $v['to_ids'] = $request_item['to_ids']; $v['comment'] = $request_item['comment']; - $v['uuid'] = $request_item['uuid']; + if (!empty($request_item['uuid'])) $v['uuid'] = $request_item['uuid']; if (isset($request_item['data'])) $v['data'] = $request_item['data']; if (empty($v['categories'])) { $v['categories'] = array(); diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index 24f8cdfe0..abe5d253f 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -220,4 +220,16 @@ class ObjectTemplate extends AppModel { } return true; } + + // simple test to see if there are any object templates - if not trigger update + public function populateIfEmpty($user) { + $result = $this->find('first', array( + 'recursive' => 1, + 'fields' => array('ObjectTemplate.id') + )); + if (empty($result)) { + $this->update($user); + } + return true; + } } From 03881e40c12ffc09836d19924732d3ea1bb4672e Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 14:00:18 +0200 Subject: [PATCH 51/77] chg: Rename two fields in the object references - source_uuid => object_uuid - destination_uuid => referenced_uuid --- INSTALL/MYSQL.sql | 4 ++-- app/Controller/ObjectReferencesController.php | 8 ++++---- app/Model/AppModel.php | 8 ++++---- app/Model/ObjectReference.php | 14 +++++++------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index c2f7d760c..87e547483 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -465,7 +465,7 @@ CREATE TABLE IF NOT EXISTS object_references ( `object_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, `source_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `destination_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, @@ -473,7 +473,7 @@ CREATE TABLE IF NOT EXISTS object_references ( `deleted` TINYINT NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `source_uuid` (`source_uuid`), - INDEX `destination_uuid` (`destination_uuid`), + INDEX `referenced_uuid` (`referenced_uuid`), INDEX `timestamp` (`timestamp`), INDEX `object_id` (`object_id`), INDEX `referenced_id` (`referenced_id`), diff --git a/app/Controller/ObjectReferencesController.php b/app/Controller/ObjectReferencesController.php index 8511f091f..458770f3e 100644 --- a/app/Controller/ObjectReferencesController.php +++ b/app/Controller/ObjectReferencesController.php @@ -54,7 +54,7 @@ class ObjectReferencesController extends AppController { )); if (!empty($target_object)) { $referenced_id = $target_object['Object']['id']; - $destination_uuid = $target_object['Object']['uuid']; + $referenced_uuid = $target_object['Object']['uuid']; if ($target_object['Object']['event_id'] != $object['Event']['id']) { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } @@ -71,7 +71,7 @@ class ObjectReferencesController extends AppController { throw new NotFoundException('Invalid target. Target has to be within the same event.'); } $referenced_id = $target_attribute['Attribute']['id']; - $destination_uuid = $target_attribute['Attribute']['uuid']; + $referenced_uuid = $target_attribute['Attribute']['uuid']; $referenced_type = 0; } $relationship_type = empty($this->request->data['ObjectReference']['relationship_type']) ? '' : $this->request->data['ObjectReference']['relationship_type']; @@ -81,11 +81,11 @@ class ObjectReferencesController extends AppController { $data = array( 'referenced_type' => $referenced_type, 'referenced_id' => $referenced_id, - 'destination_uuid' => $destination_uuid, + 'referenced_uuid' => $referenced_uuid, 'relationship_type' => $relationship_type, 'comment' => !empty($this->request->data['ObjectReference']['comment']) ? $this->request->data['ObjectReference']['comment'] : '', 'event_id' => $object['Event']['id'], - 'source_uuid' => $object['Object']['uuid'], + 'object_uuid' => $object['Object']['uuid'], 'object_id' => $objectId, 'referenced_type' => $referenced_type, 'uuid' => CakeText::uuid() diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 6eafc8495..2b9291f0f 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -741,16 +741,16 @@ class AppModel extends Model { `timestamp` int(11) NOT NULL DEFAULT 0, `object_id` int(11) NOT NULL, `event_id` int(11) NOT NULL, - `source_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, - `destination_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `object_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, + `referenced_uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL, `referenced_id` int(11) NOT NULL, `referenced_type` int(11) NOT NULL DEFAULT 0, `relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, `comment` text COLLATE utf8_bin NOT NULL, `deleted` TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), - INDEX `source_uuid` (`source_uuid`), - INDEX `destination_uuid` (`destination_uuid`), + INDEX `object_uuid` (`object_uuid`), + INDEX `referenced_uuid` (`referenced_uuid`), INDEX `timestamp` (`timestamp`), INDEX `object_id` (`object_id`), INDEX `referenced_id` (`referenced_id`), diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index 3939d0736..ea78aae1c 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -158,8 +158,8 @@ class ObjectReference extends AppModel { } } } - if (isset($reference['source_uuid'])) { - $conditions = array('Object.uuid' => $reference['source_uuid']); + if (isset($reference['object_uuid'])) { + $conditions = array('Object.uuid' => $reference['object_uuid']); } else if (isset($reference['object_id'])) { $conditions = array('Object.id' => $reference['object_id']); } else { @@ -169,9 +169,9 @@ class ObjectReference extends AppModel { 'recursive' => -1, 'conditions' => $conditions )); - if (isset($reference['destination_uuid'])) { - $conditions[0] = array('Attribute.uuid' => $reference['destination_uuid']); - $conditions[1] = array('Object.uuid' => $reference['destination_uuid']); + if (isset($reference['referenced_uuid'])) { + $conditions[0] = array('Attribute.uuid' => $reference['referenced_uuid']); + $conditions[1] = array('Object.uuid' => $reference['referenced_uuid']); } else if (isset($reference['object_id'])) { if ($reference['referenced_type'] == 1) { $conditions[0] = array('Attribute.id' => $reference['referenced_id']); @@ -206,8 +206,8 @@ class ObjectReference extends AppModel { $reference['referenced_type'] = $referenced_type; $reference['object_id'] = $sourceObject['Object']['id']; $reference['referenced_id'] = $destinationObject[$objectTypes[$referenced_type]]['id']; - $reference['destination_uuid'] = $destinationObject[$objectTypes[$referenced_type]]['uuid']; - $reference['source_uuid'] = $sourceObject['Object']['uuid']; + $reference['referenced_uuid'] = $destinationObject[$objectTypes[$referenced_type]]['uuid']; + $reference['object_uuid'] = $sourceObject['Object']['uuid']; $reference['event_id'] = $eventId; $this->save(array('ObjectReference' => $reference)); return true; From 41871d51550a41b3a93157c73269db863f296495 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 14:07:38 +0200 Subject: [PATCH 52/77] fix: Fixed the object reference's timestamp not being updated --- app/Model/ObjectReference.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index ea78aae1c..f7eedab83 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -44,7 +44,7 @@ class ObjectReference extends AppModel { $this->data['ObjectReference']['uuid'] = CakeText::uuid(); } $date = date('Y-m-d H:i:s'); - $this->data['Organisation']['timestamp'] = $date; + $this->data['ObjectReference']['timestamp'] = $date; return true; } From 60f432bb9b36d5778a86cdf26d80714db1ceb516 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 14:10:06 +0200 Subject: [PATCH 53/77] fix: Fixed the timestamp of object references not being set --- app/Model/ObjectReference.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index f7eedab83..a44d8b4c2 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -43,8 +43,8 @@ class ObjectReference extends AppModel { if (empty($this->data['ObjectReference']['uuid'])) { $this->data['ObjectReference']['uuid'] = CakeText::uuid(); } - $date = date('Y-m-d H:i:s'); - $this->data['ObjectReference']['timestamp'] = $date; + $date = new DateTime(); + $this->data['ObjectReference']['timestamp'] = $date->getTimestamp(); return true; } From 3938abe7e1bdce90c870f448574b3a9911f539b1 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 16:10:36 +0200 Subject: [PATCH 54/77] fix: Fixed the add attachments functionalities --- app/Controller/AttributesController.php | 4 +-- app/Model/Attribute.php | 1 - app/Model/MispObject.php | 3 +- app/Model/ObjectReference.php | 37 +++++++++++++------------ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 38c5e65c5..4bdbafb09 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -381,11 +381,11 @@ class AttributesController extends AppController { } } $this->loadModel('MispObject'); - $result = $this->MispObject->captureObject($eventId, array('Object' => $object), $this->Auth->user()); + $this->MispObject->captureObject(array('Object' => $object), $eventId, $this->Auth->user()); } if (!empty($result['ObjectReference'])) { foreach ($result['ObjectReference'] as $reference) { - $result = $this->MispObject->ObjectReference->smartSave($reference, $eventId); + $this->MispObject->ObjectReference->smartSave($reference, $eventId); } } } diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 692016b93..c61d0c8a7 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2800,7 +2800,6 @@ class Attribute extends AppModel { ); $this->create(); if (!$this->save($attribute, array('fieldList' => $fieldList))) { - $validationErrors['Attribute'][$k] = $this->validationErrors; $attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A'); $log->create(); $log->save(array( diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 53847c4ee..749fc27b2 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -538,6 +538,7 @@ class MispObject extends AppModel { 'conditions' => array('Object.id' => $id) )); $object['Object']['timestamp'] = $date->getTimestamp(); - return $this->save($object); + $result = $this->save($object); + return $result; } } diff --git a/app/Model/ObjectReference.php b/app/Model/ObjectReference.php index a44d8b4c2..a02dc4d04 100644 --- a/app/Model/ObjectReference.php +++ b/app/Model/ObjectReference.php @@ -88,7 +88,7 @@ class ObjectReference extends AppModel { } public function smartSave($objectReference, $eventId) { - $sides = array('source', 'destination'); + $sides = array('object', 'referenced'); $data = array(); foreach ($sides as $side) { $data[$side] = $this->Object->find('first', array( @@ -99,7 +99,7 @@ class ObjectReference extends AppModel { 'recursive' => -1, 'fields' => array('Object.id') )); - if (empty($data[$side]) && $side == 'destination') { + if (empty($data[$side]) && $side == 'referenced') { $data[$side] = $this->Attribute->find('first', array( 'conditions' => array( 'Attribute.uuid' => $objectReference[$side . '_uuid'], @@ -108,22 +108,23 @@ class ObjectReference extends AppModel { 'recursive' => -1, 'fields' => array('Attribute.id') )); - $destination_id = $data[$side]['Attribute']['id']; - $destination_type = 0; - } else if (!empty($data[$side]) && $side == 'destination') { - $destination_id = $data[$side]['Object']['id']; - $destination_type = 1; - } else if (!empty($data[$side]) && $side = 'source') { + $referenced_id = $data[$side]['Attribute']['id']; + $referenced_type = 0; + } else if (!empty($data[$side]) && $side == 'referenced') { + $referenced_id = $data[$side]['Object']['id']; + $referenced_type = 1; + } else if (!empty($data[$side]) && $side = 'object') { $object_id = $data[$side]['Object']['id']; } else { return 'Invalid ' . $side . ' uuid'; } } $this->create(); - $objectReference['destination_type'] = $destination_type; - $objectReference['destination_id'] = $destination_id; + $objectReference['referenced_type'] = $referenced_type; + $objectReference['referenced_id'] = $referenced_id; $objectReference['object_id'] = $object_id; - $result = $this->save(array('ObjectReference' => $ojectReference)); + $objectReference['event_id'] = $eventId; + $result = $this->save(array('ObjectReference' => $objectReference)); if (!$result) { return $this->validationErrors; } else { @@ -183,30 +184,30 @@ class ObjectReference extends AppModel { return true; } if ($conditions) { - $destinationObject = $this->Object->find('first', array( + $referencedObject = $this->Object->find('first', array( 'recursive' => -1, 'conditions' => $conditions[1] )); } - if (!isset($destinationObject)) { - $destinationObject = $this->Attribute->find('first', array( + if (!isset($referencedObject)) { + $referencedObject = $this->Attribute->find('first', array( 'recursive' => -1, 'conditions' => $conditions[0] )); - if (empty($destinationObject)) return true; + if (empty($referencedObject)) return true; $referenced_type = 0; } else { $referenced_type = 1; } $objectTypes = array('Attribute', 'Object'); if ($sourceObject['Object']['event_id'] != $eventId) return true; - if ($destinationObject[$objectTypes[$referenced_type]]['event_id'] != $eventId) return true; + if ($referencedObject[$objectTypes[$referenced_type]]['event_id'] != $eventId) return true; $this->create(); unset($reference['id']); $reference['referenced_type'] = $referenced_type; $reference['object_id'] = $sourceObject['Object']['id']; - $reference['referenced_id'] = $destinationObject[$objectTypes[$referenced_type]]['id']; - $reference['referenced_uuid'] = $destinationObject[$objectTypes[$referenced_type]]['uuid']; + $reference['referenced_id'] = $referencedObject[$objectTypes[$referenced_type]]['id']; + $reference['referenced_uuid'] = $referencedObject[$objectTypes[$referenced_type]]['uuid']; $reference['object_uuid'] = $sourceObject['Object']['uuid']; $reference['event_id'] = $eventId; $this->save(array('ObjectReference' => $reference)); From 77f4d49e474d013f3d2e182282e796a05317f8ec Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 7 Sep 2017 16:52:18 +0200 Subject: [PATCH 55/77] fix: Various fixes for the objects --- app/Controller/AttributesController.php | 2 ++ app/Controller/ObjectsController.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 4bdbafb09..3b5c18aa2 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -375,6 +375,8 @@ class AttributesController extends AppController { } if (!empty($result)) { foreach ($result['Object'] as $object) { + $object['distribution'] = $this->request->data['Attribute']['distribution']; + $object['sharing_group_id'] = isset($this->request->data['Attribute']['distribution']) ? $this->request->data['Attribute']['distribution'] : 0; if (!empty($object['Attribute'])) { foreach ($object['Attribute'] as $k => $attribute) { if ($attribute['value'] == $tmpfile->name) $object['Attribute'][$k]['value'] = $value['name']; diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 696b3bd08..dfabe9247 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -234,6 +234,10 @@ class ObjectsController extends AppController { 'ObjectTemplateElement' ) )); + if (empty($template)) { + $this->Session->setFlash('Object cannot be edited, no valid template found.'); + $this->redirect(array('controller' => 'events', 'action' => 'view', $object['Object']['event_id'])); + } $template = $this->MispObject->prepareTemplate($template, $object); $enabledRows = false; From 470b7e5524b925f745841250d66b936f9606564a Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 8 Sep 2017 10:31:02 +0200 Subject: [PATCH 56/77] new: Added diagnostics for the new attachment tools --- app/Controller/ServersController.php | 7 +++++++ .../Elements/healthElements/diagnostics.ctp | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php index af0e1ffff..79d1291c8 100644 --- a/app/Controller/ServersController.php +++ b/app/Controller/ServersController.php @@ -762,6 +762,13 @@ class ServersController extends AppController { if ($tab == 'diagnostics' || $tab == 'download') { $php_ini = php_ini_loaded_file(); $this->set('php_ini', $php_ini); + $advanced_attachments = shell_exec('python ' . APP . 'files/scripts/generate_file_objects.py -c'); + try { + $advanced_attachments = json_decode($advanced_attachments, true); + } catch (Exception $e) { + $advanced_attachments = false; + } + $this->set('advanced_attachments', $advanced_attachments); // check if the current version of MISP is outdated or not $version = $this->__checkVersion(); $this->set('version', $version); diff --git a/app/View/Elements/healthElements/diagnostics.ctp b/app/View/Elements/healthElements/diagnostics.ctp index 72c6d55ca..245a3ae20 100644 --- a/app/View/Elements/healthElements/diagnostics.ctp +++ b/app/View/Elements/healthElements/diagnostics.ctp @@ -180,6 +180,26 @@ +

    + Advanced attachment handler +

    + The advanced attachment tools are used by the add attachment functionality to extract additional data about the uploaded sample. +
    + + PyMISP:..... Not installed or version outdated.
    + $v): + ?> + :..... OK' : '' . h($v) . ''; ?>
    + +

    STIX and Cybox libraries

    From d77ba1ddba6f443891d92908dc17306938438395 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 13 Sep 2017 13:53:40 +0200 Subject: [PATCH 57/77] new: Added objects to object preview --- app/Controller/ServersController.php | 4 +- app/Model/AppModel.php | 4 +- app/Model/ObjectTemplate.php | 2 +- app/View/Elements/Events/eventIndexTable.ctp | 2 +- .../Elements/Servers/View/row_attribute.ctp | 146 +++++++++++++++++ app/View/Elements/Servers/View/row_object.ctp | 51 ++++++ .../Servers/View/row_object_reference.ctp | 35 +++++ .../Servers/View/row_object_referenced_by.ctp | 32 ++++ .../Elements/Servers/View/value_field.ctp | 49 ++++++ app/View/Elements/Servers/eventattribute.ctp | 148 ++++++++++++++++++ app/View/Elements/ajaxAttributeTags.ctp | 21 ++- app/View/Servers/preview_event.ctp | 140 +---------------- 12 files changed, 486 insertions(+), 148 deletions(-) create mode 100644 app/View/Elements/Servers/View/row_attribute.ctp create mode 100644 app/View/Elements/Servers/View/row_object.ctp create mode 100644 app/View/Elements/Servers/View/row_object_reference.ctp create mode 100644 app/View/Elements/Servers/View/row_object_referenced_by.ctp create mode 100644 app/View/Elements/Servers/View/value_field.ctp create mode 100644 app/View/Elements/Servers/eventattribute.ctp diff --git a/app/Controller/ServersController.php b/app/Controller/ServersController.php index 79d1291c8..c5eb2c75c 100644 --- a/app/Controller/ServersController.php +++ b/app/Controller/ServersController.php @@ -123,11 +123,13 @@ class ServersController extends AppController { $this->loadModel('Event'); $dataForView = array( 'Attribute' => array('attrDescriptions' => 'fieldDescriptions', 'distributionDescriptions' => 'distributionDescriptions', 'distributionLevels' => 'distributionLevels'), - 'Event' => array('eventDescriptions' => 'fieldDescriptions', 'analysisLevels' => 'analysisLevels') + 'Event' => array('eventDescriptions' => 'fieldDescriptions', 'analysisLevels' => 'analysisLevels'), + 'Object' => array() ); foreach ($dataForView as $m => $variables) { if ($m === 'Event') $currentModel = $this->Event; else if ($m === 'Attribute') $currentModel = $this->Event->Attribute; + else if ($m === 'Object') $currentModel = $this->Event->Object; foreach ($variables as $alias => $variable) { $this->set($alias, $currentModel->{$variable}); } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 2b9291f0f..a9e36aded 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -47,7 +47,7 @@ class AppModel extends Model { 58 => false, 59 => false, 60 => false, 61 => false, 62 => false, 63 => false, 64 => false, 65 => false, 66 => false, 67 => true, 68 => false, 69 => false, 71 => false, 72 => false, 73 => false, - 75 => false, 77 => false, 78 => false, 79 => false + 75 => false, 77 => false, 78 => false, 79 => false, 80 => false ) ) ); @@ -708,7 +708,7 @@ class AppModel extends Model { $this->__addIndex('galaxy_clusters', 'galaxy_id'); $this->__addIndex('galaxy_elements', 'galaxy_cluster_id'); break; - case '2.4.79': + case '2.4.80': $sqlArray[] = "CREATE TABLE IF NOT EXISTS objects ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci, diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index abe5d253f..2854552b9 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -224,7 +224,7 @@ class ObjectTemplate extends AppModel { // simple test to see if there are any object templates - if not trigger update public function populateIfEmpty($user) { $result = $this->find('first', array( - 'recursive' => 1, + 'recursive' => -1, 'fields' => array('ObjectTemplate.id') )); if (empty($result)) { diff --git a/app/View/Elements/Events/eventIndexTable.ctp b/app/View/Elements/Events/eventIndexTable.ctp index 14188e9a6..d29bcb7e2 100644 --- a/app/View/Elements/Events/eventIndexTable.ctp +++ b/app/View/Elements/Events/eventIndexTable.ctp @@ -69,7 +69,7 @@ -
    + + + + + + + + + + + + diff --git a/app/View/Elements/Servers/View/row_object.ctp b/app/View/Elements/Servers/View/row_object.ctp new file mode 100644 index 000000000..87dabd35d --- /dev/null +++ b/app/View/Elements/Servers/View/row_object.ctp @@ -0,0 +1,51 @@ + + + + + + + + $attribute) { + echo $this->element('/Servers/View/row_' . $attribute['objectType'], array( + 'object' => $attribute, + 'page' => $page, + 'fieldCount' => $fieldCount, + 'child' => $attrKey == $lastElement ? 'last' : true + )); + } + } +?> diff --git a/app/View/Elements/Servers/View/row_object_reference.ctp b/app/View/Elements/Servers/View/row_object_reference.ctp new file mode 100644 index 000000000..5f043dea8 --- /dev/null +++ b/app/View/Elements/Servers/View/row_object_reference.ctp @@ -0,0 +1,35 @@ +References: + + + + +
    + +    + + + +
    + +
    diff --git a/app/View/Elements/Servers/View/row_object_referenced_by.ctp b/app/View/Elements/Servers/View/row_object_referenced_by.ctp new file mode 100644 index 000000000..e0801b3fe --- /dev/null +++ b/app/View/Elements/Servers/View/row_object_referenced_by.ctp @@ -0,0 +1,32 @@ +Referenced by: + + + +
    + $reference): + foreach ($reference as $ref): + if ($type == 'object') { + $uuid = $ref['uuid']; + $output = ' (' . $ref['meta-category'] . ': ' . $ref['name'] . ')'; + } else { + $uuid = $ref['uuid']; + $output = ' (' . $ref['category'] . '/' . $ref['type'] . ': "' . $ref['value'] . '")'; + } +?> +    + + + +
    + +
    diff --git a/app/View/Elements/Servers/View/value_field.ctp b/app/View/Elements/Servers/View/value_field.ctp new file mode 100644 index 000000000..d03e2e911 --- /dev/null +++ b/app/View/Elements/Servers/View/value_field.ctp @@ -0,0 +1,49 @@ +'; + } else { + $filenameHash = explode('|', nl2br(h($object['value']))); + if (strrpos($filenameHash[0], '\\')) { + $filepath = substr($filenameHash[0], 0, strrpos($filenameHash[0], '\\')); + $filename = substr($filenameHash[0], strrpos($filenameHash[0], '\\')); + echo h($filepath); + echo '' . h($filename) . ''; + } else { + echo '' . h($filenameHash[0]) . ''; + } + if (isset($filenameHash[1])) echo '
    ' . $filenameHash[1]; + } + } else if (strpos($object['type'], '|') !== false) { + $separator = in_array($object['type'], array('ip-dst|port', 'ip-src|port')) ? ':' : '
    '; + $separator_pos = strpos('|', $object['value']); + $final_value = h($object['value']); + echo substr_replace(h($object['value']), $separator, $separator_pos, strlen($separator)); + } else if ('vulnerability' == $object['type']) { + $cveUrl = (is_null(Configure::read('MISP.cveurl'))) ? "http://www.google.com/search?q=" : Configure::read('MISP.cveurl'); + echo $this->Html->link($sigDisplay, $cveUrl . $sigDisplay, array('target' => '_blank', 'class' => $linkClass)); + } else if ('link' == $object['type']) { + echo $this->Html->link($sigDisplay, $sigDisplay, array('class' => $linkClass)); + } else if ('cortex' == $object['type']) { + echo '
    Cortex object
    '; + } else if ('text' == $object['type']) { + if ($object['category'] == 'External analysis' && preg_match('/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i', $object['value'])) { + echo '' . h($object['value']) . ''; + } else { + $sigDisplay = str_replace("\r", '', h($sigDisplay)); + $sigDisplay = str_replace(" ", ' ', $sigDisplay); + echo nl2br($sigDisplay); + } + } else if ('hex' == $object['type']) { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo '' . nl2br(h($sigDisplay)) . ' '; + } else { + $sigDisplay = str_replace("\r", '', $sigDisplay); + echo nl2br(h($sigDisplay)); + } + if (isset($object['validationIssue'])) echo '  '; +?> diff --git a/app/View/Elements/Servers/eventattribute.ctp b/app/View/Elements/Servers/eventattribute.ctp new file mode 100644 index 000000000..97dad28bb --- /dev/null +++ b/app/View/Elements/Servers/eventattribute.ctp @@ -0,0 +1,148 @@ +params->params['paging']['Event']['page'])) { + if ($this->params->params['paging']['Event']['page'] == 0) $all = true; + $page = $this->params->params['paging']['Event']['page']; + } else { + $page = 0; + } + $fieldCount = 8; +?> + +
    +
    +
    + 'hidden', @@ -42,6 +42,9 @@ echo ' :: ' . h($element['type']) . ''; ?> + + Form->input('Attribute.' . $k . '.category', $formSettings); ?> + element( 'Objects/object_value_field', @@ -98,7 +101,7 @@ )); ?> + Form->input('Attribute.' . $k . '.comment', array( 'type' => 'textarea', diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 2319a8690..4f072fbd5 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -92,6 +92,7 @@
    Save Name :: typeDescription Category Value IDS +
    + + +
    +
    + +
    +
    + +
    :
    + +
    +
    +
    + +
    +
    +
    + +
    > + > + element('/Events/View/value_field', array('object' => $object, 'linkClass' => $linkClass)); + ?> + + 0, 2 => 1); + $valueParts = explode('|', $object['value']); + foreach ($components as $component => $valuePart) { + if (isset($object['warnings'][$component]) && isset($valueParts[$valuePart])) { + foreach ($object['warnings'][$component] as $warning) $temp .= '' . h($valueParts[$valuePart]) . ': ' . h($warning) . '
    '; + } + } + echo '  '; + } + ?> +
    +
    +
    + element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['Tag'], 'tagAccess' => false)); + ?> +
    +
    +
    +
    +   +
    +
    +
      + $relatedAttribute['info'], 'Correlating Value' => $relatedAttribute['value'], 'date' => isset($relatedAttribute['date']) ? $relatedAttribute['date'] : 'N/A'); + $popover = ''; + foreach ($relatedData as $k => $v) { + $popover .= '' . h($k) . ': ' . h($v) . '
      '; + } + echo '
    • '; + if ($relatedAttribute['org_id'] == $me['org_id']) { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => 'red')); + } else { + echo $this->Html->link($relatedAttribute['id'], array('controller' => 'events', 'action' => 'view', $relatedAttribute['id'], true, $event['Event']['id']), array('class' => $otherColour)); + } + echo "
    • "; + echo ' '; + } + } + ?> +
    +
    +
      + $v): + if ($k == 'id') continue; + $popover .= '' . Inflector::humanize(h($k)) . ': ' . h($v) . '
      '; + endforeach; + ?> +
    • + Html->link($feed['id'], array('controller' => 'feeds', 'action' => 'previewIndex', $feed['id']), array('style' => 'margin-right:3px;')); + else: + ?> + + +
    • + +
    +
    +
    +
    + +
    +
    + + + Name: + +
    +
    + Meta-category:
    + Description:
    + Template: +
    + element('/Servers/View/row_object_reference', array( + 'object' => $object + )); + if (!empty($object['referenced_by'])) { + echo $this->element('/Servers/View/row_object_referenced_by', array( + 'object' => $object + )); + } + ?> +
      
    + + + + + + + + + + + + 'attribute', + 3 => 'object' + ); + $focusedRow = false; + foreach ($event['objects'] as $k => $object): + $insertBlank = false; + echo $this->element('/Servers/View/row_' . $object['objectType'], array( + 'object' => $object, + 'k' => $k, + 'page' => $page, + 'fieldCount' => $fieldCount + )); + if (!empty($focus) && ($object['objectType'] == 'object' || $object['objectType'] == 'attribute') && $object['uuid'] == $focus) { + $focusedRow = $k; + } + if ($object['objectType'] == 'object'): + ?> + + +
    Paginator->sort('timestamp', 'Date');?>Paginator->sort('category');?>Paginator->sort('type');?>Paginator->sort('value');?>TagsPaginator->sort('comment');?>Related EventsFeed hitsPaginator->sort('to_ids', 'IDS');?>
    +
    + + +Js->writeBuffer(); +?> diff --git a/app/View/Elements/ajaxAttributeTags.ctp b/app/View/Elements/ajaxAttributeTags.ctp index 9d1d944a1..7a6c2a232 100644 --- a/app/View/Elements/ajaxAttributeTags.ctp +++ b/app/View/Elements/ajaxAttributeTags.ctp @@ -2,13 +2,26 @@ $tag); $tagClass = $full ? 'tagFirstHalf' : 'tag'; ?>
    - - -
    x
    - + + + + + +
    x
    +

    - params->params['paging']['Event']['page']) && $this->params->params['paging']['Event']['page'] == 0) $all = true; - ?> - -
    - - - - - - - - - - - - - - - $object): - $extra = $extra2 = $extra3 = ''; - $currentType = 'denyForm'; - if ($object['objectType'] == 0 ) { - $currentType = 'Attribute'; - if ($object['hasChildren'] == 1) { - $extra = 'highlight1'; - $extra3 = 'highlightBlueSides highlightBlueTop'; - } - } else $extra = 'highlight2'; - if ($object['objectType'] == 1) { - $extra2 = '1'; - $extra3 = 'highlightBlueSides'; - if (isset($object['firstChild'])) { - $extra3 .= ' highlightBlueTop'; - } - if (isset($object['lastChild'])) { - $extra3 .= ' highlightBlueBottom'; - } - } - ?> - - - - - - - - - - - - - - -
    Paginator->sort('date');?>Paginator->sort('category');?>Paginator->sort('type');?>Paginator->sort('value');?>TagsPaginator->sort('comment');?>Related EventsPaginator->sort('to_ids', 'IDS');?>Paginator->sort('distribution');?>
    - - - - -   - -
      - -
    • - -
    -
    -   -
    -
    - + element('Servers/eventattribute'); ?>
    Date: Wed, 13 Sep 2017 14:25:13 +0200 Subject: [PATCH 58/77] fix: Updated the xml export tool to support objects - though why do we still support XML?... --- app/Lib/Tools/XMLConverterTool.php | 132 ++++++++++++++++------------- 1 file changed, 73 insertions(+), 59 deletions(-) diff --git a/app/Lib/Tools/XMLConverterTool.php b/app/Lib/Tools/XMLConverterTool.php index 499081218..a493dd8fd 100644 --- a/app/Lib/Tools/XMLConverterTool.php +++ b/app/Lib/Tools/XMLConverterTool.php @@ -35,6 +35,65 @@ class XMLConverterTool { return $text; } + private function __rearrange($data, $model, $container) { + if (isset($data[$model])) { + $data[$container][$model] = $data[$model]; + unset($data[$model]); + } + return $data; + } + + private function __rearrangeAttributes($attributes) { + foreach ($attributes as $key => $value) { + $this->__sanitizeField($attributes[$key]['value']); + $this->__sanitizeField($attributes[$key]['comment']); + unset($attributes[$key]['value1'], $attributes[$key]['value2'], $attributes[$key]['category_order']); + if (isset($event['Event']['RelatedAttribute']) && isset($event['Event']['RelatedAttribute'][$value['id']])) { + $attributes[$key]['RelatedAttribute'] = $event['Event']['RelatedAttribute'][$value['id']]; + foreach ($attributes[$key]['RelatedAttribute'] as &$ra) { + $ra = array('Attribute' => array(0 => $ra)); + } + } + if (!empty($attributes[$key]['Feed'])) { + foreach ($attributes[$key]['Feed'] as $fKey => $feed) { + $this->__sanitizeField($attributes[$key]['Feed'][$fKey]['name']); + $this->__sanitizeField($attributes[$key]['Feed'][$fKey]['url']); + $this->__sanitizeField($attributes[$key]['Feed'][$fKey]['provider']); + } + } + if (isset($attributes[$key]['ShadowAttribute'])) { + foreach ($attributes[$key]['ShadowAttribute'] as $skey => $svalue) { + $this->__sanitizeField($attributes[$key]['ShadowAttribute'][$skey]['value']); + $this->__sanitizeField($attributes[$key]['ShadowAttribute'][$skey]['comment']); + $attributes[$key]['ShadowAttribute'][$skey]['Org'] = array(0 => $attributes[$key]['ShadowAttribute'][$skey]['Org']); + if (isset($attributes[$key]['ShadowAttribute'][$skey]['EventOrg'])) $attributes[$key]['ShadowAttribute'][$skey]['EventOrg'] = array(0 => $attributes[$key]['ShadowAttribute'][$skey]['EventOrg']); + } + } + if (isset($attributes[$key]['SharingGroup']['SharingGroupOrg'])) { + foreach ($attributes[$key]['SharingGroup']['SharingGroupOrg'] as $k => $sgo) { + $attributes[$key]['SharingGroup']['SharingGroupOrg'][$k]['Organisation'] = array(0 => $attributes[$key]['SharingGroup']['SharingGroupOrg'][$k]['Organisation']); + } + } + if (isset($attributes[$key]['SharingGroup']['SharingGroupServer'])) { + foreach ($attributes[$key]['SharingGroup']['SharingGroupServer'] as $k => $sgs) { + $attributes[$key]['SharingGroup']['SharingGroupServer'][$k]['Server'] = array(0 => $attributes[$key]['SharingGroup']['SharingGroupServer'][$k]['Server']); + } + } + if (isset($attributes[$key]['SharingGroup'])) { + $attributes[$key]['SharingGroup'][0] = $attributes[$key]['SharingGroup']; + unset($attributes[$key]['SharingGroup']); + } + if (isset($attributes[$key]['AttributeTag'])) { + foreach ($attributes[$key]['AttributeTag'] as $atk => $tag) { + unset($tag['Tag']['org_id']); + $attributes[$key]['Tag'][$atk] = $tag['Tag']; + } + unset($attributes[$key]['AttributeTag']); + } + } + return $attributes; + } + public function convertArray($event, $isSiteAdmin=false) { $event['Event']['Org'][0] = $event['Org']; $event['Event']['Orgc'][0] = $event['Orgc']; @@ -51,12 +110,11 @@ class XMLConverterTool { if (isset($event['SharingGroup'])) { $event['Event']['SharingGroup'][0] = $event['SharingGroup']; } - if (isset($event['Attribute'])) $event['Event']['Attribute'] = $event['Attribute']; - if (isset($event['ShadowAttribute'])) { - $event['Event']['ShadowAttribute'] = $event['ShadowAttribute']; - unset($event['ShadowAttribute']); - } - if (isset($event['RelatedEvent'])) if (isset($event['RelatedEvent'])) $event['Event']['RelatedEvent'] = $event['RelatedEvent']; + $event = $this->__rearrange($event, 'Attribute', 'Event'); + $event = $this->__rearrange($event, 'Object', 'Event'); + $event = $this->__rearrange($event, 'ShadowAttribute', 'Event'); + $event = $this->__rearrange($event, 'RelatedEvent', 'Event'); + $event = $this->__rearrange($event, 'RelatedAttribute', 'Event'); // legacy unset($event['Event']['org']); @@ -68,12 +126,6 @@ class XMLConverterTool { $event['Event']['Tag'][$k] = $tag['Tag']; } } - $this->__sanitizeField($event['Event']['info']); - if (isset($event['RelatedAttribute'])) { - $event['Event']['RelatedAttribute'] = $event['RelatedAttribute']; - unset($event['RelatedAttribute']); - } - else $event['Event']['RelatedAttribute'] = array(); foreach ($event['Event']['RelatedAttribute'] as &$attribute_w_relation) { foreach ($attribute_w_relation as &$relation) { $this->__sanitizeField($relation['info']); @@ -91,55 +143,13 @@ class XMLConverterTool { if (isset($event['Event']['Attribute'])) { // remove value1 and value2 from the output and remove invalid utf8 characters for the xml parser - foreach ($event['Event']['Attribute'] as $key => $value) { - $this->__sanitizeField($event['Event']['Attribute'][$key]['value']); - $this->__sanitizeField($event['Event']['Attribute'][$key]['comment']); - unset($event['Event']['Attribute'][$key]['value1'], $event['Event']['Attribute'][$key]['value2'], $event['Event']['Attribute'][$key]['category_order']); - if (isset($event['Event']['RelatedAttribute']) && isset($event['Event']['RelatedAttribute'][$value['id']])) { - $event['Event']['Attribute'][$key]['RelatedAttribute'] = $event['Event']['RelatedAttribute'][$value['id']]; - foreach ($event['Event']['Attribute'][$key]['RelatedAttribute'] as &$ra) { - $ra = array('Attribute' => array(0 => $ra)); - } - } - if (!empty($event['Event']['Attribute'][$key]['Feed'])) { - foreach ($event['Event']['Attribute'][$key]['Feed'] as $fKey => $feed) { - $this->__sanitizeField($event['Event']['Attribute'][$key]['Feed'][$fKey]['name']); - $this->__sanitizeField($event['Event']['Attribute'][$key]['Feed'][$fKey]['url']); - $this->__sanitizeField($event['Event']['Attribute'][$key]['Feed'][$fKey]['provider']); - } - } - if (isset($event['Event']['Attribute'][$key]['ShadowAttribute'])) { - foreach ($event['Event']['Attribute'][$key]['ShadowAttribute'] as $skey => $svalue) { - $this->__sanitizeField($event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['value']); - $this->__sanitizeField($event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['comment']); - $event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['Org'] = array(0 => $event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['Org']); - if (isset($event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['EventOrg'])) $event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['EventOrg'] = array(0 => $event['Event']['Attribute'][$key]['ShadowAttribute'][$skey]['EventOrg']); - } - } - if (isset($event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupOrg'])) { - foreach ($event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupOrg'] as $k => $sgo) { - $event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupOrg'][$k]['Organisation'] = array(0 => $event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupOrg'][$k]['Organisation']); - } - } - if (isset($event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupServer'])) { - foreach ($event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupServer'] as $k => $sgs) { - $event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupServer'][$k]['Server'] = array(0 => $event['Event']['Attribute'][$key]['SharingGroup']['SharingGroupServer'][$k]['Server']); - } - } - if (isset($event['Event']['Attribute'][$key]['SharingGroup'])) { - $event['Event']['Attribute'][$key]['SharingGroup'][0] = $event['Event']['Attribute'][$key]['SharingGroup']; - unset($event['Event']['Attribute'][$key]['SharingGroup']); - } - if (isset($event['Event']['Attribute'][$key]['AttributeTag'])) { - foreach ($event['Event']['Attribute'][$key]['AttributeTag'] as $atk => $tag) { - unset($tag['Tag']['org_id']); - $event['Event']['Attribute'][$key]['Tag'][$atk] = $tag['Tag']; - } - unset($event['Event']['Attribute'][$key]['AttributeTag']); - } + $event['Event']['Attribute'] = $this->__rearrangeAttributes($event['Event']['Attribute']); + } + if (!empty($event['Event']['Object'])) { + foreach ($event['Event']['Object'] as $k => $v) { + $event['Event']['Object'][$k]['Attribute'] = $this->__rearrangeAttributes($event['Event']['Object'][$k]['Attribute']); } } - unset($event['Event']['RelatedAttribute']); if (isset($event['Event']['ShadowAttribute'])) { // remove invalid utf8 characters for the xml parser foreach ($event['Event']['ShadowAttribute'] as $key => $value) { @@ -189,4 +199,8 @@ class XMLConverterTool { if ($mispVersion) $result .= '' . $mispVersion . ''; return $result . '' . PHP_EOL; } + + private function __prepareAttributes($attributes) { + return $attributes; + } } From f078f073255079323ca598030d99b5ab170eca70 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 14 Sep 2017 09:33:20 +0200 Subject: [PATCH 59/77] fix: Unpublish event on object add --- app/Controller/ObjectsController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index dfabe9247..c2a736da4 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -128,6 +128,7 @@ class ObjectsController extends AppController { $error = $this->MispObject->ObjectTemplate->checkTemplateConformity($template, $object); if ($error === true) { $result = $this->MispObject->saveObject($object, $eventId, $template, $this->Auth->user(), $errorBehaviour = 'halt'); + if ($result) $this->MispObject->Event->unpublishEvent($eventId); } else { $result = false; } From 9ff81f5546e32602aa1bd807e65dbdeb03de3aff Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 14 Sep 2017 09:33:54 +0200 Subject: [PATCH 60/77] new: Add way to flatten attributes for certain exports (hids, nids) --- app/Model/Attribute.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index c61d0c8a7..782156e28 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -1661,7 +1661,8 @@ class Attribute extends AppModel { $options = array( 'conditions' => $conditions, 'group' => array('Attribute.type', 'Attribute.value1'), - 'enforceWarninglist' => $enforceWarninglist + 'enforceWarninglist' => $enforceWarninglist, + 'flatten' => true ); $items = $this->fetchAttributes($user, $options); if (empty($items)) continue; @@ -1722,7 +1723,8 @@ class Attribute extends AppModel { 'contain' => array('Event'=> array('fields' => array('Event.id', 'Event.threat_level_id'))), 'group' => array('Attribute.type', 'Attribute.value1'), // fields to GROUP BY 'enforceWarninglist' => $enforceWarninglist, - 'includeAllTags' => $includeAllTags + 'includeAllTags' => $includeAllTags, + 'flatten' => true ); $items = $this->fetchAttributes($user, $params); if (empty($items)) continue; @@ -2311,12 +2313,13 @@ class Attribute extends AppModel { } if (isset($options['fields'])) $params['fields'] = $options['fields']; if (isset($options['conditions'])) $params['conditions']['AND'][] = $options['conditions']; + if (empty($options['flatten'])) $params['conditions']['AND'][] = array('NOT' => array('Attribute.object_id' => 0)); if (isset($options['order'])) $params['order'] = $options['order']; if (!isset($options['withAttachments'])) $options['withAttachments'] = false; else ($params['order'] = array()); if (!isset($options['enforceWarninglist'])) $options['enforceWarninglist'] = false; if (!$user['Role']['perm_sync'] || !isset($options['deleted']) || !$options['deleted']) $params['conditions']['AND']['Attribute.deleted'] = 0; - if (isset($options['group'])) $params['group'] = array_merge(array('Attribute.id'), $options['group']); + if (isset($options['group'])) $params['group'] = empty($options['group']) ? $options['group'] : false; if (Configure::read('MISP.unpublishedprivate')) $params['conditions']['AND'][] = array('OR' => array('Event.published' => 1, 'Event.orgc_id' => $user['org_id'])); if (!empty($options['list'])) { if (!empty($options['event_ids'])) { @@ -2326,7 +2329,6 @@ class Attribute extends AppModel { $fields = array('Attribute.event_id'); $group = false; } - $start = microtime(true); $results = $this->find('list', array( 'conditions' => $params['conditions'], 'recursive' => -1, From b3a60d84e2f80c031f67ce347f4f23098fad7caa Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 14 Sep 2017 09:34:42 +0200 Subject: [PATCH 61/77] fix: Fixed the missing refresh on attribute tags when a new tag is added --- app/View/Elements/Events/View/row_attribute.ctp | 2 +- app/webroot/js/misp.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index da5ad27c6..eae0851c2 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -94,7 +94,7 @@
    -
    +
    element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['AttributeTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']) )); ?>
    diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 92d513782..352962bca 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -777,7 +777,7 @@ function loadAttributeTags(id) { dataType:"html", cache: false, success:function (data, textStatus) { - $("#ShadowAttribute_"+id+"_tr .attributeTagContainer").html(data); + $("#Attribute_"+id+"_tr .attributeTagContainer").html(data); }, url:"/tags/showAttributeTag/" + id }); From 64d16a420ec818e21d7451d061338446b4e8ce40 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 15 Sep 2017 16:52:49 +0200 Subject: [PATCH 62/77] new: Objects tied into e-mailing --- app/Model/Attribute.php | 2 +- app/Model/Event.php | 103 +++++++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 782156e28..caa4ba819 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2313,7 +2313,7 @@ class Attribute extends AppModel { } if (isset($options['fields'])) $params['fields'] = $options['fields']; if (isset($options['conditions'])) $params['conditions']['AND'][] = $options['conditions']; - if (empty($options['flatten'])) $params['conditions']['AND'][] = array('NOT' => array('Attribute.object_id' => 0)); + if (empty($options['flatten'])) $params['conditions']['AND'][] = array('Attribute.object_id' => 0); if (isset($options['order'])) $params['order'] = $options['order']; if (!isset($options['withAttachments'])) $options['withAttachments'] = false; else ($params['order'] = array()); diff --git a/app/Model/Event.php b/app/Model/Event.php index f501c8ae3..9fd64debb 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1937,6 +1937,63 @@ class Event extends AppModel { return true; } + private function __buildAlertEmailObject($user, &$body, &$bodyTempOther, $objects, $owner, $oldpublish) { + foreach ($objects as $object) { + if (!$owner && $object['distribution'] == 0) continue; + if ($object['distribution'] == 4 && !$this->Event->SharingGroup->checkIfAuthorised($user, $object['sharing_group_id'])) continue; + if (isset($oldpublish) && isset($object['timestamp']) && $object['timestamp'] > $oldpublish) { + $body .= '* '; + } else { + $body .= ' '; + } + $body .= $object['name'] . '/' . $object['meta-category'] . "\n"; + if (!empty($object['Attribute'])) { + $body = $this->__buildAlertEmailAttribute($user, $body, $bodyTempOther, $object['Attribute'], $owner, $oldpublish, ' '); + } + } + } + + private function __buildAlertEmailAttribute($user, &$body, &$bodyTempOther, $attributes, $owner, $oldpublish, $indent = ' ') { + $appendlen = 20; + foreach ($attributes as $attribute) { + if (!$owner && $attribute['distribution'] == 0) continue; + if ($attribute['distribution'] == 4 && !$this->Event->SharingGroup->checkIfAuthorised($user, $attribute['sharing_group_id'])) continue; + $ids = ''; + if ($attribute['to_ids']) $ids = ' (IDS)'; + $strRepeatCount = $appendlen - 2 - strlen($attribute['type']); + $strRepeat = ($strRepeatCount > 0) ? str_repeat(' ', $strRepeatCount) : ''; + if (isset($oldpublish) && isset($attribute['timestamp']) && $attribute['timestamp'] > $oldpublish) { + $line = '* ' . $indent . $attribute['category'] . '/' . $attribute['type'] . $strRepeat . ': ' . $attribute['value'] . $ids . " *\n"; + } else { + $line = $indent . $attribute['category'] . '/' . $attribute['type'] . $strRepeat . ': ' . $attribute['value'] . $ids . "\n"; + } + // Defanging URLs (Not "links") emails domains/ips in notification emails + if ('url' == $attribute['type'] || 'uri' == $attribute['type']) { + $line = str_ireplace("http","hxxp", $line); + $line = str_ireplace(".","[.]", $line); + } + else if (in_array($attribute['type'], array('email-src', 'email-dst', 'whois-registrant-email', 'dns-soa-email', 'email-reply-to'))) { + $line = str_replace("@","[at]", $line); + } + else if (in_array($attribute['type'], array('hostname', 'domain', 'ip-src', 'ip-dst', 'domain|ip'))) { + $line = str_replace(".","[.]", $line); + } + if (!empty($attribute['AttributeTag'])) { + $line .= ' - Tags: '; + foreach ($attribute['AttributeTag'] as $k => $aT) { + if ($k > 0) { + $line .= ', '; + } + $line .= $aT['Tag']['name']; + } + $line .= "\n"; + } + if ('other' == $attribute['type']) // append the 'other' attribute types to the bottom. + $bodyTempOther .= $line; + else $body .= $line; + } + } + private function __buildAlertEmailBody($event, $user, $oldpublish, $sgModel) { $owner = false; if ($user['org_id'] == $event['Event']['orgc_id'] || $user['org_id'] == $event['Event']['org_id'] || $user['Role']['perm_site_admin']) $owner = true; @@ -1973,46 +2030,14 @@ class Event extends AppModel { } $body .= '==============================================' . "\n"; } - $body .= 'Attributes (* indicates a new or modified attribute):' . "\n"; $bodyTempOther = ""; - if (isset($event['Attribute'])) { - foreach ($event['Attribute'] as &$attribute) { - if (!$owner && $attribute['distribution'] == 0) continue; - if ($attribute['distribution'] == 4 && !$sgModel->checkIfAuthorised($user, $attribute['sharing_group_id'])) continue; - $ids = ''; - if ($attribute['to_ids']) $ids = ' (IDS)'; - $strRepeatCount = $appendlen - 2 - strlen($attribute['type']); - $strRepeat = ($strRepeatCount > 0) ? str_repeat(' ', $strRepeatCount) : ''; - if (isset($oldpublish) && isset($attribute['timestamp']) && $attribute['timestamp'] > $oldpublish) { - $line = '* ' . $attribute['category'] . '/' . $attribute['type'] . $strRepeat . ': ' . $attribute['value'] . $ids . " *\n"; - } else { - $line = $attribute['category'] . '/' . $attribute['type'] . $strRepeat . ': ' . $attribute['value'] . $ids . "\n"; - } - // Defanging URLs (Not "links") emails domains/ips in notification emails - if ('url' == $attribute['type'] || 'uri' == $attribute['type']) { - $line = str_ireplace("http","hxxp", $line); - $line = str_ireplace(".","[.]", $line); - } - else if (in_array($attribute['type'], array('email-src', 'email-dst', 'whois-registrant-email', 'dns-soa-email', 'email-reply-to'))) { - $line = str_replace("@","[at]", $line); - } - else if (in_array($attribute['type'], array('hostname', 'domain', 'ip-src', 'ip-dst', 'domain|ip'))) { - $line = str_replace(".","[.]", $line); - } - if (!empty($attribute['AttributeTag'])) { - $line .= ' - Tags: '; - foreach ($attribute['AttributeTag'] as $k => $aT) { - if ($k > 0) { - $line .= ', '; - } - $line .= $aT['Tag']['name']; - } - $line .= "\n"; - } - if ('other' == $attribute['type']) // append the 'other' attribute types to the bottom. - $bodyTempOther .= $line; - else $body .= $line; - } + if (!empty($event['Attribute'])) { + $body .= 'Attributes (* indicates a new or modified attribute):' . "\n"; + $this->__buildAlertEmailAttribute($user, $body, $bodyTempOther, $event['Attribute'], $owner, $oldpublish); + } + if (!empty($event['Object'])) { + $body .= 'Objects (* indicates a new or modified object):' . "\n"; + $this->__buildAlertEmailObject($user, $body, $bodyTempOther, $event['Object'], $owner, $oldpublish); } if (!empty($bodyTempOther)) { $body .= "\n"; From ae47a4094029939d3354f52af5c0c943d0a66979 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 15 Sep 2017 16:53:30 +0200 Subject: [PATCH 63/77] fix: Referenced by counter fixed, fixes #2479 --- app/View/Elements/Events/View/row_object_referenced_by.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/Events/View/row_object_referenced_by.ctp b/app/View/Elements/Events/View/row_object_referenced_by.ctp index e0801b3fe..5af76b072 100644 --- a/app/View/Elements/Events/View/row_object_referenced_by.ctp +++ b/app/View/Elements/Events/View/row_object_referenced_by.ctp @@ -1,8 +1,8 @@ Referenced by: Date: Fri, 15 Sep 2017 17:01:31 +0200 Subject: [PATCH 64/77] fix: No attributes set in the objects add form makes MISP barf up notices instead of gracefully showing an error - fixes #2476 --- app/Controller/ObjectsController.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index c2a736da4..f515cfb6b 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -117,11 +117,15 @@ class ObjectsController extends AppController { // we pre-validate the attributes before we create an object at this point // This allows us to stop the process and return an error (API) or return // to the add form - foreach ($object['Attribute'] as $k => $attribute) { - $object['Attribute'][$k]['event_id'] = $eventId; - $this->MispObject->Event->Attribute->set($attribute); - if (!$this->MispObject->Event->Attribute->validates()) { - $error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->MispObject->Event->Attribute->validationErrors); + if (empty($object['Attribute'])) { + $error = 'Could not save the object as no attributes were set.'; + } else { + foreach ($object['Attribute'] as $k => $attribute) { + $object['Attribute'][$k]['event_id'] = $eventId; + $this->MispObject->Event->Attribute->set($attribute); + if (!$this->MispObject->Event->Attribute->validates()) { + $error = 'Could not save object as at least one attribute has failed validation (' . $attribute['object_relation'] . '). ' . json_encode($this->MispObject->Event->Attribute->validationErrors); + } } } if (empty($error)) { From 80e70fd240f55f5416c437378960e1f77f3e343b Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 15 Sep 2017 17:26:10 +0200 Subject: [PATCH 65/77] fix: Fixed notices described in #2482 --- app/Model/MispObject.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 749fc27b2..130d6c0fc 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -294,7 +294,7 @@ class MispObject extends AppModel { } $template['ObjectTemplateElement'][] = $v; } else { - $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['object_relation'] . '") that would not pass validation due to this.'; + $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template_object_elements[$k]['object_relation'] . '") that would not pass validation due to this.'; } } else { foreach($request_rearranged[$v['object_relation']] as $request_item) { @@ -315,7 +315,7 @@ class MispObject extends AppModel { } $template['ObjectTemplateElement'][] = $v; } else { - $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template['ObjectTemplateElement'][$k]['object_relation'] . '") that would not pass validation due to this.'; + $template['warnings'][] = 'Missing attribute type "' . $v['type'] . '" found. Omitted template element ("' . $template_object_elements[$k]['object_relation'] . '") that would not pass validation due to this.'; } } } From 9eb3ea211494be1098b3ac2952d49184c93da168 Mon Sep 17 00:00:00 2001 From: iglocska Date: Sun, 17 Sep 2017 12:26:06 +0200 Subject: [PATCH 66/77] fix: When deleting an attirbute/objects, object references to it are not deleted, fixes #2477 - force a reference deletion on attribute/object deletion - changed it to match deletion type - soft-deleting an attribute/object soft-deletes all references to it - hard-deleting an attribute/object hard-deletes all references to it --- app/Controller/AttributesController.php | 11 +++++++++++ app/Controller/ObjectsController.php | 11 +++++++++++ app/Model/Attribute.php | 9 +++++++++ app/Model/MispObject.php | 12 ++++++++++++ 4 files changed, 43 insertions(+) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 3b5c18aa2..7bcc51cfa 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -1072,6 +1072,17 @@ class AttributesController extends AppController { $result['Attribute']['deleted'] = 1; $result['Attribute']['timestamp'] = $date->getTimestamp(); $save = $this->Attribute->save($result); + $object_refs = $this->Attribute->Object->ObjectReference->find('all', array( + 'conditions' => array( + 'ObjectReference.referenced_type' => 0, + 'ObjectReference.referenced_id' => $id, + ), + 'recursive' => -1 + )); + foreach ($object_refs as $ref) { + $ref['ObjectReference']['deleted'] = 1; + $this->Attribute->Object->ObjectReference->save($ref); + } } // attachment will be deleted with the beforeDelete() function in the Model if ($save) { diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index f515cfb6b..b85c0194e 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -477,6 +477,17 @@ class ObjectsController extends AppController { $object['Event']['timestamp'] = $date->getTimestamp(); $object['Event']['published'] = 0; $this->MispObject->Event->save($object, array('fieldList' => array('published', 'timestamp', 'info'))); + $object_refs = $this->MispObject->ObjectReference->find('all', array( + 'conditions' => array( + 'ObjectReference.referenced_type' => 1, + 'ObjectReference.referenced_id' => $id, + ), + 'recursive' => -1 + )); + foreach ($object_refs as $ref) { + $ref['ObjectReference']['deleted'] = 1; + $this->MispObject->ObjectReference->save($ref); + } return true; } } diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index caa4ba819..95fc06c96 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -598,6 +598,15 @@ class Attribute extends AppModel { if (Configure::read('MISP.enable_advanced_correlations') && in_array($this->data['Attribute']['type'], array('ip-src', 'ip-dst', 'domain-ip')) && strpos($this->data['Attribute']['value'], '/')) { $this->setCIDRList(); } + if (!empty($this->data['Attribute']['id'])) { + $this->Object->ObjectReference->deleteAll( + array( + 'ObjectReference.referenced_type' => 0, + 'ObjectReference.referenced_id' => $this->data['Attribute']['id'], + ), + false + ); + } } public function beforeValidate($options = array()) { diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index 130d6c0fc..e6904ce81 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -74,6 +74,18 @@ class MispObject extends AppModel { return true; } + public function afterDelete() { + if (!empty($this->data[$this->alias]['id'])) { + $this->ObjectReference->deleteAll( + array( + 'ObjectReference.referenced_type' => 1, + 'ObjectReference.referenced_id' => $this->data[$this->alias]['id'], + ), + false + ); + } + } + public function saveObject($object, $eventId, $template, $user, $errorBehaviour = 'drop') { $this->create(); $templateFields = array( From bb99706cf3225b6055b86d4b69cd9597f05e1c6c Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 00:38:30 +0200 Subject: [PATCH 67/77] new: Various object template improvements - allow multiple versions of a template to be stored at the same time - select which version is the primary version of a template - disable/enable templates - edit objects with one of the older versions of a template if the object's version requires that - various UI / bug fixes --- INSTALL/MYSQL.sql | 1 + app/Controller/ObjectTemplatesController.php | 56 +++++++++- app/Controller/ObjectsController.php | 17 ++- app/Model/AppModel.php | 1 + app/Model/ObjectTemplate.php | 103 ++++++++---------- app/Model/ObjectTemplateElement.php | 12 +- .../ObjectTemplates/ajax/getToggleField.ctp | 4 + app/View/ObjectTemplates/index.ctp | 41 ++++++- app/View/Objects/add.ctp | 2 +- app/files/misp-objects | 2 +- app/webroot/js/misp.js | 11 ++ 11 files changed, 183 insertions(+), 67 deletions(-) create mode 100644 app/View/ObjectTemplates/ajax/getToggleField.ctp diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 87e547483..407104ae4 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -513,6 +513,7 @@ CREATE TABLE IF NOT EXISTS object_templates ( `version` int(11) NOT NULL, `requirements` text COLLATE utf8_bin, `fixed` tinyint(1) NOT NULL DEFAULT 0, + `active` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `user_id` (`user_id`), INDEX `org_id` (`org_id`), diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index d1e949f9a..9e99423c2 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -20,8 +20,10 @@ class ObjectTemplatesController extends AppController { $this->ObjectTemplate->populateIfEmpty($this->Auth->user()); $templates_raw = $this->ObjectTemplate->find('all', array( 'recursive' => -1, + 'conditions' => array('ObjectTemplate.active' => 1), 'fields' => array('id', 'meta-category', 'name', 'description', 'org_id'), - 'contain' => array('Organisation.name') + 'contain' => array('Organisation.name'), + 'sort' => array('ObjectTemplate.name asc') )); $templates = array('all' => array()); foreach ($templates_raw as $k => $template) { @@ -30,6 +32,9 @@ class ObjectTemplatesController extends AppController { $templates[$templates_raw[$k]['ObjectTemplate']['meta-category']][] = $template['ObjectTemplate']; $templates['all'][] = $template['ObjectTemplate']; } + foreach ($templates as $category => $template_list) { + $templates[$category] = Hash::sort($templates[$category], '{n}.name'); + } $template_categories = array_keys($templates); $this->layout = false; $this->set('template_categories', $template_categories); @@ -61,6 +66,29 @@ class ObjectTemplatesController extends AppController { } } + public function delete($id) { + if (!$this->request->is('post') && !$this->request->is('put') && !$this->request->is('delete')) { + throw new MethodNotAllowedException(); + } + $this->ObjectTemplate->id = $id; + if (!$this->ObjectTemplate->exists()) { + throw new NotFoundException('Invalid ObjectTemplate'); + } + if ($this->ObjectTemplate->delete()) { + if ($this->_isRest()) { + return $this->RestResponse->saveSuccessResponse('ObjectTemplates', 'admin_delete', $id, $this->response->type()); + } else { + $this->Session->setFlash(__('ObjectTemplate deleted')); + } + } + if ($this->_isRest()) { + return $this->RestResponse->saveFailResponse('ObjectTemplates', 'admin_delete', $id, $this->ObjectTemplate->validationErrors, $this->response->type()); + } else { + $this->Session->setFlash('ObjectTemplate could not be deleted'); + } + $this->redirect($this->referer()); + } + public function viewElements($id, $context = 'all') { $elements = $this->ObjectTemplate->ObjectTemplateElement->find('all', array( 'conditions' => array('ObjectTemplateElement.object_template_id' => $id) @@ -70,7 +98,13 @@ class ObjectTemplatesController extends AppController { $this->render('ajax/view_elements'); } - public function index() { + public function index($all = false) { + if (!$all || !$this->_isSiteAdmin()) { + $this->paginate['conditions'][] = array('ObjectTemplate.active' => 1); + $this->set('all', false); + } else { + $this->set('all', true); + } if ($this->_isRest()) { $rules = $this->paginate; unset($rules['limit']); @@ -78,6 +112,7 @@ class ObjectTemplatesController extends AppController { $objectTemplates = $this->ObjectTemplate->find('all', $rules); return $this->RestResponse->viewData($objectTemplates, $this->response->type()); } else { + $this->paginate['order'] = array('ObjectTemplate.name' => 'ASC'); $objectTemplates = $this->paginate(); $this->set('list', $objectTemplates); } @@ -147,4 +182,21 @@ class ObjectTemplatesController extends AppController { } $this->redirect(array('controller' => 'ObjectTemplates', 'action' => 'index')); } + + public function activate() { + $id = $this->request->data['ObjectTemplate']['data']; + if (!is_numeric($id)) return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Template not found.')), 'status' => 200, 'type' => 'json')); + $result = $this->ObjectTemplate->setActive($id); + if ($result === false) { + return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Template\'s state could not be toggeled.')), 'status' => 200, 'type' => 'json')); + } + $message = (($result == 1) ? 'activated' : 'disabled'); + return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Template ' . $message . '.')), 'status' => 200, 'type' => 'json')); + } + + public function getToggleField() { + if (!$this->request->is('ajax')) throw new MethodNotAllowedException('This action is available via AJAX only.'); + $this->layout = 'ajax'; + $this->render('ajax/getToggleField'); + } } diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index b85c0194e..8e765719f 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -87,14 +87,29 @@ class ObjectsController extends AppController { throw new NotFoundException('Invalid event.'); } $eventId = $event['Event']['id']; - $template = $this->MispObject->ObjectTemplate->find('first', array( + $templates = $this->MispObject->ObjectTemplate->find('all', array( 'conditions' => array('ObjectTemplate.id' => $templateId), 'recursive' => -1, 'contain' => array( 'ObjectTemplateElement' ) )); + $template_version = false; + $template = false; + foreach ($templates as $temp) { + if (!empty($template_version)) { + if (intval($template['ObjectTemplate']['version']) > intval($template_version)) { + $template = $temp; + } + } else { + $template = $temp; + } + } $error = false; + if (empty($template)) { + $error = 'No valid template found to edit the object.'; + } + // If we have received a POST request if ($this->request->is('post')) { if (isset($this->request->data['request'])) { diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a9e36aded..032164a74 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -779,6 +779,7 @@ class AppModel extends Model { `version` int(11) NOT NULL, `requirements` text COLLATE utf8_bin, `fixed` tinyint(1) NOT NULL DEFAULT 0, + `active` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (id), INDEX `user_id` (`user_id`), INDEX `org_id` (`org_id`), diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index 2854552b9..2ab91d0f8 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -22,10 +22,6 @@ class ObjectTemplate extends AppModel { ) ); public $hasMany = array( - 'Attribute' => array( - 'className' => 'Attribute', - 'dependent' => true, - ), 'ObjectTemplateElement' => array( 'className' => 'ObjectTemplateElement', 'dependent' => true, @@ -65,14 +61,18 @@ class ObjectTemplate extends AppModel { $file->close(); if (!isset($template['version'])) $template['version'] = 1; $current = $this->find('first', array( + 'fields' => array('MAX(version) AS version', 'uuid'), 'conditions' => array('uuid' => $template['uuid']), - 'recursive' => -1 + 'recursive' => -1, + 'group' => array('uuid') )); + if (!empty($current)) $current['ObjectTemplate']['version'] = $current[0]['version']; if (empty($current) || $template['version'] > $current['ObjectTemplate']['version']) { $result = $this->__updateObjectTemplate($template, $current, $user); if ($result === true) { - $updated['success'][$result] = array('name' => $template['name'], 'new' => $template['version']); - if (!empty($current)) $updated['success'][$result]['old'] = $current['ObjectTemplate']['version']; + $temp = array('name' => $template['name'], 'new' => $template['version']); + if (!empty($current)) $temp['old'] = $current['ObjectTemplate']['version']; + $updated['success'][] = $temp; } else { $updated['fails'][] = array('name' => $template['name'], 'fail' => json_encode($result)); } @@ -90,65 +90,23 @@ class ObjectTemplate extends AppModel { $template['requirements'][$field] = $template[$field]; } } - if (empty($current)) { - $template['user_id'] = $user['id']; - $template['org_id'] = $user['org_id']; - $template['fixed'] = 1; - $this->create(); - $result = $this->save($template); - } else { - $fieldsToUpdate = array('version', 'description', 'meta-category', 'name', 'requirements', 'fixed'); - foreach ($fieldsToUpdate as $field) { - if (isset($template[$field]) && $current['ObjectTemplate'][$field] != $template[$field]) { - $current['ObjectTemplate'][$field] = $template[$field]; - } - } - $result = $this->save($current); - } + $template['user_id'] = $user['id']; + $template['org_id'] = $user['org_id']; + $template['fixed'] = 1; + $this->create(); + $result = $this->save($template); if (!$result) { return $this->validationErrors; } $id = $this->id; - $existingTemplateElementsTemp = $this->ObjectTemplateElement->find('all', array( - 'recursive' => -1, - 'conditions' => array('object_template_id' => $id) - )); - $existingTemplateElements = array(); - if (!empty($existingTemplateElementsTemp)) { - foreach ($existingTemplateElementsTemp as $k => $v) { - $existingTemplateElements[$v['ObjectTemplateElement']['object_relation']] = $v['ObjectTemplateElement']; - } - } - unset($existingTemplateElementsTemp); + $this->setActive($id); $fieldsToCompare = array('object_relation', 'type', 'ui-priority', 'categories', 'sane_default', 'values_list', 'multiple'); foreach ($template['attributes'] as $k => $attribute) { $attribute['object_relation'] = $k; $attribute = $this->__convertJSONToElement($attribute); - if (isset($existingTemplateElements[$k])) { - $update_required = false; - foreach ($fieldsToCompare as $field) { - if (isset($attribute[$field])) { - if ($existingTemplateElements[$k][$field] != $attribute[$field]) { - $update_required = true; - } - } - } - if ($update_required) { - $attribute['id'] = $existingTemplateElements[$k]['id']; - $attribute['object_template_id'] = $id; - $result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); - } - if (isset($existingTemplateElements[$k])) unset($existingTemplateElements[$k]); - } else { - $this->ObjectTemplateElement->create(); - $attribute['object_template_id'] = $id; - $result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); - } - } - if (!empty($existingTemplateElements)) { - foreach ($existingTemplateElements as $k2 => $v2) { - $this->ObjectTemplateElement->delete($v2['id']); - } + $this->ObjectTemplateElement->create(); + $attribute['object_template_id'] = $id; + $result = $this->ObjectTemplateElement->save(array('ObjectTemplateElement' => $attribute)); } return true; } @@ -232,4 +190,33 @@ class ObjectTemplate extends AppModel { } return true; } + + public function setActive($id) { + $template = $this->find('first', array( + 'recursive' => -1, + 'conditions' => array('ObjectTemplate.id' => $id) + )); + if (empty($template)) return false; + if ($template['ObjectTemplate']['active']) { + $template['ObjectTemplate']['active'] = 0; + $this->save($template); + return 0; + } + $similar_templates = $this->find('all', array( + 'recursive' => -1, + 'conditions' => array( + 'ObjectTemplate.uuid' => $template['ObjectTemplate']['uuid'], + 'NOT' => array( + 'ObjectTemplate.id' => $template['ObjectTemplate']['id'] + ) + ) + )); + $template['ObjectTemplate']['active'] = 1; + $this->save($template); + foreach ($similar_templates as $st) { + $st['ObjectTemplate']['active'] = 0; + $this->save($st); + } + return 1; + } } diff --git a/app/Model/ObjectTemplateElement.php b/app/Model/ObjectTemplateElement.php index bc8d6eec6..202d81474 100644 --- a/app/Model/ObjectTemplateElement.php +++ b/app/Model/ObjectTemplateElement.php @@ -19,9 +19,15 @@ class ObjectTemplateElement extends AppModel { public function afterFind($results, $primary = false) { foreach ($results as $k => $result) { - $results[$k]['ObjectTemplateElement']['categories'] = json_decode($results[$k]['ObjectTemplateElement']['categories'], true); - $results[$k]['ObjectTemplateElement']['values_list'] = json_decode($results[$k]['ObjectTemplateElement']['values_list'], true); - $results[$k]['ObjectTemplateElement']['sane_default'] = json_decode($results[$k]['ObjectTemplateElement']['sane_default'], true); + if (isset($results[$k]['ObjectTemplateElement']['categories'])) { + $results[$k]['ObjectTemplateElement']['categories'] = json_decode($results[$k]['ObjectTemplateElement']['categories'], true); + } + if (isset($results[$k]['ObjectTemplateElement']['values_list'])) { + $results[$k]['ObjectTemplateElement']['values_list'] = json_decode($results[$k]['ObjectTemplateElement']['values_list'], true); + } + if (isset($results[$k]['ObjectTemplateElement']['sane_default'])) { + $results[$k]['ObjectTemplateElement']['sane_default'] = json_decode($results[$k]['ObjectTemplateElement']['sane_default'], true); + } } return $results; } diff --git a/app/View/ObjectTemplates/ajax/getToggleField.ctp b/app/View/ObjectTemplates/ajax/getToggleField.ctp new file mode 100644 index 000000000..b4aa6143d --- /dev/null +++ b/app/View/ObjectTemplates/ajax/getToggleField.ctp @@ -0,0 +1,4 @@ +Form->create('ObjectTemplate', array('url' => '/ObjectTemplates/activate', 'id' => 'ObjectTemplateIndexForm')); +echo $this->Form->input('data', array('label' => false, 'style' => 'display:none;')); +echo $this->Form->end(); diff --git a/app/View/ObjectTemplates/index.ctp b/app/View/ObjectTemplates/index.ctp index 402673f33..9b804463f 100644 --- a/app/View/ObjectTemplates/index.ctp +++ b/app/View/ObjectTemplates/index.ctp @@ -16,8 +16,34 @@ ?>
    +
    + Form->create('ObjectTemplate', array('url' => '/ObjectTemplates/activate')); + echo $this->Form->input('data', array('label' => false, 'style' => 'display:none;')); + echo $this->Form->end(); + } + ?> +
    +
    + + Enabled + All + +
    + + + @@ -33,6 +59,15 @@ foreach ($list as $template): $td_attributes = 'ondblclick="document.location.href =\'/objectTemplates/view/' . h($template['ObjectTemplate']['id']) . '\'"'; ?> + + + ?> - element('side_menu', array('menuList' => 'objectTemplates', 'menuItem' => 'index')); diff --git a/app/View/Objects/add.ctp b/app/View/Objects/add.ctp index 4f072fbd5..d1efafecb 100644 --- a/app/View/Objects/add.ctp +++ b/app/View/Objects/add.ctp @@ -9,7 +9,7 @@
    Object Template
     
    diff --git a/app/files/misp-objects b/app/files/misp-objects index d34dd5fb6..10b21c6aa 160000 --- a/app/files/misp-objects +++ b/app/files/misp-objects @@ -1 +1 @@ -Subproject commit d34dd5fb606f1c4d882733d16c16103fe429991c +Subproject commit 10b21c6aacb536e7646158f950e6ad972293c830 diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 352962bca..3d978c3de 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -162,6 +162,12 @@ function toggleSetting(e, setting, id) { replacementForm = '/favourite_tags/getToggleField/'; searchString = 'Adding'; break; + case 'activate_object_template': + formID = '#ObjectTemplateIndexForm'; + dataDiv = '#ObjectTemplateData'; + replacementForm = '/ObjectTemplates/getToggleField/'; + searchString = 'activated'; + break; } $(dataDiv).val(id); var formData = $(formID).serialize(); @@ -3210,6 +3216,11 @@ $('.add_object_attribute_row').click(function() { }); }); +$('.quickToggleCheckbox').toggle(function() { + var url = $(this).data('checkbox-url'); + +}); + (function(){ "use strict"; $(".datepicker").datepicker({ From 242fbce3e10c8c690de060b2baac78e3c566613c Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 00:42:26 +0200 Subject: [PATCH 68/77] fix: ACL updated --- app/Controller/Component/ACLComponent.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 3a8bdb65a..eac6ea1aa 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -193,14 +193,24 @@ class ACLComponent extends Component { ), 'objects' => array( 'add' => array('perm_add'), + 'addValueField' => array('perm_add'), 'delete' => array('perm_add'), 'edit' => array('perm_add'), + 'get_row' => array('perm_add'), + 'revise_object' => array('perm_add'), + 'view' => array('*'), + ), + 'objectReferences' => array( + 'add' => array('perm_add'), + 'delete' => array('perm_add'), 'view' => array('*'), ), 'objectTemplates' => array( + 'activate' => array(), 'add' => array('perm_object_template'), 'edit' => array('perm_object_template'), 'delete' => array('perm_object_template'), + 'getToggleField' => array(), 'objectChoice' => array('*'), 'view' => array('*'), 'viewElements' => array('*'), From 50911c9f8509fd6f3e47823a54da8887f7a6bf44 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 10:19:37 +0200 Subject: [PATCH 69/77] chg: cakephp updated --- app/Lib/cakephp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Lib/cakephp b/app/Lib/cakephp index 1d1a762a3..c3a612aa9 160000 --- a/app/Lib/cakephp +++ b/app/Lib/cakephp @@ -1 +1 @@ -Subproject commit 1d1a762a37298f41c2ffc2706db6d36ccbfba939 +Subproject commit c3a612aa94d30a4c51653f40f55ce07177300307 From d3ade5285ff5417eb808de4b1dfe3ec725f02217 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 15:15:18 +0200 Subject: [PATCH 70/77] chg: PyMISP updated --- PyMISP | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PyMISP b/PyMISP index bfa5b67c1..9abaed049 160000 --- a/PyMISP +++ b/PyMISP @@ -1 +1 @@ -Subproject commit bfa5b67c1d17467ae18b29cb1e328a19698a3146 +Subproject commit 9abaed0499a7764359b6a082544519c39b713024 From b96dbdc2ab1e5703f3473877f3dcd37645c77878 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 15:16:30 +0200 Subject: [PATCH 71/77] fix: some minor bug fixes --- app/Controller/AttributesController.php | 4 +++ app/View/ObjectReferences/ajax/add.ctp | 48 ++++++++++++------------- app/webroot/css/main.css | 9 +++++ 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index 7bcc51cfa..27bd870e5 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -362,6 +362,8 @@ class AttributesController extends AppController { if ($this->request->data['Attribute']['malware']) { if ($this->request->data['Attribute']['advanced']) { $result = $this->Attribute->advancedAddMalwareSample($tmpfile); + if ($result) $success++; + else $fails[] = $filename; } else { $result = $this->Attribute->simpleAddMalwareSample( $eventId, @@ -372,6 +374,8 @@ class AttributesController extends AppController { $filename, $tmpfile ); + if ($result) $success++; + else $fails[] = $filename; } if (!empty($result)) { foreach ($result['Object'] as $object) { diff --git a/app/View/ObjectReferences/ajax/add.ctp b/app/View/ObjectReferences/ajax/add.ctp index a400604af..bcc3c8ab3 100644 --- a/app/View/ObjectReferences/ajax/add.ctp +++ b/app/View/ObjectReferences/ajax/add.ctp @@ -37,32 +37,32 @@
    - Form->input('uuid', array( - 'label' => 'Target UUID', - 'div' => false, - 'style' => 'width:320px;' - )); - ?> -
    - +
    +
    diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 24448f8af..1658136fb 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -1616,6 +1616,7 @@ a.discrete { padding:5px; border-radius:5px; height:228px; + width:320px; overflow-y:scroll; line-height:12px; } @@ -1895,6 +1896,14 @@ table tr:hover .down-expand-button { background: rgb(221, 221, 221) !important; } +.wrap-text { + word-wrap: break-word; +} + +.nowrap { + word-wrap:nowrap; +} + .strikethrough { text-decoration: line-through; } From cae1085443bb1a2de9a0bfd7df5dbce2bfd1a909 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 16:13:35 +0200 Subject: [PATCH 72/77] fix: Correlation improvements --- app/Model/Event.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index d174db97f..2fc86db02 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -610,17 +610,14 @@ class Event extends AppModel { // ii. Atttibute has a distribution between 1-3 (community only, connected communities, all orgs) // iii. Attribute has a sharing group that the user is accessible to view $conditionsCorrelation = $this->__buildEventConditionsCorrelation($user, $eventId, $sgids); - $correlations = $this->Correlation->find('all',array( - 'fields' => 'Correlation.event_id', + $correlations = $this->Correlation->find('list', array( + 'fields' => array('Correlation.event_id', 'Correlation.event_id'), 'conditions' => $conditionsCorrelation, 'recursive' => 0, + 'group' => 'Correlation.event_id', 'order' => array('Correlation.event_id DESC'))); - $relatedEventIds = array(); - foreach ($correlations as $correlation) { - $relatedEventIds[] = $correlation['Correlation']['event_id']; - } - $relatedEventIds = array_unique($relatedEventIds); + $relatedEventIds = array_values($correlations); // now look up the event data for these attributes $conditions = array("Event.id" => $relatedEventIds); $fields = array('id', 'date', 'threat_level_id', 'info', 'published', 'uuid', 'analysis', 'timestamp', 'distribution', 'org_id', 'orgc_id'); @@ -714,11 +711,11 @@ class Event extends AppModel { $correlations = $this->{$settings[$context]['correlationModel']}->find('all',array( 'fields' => $settings[$context]['correlationModel'] . '.*', 'conditions' => $conditionsCorrelation, - 'recursive' => 0, + 'recursive' => -1, 'order' => false )); $relatedAttributes = array(); - foreach ($correlations as $correlation) { + foreach ($correlations as $k => $correlation) { $current = array( 'id' => $correlation[$settings[$context]['correlationModel']]['event_id'], 'org_id' => $correlation[$settings[$context]['correlationModel']]['org_id'], @@ -728,6 +725,7 @@ class Event extends AppModel { if (empty($relatedAttributes[$correlation[$settings[$context]['correlationModel']][$settings[$context]['parentIdField']]]) || !in_array($current, $relatedAttributes[$correlation[$settings[$context]['correlationModel']][$settings[$context]['parentIdField']]])) { $relatedAttributes[$correlation[$settings[$context]['correlationModel']][$settings[$context]['parentIdField']]][] = $current; } + unset($correlations[$k]); } return $relatedAttributes; } From 6b68250c177fa5556c2019159e4783b3c780b849 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 16:54:27 +0200 Subject: [PATCH 73/77] fix: Added an upper limit for max correlations / event - super edge-case test instance got crushed by memory usage --- app/Model/Event.php | 5 ++++- app/Model/Server.php | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index 2fc86db02..3939c28d7 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -708,11 +708,14 @@ class Event extends AppModel { } else { $conditionsCorrelation = array($settings[$context]['correlationModel'] . '.1_event_id' => $id); } + $max_correlations = Configure::read('MISP.max_correlations_per_event'); + if (empty($max_correlations)) $max_correlations = 5000; $correlations = $this->{$settings[$context]['correlationModel']}->find('all',array( 'fields' => $settings[$context]['correlationModel'] . '.*', 'conditions' => $conditionsCorrelation, 'recursive' => -1, - 'order' => false + 'order' => false, + 'limit' => $max_correlations )); $relatedAttributes = array(); foreach ($correlations as $k => $correlation) { diff --git a/app/Model/Server.php b/app/Model/Server.php index 342de9496..1ad6c4178 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -137,6 +137,15 @@ class Server extends AppModel { 'type' => 'boolean', 'null' => true ), + 'max_correlations_per_event' => array( + 'level' => 1, + 'description' => 'Sets the maximum number of correlations that can be fetched with a single event. For extreme edge cases this can prevent memory issues. The default value is 5k.', + 'value' => 5000, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => true + ), 'maintenance_message' => array( 'level' => 2, 'description' => 'The message that users will see if the instance is not live.', From 8179587becf044484c750c9818d50746fde499a9 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 17:09:21 +0200 Subject: [PATCH 74/77] fix: Updated PyMISP --- PyMISP | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PyMISP b/PyMISP index 9abaed049..6eb807381 160000 --- a/PyMISP +++ b/PyMISP @@ -1 +1 @@ -Subproject commit 9abaed0499a7764359b6a082544519c39b713024 +Subproject commit 6eb807381dfa3d2a9dd2a42fdd219d6f3cdfd1ff From d32c8e8d91a2f2b0f5ff1cd9e75189cb7809a782 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 17:20:04 +0200 Subject: [PATCH 75/77] fix: Fixed double attachment of hashes for malware-samples --- app/Model/Attribute.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index 95fc06c96..51255437b 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -2788,8 +2788,6 @@ class Attribute extends AppModel { unset($attribute['id']); if (isset($attribute['encrypt'])) { $result = $this->handleMaliciousBase64($eventId, $attribute['value'], $attribute['data'], array('md5')); - $attribute['data'] = $result['data']; - $attribute['value'] = $attribute['value'] . '|' . $result['md5']; } $fieldList = array( 'event_id', From 3ffc3e3326d5c5b014ef690a6483fa6731603a52 Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 17:24:10 +0200 Subject: [PATCH 76/77] chg: Updated taxonomies --- app/files/taxonomies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/taxonomies b/app/files/taxonomies index 6a6168b4a..e89715212 160000 --- a/app/files/taxonomies +++ b/app/files/taxonomies @@ -1 +1 @@ -Subproject commit 6a6168b4a5235ce74053a3319a4e30c0ded54c61 +Subproject commit e89715212ce7dfa2e29df07aa28a679bcdff64dd From f61fb8190e4533c9c07b083a7cd396968cc0c93f Mon Sep 17 00:00:00 2001 From: iglocska Date: Mon, 18 Sep 2017 17:26:01 +0200 Subject: [PATCH 77/77] chg: Version bumps all around --- VERSION.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.json b/VERSION.json index b1f16b13a..f734a1088 100644 --- a/VERSION.json +++ b/VERSION.json @@ -1 +1 @@ -{"major":2, "minor":4, "hotfix":79} +{"major":2, "minor":4, "hotfix":80}
    Paginator->sort('active');?> Paginator->sort('id');?> Paginator->sort('name');?> Paginator->sort('uuid');?>
    > + /> + > >