From bdcfe06cf3a1c432484bff1121f67cad45e12be5 Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Tue, 12 Dec 2023 10:57:57 +0100 Subject: [PATCH 01/33] add: [upload_stix] Handling cluster distribution and sharing group for content imported from STIX 2.x --- app/Controller/EventsController.php | 31 ++++++++++--- app/Model/Event.php | 13 +++++- app/View/Events/upload_stix.ctp | 67 ++++++++++++++++++++++++---- app/files/scripts/stix2/stix2misp.py | 31 ++++++++++--- 4 files changed, 121 insertions(+), 21 deletions(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 67175c2f0..f7b5d2edc 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -2408,14 +2408,14 @@ class EventsController extends AppController } if (isset($this->params['named']['distribution'])) { $distribution = intval($this->params['named']['distribution']); - if (array_key_exists($distribution, $distributionLevels)) { - $initialDistribution = $distribution; - } else { + if (!array_key_exists($distribution, $distributionLevels)) { throw new MethodNotAllowedException(__('Wrong distribution level')); } + } else { + $distribution = $initialDistribution; } $sharingGroupId = null; - if ($initialDistribution == 4) { + if ($distribution == 4) { if (!isset($this->params['named']['sharing_group_id'])) { throw new MethodNotAllowedException(__('The sharing group id is needed when the distribution is set to 4 ("Sharing group").')); } @@ -2424,8 +2424,25 @@ class EventsController extends AppController throw new MethodNotAllowedException(__('Please select a valid sharing group id.')); } } + $clusterDistribution = $initialDistribution; + $clusterSharingGroupId = null; if (isset($this->params['named']['galaxies_as_tags'])) { $galaxies_as_tags = $this->params['named']['galaxies_as_tags']; + if (isset($this->params['name']['cluster_distribution'])) { + $clusterDistribution = intval($this->params['named']['cluster_distribution']); + if (!array_key_exists($clusterDistribution, $distributionLevels)) { + throw new MethodNotAllowedException(__('Wrong cluster distribution level')); + } + if ($clusterDistribution == 4) { + if (!isset($this->params['named']['cluster_sharing_group_id'])) { + throw new MethodNotAllowedException(__('The cluster sharing group id is needed when the cluster distribution is set to 4 ("Sharing group").')); + } + $clusterSharingGroupId = intval($this->params['named']['cluster_sharing_group_id']); + if (!array_key_exists($clusterSharingGroupId, $sgs)) { + throw new MethodNotAllowedException(__('Please select a valid cluster sharing group id.')); + } + } + } } if (isset($this->params['named']['debugging'])) { $debug = $this->params['named']['debugging']; @@ -2437,9 +2454,11 @@ class EventsController extends AppController $stix_version, 'uploaded_stix_file.' . ($stix_version == '1' ? 'xml' : 'json'), $publish, - $initialDistribution, + $distribution, $sharingGroupId, $galaxies_as_tags, + $clusterDistribution, + $clusterSharingGroupId, $debug ); if (is_numeric($result)) { @@ -2471,6 +2490,8 @@ class EventsController extends AppController $this->data['Event']['distribution'], $this->data['Event']['sharing_group_id'], $this->data['Event']['galaxies_handling'], + $this->data['Event']['cluster_distribution'], + $this->data['Event']['cluster_sharing_group_id'], $debug ); if (is_numeric($result)) { diff --git a/app/Model/Event.php b/app/Model/Event.php index 5b15fb915..fd5d6d43e 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -5925,12 +5925,18 @@ class Event extends AppModel * @param string $stix_version * @param string $original_file * @param bool $publish + * @param int $distribution + * @param int $sharingGroupId + * @param bool $galaxiesAsTags + * @param int $clusterDistribution + * @param int $clusterSharingGroupId + * @param bool $debug * @return int|string|array * @throws JsonException * @throws InvalidArgumentException * @throws Exception */ - public function upload_stix(array $user, $file, $stix_version, $original_file, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $debug = false) + public function upload_stix(array $user, $file, $stix_version, $original_file, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $clusterDistribution, $clusterSharingGroupId, $debug = false) { $scriptDir = APP . 'files' . DS . 'scripts'; if ($stix_version == '2' || $stix_version == '2.0' || $stix_version == '2.1') { @@ -5947,6 +5953,11 @@ class Event extends AppModel } if ($galaxiesAsTags) { $shell_command[] = '--galaxies_as_tags'; + } else { + array_push($shell_command, '--cluster_distribution', $clusterDistribution); + if ($clusterDistribution == 4) { + array_push($shell_command, '--cluster_sharing_group_id', $clusterSharingGroupId); + } } if ($debug) { $shell_command[] = '--debug'; diff --git a/app/View/Events/upload_stix.ctp b/app/View/Events/upload_stix.ctp index ef49d101e..2a1888fa7 100644 --- a/app/View/Events/upload_stix.ctp +++ b/app/View/Events/upload_stix.ctp @@ -27,17 +27,16 @@ 'label' => __('Distribution ') . $distributionFormInfo, 'selected' => $initialDistribution, )); -?> -
Form->input('publish', array( @@ -68,6 +67,36 @@ 'label' => __('How to handle Galaxies and Clusters') . $galaxiesFormInfo, 'selected' => 0 )); +?> +
+element( + 'genericElements/Form/formInfo', + [ + 'field' => [ + 'field' => 'cluster_distribution' + ], + 'modelForForm' => 'Event', + 'fieldDesc' => $fieldDesc['distribution'], + ] + ); + $clusterDistribution = $this->Form->input( + 'cluster_distribution', array( + 'options' => $distributionLevels, + 'label' => __('Cluster distribution ') . $clusterDistributionFormInfo, + 'selected' => $initialDistribution, + ) + ); + echo ''; + if (!empty($sharingGroups)) { + $clusterSGContainer = $this->Form->input( + 'cluster_sharing_group_id', array( + 'options' => array($sharingGroups), + 'label' => __('Cluster Sharing Group'), + ) + ); + echo ''; + } } if ($me['Role']['perm_site_admin'] && Configure::read('debug') > 0) { $debugFormInfo = $this->element( @@ -108,4 +137,26 @@ $(function(){ } }).change(); }); +$(function(){ + $('#EventGalaxiesHandling').change(function() { + if ($(this).val() == 0) { + $('#ClusterDistribution').show(); + if ($('#EventClusterDistribution').val() == 4) { + $('#ClusterSGContainer').show(); + } + } else { + $('#ClusterDistribution').hide(); + $('#ClusterSGContainer').hide(); + } + }).change(); +}); +$(function(){ + $('#EventClusterDistribution').change(function() { + if ($(this).val() == 4 && $('#EventGalaxiesHandling').val() == 0) { + $('#ClusterSGContainer').show(); + } else { + $('#ClusterSGContainer').hide(); + } + }).change(); +}); \ No newline at end of file diff --git a/app/files/scripts/stix2/stix2misp.py b/app/files/scripts/stix2/stix2misp.py index ed5fa15ef..d6d0bbc65 100644 --- a/app/files/scripts/stix2/stix2misp.py +++ b/app/files/scripts/stix2/stix2misp.py @@ -33,6 +33,21 @@ from misp_stix_converter import ( from stix2.parsing import parse as stix2_parser +def _get_stix_parser(from_misp, args): + arguments = { + 'distribution': args.distribution, + 'galaxies_as_tags': args.galaxies_as_tags + } + if args.distribution == 4 and args.sharing_group_id is not None: + arguments['sharing_group_id'] = args.sharing_group_id + if from_misp: + return 'InternalSTIX2toMISPParser', arguments + arguments['cluster_distribution'] = args.cluster_distribution + if args.cluster_distribution == 4 and args.cluster_sharing_group_id is not None: + arguments['cluster_sharing_group_id'] = args.cluster_sharing_group_id + return 'ExternalSTIX2toMISPParser', arguments + + def _handle_return_message(traceback): if isinstance(traceback, dict): messages = [] @@ -51,13 +66,7 @@ def _process_stix_file(args: argparse.ArgumentParser): f.read(), allow_custom=True, interoperability=True ) stix_version = getattr(bundle, 'version', '2.1') - to_call = 'Internal' if _from_misp(bundle.objects) else 'External' - arguments = { - 'distribution': args.distribution, - 'galaxies_as_tags': args.galaxies_as_tags - } - if args.distribution == 4 and args.sharing_group_id is not None: - arguments['sharing_group_id'] = args.sharing_group_id + to_call, arguments = _get_stix_parser(_from_misp(bundle.objects), args) parser = globals()[f'{to_call}STIX2toMISPParser'](**arguments) parser.load_stix_bundle(bundle) parser.parse_stix_bundle() @@ -107,6 +116,14 @@ if __name__ == '__main__': '--galaxies_as_tags', action='store_true', help='Import MISP Galaxies as tag names.' ) + argparser.add_argument( + '--cluster_distribution', type=int, default=0, + help='Cluster distribution level for clusters generated from STIX 2.x objects' + ) + argparser.add_argument( + '--cluster_sharing_group_id', type=int, + help='Cluster sharing group id when the cluster distribution level is 4.' + ) try: args = argparser.parse_args() _process_stix_file(args) From 5e8b122c2ed2bebdd8485e1b9c30bf87b5e30c1b Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Tue, 12 Dec 2023 11:24:30 +0100 Subject: [PATCH 02/33] chg: [misp-stix] Bumped latest version --- app/files/scripts/misp-stix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/scripts/misp-stix b/app/files/scripts/misp-stix index 5b678c26b..b0a1dcffa 160000 --- a/app/files/scripts/misp-stix +++ b/app/files/scripts/misp-stix @@ -1 +1 @@ -Subproject commit 5b678c26b95e0f34ad4515292f09e192aa0e4730 +Subproject commit b0a1dcffa44aee1af5b316babf323fcb24a6c0e1 From c5baab3328b665f915d532b0befc6f8ddeb74351 Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Tue, 12 Dec 2023 11:34:05 +0100 Subject: [PATCH 03/33] fix: [stix2 import] Fixed STIX2 parser name --- app/files/scripts/stix2/stix2misp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/scripts/stix2/stix2misp.py b/app/files/scripts/stix2/stix2misp.py index d6d0bbc65..9164dd697 100644 --- a/app/files/scripts/stix2/stix2misp.py +++ b/app/files/scripts/stix2/stix2misp.py @@ -67,7 +67,7 @@ def _process_stix_file(args: argparse.ArgumentParser): ) stix_version = getattr(bundle, 'version', '2.1') to_call, arguments = _get_stix_parser(_from_misp(bundle.objects), args) - parser = globals()[f'{to_call}STIX2toMISPParser'](**arguments) + parser = globals()[to_call](**arguments) parser.load_stix_bundle(bundle) parser.parse_stix_bundle() with open(f'{args.input}.out', 'wt', encoding='utf-8') as f: From debae13bc21d3296e0a7fd78c3bb04986167f0ae Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Thu, 14 Dec 2023 11:41:47 +0100 Subject: [PATCH 04/33] chg: [upload_stix] Casting distributions and sharing group IDs type --- app/Model/Event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index fd5d6d43e..5b6248656 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -5936,7 +5936,7 @@ class Event extends AppModel * @throws InvalidArgumentException * @throws Exception */ - public function upload_stix(array $user, $file, $stix_version, $original_file, $publish, $distribution, $sharingGroupId, $galaxiesAsTags, $clusterDistribution, $clusterSharingGroupId, $debug = false) + public function upload_stix(array $user, $file, $stix_version, $original_file, bool $publish, int $distribution, int $sharingGroupId, bool $galaxiesAsTags, int $clusterDistribution, int $clusterSharingGroupId, $debug = false) { $scriptDir = APP . 'files' . DS . 'scripts'; if ($stix_version == '2' || $stix_version == '2.0' || $stix_version == '2.1') { From d70150d237dc238980547f22c01834be52467c14 Mon Sep 17 00:00:00 2001 From: Christian Studer Date: Thu, 14 Dec 2023 14:10:58 +0100 Subject: [PATCH 05/33] fix: [upload_stix] Avoiding issues with sharing group arguments being null --- app/Model/Event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index 5b6248656..32e10f9f9 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -5936,7 +5936,7 @@ class Event extends AppModel * @throws InvalidArgumentException * @throws Exception */ - public function upload_stix(array $user, $file, $stix_version, $original_file, bool $publish, int $distribution, int $sharingGroupId, bool $galaxiesAsTags, int $clusterDistribution, int $clusterSharingGroupId, $debug = false) + public function upload_stix(array $user, $file, $stix_version, $original_file, bool $publish, int $distribution, ?int $sharingGroupId, bool $galaxiesAsTags, int $clusterDistribution, ?int $clusterSharingGroupId, $debug = false) { $scriptDir = APP . 'files' . DS . 'scripts'; if ($stix_version == '2' || $stix_version == '2.0' || $stix_version == '2.1') { From 720336f65d9f61db9b267bb3b09363ffc96811b9 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 21 Feb 2024 16:17:44 +0100 Subject: [PATCH 06/33] chg: [analyst-data:datetimes] Moved datetime manamgent of created and modified field from the DB to the app. - This change is to enforce the usage of UTC time as using MySQL's CURRENT_TIMESTAMP uses the TZ of the server --- app/Controller/AnalystDataController.php | 2 +- app/Model/AnalystData.php | 16 +++++++++++++++- app/Model/AppModel.php | 9 +++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/Controller/AnalystDataController.php b/app/Controller/AnalystDataController.php index ef7b305fb..c0e6182d4 100644 --- a/app/Controller/AnalystDataController.php +++ b/app/Controller/AnalystDataController.php @@ -89,7 +89,7 @@ class AnalystDataController extends AppController return $analystData; }, 'beforeSave' => function(array $analystData): array { - $analystData[$this->modelSelection]['modified'] = date ('Y-m-d H:i:s'); + $analystData[$this->modelSelection]['modified'] = date('Y-m-d H:i:s'); return $analystData; } ]; diff --git a/app/Model/AnalystData.php b/app/Model/AnalystData.php index f7023afe4..8e993e56e 100644 --- a/app/Model/AnalystData.php +++ b/app/Model/AnalystData.php @@ -126,7 +126,7 @@ class AnalystData extends AppModel public function beforeValidate($options = array()) { - parent::beforeValidate(); + parent::beforeValidate($options); if (empty($this->id) && empty($this->data[$this->current_type]['uuid'])) { $this->data[$this->current_type]['uuid'] = CakeText::uuid(); } @@ -142,6 +142,20 @@ class AnalystData extends AppModel return true; } + public function beforeSave($options = []) + { + parent::beforeSave($options); + if (empty($this->data[$this->current_type]['created'])) { + $this->data[$this->current_type]['created'] = (new DateTime())->format('c'); + } + if (empty($this->data[$this->current_type]['modified'])) { + $this->data[$this->current_type]['modified'] = (new DateTime())->format('c'); + } + $this->data[$this->current_type]['modified'] = (new DateTime($this->data[$this->current_type]['modified'], new DateTimeZone('UTC')))->format('c'); + $this->data[$this->current_type]['created'] = (new DateTime($this->data[$this->current_type]['created'], new DateTimeZone('UTC')))->format('c'); + return true; + } + /** * Checks if user can modify given analyst data * diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 3077fa34e..48582c699 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -91,6 +91,7 @@ class AppModel extends Model 105 => false, 106 => false, 107 => false, 108 => false, 109 => false, 110 => false, 111 => false, 112 => false, 113 => true, 114 => false, 115 => false, 116 => false, 117 => false, 118 => false, 119 => false, 120 => false, 121 => false, 122 => false, + 123 => false, ); const ADVANCED_UPDATES_DESCRIPTION = array( @@ -2155,6 +2156,14 @@ class AppModel extends Model UNIQUE KEY `unique_element` (`element_uuid`, `collection_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; break; + case 123: + $sqlArray[] = 'ALTER TABLE `notes` MODIFY `created` datetime NOT NULL'; + $sqlArray[] = 'ALTER TABLE `opinions` MODIFY `created` datetime NOT NULL;'; + $sqlArray[] = 'ALTER TABLE `relationships` MODIFY `created` datetime NOT NULL;'; + $sqlArray[] = 'ALTER TABLE `notes` MODIFY `modified` datetime NOT NULL;'; + $sqlArray[] = 'ALTER TABLE `opinions` MODIFY `modified` datetime NOT NULL;'; + $sqlArray[] = 'ALTER TABLE `relationships` MODIFY `modified` datetime NOT NULL;'; + 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 396837675e420d327eafd937ea346016e06dd241 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 21 Feb 2024 16:20:26 +0100 Subject: [PATCH 07/33] chg: [analyst-data:thread] Gracefully catch cases where the related object is not found when generating link URL --- app/View/Elements/genericElements/Analyst_data/thread.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/genericElements/Analyst_data/thread.ctp b/app/View/Elements/genericElements/Analyst_data/thread.ctp index 1bb31583d..f2c02767f 100644 --- a/app/View/Elements/genericElements/Analyst_data/thread.ctp +++ b/app/View/Elements/genericElements/Analyst_data/thread.ctp @@ -89,9 +89,9 @@ function getURLFromRelationship(note) { if (note.related_object_type == 'Event') { return baseurl + '/events/view/' + note.related_object_uuid } else if (note.related_object_type == 'Attribute') { - return baseurl + '/events/view/' + note.attribute.event_id + '/focus:' + note.related_object_uuid + return note?.attribute?.event_id ? baseurl + '/events/view/' + note.attribute.event_id + '/focus:' + note.related_object_uuid : '#' } else if (note.related_object_type == 'Object') { - return baseurl + '/events/view/' + note.object.event_id + '/focus:' + note.related_object_uuid + return note?.object?.event_id ? baseurl + '/events/view/' + note.object.event_id + '/focus:' + note.related_object_uuid : '#' } return '#' } From b7242f7daef267a62c59ff13a247189adab86afd Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 21 Feb 2024 16:26:06 +0100 Subject: [PATCH 08/33] chg: [analyst-data:UI] Added highlight on note opener button - As request by gallypette --- app/View/Elements/genericElements/Analyst_data/generic.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/View/Elements/genericElements/Analyst_data/generic.ctp b/app/View/Elements/genericElements/Analyst_data/generic.ctp index be41a365e..3784d94a9 100644 --- a/app/View/Elements/genericElements/Analyst_data/generic.ctp +++ b/app/View/Elements/genericElements/Analyst_data/generic.ctp @@ -43,7 +43,7 @@ $relationshipsCount = count($relationships); - + From 7b661f740a90d39375d38ae6a8efa8d6ae262351 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 21 Feb 2024 16:26:06 +0100 Subject: [PATCH 09/33] chg: [analyst-data:UI] Added highlight on note opener button - As request by gallypette --- app/View/Elements/genericElements/Analyst_data/generic.ctp | 2 +- app/webroot/css/main.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/View/Elements/genericElements/Analyst_data/generic.ctp b/app/View/Elements/genericElements/Analyst_data/generic.ctp index be41a365e..3784d94a9 100644 --- a/app/View/Elements/genericElements/Analyst_data/generic.ctp +++ b/app/View/Elements/genericElements/Analyst_data/generic.ctp @@ -43,7 +43,7 @@ $relationshipsCount = count($relationships); - + diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 1b691b0a1..772c138db 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -2936,6 +2936,10 @@ Query builder padding-right: 3px; } +.highlight-on-hover:hover { + filter: brightness(1.1); +} + .special-tag { animation: special-tag-color 4s infinite linear; } From 224415c3b4a5c7ee83eda640c695889a6052920d Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 08:32:47 +0100 Subject: [PATCH 10/33] chg: [analyst-data:edit] Added support of editable fields --- app/Controller/AnalystDataController.php | 1 + app/Model/AnalystData.php | 14 ++++++++++++++ app/Model/Note.php | 7 +++++-- app/Model/Opinion.php | 8 ++++++-- app/Model/Relationship.php | 7 +++++-- app/View/AnalystData/add.ctp | 2 ++ 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/app/Controller/AnalystDataController.php b/app/Controller/AnalystDataController.php index c0e6182d4..9659555d8 100644 --- a/app/Controller/AnalystDataController.php +++ b/app/Controller/AnalystDataController.php @@ -80,6 +80,7 @@ class AnalystDataController extends AppController $this->set('id', $id); $conditions = $this->AnalystData->buildConditions($this->Auth->user()); $params = [ + 'fields' => $this->AnalystData->getEditableFields(), 'conditions' => $conditions, 'afterFind' => function(array $analystData): array { $canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection); diff --git a/app/Model/AnalystData.php b/app/Model/AnalystData.php index 8e993e56e..774cf94a4 100644 --- a/app/Model/AnalystData.php +++ b/app/Model/AnalystData.php @@ -36,6 +36,15 @@ class AnalystData extends AppModel 'Relationship', ]; + protected const BASE_EDITABLE_FIELDS = [ + 'language', + 'authors', + 'modified', + 'distribution', + 'sharing_group_id', + ]; + protected $EDITABLE_FIELDS = []; + /** @var object|null */ protected $Note; /** @var object|null */ @@ -156,6 +165,11 @@ class AnalystData extends AppModel return true; } + public function getEditableFields(): array + { + return array_merge(self::BASE_EDITABLE_FIELDS, $this->EDITABLE_FIELDS); + } + /** * Checks if user can modify given analyst data * diff --git a/app/Model/Note.php b/app/Model/Note.php index 9f41df961..481a3711c 100644 --- a/app/Model/Note.php +++ b/app/Model/Note.php @@ -15,8 +15,11 @@ class Note extends AnalystData public $current_type = 'Note'; public $current_type_id = 0; - public $validate = array( - ); + public const EDITABLE_FIELDS = [ + 'note', + ]; + + public $validate = []; public function beforeValidate($options = array()) { diff --git a/app/Model/Opinion.php b/app/Model/Opinion.php index 33b65f29c..8a7c589d2 100644 --- a/app/Model/Opinion.php +++ b/app/Model/Opinion.php @@ -15,8 +15,12 @@ class Opinion extends AnalystData public $current_type = 'Opinion'; public $current_type_id = 1; - public $validate = array( - ); + public const EDITABLE_FIELDS = [ + 'opinion', + 'comment', + ]; + + public $validate = []; public function beforeValidate($options = array()) { diff --git a/app/Model/Relationship.php b/app/Model/Relationship.php index ea73cd777..b469e24a5 100644 --- a/app/Model/Relationship.php +++ b/app/Model/Relationship.php @@ -15,8 +15,11 @@ class Relationship extends AnalystData public $current_type = 'Relationship'; public $current_type_id = 2; - public $validate = array( - ); + protected $EDITABLE_FIELDS = [ + 'relationship_type', + ]; + + public $validate = []; /** @var object|null */ protected $Event; diff --git a/app/View/AnalystData/add.ctp b/app/View/AnalystData/add.ctp index de7ae5437..9d6eaf389 100644 --- a/app/View/AnalystData/add.ctp +++ b/app/View/AnalystData/add.ctp @@ -89,10 +89,12 @@ if ($modelSelection === 'Note') { 'options' => $dropdownData['valid_targets'], 'type' => 'dropdown', 'stayInLine' => 1, + 'disabled' => !empty($this->data[$modelSelection]['related_object_type']), ], [ 'field' => 'related_object_uuid', 'class' => 'span4', + 'disabled' => !empty($this->data[$modelSelection]['related_object_uuid']), ], sprintf('
', __('Related Object'), __('- No UUID provided -')) ] From 48a7addb04587adfe14d16592db079e5db464b6a Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 08:40:58 +0100 Subject: [PATCH 11/33] fix: [galaxyCluster:view/analystData] Load assets before trying to render notes --- app/View/GalaxyClusters/view.ctp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/View/GalaxyClusters/view.ctp b/app/View/GalaxyClusters/view.ctp index f998d711f..02406b306 100755 --- a/app/View/GalaxyClusters/view.ctp +++ b/app/View/GalaxyClusters/view.ctp @@ -1,4 +1,9 @@ element('genericElements/assetLoader', [ + 'js' => ['doT', 'moment.min'], + 'css' => ['analyst-data',], +]); + $extendedFromHtml = ''; if (!empty($cluster['GalaxyCluster']['extended_from'])) { $element = $this->element('genericElements/IndexTable/Fields/links', array( @@ -174,10 +179,6 @@ $options = [ 'relationships' => $cluster['GalaxyCluster']['Relationship'] ?? [], ]; -echo $this->element('genericElements/assetLoader', [ - 'js' => ['doT', 'moment.min'], - 'css' => ['analyst-data',], -]); echo $this->element('genericElements/Analyst_data/thread', $options); ?> \ No newline at end of file From a2497f5763b3c4aa0c2482c5d33d2740f1042407 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 08:56:12 +0100 Subject: [PATCH 12/33] fix: [db_schema] Bumped db_version --- db_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db_schema.json b/db_schema.json index eddfd6cba..aa1f9f524 100644 --- a/db_schema.json +++ b/db_schema.json @@ -10547,5 +10547,5 @@ "uuid": false } }, - "db_version": "122" + "db_version": "123" } From b8c2c7be642178f78901a49bdc40aac447dfcef8 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 10:20:11 +0100 Subject: [PATCH 13/33] fix: [login:UI] Reverted change that swapped `main_logo` with `home_logo` --- app/View/Users/login.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp index 4b947894a..1077fe454 100644 --- a/app/View/Users/login.ctp +++ b/app/View/Users/login.ctp @@ -17,7 +17,7 @@


- + From 6655697dbc2a43f9ca4fe9295de2630f62634fb1 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 10:30:15 +0100 Subject: [PATCH 14/33] fix: [users:login] Check file existence in the correct location --- app/View/Users/login.ctp | 2 +- app/View/Users/logout401.ctp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp index 1077fe454..476ba7319 100644 --- a/app/View/Users/login.ctp +++ b/app/View/Users/login.ctp @@ -16,7 +16,7 @@ ?>

- + diff --git a/app/View/Users/logout401.ctp b/app/View/Users/logout401.ctp index 21b1fbab2..36ab957fa 100644 --- a/app/View/Users/logout401.ctp +++ b/app/View/Users/logout401.ctp @@ -29,7 +29,7 @@

- + From a1bba71204cbb54de21eac5d324ff14288e89574 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 10:30:15 +0100 Subject: [PATCH 15/33] fix: [users:login] Check file existence in the correct location --- app/View/Users/login.ctp | 4 ++-- app/View/Users/logout401.ctp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp index 077d83aa9..e80556cd6 100644 --- a/app/View/Users/login.ctp +++ b/app/View/Users/login.ctp @@ -16,8 +16,8 @@ ?>

- - + + diff --git a/app/View/Users/logout401.ctp b/app/View/Users/logout401.ctp index 21b1fbab2..36ab957fa 100644 --- a/app/View/Users/logout401.ctp +++ b/app/View/Users/logout401.ctp @@ -29,7 +29,7 @@

- + From 8d9eef79fb2836ab11c0510b6d67141a0886878e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 22 Feb 2024 15:49:54 +0100 Subject: [PATCH 16/33] Revert "fix: [users:login] Check file existence in the correct location" This reverts commit a1bba71204cbb54de21eac5d324ff14288e89574. --- app/View/Users/login.ctp | 4 ++-- app/View/Users/logout401.ctp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/View/Users/login.ctp b/app/View/Users/login.ctp index e80556cd6..077d83aa9 100644 --- a/app/View/Users/login.ctp +++ b/app/View/Users/login.ctp @@ -16,8 +16,8 @@ ?>

- - + + diff --git a/app/View/Users/logout401.ctp b/app/View/Users/logout401.ctp index 36ab957fa..21b1fbab2 100644 --- a/app/View/Users/logout401.ctp +++ b/app/View/Users/logout401.ctp @@ -29,7 +29,7 @@

- + From 45e23c85099b9c3f2b1724585de2bc20baa07700 Mon Sep 17 00:00:00 2001 From: iglocska Date: Thu, 22 Feb 2024 17:03:48 +0100 Subject: [PATCH 17/33] fix: [processtool] make old versions happy - proc_open only started accepting $command as an array in 7.4 --- app/Lib/Tools/ProcessTool.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/Lib/Tools/ProcessTool.php b/app/Lib/Tools/ProcessTool.php index b0003938b..30b28e18d 100644 --- a/app/Lib/Tools/ProcessTool.php +++ b/app/Lib/Tools/ProcessTool.php @@ -55,8 +55,19 @@ class ProcessTool if ($logToFile) { self::logMessage('Running command ' . implode(' ', $command)); } - - $process = proc_open($command, $descriptorSpec, $pipes, $cwd); + if (version_compare(phpversion(), '7.4.0', '<')) { + $temp = []; + foreach ($command as $k => $part) { + if ($k >= 1) { + $part = escapeshellarg($part); + } + $temp[] = $part; + } + $command_stringified = implode(' ', $temp); + $process = proc_open($command_stringified, $descriptorSpec, $pipes, $cwd); + } else { + $process = proc_open($command, $descriptorSpec, $pipes, $cwd); + } if (!$process) { $commandForException = self::commandFormat($command); throw new Exception("Command '$commandForException' could be started."); From 105e7fc267bcf634033231362a7a010f3b7825f8 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Fri, 23 Feb 2024 09:43:53 +0100 Subject: [PATCH 18/33] Minor documentation changes; add example to create users via REST API --- app/Plugin/AadAuth/README.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/Plugin/AadAuth/README.md b/app/Plugin/AadAuth/README.md index d5e9447eb..07e49e486 100755 --- a/app/Plugin/AadAuth/README.md +++ b/app/Plugin/AadAuth/README.md @@ -1,16 +1,16 @@ -# Configure Azure AD to use SIngle SignOn for MISP +# Configure Azure AD to use Single Sign-On (SSO) for MISP -This plugin enables authentication with an Azure Active Directory server. Under the hood it uses oAuth2. There are still a number of rough edges but in general the plugin works. +This plugin enables authentication with an Azure Active Directory (now called [Entra-ID](https://www.microsoft.com/en-us/security/business/identity-access/microsoft-entra-id)) server. Under the hood it uses oAuth2. There are still a number of rough edges but in general the plugin works. It supports verification if a user has the proper ‘MISP AD’ groups. Users should already exist in MISP. Future enhancement could include auto-create users ## Azure AD — App Registration Configuration -In Azure, add a new App Registration. Select Web and set the Redirect URI to your MISP server login page `https://misp.yourdomain.com/users/login`. The MISP instance does not need to be publicly accessible if it is reachable by your browser. The redirect URI that you specify here must be the same as used in the MISP configuration. +In Azure, add a new App Registration. Select Web and set the Redirect URI to your MISP server login page `https://misp.yourdomain.com/users/login`. The MISP instance does not need to be publicly accessible if it is reachable by your browser. The redirect URI that you specify here must be the same as used in the MISP configuration (including `/users/login`). You can add as many redirect URIs as needed, meaning you can have multiple MISP servers use the same Azure App. ![AppReg Configuration](.images/Picture29.png) -On the Overview page of the new MISP App Registration capture the following inforamtion. +On the Overview page of the new MISP App Registration capture the following information. - [x] Application (client) ID - [x] Directory (tenant) ID @@ -44,7 +44,7 @@ Create the following groups in Azure AD, these can be called anything you like f Make a name of your groups, we'll need these later. - [x] Misp Users -- [x] Misp ORG Admins +- [x] Misp Org Admins - [x] Misp Site Admins ## Enable the AAD Plugin for MISP @@ -122,7 +122,7 @@ Scroll down to near the bottom of the page and add in the following configuratio ), ``` -Add the information we made a note of earlier when creating the `App Registation` and optionally the Azure AD groups you created. +Add the information we made a note of earlier when creating the `App Registration` and optionally the Azure AD groups you created. ![AadAuth.configuration](.images/Picture38.png) @@ -139,4 +139,12 @@ Additionally, it is recommended to set the following settings in the MISP config * `MISP.disable_user_login_change => true`: Removes the ability of users to change their username (email), except for site admins. * `MISP.disable_user_password_change => true`: Removes the ability of users to change their own password. -This way users will not be able to change their passwords and by-pass the AAD login flow. \ No newline at end of file +This way users will not be able to change their passwords and by-pass the AAD login flow. + +# Create users via the MISP REST API + +Because users already need to exist in MISP before they can authenticate with AAD it can be useful to provision them in an automated fashion. This can be done by creating the users via the MISP REST API. The below `curl` command provides an example on how to do this. Note that you need an API key. + +``` +curl -k -d '{"email":"newuser@mycompany.com", "role_id":"3", "org_id":"1", "enable_password":"1", "change_pw":"0"}' -H "Authorization: API_KEY" -H "Accept: application/json" -H "Content-type: application/json" -X POST htps://misp.mycompany.com/admin/users/add +``` From 7e7dcec24091e1cf26283c478c3532e42504ce24 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 23 Feb 2024 10:01:18 +0100 Subject: [PATCH 19/33] fix: [UI] Catch exception when custom file is not readable --- app/View/Helper/ImageHelper.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/View/Helper/ImageHelper.php b/app/View/Helper/ImageHelper.php index 7507eb921..90db5c22a 100644 --- a/app/View/Helper/ImageHelper.php +++ b/app/View/Helper/ImageHelper.php @@ -27,8 +27,15 @@ class ImageHelper extends AppHelper throw new InvalidArgumentException("Only SVG and PNG images are supported"); } - $fileContent = base64_encode(FileAccessTool::readFromFile($imagePath)); - $base64 = "data:$mime;base64,$fileContent"; + try { + $fileContent = FileAccessTool::readFromFile($imagePath); + } catch (Exception $e) { + CakeLog::warning($e); + return 'data:null'; // in case file doesn't exists or is not readable + } + + $fileContentEncoded = base64_encode($fileContent); + $base64 = "data:$mime;base64,$fileContentEncoded"; return $this->imageCache[$imagePath] = $base64; } From e6ec7871e3cded984f5aa9caba8727564e260954 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 23 Feb 2024 10:18:29 +0100 Subject: [PATCH 20/33] fix: [notes] changed timestamp output to not include timezone - doesn't work on all versions of mariadb/mysql --- app/Model/AnalystData.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Model/AnalystData.php b/app/Model/AnalystData.php index 774cf94a4..42ff8e268 100644 --- a/app/Model/AnalystData.php +++ b/app/Model/AnalystData.php @@ -155,13 +155,13 @@ class AnalystData extends AppModel { parent::beforeSave($options); if (empty($this->data[$this->current_type]['created'])) { - $this->data[$this->current_type]['created'] = (new DateTime())->format('c'); + $this->data[$this->current_type]['created'] = (new DateTime())->format('Y-m-d H:i:s'); } if (empty($this->data[$this->current_type]['modified'])) { - $this->data[$this->current_type]['modified'] = (new DateTime())->format('c'); + $this->data[$this->current_type]['modified'] = (new DateTime())->format('Y-m-d H:i:s'); } - $this->data[$this->current_type]['modified'] = (new DateTime($this->data[$this->current_type]['modified'], new DateTimeZone('UTC')))->format('c'); - $this->data[$this->current_type]['created'] = (new DateTime($this->data[$this->current_type]['created'], new DateTimeZone('UTC')))->format('c'); + $this->data[$this->current_type]['modified'] = (new DateTime($this->data[$this->current_type]['modified'], new DateTimeZone('UTC')))->format('Y-m-d H:i:s'); + $this->data[$this->current_type]['created'] = (new DateTime($this->data[$this->current_type]['created'], new DateTimeZone('UTC')))->format('Y-m-d H:i:s'); return true; } From 52ff88d5c89546953c7177c8d14b26598449148a Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 23 Feb 2024 10:28:36 +0100 Subject: [PATCH 21/33] fix: [internal] exif_imagetype is not standard part of PHP --- app/Controller/OrganisationsController.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Controller/OrganisationsController.php b/app/Controller/OrganisationsController.php index 2150a5b59..fd0f98931 100644 --- a/app/Controller/OrganisationsController.php +++ b/app/Controller/OrganisationsController.php @@ -481,8 +481,8 @@ class OrganisationsController extends AppController $extension = pathinfo($logo['name'], PATHINFO_EXTENSION); $filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png'); - if ($logo['size'] > 250*1024) { - $this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250kB.')); + if ($logo['size'] > 250 * 1024) { + $this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250 kB.')); return false; } @@ -492,10 +492,12 @@ class OrganisationsController extends AppController } $imgMime = mime_content_type($logo['tmp_name']); - if ($extension === 'png' && !exif_imagetype($logo['tmp_name'])) { + if ($extension === 'png' && (function_exists('exif_imagetype') && !exif_imagetype($logo['tmp_name']))) { $this->Flash->error(__('This is not a valid PNG image.')); return false; - } else if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) { + } + + if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) { $this->Flash->error(__('This is not a valid SVG image.')); return false; } From f8a92524eea1d5c6a3941bd44484caab4d00ded8 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 20 Feb 2024 15:55:16 +0100 Subject: [PATCH 22/33] fix: [UI] Custom logos --- app/Model/AppModel.php | 1 - app/Model/Server.php | 24 +- app/View/Elements/footer.ctp | 2 +- app/View/Elements/global_menu.ctp | 1097 +++++++++++++++-------------- app/View/Helper/ImageHelper.php | 35 + app/View/Helper/OrgImgHelper.php | 9 +- app/View/Users/login.ctp | 4 +- app/files/img/custom/empty | 0 8 files changed, 608 insertions(+), 564 deletions(-) create mode 100644 app/View/Helper/ImageHelper.php create mode 100644 app/files/img/custom/empty diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 7f7bea0a4..b3a5a088e 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -4087,7 +4087,6 @@ class AppModel extends Model $newImageDir = APP . 'files/img'; $oldOrgDir = new Folder($oldImageDir . '/orgs'); $oldCustomDir = new Folder($oldImageDir . '/custom'); - $result = false; $result = $oldOrgDir->copy([ 'from' => $oldImageDir . '/orgs', 'to' => $newImageDir . '/orgs', diff --git a/app/Model/Server.php b/app/Model/Server.php index 14a3f4ab6..bf6858584 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -2543,33 +2543,33 @@ class Server extends AppModel public function getFileRules() { - return array( - 'orgs' => array( + return [ + 'orgs' => [ 'name' => __('Organisation logos'), 'description' => __('The logo used by an organisation on the event index, event view, discussions, proposals, etc. Make sure that the filename is in the org.png format, where org is the case-sensitive organisation name.'), - 'expected' => array(), + 'expected' => [], 'valid_format' => __('48x48 pixel .png files or .svg file'), - 'path' => APP . 'webroot' . DS . 'img' . DS . 'orgs', + 'path' => APP . 'files' . DS . 'img' . DS . 'orgs', 'regex' => '.*\.(png|svg)$', 'regex_error' => __('Filename must be in the following format: *.png or *.svg'), - 'files' => array(), - ), - 'img' => array( + 'files' => [], + ], + 'img' => [ 'name' => __('Additional image files'), 'description' => __('Image files uploaded into this directory can be used for various purposes, such as for the login page logos'), - 'expected' => array( + 'expected' => [ 'MISP.footer_logo' => Configure::read('MISP.footer_logo'), 'MISP.home_logo' => Configure::read('MISP.home_logo'), 'MISP.welcome_logo' => Configure::read('MISP.welcome_logo'), 'MISP.welcome_logo2' => Configure::read('MISP.welcome_logo2'), - ), + ], 'valid_format' => __('PNG or SVG file'), - 'path' => APP . 'webroot' . DS . 'img' . DS . 'custom', + 'path' => APP . 'files' . DS . 'img' . DS . 'custom', 'regex' => '.*\.(png|svg)$', 'regex_error' => __('Filename must be in the following format: *.png or *.svg'), 'files' => array(), - ), - ); + ], + ]; } public function grabFiles() diff --git a/app/View/Elements/footer.ctp b/app/View/Elements/footer.ctp index a23c721c0..8159f267e 100644 --- a/app/View/Elements/footer.ctp +++ b/app/View/Elements/footer.ctp @@ -34,7 +34,7 @@
Html->image('custom/' . h(Configure::read('MISP.footer_logo')), array('alt' => 'Footer Logo', 'onerror' => "this.style.display='none';", 'style' => 'height:24px')); + echo '' . __('Footer logo') . ''; } ?>
diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 5cdfae0c1..eca4c1b43 100755 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -1,549 +1,560 @@ 'root', - 'url' => empty($homepage['path']) ? $baseurl .'/' : $baseurl . h($homepage['path']), - 'html' => Configure::read('MISP.home_logo') ? '' . __('Home') . '' : __('Home'), - ), - array( - 'type' => 'root', - 'text' => __('Event Actions'), - 'children' => array( - array( - 'text' => __('List Events'), - 'url' => $baseurl . '/events/index' - ), - array( - 'text' => __('Add Event'), - 'url' => $baseurl . '/events/add', - 'requirement' => $this->Acl->canAccess('events', 'add'), - ), - array( - 'text' => __('List Attributes'), - 'url' => $baseurl . '/attributes/index' - ), - array( - 'text' => __('Search Attributes'), - 'url' => $baseurl . '/attributes/search' - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('View Proposals'), - 'url' => $baseurl . '/shadow_attributes/index/all:0' - ), - array( - 'text' => __('Events with proposals'), - 'url' => $baseurl . '/events/proposalEventIndex' - ), - array( - 'url' => $baseurl . '/event_delegations/index/context:pending', - 'text' => __('View delegation requests'), - 'requirement' => $this->Acl->canAccess('event_delegations', 'index'), - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('List Tags'), - 'url' => $baseurl . '/tags/index' - ), - array( - 'text' => __('Add Tag'), - 'url' => $baseurl . '/tags/add', - 'requirement' => $this->Acl->canAccess('tags', 'add'), - ), - array( - 'text' => __('List Tag Collections'), - 'url' => $baseurl . '/tag_collections/index' - ), - array( - 'text' => __('List Taxonomies'), - 'url' => $baseurl . '/taxonomies/index' - ), - array( - 'text' => __('List Templates'), - 'url' => $baseurl . '/templates/index' - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('Export'), - 'url' => $baseurl . '/events/export' - ), - array( - 'text' => __('Automation'), - 'url' => $baseurl . '/events/automation', - 'requirement' => $this->Acl->canAccess('events', 'automation'), - ), - array( - 'type' => 'separator', - 'requirement' => - Configure::read('MISP.enableEventBlocklisting') !== false && - !$isSiteAdmin && - $hostOrgUser - ), - array( - 'text' => __('Blocklist Event'), - 'url' => $baseurl . '/eventBlocklists/add', - 'requirement' => - Configure::read('MISP.enableEventBlocklisting') !== false && - !$isSiteAdmin && $hostOrgUser - ), - array( - 'text' => __('Manage Event Blocklists'), - 'url' => $baseurl . '/eventBlocklists', - 'requirement' => - Configure::read('MISP.enableEventBlocklisting') !== false && - !$isSiteAdmin && $hostOrgUser - ) - ) - ), - array( - 'type' => 'root', - 'text' => __('Dashboard'), - 'url' => $baseurl . '/dashboards' - ), - array( - 'type' => 'root', - 'text' => __('Galaxies'), - 'url' => $baseurl . '/galaxies/index', - 'children' => array( - array( - 'text' => __('List Galaxies'), - 'url' => $baseurl . '/galaxies/index' - ), - array( - 'text' => __('List Relationships'), - 'url' => $baseurl . '/galaxy_cluster_relations/index' - ), - ) - ), - array( - 'type' => 'root', - 'text' => __('Input Filters'), - 'children' => array( - array( - 'text' => __('Import Regexp'), - 'url' => $baseurl . '/admin/regexp/index', - 'requirement' => $isAclRegexp - ), - array( - 'text' => __('Import Regexp'), - 'url' => $baseurl . '/regexp/index', - 'requirement' => !$isAclRegexp - ), - array( - 'text' => __('Signature Allowedlist'), - 'url' => $baseurl . '/admin/allowedlists/index', - 'requirement' => $isAclRegexp - ), - array( - 'text' => __('Signature Allowedlist'), - 'url' => $baseurl . '/allowedlists/index', - 'requirement' => !$isAclRegexp - ), - array( - 'text' => __('Warninglists'), - 'url' => $baseurl . '/warninglists/index' - ), - array( - 'text' => __('Noticelists'), - 'url' => $baseurl . '/noticelists/index' - ), - array( - 'text' => __('Correlation Exclusions'), - 'url' => $baseurl . '/correlation_exclusions/index', - 'requirement' => $this->Acl->canAccess('correlation_exclusions', 'index'), - ) - ) - ), - array( - 'type' => 'root', - 'text' => __('Global Actions'), - 'children' => array( - array( - 'text' => __('News'), - 'url' => $baseurl . '/news' - ), - array( - 'text' => __('My Profile'), - 'url' => $baseurl . '/users/view/me' - ), - array( - 'text' => __('My Settings'), - 'url' => $baseurl . '/user_settings/index/user_id:me' - ), - array( - 'text' => __('Set Setting'), - 'url' => $baseurl . '/user_settings/setSetting' - ), - array( - 'text' => __('Organisations'), - 'url' => $baseurl . '/organisations/index', - 'requirement' => $this->Acl->canAccess('organisations', 'index'), - ), - array( - 'text' => __('Role Permissions'), - 'url' => $baseurl . '/roles/index' - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('List Object Templates'), - 'url' => $baseurl . '/objectTemplates/index' - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('List Sharing Groups'), - 'url' => $baseurl . '/sharing_groups/index' - ), - array( - 'text' => __('Add Sharing Group'), - 'url' => $baseurl . '/sharing_groups/add', - 'requirement' => $this->Acl->canAccess('sharing_groups', 'add'), - ), - array( - 'text' => __('List Sharing Groups Blueprints'), - 'url' => $baseurl . '/sharing_group_blueprints/index', - 'requirement' => $this->Acl->canAccess('sharing_group_blueprints', 'index'), - ), - array( - 'text' => __('Add Sharing Group Blueprint'), - 'url' => $baseurl . '/sharing_group_blueprints/add', - 'requirement' => $this->Acl->canAccess('sharing_group_blueprints', 'add'), - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('Decaying Models Tool'), - 'url' => $baseurl . '/decayingModel/decayingTool', - 'requirement' => $isAdmin - ), - array( - 'text' => __('List Decaying Models'), - 'url' => $baseurl . '/decayingModel/index', - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('User Guide'), - 'url' => 'https://www.circl.lu/doc/misp/' - ), - array( - 'text' => __('Categories & Types'), - 'url' => $baseurl . '/pages/display/doc/categories_and_types' - ), - array( - 'text' => __('Terms & Conditions'), - 'url' => $baseurl . '/users/terms' - ), - array( - 'text' => __('Statistics'), - 'url' => $baseurl . '/users/statistics' - ), - array( - 'type' => 'separator', - 'requirement' => $this->Acl->canAccess('threads', 'index'), - ), - array( - 'text' => __('List Discussions'), - 'url' => $baseurl . '/threads/index', - 'requirement' => $this->Acl->canAccess('threads', 'index'), - ), - array( - 'text' => __('Start Discussion'), - 'url' => $baseurl . '/posts/add', - 'requirement' => $this->Acl->canAccess('posts', 'add'), - ) - ) - ), - array( - 'type' => 'root', - 'text' => __('Sync Actions'), - 'requirement' => $isAclSync || $isAdmin || $hostOrgUser, - 'children' => array( - array( - 'text' => __('Create Sync Config'), - 'url' => $baseurl . '/servers/createSync', - 'requirement' => $isAclSync && !$isSiteAdmin - ), - array( - 'text' => __('Remote Servers'), - 'url' => $baseurl . '/servers/index', - 'requirement' => $this->Acl->canAccess('servers', 'index'), - ), - array( - 'text' => __('Feeds'), - 'url' => $baseurl . '/feeds/index', - 'requirement' => $this->Acl->canAccess('feeds', 'index'), - ), - array( - 'text' => __('SightingDB'), - 'url' => $baseurl . '/sightingdb/index', - 'requirement' => $this->Acl->canAccess('sightingdb', 'index'), - ), - array( - 'text' => __('Communities'), - 'url' => $baseurl . '/communities/index', - 'requirement' => $this->Acl->canAccess('communities', 'index'), - ), - array( - 'text' => __('Cerebrates'), - 'url' => $baseurl . '/cerebrates/index', - 'requirement' => $this->Acl->canAccess('cerebrates', 'index'), - ), - array( - 'text' => __('TAXII Servers'), - 'url' => $baseurl . '/TaxiiServers/index', - 'requirement' => $this->Acl->canAccess('taxiiServers', 'index'), - ), - array( - 'text' => __('Event ID translator'), - 'url' => '/servers/idTranslator', - 'requirement' => $this->Acl->canAccess('servers', 'idTranslator') - ) - ) - ), - array( - 'type' => 'root', - 'text' => __('Administration'), - 'url' => $baseurl . '/servers/serverSettings', - 'requirement' => $isAdmin, - 'children' => array( - array( - 'text' => __('List Users'), - 'url' => $baseurl . '/admin/users/index' - ), - array( - 'text' => __('List Auth Keys'), - 'url' => $baseurl . '/auth_keys/index' - ), - array( - 'text' => __('List User Settings'), - 'url' => $baseurl . '/user_settings/index/user_id:all' - ), - array( - 'text' => __('Set User Setting'), - 'url' => $baseurl . '/user_settings/setSetting' - ), - array( - 'text' => __('Add User'), - 'url' => $baseurl . '/admin/users/add', - 'requirement' => $this->Acl->canAccess('users', 'admin_add'), - ), - array( - 'text' => __('Contact Users'), - 'url' => $baseurl . '/admin/users/email' - ), - array( - 'text' => __('User Registrations'), - 'url' => $baseurl . '/users/registrations', - 'requirement' => $this->Acl->canAccess('users', 'registrations'), - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('List Organisations'), - 'url' => $baseurl . '/organisations/index' - ), - array( - 'text' => __('Add Organisations'), - 'url' => $baseurl . '/admin/organisations/add', - 'requirement' => $this->Acl->canAccess('organisations', 'admin_add'), - ), - array( - 'type' => 'separator' - ), - array( - 'text' => __('List Roles'), - 'url' => $baseurl . '/roles/index' - ), - array( - 'text' => __('Add Roles'), - 'url' => $baseurl . '/admin/roles/add', - 'requirement' => $isSiteAdmin - ), - array( - 'type' => 'separator', - 'requirement' => $isSiteAdmin, - ), - array( - 'text' => __('Server Settings & Maintenance'), - 'url' => $baseurl . '/servers/serverSettings', - 'requirement' => $isSiteAdmin - ), - array( - 'type' => 'separator', - 'requirement' => $isSiteAdmin - ), - array( - 'text' => __('Jobs'), - 'url' => $baseurl . '/jobs/index', - 'requirement' => Configure::read('MISP.background_jobs') && $isSiteAdmin - ), - array( - 'text' => __('Scheduled Tasks'), - 'url' => $baseurl . '/tasks', - 'requirement' => Configure::read('MISP.background_jobs') && $isSiteAdmin - ), - array( - 'html' => sprintf( - '%s', - __('Workflows') - ), - 'url' => $baseurl . '/workflows/triggers', - 'requirement' => $isSiteAdmin - ), - array( - 'type' => 'separator', - 'requirement' => $isSiteAdmin - ), - array( - 'text' => __('Event Block Rules'), - 'url' => $baseurl . '/servers/eventBlockRule', - 'requirement' => $isSiteAdmin - ), - array( - 'text' => __('Event Blocklists'), - 'url' => $baseurl . '/eventBlocklists', - 'requirement' => Configure::read('MISP.enableEventBlocklisting') !== false && $isSiteAdmin - ), - array( - 'text' => __('Org Blocklists'), - 'url' => $baseurl . '/orgBlocklists', - 'requirement' => Configure::read('MISP.enableOrgBlocklisting') !== false && $isSiteAdmin - ), - [ - 'type' => 'separator', - 'requirement' => $isSiteAdmin - ], - [ - 'text' => __('Top Correlations'), - 'url' => $baseurl . '/correlations/top', - 'requirement' => $isSiteAdmin - ], - [ - 'html' => sprintf( - '%s', - __('Over-correlating values') - ), - 'url' => $baseurl . '/correlations/overCorrelations', - 'requirement' => $isSiteAdmin - ] - ) - ), - array( - 'type' => 'root', - 'text' => __('Logs'), - 'requirement' => $isAclAudit, - 'children' => array( - array( - 'text' => __('Application Logs'), - 'url' => $baseurl . '/logs/index' - ), - array( - 'text' => __('Audit Logs'), - 'url' => $baseurl . '/admin/audit_logs/index', - 'requirement' => Configure::read('MISP.log_new_audit') && $this->Acl->canAccess('auditLogs', 'admin_index'), - ), - array( - 'text' => __('Access Logs'), - 'url' => $baseurl . '/admin/access_logs/index', - 'requirement' => $isSiteAdmin - ), - array( - 'text' => __('Search Logs'), - 'url' => $baseurl . '/admin/logs/search', - 'requirement' => $this->Acl->canAccess('logs', 'admin_search') - ) - ) - ), - array( - 'type' => 'root', - 'text' => __('API'), - 'children' => array( - array( - 'text' => __('OpenAPI'), - 'url' => $baseurl . '/api/openapi' - ), - array( - 'text' => __('REST client'), - 'url' => $baseurl . '/api/rest', - 'requirement' => $this->Acl->canAccess('api', 'rest') - ) - ) - ) - ); - $menu_right = array( - array( - 'type' => 'root', - 'url' => '#', - 'html' => sprintf( - '', - (!empty($homepage['path']) && $homepage['path'] === $this->here) ? 'orange' : '', - __('Set the current page as your home page in MISP'), - __('Set the current page as your home page in MISP'), - h($this->here) - ) - ), - array( - 'type' => 'root', - 'url' => empty($homepage['path']) ? $baseurl : $baseurl . h($homepage['path']), - 'html' => '' - ), - [ - 'type' => 'root', - 'url' => Configure::read('MISP.menu_custom_right_link'), - 'html' => Configure::read('MISP.menu_custom_right_link_html'), - 'requirement' => !empty(Configure::read('MISP.menu_custom_right_link')), - ], - array( - 'type' => 'root', - 'url' => $baseurl . '/dashboards', - 'html' => sprintf( - '%s%s   %s', - h($me['email']), - $this->UserName->prepend($me['email']), - h($this->UserName->convertEmailToName($me['email'])), - isset($hasNotifications) ? sprintf( - '', - $hasNotifications ? 'red' : 'white', - __('Notifications') - ) : '' - ) - ), - array( - 'url' => $baseurl . '/users/logout', - 'text' => __('Log out'), - 'requirement' => empty(Configure::read('Plugin.CustomAuth_disable_logout')) - ) - ); - } - $isHal = date('Y-10-31') == date('Y-m-d'); - if ($isHal) { - $tmp = [ - 'type' => 'root', - 'url'=> '#', - 'html' => ' - - - ' - ]; - if (isset($menu_right)) { - $menu_right = array_merge([$tmp], $menu_right); +if (!empty($me)) { + if (Configure::read('MISP.home_logo')) { + $logoPath = APP . 'files/img/custom/' . Configure::read('MISP.home_logo'); + if (file_exists($logoPath)) { + $logoHtml = '' . __('Home') . ''; + } else { + $logoHtml = __('Home'); } + } else { + $logoHtml = __('Home'); } + + // New approach how to define menu requirements. It takes ACLs from ACLComponent. + $menu = array( + array( + 'type' => 'root', + 'url' => empty($homepage['path']) ? $baseurl .'/' : $baseurl . h($homepage['path']), + 'html' => $logoHtml + ), + array( + 'type' => 'root', + 'text' => __('Event Actions'), + 'children' => array( + array( + 'text' => __('List Events'), + 'url' => $baseurl . '/events/index' + ), + array( + 'text' => __('Add Event'), + 'url' => $baseurl . '/events/add', + 'requirement' => $this->Acl->canAccess('events', 'add'), + ), + array( + 'text' => __('List Attributes'), + 'url' => $baseurl . '/attributes/index' + ), + array( + 'text' => __('Search Attributes'), + 'url' => $baseurl . '/attributes/search' + ), + [ + 'type' => 'separator' + ], + array( + 'text' => __('View Proposals'), + 'url' => $baseurl . '/shadow_attributes/index/all:0' + ), + array( + 'text' => __('Events with proposals'), + 'url' => $baseurl . '/events/proposalEventIndex' + ), + array( + 'url' => $baseurl . '/event_delegations/index/context:pending', + 'text' => __('View delegation requests'), + 'requirement' => $this->Acl->canAccess('event_delegations', 'index'), + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('List Tags'), + 'url' => $baseurl . '/tags/index' + ), + array( + 'text' => __('Add Tag'), + 'url' => $baseurl . '/tags/add', + 'requirement' => $this->Acl->canAccess('tags', 'add'), + ), + array( + 'text' => __('List Tag Collections'), + 'url' => $baseurl . '/tag_collections/index' + ), + array( + 'text' => __('List Taxonomies'), + 'url' => $baseurl . '/taxonomies/index' + ), + array( + 'text' => __('List Templates'), + 'url' => $baseurl . '/templates/index' + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('Export'), + 'url' => $baseurl . '/events/export' + ), + array( + 'text' => __('Automation'), + 'url' => $baseurl . '/events/automation', + 'requirement' => $this->Acl->canAccess('events', 'automation'), + ), + array( + 'type' => 'separator', + 'requirement' => + Configure::read('MISP.enableEventBlocklisting') !== false && + !$isSiteAdmin && + $hostOrgUser + ), + array( + 'text' => __('Blocklist Event'), + 'url' => $baseurl . '/eventBlocklists/add', + 'requirement' => + Configure::read('MISP.enableEventBlocklisting') !== false && + !$isSiteAdmin && $hostOrgUser + ), + array( + 'text' => __('Manage Event Blocklists'), + 'url' => $baseurl . '/eventBlocklists', + 'requirement' => + Configure::read('MISP.enableEventBlocklisting') !== false && + !$isSiteAdmin && $hostOrgUser + ) + ) + ), + array( + 'type' => 'root', + 'text' => __('Dashboard'), + 'url' => $baseurl . '/dashboards' + ), + array( + 'type' => 'root', + 'text' => __('Galaxies'), + 'url' => $baseurl . '/galaxies/index', + 'children' => array( + array( + 'text' => __('List Galaxies'), + 'url' => $baseurl . '/galaxies/index' + ), + array( + 'text' => __('List Relationships'), + 'url' => $baseurl . '/galaxy_cluster_relations/index' + ), + ) + ), + array( + 'type' => 'root', + 'text' => __('Input Filters'), + 'children' => array( + array( + 'text' => __('Import Regexp'), + 'url' => $baseurl . '/admin/regexp/index', + 'requirement' => $isAclRegexp + ), + array( + 'text' => __('Import Regexp'), + 'url' => $baseurl . '/regexp/index', + 'requirement' => !$isAclRegexp + ), + array( + 'text' => __('Signature Allowedlist'), + 'url' => $baseurl . '/admin/allowedlists/index', + 'requirement' => $isAclRegexp + ), + array( + 'text' => __('Signature Allowedlist'), + 'url' => $baseurl . '/allowedlists/index', + 'requirement' => !$isAclRegexp + ), + array( + 'text' => __('Warninglists'), + 'url' => $baseurl . '/warninglists/index' + ), + array( + 'text' => __('Noticelists'), + 'url' => $baseurl . '/noticelists/index' + ), + array( + 'text' => __('Correlation Exclusions'), + 'url' => $baseurl . '/correlation_exclusions/index', + 'requirement' => $this->Acl->canAccess('correlation_exclusions', 'index'), + ) + ) + ), + array( + 'type' => 'root', + 'text' => __('Global Actions'), + 'children' => array( + array( + 'text' => __('News'), + 'url' => $baseurl . '/news' + ), + array( + 'text' => __('My Profile'), + 'url' => $baseurl . '/users/view/me' + ), + array( + 'text' => __('My Settings'), + 'url' => $baseurl . '/user_settings/index/user_id:me' + ), + array( + 'text' => __('Set Setting'), + 'url' => $baseurl . '/user_settings/setSetting' + ), + array( + 'text' => __('Organisations'), + 'url' => $baseurl . '/organisations/index', + 'requirement' => $this->Acl->canAccess('organisations', 'index'), + ), + array( + 'text' => __('Role Permissions'), + 'url' => $baseurl . '/roles/index' + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('List Object Templates'), + 'url' => $baseurl . '/objectTemplates/index' + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('List Sharing Groups'), + 'url' => $baseurl . '/sharing_groups/index' + ), + array( + 'text' => __('Add Sharing Group'), + 'url' => $baseurl . '/sharing_groups/add', + 'requirement' => $this->Acl->canAccess('sharing_groups', 'add'), + ), + array( + 'text' => __('List Sharing Groups Blueprints'), + 'url' => $baseurl . '/sharing_group_blueprints/index', + 'requirement' => $this->Acl->canAccess('sharing_group_blueprints', 'index'), + ), + array( + 'text' => __('Add Sharing Group Blueprint'), + 'url' => $baseurl . '/sharing_group_blueprints/add', + 'requirement' => $this->Acl->canAccess('sharing_group_blueprints', 'add'), + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('Decaying Models Tool'), + 'url' => $baseurl . '/decayingModel/decayingTool', + 'requirement' => $isAdmin + ), + array( + 'text' => __('List Decaying Models'), + 'url' => $baseurl . '/decayingModel/index', + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('User Guide'), + 'url' => 'https://www.circl.lu/doc/misp/' + ), + array( + 'text' => __('Categories & Types'), + 'url' => $baseurl . '/pages/display/doc/categories_and_types' + ), + array( + 'text' => __('Terms & Conditions'), + 'url' => $baseurl . '/users/terms' + ), + array( + 'text' => __('Statistics'), + 'url' => $baseurl . '/users/statistics' + ), + array( + 'type' => 'separator', + 'requirement' => $this->Acl->canAccess('threads', 'index'), + ), + array( + 'text' => __('List Discussions'), + 'url' => $baseurl . '/threads/index', + 'requirement' => $this->Acl->canAccess('threads', 'index'), + ), + array( + 'text' => __('Start Discussion'), + 'url' => $baseurl . '/posts/add', + 'requirement' => $this->Acl->canAccess('posts', 'add'), + ) + ) + ), + array( + 'type' => 'root', + 'text' => __('Sync Actions'), + 'requirement' => $isAclSync || $isAdmin || $hostOrgUser, + 'children' => array( + array( + 'text' => __('Create Sync Config'), + 'url' => $baseurl . '/servers/createSync', + 'requirement' => $isAclSync && !$isSiteAdmin + ), + array( + 'text' => __('Remote Servers'), + 'url' => $baseurl . '/servers/index', + 'requirement' => $this->Acl->canAccess('servers', 'index'), + ), + array( + 'text' => __('Feeds'), + 'url' => $baseurl . '/feeds/index', + 'requirement' => $this->Acl->canAccess('feeds', 'index'), + ), + array( + 'text' => __('SightingDB'), + 'url' => $baseurl . '/sightingdb/index', + 'requirement' => $this->Acl->canAccess('sightingdb', 'index'), + ), + array( + 'text' => __('Communities'), + 'url' => $baseurl . '/communities/index', + 'requirement' => $this->Acl->canAccess('communities', 'index'), + ), + array( + 'text' => __('Cerebrates'), + 'url' => $baseurl . '/cerebrates/index', + 'requirement' => $this->Acl->canAccess('cerebrates', 'index'), + ), + array( + 'text' => __('TAXII Servers'), + 'url' => $baseurl . '/TaxiiServers/index', + 'requirement' => $this->Acl->canAccess('taxiiServers', 'index'), + ), + array( + 'text' => __('Event ID translator'), + 'url' => '/servers/idTranslator', + 'requirement' => $this->Acl->canAccess('servers', 'idTranslator') + ) + ) + ), + array( + 'type' => 'root', + 'text' => __('Administration'), + 'url' => $baseurl . '/servers/serverSettings', + 'requirement' => $isAdmin, + 'children' => array( + array( + 'text' => __('List Users'), + 'url' => $baseurl . '/admin/users/index' + ), + array( + 'text' => __('List Auth Keys'), + 'url' => $baseurl . '/auth_keys/index' + ), + array( + 'text' => __('List User Settings'), + 'url' => $baseurl . '/user_settings/index/user_id:all' + ), + array( + 'text' => __('Set User Setting'), + 'url' => $baseurl . '/user_settings/setSetting' + ), + array( + 'text' => __('Add User'), + 'url' => $baseurl . '/admin/users/add', + 'requirement' => $this->Acl->canAccess('users', 'admin_add'), + ), + array( + 'text' => __('Contact Users'), + 'url' => $baseurl . '/admin/users/email' + ), + array( + 'text' => __('User Registrations'), + 'url' => $baseurl . '/users/registrations', + 'requirement' => $this->Acl->canAccess('users', 'registrations'), + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('List Organisations'), + 'url' => $baseurl . '/organisations/index' + ), + array( + 'text' => __('Add Organisations'), + 'url' => $baseurl . '/admin/organisations/add', + 'requirement' => $this->Acl->canAccess('organisations', 'admin_add'), + ), + array( + 'type' => 'separator' + ), + array( + 'text' => __('List Roles'), + 'url' => $baseurl . '/roles/index' + ), + array( + 'text' => __('Add Roles'), + 'url' => $baseurl . '/admin/roles/add', + 'requirement' => $isSiteAdmin + ), + array( + 'type' => 'separator', + 'requirement' => $isSiteAdmin, + ), + array( + 'text' => __('Server Settings & Maintenance'), + 'url' => $baseurl . '/servers/serverSettings', + 'requirement' => $isSiteAdmin + ), + array( + 'type' => 'separator', + 'requirement' => $isSiteAdmin + ), + array( + 'text' => __('Jobs'), + 'url' => $baseurl . '/jobs/index', + 'requirement' => Configure::read('MISP.background_jobs') && $isSiteAdmin + ), + array( + 'text' => __('Scheduled Tasks'), + 'url' => $baseurl . '/tasks', + 'requirement' => Configure::read('MISP.background_jobs') && $isSiteAdmin + ), + array( + 'html' => sprintf( + '%s', + __('Workflows') + ), + 'url' => $baseurl . '/workflows/triggers', + 'requirement' => $isSiteAdmin + ), + array( + 'type' => 'separator', + 'requirement' => $isSiteAdmin + ), + array( + 'text' => __('Event Block Rules'), + 'url' => $baseurl . '/servers/eventBlockRule', + 'requirement' => $isSiteAdmin + ), + array( + 'text' => __('Event Blocklists'), + 'url' => $baseurl . '/eventBlocklists', + 'requirement' => Configure::read('MISP.enableEventBlocklisting') !== false && $isSiteAdmin + ), + array( + 'text' => __('Org Blocklists'), + 'url' => $baseurl . '/orgBlocklists', + 'requirement' => Configure::read('MISP.enableOrgBlocklisting') !== false && $isSiteAdmin + ), + [ + 'type' => 'separator', + 'requirement' => $isSiteAdmin + ], + [ + 'text' => __('Top Correlations'), + 'url' => $baseurl . '/correlations/top', + 'requirement' => $isSiteAdmin + ], + [ + 'html' => sprintf( + '%s', + __('Over-correlating values') + ), + 'url' => $baseurl . '/correlations/overCorrelations', + 'requirement' => $isSiteAdmin + ] + ) + ), + array( + 'type' => 'root', + 'text' => __('Logs'), + 'requirement' => $isAclAudit, + 'children' => array( + array( + 'text' => __('Application Logs'), + 'url' => $baseurl . '/logs/index' + ), + array( + 'text' => __('Audit Logs'), + 'url' => $baseurl . '/admin/audit_logs/index', + 'requirement' => Configure::read('MISP.log_new_audit') && $this->Acl->canAccess('auditLogs', 'admin_index'), + ), + array( + 'text' => __('Access Logs'), + 'url' => $baseurl . '/admin/access_logs/index', + 'requirement' => $isSiteAdmin + ), + array( + 'text' => __('Search Logs'), + 'url' => $baseurl . '/admin/logs/search', + 'requirement' => $this->Acl->canAccess('logs', 'admin_search') + ) + ) + ), + array( + 'type' => 'root', + 'text' => __('API'), + 'children' => array( + array( + 'text' => __('OpenAPI'), + 'url' => $baseurl . '/api/openapi' + ), + array( + 'text' => __('REST client'), + 'url' => $baseurl . '/api/rest', + 'requirement' => $this->Acl->canAccess('api', 'rest') + ) + ) + ) + ); + $menu_right = array( + array( + 'type' => 'root', + 'url' => '#', + 'html' => sprintf( + '', + (!empty($homepage['path']) && $homepage['path'] === $this->here) ? 'orange' : '', + __('Set the current page as your home page in MISP'), + __('Set the current page as your home page in MISP'), + h($this->here) + ) + ), + array( + 'type' => 'root', + 'url' => empty($homepage['path']) ? $baseurl : $baseurl . h($homepage['path']), + 'html' => '' + ), + [ + 'type' => 'root', + 'url' => Configure::read('MISP.menu_custom_right_link'), + 'html' => Configure::read('MISP.menu_custom_right_link_html'), + 'requirement' => !empty(Configure::read('MISP.menu_custom_right_link')), + ], + array( + 'type' => 'root', + 'url' => $baseurl . '/dashboards', + 'html' => sprintf( + '%s%s   %s', + h($me['email']), + $this->UserName->prepend($me['email']), + h($this->UserName->convertEmailToName($me['email'])), + isset($hasNotifications) ? sprintf( + '', + $hasNotifications ? 'red' : 'white', + __('Notifications') + ) : '' + ) + ), + array( + 'url' => $baseurl . '/users/logout', + 'text' => __('Log out'), + 'requirement' => empty(Configure::read('Plugin.CustomAuth_disable_logout')) + ) + ); +} +$isHal = date('Y-10-31') == date('Y-m-d'); +if ($isHal) { + $tmp = [ + 'type' => 'root', + 'url'=> '#', + 'html' => ' + + + ' + ]; + if (isset($menu_right)) { + $menu_right = array_merge([$tmp], $menu_right); + } +} ?>