From cb82ef2755bef2626744ac471513c1d1da51b70b Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 27 Nov 2020 16:38:32 +0100 Subject: [PATCH 001/385] chg: [composer] Raise minimal PHP version to 7.2 and disable support for 8.0 --- app/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/composer.json b/app/composer.json index fcfb31528..c5d19b01a 100644 --- a/app/composer.json +++ b/app/composer.json @@ -2,10 +2,12 @@ "prefer-stable": true, "minimum-stability": "dev", "require": { - "php": ">=7.0.0", + "php": ">=7.2.0,<8.0.0", "ext-json": "*", "ext-mbstring": "*", "ext-xml": "*", + "ext-dom": "*", + "ext-simplexml": "*", "ext-pcre": "*", "kamisama/cake-resque": "4.1.2", "pear/crypt_gpg": "1.6.3", From 0a19148fbc33a96c7d34d12e9dfeb219ba0adad9 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 30 Nov 2020 16:29:31 +0100 Subject: [PATCH 002/385] new: [objectTemplate] Allow fetching the raw template stored on disk by UUID or name --- app/Controller/Component/ACLComponent.php | 1 + app/Controller/ObjectTemplatesController.php | 9 +++++ app/Model/ObjectTemplate.php | 37 ++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index 408742702..380ad9ed8 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -397,6 +397,7 @@ class ACLComponent extends Component 'edit' => array('perm_object_template'), 'delete' => array('perm_object_template'), 'getToggleField' => array(), + 'getRaw' => array('perm_object_template'), 'objectChoice' => array('*'), 'objectMetaChoice' => array('perm_add'), 'view' => array('*'), diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index 11046e82a..1b1686ae4 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -295,4 +295,13 @@ class ObjectTemplatesController extends AppController $this->layout = 'ajax'; $this->render('ajax/getToggleField'); } + + public function getRaw($uuidOrName) + { + $template = $this->ObjectTemplate->getRawFromDisk($uuidOrName); + if (empty($template)) { + throw new NotFoundException(__('Template not found')); + } + return $this->RestResponse->viewData($template, $this->response->type()); + } } diff --git a/app/Model/ObjectTemplate.php b/app/Model/ObjectTemplate.php index 2608c01b1..9c8e59a51 100644 --- a/app/Model/ObjectTemplate.php +++ b/app/Model/ObjectTemplate.php @@ -316,4 +316,41 @@ class ObjectTemplate extends AppModel } return 1; } + + public function getRawFromDisk($uuidOrName) + { + $templates = $this->readTemplatesFromDisk(); + if (Validation::uuid($uuidOrName)) { + return $templates[$uuidOrName]; + } else { + foreach ($templates as $uuid => $template) { + if ($template['name'] == $uuidOrName) { + return $template; + } + } + } + } + + private function readTemplatesFromDisk() + { + $objectsDir = APP . 'files/misp-objects/objects'; + $directories = glob($objectsDir . '/*', GLOB_ONLYDIR); + foreach ($directories as $k => $dir) { + $dir = str_replace($objectsDir, '', $dir); + $directories[$k] = $dir; + } + $templates = []; + foreach ($directories as $dir) { + if (!file_exists($objectsDir . DS . $dir . DS . 'definition.json')) { + continue; + } + $file = new File($objectsDir . DS . $dir . DS . 'definition.json'); + $template = json_decode($file->read(), true); + if (isset($template['uuid'])) { + $templates[$template['uuid']] = $template; + } + $file->close(); + } + return $templates; + } } From b9878b805d2245bf92a5570a79c2e98d083de5e9 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 4 Dec 2020 16:07:39 +0100 Subject: [PATCH 003/385] fix: [db_schema] added cerebrate --- db_schema.json | 147 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/db_schema.json b/db_schema.json index a7c7f8bf5..be08b5462 100644 --- a/db_schema.json +++ b/db_schema.json @@ -555,6 +555,151 @@ "extra": "" } ], + "cerebrates": [ + { + "column_name": "id", + "is_nullable": "NO", + "data_type": "int", + "character_maximum_length": null, + "numeric_precision": "10", + "collation_name": null, + "column_type": "int(11)", + "column_default": null, + "extra": "auto_increment" + }, + { + "column_name": "name", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "191", + "numeric_precision": null, + "collation_name": "utf8mb4_unicode_ci", + "column_type": "varchar(191)", + "column_default": null, + "extra": "" + }, + { + "column_name": "url", + "is_nullable": "NO", + "data_type": "varchar", + "character_maximum_length": "255", + "numeric_precision": null, + "collation_name": "utf8mb4_unicode_ci", + "column_type": "varchar(255)", + "column_default": null, + "extra": "" + }, + { + "column_name": "authkey", + "is_nullable": "YES", + "data_type": "varchar", + "character_maximum_length": "40", + "numeric_precision": null, + "collation_name": "ascii_general_ci", + "column_type": "varchar(40)", + "column_default": null, + "extra": "" + }, + { + "column_name": "open", + "is_nullable": "YES", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + }, + { + "column_name": "org_id", + "is_nullable": "NO", + "data_type": "int", + "character_maximum_length": null, + "numeric_precision": "10", + "collation_name": null, + "column_type": "int(11)", + "column_default": null, + "extra": "" + }, + { + "column_name": "pull_orgs", + "is_nullable": "YES", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + }, + { + "column_name": "pull_sharing_groups", + "is_nullable": "YES", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + }, + { + "column_name": "self_signed", + "is_nullable": "YES", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + }, + { + "column_name": "cert_file", + "is_nullable": "YES", + "data_type": "varchar", + "character_maximum_length": "255", + "numeric_precision": null, + "collation_name": "utf8mb4_unicode_ci", + "column_type": "varchar(255)", + "column_default": null, + "extra": "" + }, + { + "column_name": "client_cert_file", + "is_nullable": "YES", + "data_type": "varchar", + "character_maximum_length": "255", + "numeric_precision": null, + "collation_name": "utf8mb4_unicode_ci", + "column_type": "varchar(255)", + "column_default": null, + "extra": "" + }, + { + "column_name": "internal", + "is_nullable": "NO", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + }, + { + "column_name": "skip_proxy", + "is_nullable": "NO", + "data_type": "tinyint", + "character_maximum_length": null, + "numeric_precision": "3", + "collation_name": null, + "column_type": "tinyint(1)", + "column_default": "0", + "extra": "" + } + ], "correlations": [ { "column_name": "id", @@ -7774,5 +7919,5 @@ "id": true } }, - "db_version": "63" + "db_version": "64" } From 0bfc0bf38a7758b27c5c446fec5e3b905e5a54ab Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 16:20:02 +0100 Subject: [PATCH 004/385] fix: [security] XSS in authkey comment field --- .../genericElements/SingleViews/Fields/genericField.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/View/Elements/genericElements/SingleViews/Fields/genericField.ctp b/app/View/Elements/genericElements/SingleViews/Fields/genericField.ctp index 748d59c77..6f7a8cb12 100644 --- a/app/View/Elements/genericElements/SingleViews/Fields/genericField.ctp +++ b/app/View/Elements/genericElements/SingleViews/Fields/genericField.ctp @@ -3,7 +3,7 @@ if (!empty($field['raw'])) { $string = $field['raw']; } else { $value = Hash::extract($data, $field['path']); - $string = empty($value[0]) ? '' : $value[0]; + $string = empty($value[0]) ? '' : h($value[0]); } if (!empty($field['url'])) { if (!empty($field['url_vars'])) { From 1381e6c0d4c22411421f7909e26f8cdae236446d Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 20:40:26 +0100 Subject: [PATCH 005/385] chg: [shibb] Newly created org should be local --- app/Model/Organisation.php | 29 ++++++++++++------- .../Auth/ApacheShibbAuthenticate.php | 2 +- tests/testlive_security.py | 1 + 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/app/Model/Organisation.php b/app/Model/Organisation.php index c6929c344..e37e1ac5b 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -233,20 +233,27 @@ class Organisation extends AppModel return $existingOrg[$this->alias]['id']; } - public function createOrgFromName($name, $user_id, $local) + /** + * @param string $name Organisation name + * @param int $userId Organisation creator + * @param bool $local True if organisation should be marked as local + * @return int Existing or newly created organisation ID + * @throws Exception + */ + public function createOrgFromName($name, $userId, $local) { - $existingOrg = $this->find('first', array( - 'recursive' => -1, - 'conditions' => array('name' => $name) - )); + $existingOrg = $this->find('first', [ + 'recursive' => -1, + 'conditions' => ['name' => $name], + 'fields' => ['id'], + ]); if (empty($existingOrg)) { $this->create(); - $organisation = array( - 'uuid' =>CakeText::uuid(), - 'name' => $name, - 'local' => $local, - 'created_by' => $user_id - ); + $organisation = [ + 'name' => $name, + 'local' => $local, + 'created_by' => $userId, + ]; $this->save($organisation); return $this->id; } diff --git a/app/Plugin/ShibbAuth/Controller/Component/Auth/ApacheShibbAuthenticate.php b/app/Plugin/ShibbAuth/Controller/Component/Auth/ApacheShibbAuthenticate.php index 44e14c20e..17ad12c30 100644 --- a/app/Plugin/ShibbAuth/Controller/Component/Auth/ApacheShibbAuthenticate.php +++ b/app/Plugin/ShibbAuth/Controller/Component/Auth/ApacheShibbAuthenticate.php @@ -166,7 +166,7 @@ class ApacheShibbAuthenticate extends BaseAuthenticate if ($user) { $orgUserId = $user['id']; } - $orgId = $orgModel->createOrgFromName($org, $orgUserId, 0); // Created with local set to 0 by default + $orgId = $orgModel->createOrgFromName($org, $orgUserId, true); CakeLog::info("User organisation `$org` created with ID $orgId."); } else { $orgId = $orgAux['Organisation']['id']; diff --git a/tests/testlive_security.py b/tests/testlive_security.py index a7db50f60..93b0a798e 100644 --- a/tests/testlive_security.py +++ b/tests/testlive_security.py @@ -667,6 +667,7 @@ class TestSecurity(unittest.TestCase): self.assertEqual(session.headers["Email-Tag"], json_response["User"]["email"]) self.assertEqual(3, int(json_response["User"]["role_id"])) self.assertEqual(session.headers["Federation-Tag"], json_response["Organisation"]["name"]) + self.assertEqual(1, int(json_response["Organisation"]["local"]), "Newly created org should be local") self.admin_misp_connector.delete_user(json_response["User"]["id"]) self.admin_misp_connector.delete_organisation(json_response["User"]["org_id"]) From 82a1a38bb442c0ff8d9b1b58aef548fe15be48d8 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 21:40:27 +0100 Subject: [PATCH 006/385] fix: [UI] Sort countries by name --- app/Model/Organisation.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Model/Organisation.php b/app/Model/Organisation.php index c6929c344..98f538f5b 100644 --- a/app/Model/Organisation.php +++ b/app/Model/Organisation.php @@ -507,10 +507,9 @@ class Organisation extends AppModel */ public function getCountries() { - $countries = ['International']; - foreach ($this->getCountryGalaxyCluster() as $country) { - $countries[] = $country['description']; - } + $countries = array_column($this->getCountryGalaxyCluster(), 'description'); + sort($countries); + array_unshift($countries, 'Internation'); return $countries; } } From a49669b346a8b9a7bab8eb26801c8015e9094ea2 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 22:13:54 +0100 Subject: [PATCH 007/385] fix: [UI] Show `Add Cluster` in menu just when user has permission to add cluster --- .../genericElements/SideMenu/side_menu.ctp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index 40fe28d2a..168e30744 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -1254,7 +1254,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); 'text' => __('List Relationships') )); if ($isSiteAdmin) { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_post_link', array( 'element_id' => 'update', 'url' => $baseurl . '/galaxies/update', @@ -1269,7 +1269,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); )); } if ($isSiteAdmin || $me['Role']['perm_galaxy_editor']) { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'url' => $baseurl . '/galaxies/import', 'text' => __('Import Galaxy Clusters') @@ -1283,7 +1283,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); )); } if ($menuItem === 'viewGraph' || $menuItem === 'view_cluster' || $menuItem === 'update_cluster' || $menuItem === 'add_cluster' || $menuItem === 'edit_cluster') { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'view', 'url' => $baseurl . '/galaxies/view/' . h($galaxy_id), @@ -1303,7 +1303,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); 'text' => __('Edit Cluster') )); } - if ($isSiteAdmin || $me['Role']['perm_galaxy_editor']) { + if ($canAccess('galaxyClusters', 'add')) { echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'add_cluster', 'url' => $baseurl . '/galaxy_clusters/add/' . h($galaxy_id), @@ -1321,7 +1321,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); $isSiteAdmin || (isset($cluster['GalaxyCluster']['orgc_id']) && $cluster['GalaxyCluster']['orgc_id'] == $me['org_id']) ) ) { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'onClick' => array( 'function' => 'publishPopup', @@ -1333,7 +1333,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); } } if ($menuItem !== 'add_cluster') { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'viewGraph', 'url' => $baseurl . '/galaxies/viewGraph/' . h($id), @@ -1342,17 +1342,19 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); } } if ($menuItem === 'view' || $menuItem === 'export') { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'view', 'url' => $baseurl . '/galaxies/view/' . h($galaxy['Galaxy']['id']), 'text' => __('View Galaxy') )); - echo $this->element('/genericElements/SideMenu/side_menu_link', array( - 'element_id' => 'add_cluster', - 'url' => $baseurl . '/galaxy_clusters/add/' . h($galaxy['Galaxy']['id']), - 'text' => __('Add Cluster') - )); + if ($canAccess('galaxyClusters', 'add')) { + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'element_id' => 'add_cluster', + 'url' => $baseurl . '/galaxy_clusters/add/' . h($galaxy['Galaxy']['id']), + 'text' => __('Add Cluster') + )); + } } break; @@ -1361,7 +1363,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); 'url' => $baseurl . '/galaxies/index', 'text' => __('List Galaxies') )); - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'view', 'url' => $baseurl . '/galaxies/view/' . h($galaxy_id), @@ -1380,7 +1382,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); )); } if ($menuItem === 'view') { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + echo $divider; if ( isset($cluster['GalaxyCluster']['published']) && !$cluster['GalaxyCluster']['published'] && isset($cluster['GalaxyCluster']['orgc_id']) && $cluster['GalaxyCluster']['orgc_id'] == $me['org_id'] && @@ -1417,8 +1419,8 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); 'url' => $baseurl . '/galaxy_cluster_relations/index', 'text' => __('List Relationships') )); - if ($isSiteAdmin || $me['Role']['perm_galaxy_editor']) { - echo $this->element('/genericElements/SideMenu/side_menu_divider'); + if ($isSiteAdmin || $me['Role']['perm_galaxy_editor']) { + echo $divider; echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'add', 'url' => $baseurl . '/galaxy_cluster_relations/add/', From 44caab8f48318df56726c24bceb5c340e8735568 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 01:07:18 +0100 Subject: [PATCH 008/385] fix: [pgp] Key info for older GPG versions --- app/Lib/Tools/CryptGpgExtended.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/Lib/Tools/CryptGpgExtended.php b/app/Lib/Tools/CryptGpgExtended.php index 15ba96c9c..3ea777b43 100644 --- a/app/Lib/Tools/CryptGpgExtended.php +++ b/app/Lib/Tools/CryptGpgExtended.php @@ -73,7 +73,8 @@ class CryptGpgExtended extends Crypt_GPG } /** - * Return key info without importing it. + * Return key info without importing it when GPG supports --import-options show-only, otherwise just import and + * then return details. * * @param string $key * @return Crypt_GPG_Key[] @@ -82,6 +83,18 @@ class CryptGpgExtended extends Crypt_GPG */ public function keyInfo($key) { + $version = $this->engine->getVersion(); + if (version_compare($version, '2.1.23', 'le')) { + $importResult = $this->importKey($key); + $keys = []; + foreach ($importResult['fingerprints'] as $fingerprint) { + foreach ($this->getKeys($fingerprint) as $key) { + $keys[] = $key; + } + } + return $keys; + } + $input = $this->_prepareInput($key, false, false); $output = ''; From c3cc091954ca5a33d53a14bc95d0436f0f1f65c3 Mon Sep 17 00:00:00 2001 From: Frank Olbricht Date: Sat, 5 Dec 2020 10:23:37 -0700 Subject: [PATCH 009/385] Don't fail writable attachment dir test for S3 --- app/Model/Server.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Model/Server.php b/app/Model/Server.php index 8e2313fd4..0395c6473 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -3834,6 +3834,9 @@ class Server extends AppModel if (substr($value, 0, 7) === "phar://") { return 'Phar protocol not allowed.'; } + if (substr($value, 0, 5) === "s3://") { + return true; + } if (!is_dir($value)) { return 'Not a valid directory.'; } From cb064dd9ce040447e75213825295130393c8b0cb Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 22:58:30 +0100 Subject: [PATCH 010/385] new: [security] Test if user can see sharing groups --- .github/workflows/main.yml | 4 +- app/Controller/SharingGroupsController.php | 80 +++--- app/Model/SharingGroup.php | 12 +- tests/modify_config.php | 10 +- tests/testlive_security.py | 289 ++++++++++++++++----- 5 files changed, 273 insertions(+), 122 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7e5fb90a9..a6fdced62 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,7 +66,7 @@ jobs: run: | git submodule update --init --recursive sudo apt-get -y update - sudo apt-get -y install python3 python3-venv virtualenv python3-pip python3-nose python3-redis python3-lxml apache2 curl libapache2-mod-php libfuzzy-dev libemail-address-perl libemail-outlook-message-perl + sudo apt-get -y install curl python3 python3-venv virtualenv python3-pip python3-nose python3-redis python3-lxml apache2 libapache2-mod-php$php_version php$php_version-xml php$php_version-mbstring php$php_version-mysql libfuzzy-dev libemail-address-perl libemail-outlook-message-perl sudo pip3 install --upgrade pip setuptools requests pyzmq poetry sudo pip3 install --upgrade -r requirements.txt sudo chown $USER:www-data $HOME/.composer @@ -220,7 +220,7 @@ jobs: sudo chown -R $USER:www-data `pwd`/app/Config sudo chmod -R 777 `pwd`/app/Config - python3 tests/testlive_security.py + python3 tests/testlive_security.py -v pushd tests ./curl_tests_GH.sh $AUTH $HOST diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index 409782735..43aa8c478 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -1,6 +1,9 @@ SharingGroup->id = $id; - if (!$this->SharingGroup->exists()) { - throw new NotFoundException('Invalid sharing group.'); - } - if (!$this->_isSiteAdmin() && !$this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $id)) { - throw new MethodNotAllowedException('Action not allowed.'); - } // check if the user is eligible to edit the SG (original creator or extend) $sharingGroup = $this->SharingGroup->find('first', array( - 'conditions' => array('SharingGroup.id' => $id), + 'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id], 'recursive' => -1, 'contain' => array( - 'SharingGroupOrg' => array( - 'Organisation' => array('name', 'local', 'id') - ), - 'SharingGroupServer' => array( - 'Server' => array( - 'fields' => array('name', 'url', 'id') - ) - ), - 'Organisation' => array( - 'fields' => array('name', 'local', 'id') - ), + 'SharingGroupOrg' => array( + 'Organisation' => array('name', 'local', 'id') + ), + 'SharingGroupServer' => array( + 'Server' => array( + 'fields' => array('name', 'url', 'id') + ) + ), + 'Organisation' => array( + 'fields' => array('name', 'local', 'id') + ), ), )); + if (!$this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sharingGroup['SharingGroup']['id'])) { + throw new MethodNotAllowedException('Action not allowed.'); + } if ($this->request->is('post')) { if ($this->_isRest()) { if (isset($this->request->data['SharingGroup'])) { @@ -179,24 +177,24 @@ class SharingGroupsController extends AppController $id = $this->SharingGroup->captureSG($this->request->data, $this->Auth->user()); if ($id) { $sg = $this->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'simplified', false, $id); - return $this->RestResponse->viewData($sg, $this->response->type()); + return $this->RestResponse->viewData($sg[0], $this->response->type()); } else { return $this->RestResponse->saveFailResponse('SharingGroup', 'add', false, 'Could not save sharing group.', $this->response->type()); } } else { $json = json_decode($this->request->data['SharingGroup']['json'], true); $sg = $json['sharingGroup']; - $sg['id'] = $id; + $sg['id'] = $sharingGroup['SharingGroup']['id']; $fields = array('name', 'releasability', 'description', 'active', 'roaming'); - $existingSG = $this->SharingGroup->find('first', array('recursive' => -1, 'conditions' => array('SharingGroup.id' => $id))); + $existingSG = $this->SharingGroup->find('first', array('recursive' => -1, 'conditions' => array('SharingGroup.id' => $sharingGroup['SharingGroup']['id']))); foreach ($fields as $field) { $existingSG['SharingGroup'][$field] = $sg[$field]; } unset($existingSG['SharingGroup']['modified']); if ($this->SharingGroup->save($existingSG)) { - $this->SharingGroup->SharingGroupOrg->updateOrgsForSG($id, $json['organisations'], $sharingGroup['SharingGroupOrg'], $this->Auth->user()); - $this->SharingGroup->SharingGroupServer->updateServersForSG($id, $json['servers'], $sharingGroup['SharingGroupServer'], $json['sharingGroup']['roaming'], $this->Auth->user()); - $this->redirect('/SharingGroups/view/' . $id); + $this->SharingGroup->SharingGroupOrg->updateOrgsForSG($sharingGroup['SharingGroup']['id'], $json['organisations'], $sharingGroup['SharingGroupOrg'], $this->Auth->user()); + $this->SharingGroup->SharingGroupServer->updateServersForSG($sharingGroup['SharingGroup']['id'], $json['servers'], $sharingGroup['SharingGroupServer'], $json['sharingGroup']['roaming'], $this->Auth->user()); + $this->redirect('/SharingGroups/view/' . $sharingGroup['SharingGroup']['id']); } else { $validationReplacements = array( 'notempty' => 'This field cannot be left empty.', @@ -221,7 +219,7 @@ class SharingGroupsController extends AppController 'fields' => array('id', 'name') )); $this->set('sharingGroup', $sharingGroup); - $this->set('id', $id); + $this->set('id', $sharingGroup['SharingGroup']['id']); $this->set('orgs', $orgs); $this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl')); // We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit @@ -236,15 +234,15 @@ class SharingGroupsController extends AppController if (!$this->request->is('post') && !$this->request->is('delete')) { throw new MethodNotAllowedException(__('Action not allowed, post or delete request expected.')); } - if (!$this->SharingGroup->checkIfOwner($this->Auth->user(), $id)) { + $deletedSg = $this->SharingGroup->find('first', array( + 'conditions' => Validation::uuid($id) ? ['uuid' => $id] : ['id' => $id], + 'recursive' => -1, + 'fields' => ['id', 'active'], + )); + if (empty($deletedSg) || !$this->SharingGroup->checkIfOwner($this->Auth->user(), $deletedSg['SharingGroup']['id'])) { throw new MethodNotAllowedException('Action not allowed.'); } - $deletedSg = $this->SharingGroup->find('first', array( - 'conditions' => array('id' => $id), - 'recursive' => -1, - 'fields' => array('active') - )); - if ($this->SharingGroup->delete($id)) { + if ($this->SharingGroup->delete($deletedSg['SharingGroup']['id'])) { if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('SharingGroups', 'delete', $id, $this->response->type()); } @@ -303,9 +301,9 @@ class SharingGroupsController extends AppController if (!$this->SharingGroup->checkIfAuthorised($this->Auth->user(), $id)) { throw new MethodNotAllowedException('Sharing group doesn\'t exist or you do not have permission to access it.'); } - $this->SharingGroup->id = $id; - $this->SharingGroup->contain( - array( + $sg = $this->SharingGroup->find('first', [ + 'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id], + 'contain' => array( 'SharingGroupOrg' => array( 'Organisation' => array( 'fields' => array('id', 'name', 'uuid', 'local') @@ -318,9 +316,7 @@ class SharingGroupsController extends AppController ) ) ) - ); - $this->SharingGroup->read(); - $sg = $this->SharingGroup->data; + ]); if (isset($sg['SharingGroupServer'])) { foreach ($sg['SharingGroupServer'] as $key => $sgs) { if ($sgs['server_id'] == 0) { @@ -350,8 +346,8 @@ class SharingGroupsController extends AppController if ($this->_isRest()) { return $this->RestResponse->viewData($sg, $this->response->type()); } - $this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $id)); - $this->set('id', $id); + $this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sg['SharingGroup']['id'])); + $this->set('id', $sg['SharingGroup']['id']); $this->set('sg', $sg); } diff --git a/app/Model/SharingGroup.php b/app/Model/SharingGroup.php index e3e60ee58..f984fb6eb 100644 --- a/app/Model/SharingGroup.php +++ b/app/Model/SharingGroup.php @@ -426,23 +426,23 @@ class SharingGroup extends AppModel return $this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id]; } if (Validation::uuid($id)) { - $sgid = $this->SharingGroup->find('first', array( + $sgid = $this->find('first', array( 'conditions' => array('SharingGroup.uuid' => $id), 'recursive' => -1, 'fields' => array('SharingGroup.id') )); if (empty($sgid)) { - throw new MethodNotAllowedException('Invalid sharing group.'); + return false; } $id = $sgid['SharingGroup']['id']; + } else { + if (!$this->exists($id)) { + return false; + } } if (!isset($user['id'])) { throw new MethodNotAllowedException('Invalid user.'); } - $this->id = $id; - if (!$this->exists()) { - return false; - } if (($adminCheck && $user['Role']['perm_site_admin']) || $this->SharingGroupServer->checkIfAuthorised($id) || $this->SharingGroupOrg->checkIfAuthorised($id, $user['org_id'])) { $this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id] = true; return true; diff --git a/tests/modify_config.php b/tests/modify_config.php index 16c4ef83a..c84f0dccc 100644 --- a/tests/modify_config.php +++ b/tests/modify_config.php @@ -29,16 +29,22 @@ if (!is_writable($configFile)) { $perms = substr(sprintf('%o', fileperms($configFile)), -4); fail(3, "File $configFile is not writeable (owner $owner, permissions $perms)."); } +if (function_exists('opcache_invalidate')) { + opcache_invalidate($configFile, true); +} require_once $configFile; if (!isset($config)) { fail(3, "Original config variable not found."); } if ($argv[1] === 'modify') { - $merged = array_merge_recursive($config, $newConfig); + $merged = array_replace_recursive($config, $newConfig); } else { $merged = $newConfig; } -file_put_contents($configFile, " bool: session = requests.Session() @@ -86,36 +81,19 @@ def login(url: str, email: str, password: str) -> bool: class MISPSetting: - def __init__(self, connection: PyMISP, setting: str, value): - self.__connection = connection - self.__setting = setting - self.__value = value - - def __enter__(self): - original = self.__connection.get_server_setting(self.__setting) - if "value" not in original: - raise Exception(original) - self.__original = original["value"] - - result = self.__connection.set_server_setting(self.__setting, self.__value, force=True) - if "saved" not in result or not result["saved"]: - raise Exception(result) - - def __exit__(self, exc_type, exc_val, exc_tb): - result = self.__connection.set_server_setting(self.__setting, self.__original, force=True) - if "saved" not in result or not result["saved"]: - raise Exception(result) - - -class MISPComplexSetting: - def __init__(self, new_setting: dict): + def __init__(self, admin_connector: PyMISP, new_setting: dict): + self.admin_connector = admin_connector self.new_setting = new_setting def __enter__(self): self.original = self.__run("modify", json.dumps(self.new_setting)) + # Try to reset config cache + self.admin_connector.get_server_setting("MISP.live") def __exit__(self, exc_type, exc_val, exc_tb): self.__run("replace", self.original) + # Try to reset config cache + self.admin_connector.get_server_setting("MISP.live") @staticmethod def __run(command: str, data: str) -> str: @@ -201,6 +179,8 @@ class TestSecurity(unittest.TestCase): def setUp(self): # Do not show warning about not closed resources, because that something we want warnings.simplefilter("ignore", ResourceWarning) + # TODO: Try to reload config cache + self.admin_misp_connector.get_server_setting("MISP.live") def test_not_logged_in(self): session = requests.Session() @@ -212,15 +192,15 @@ class TestSecurity(unittest.TestCase): self.assertEqual(url + "/users/login", r.headers['Location'], path) # Should be accessible without login - for path in ("/users/login", ): + for path in ("/users/login",): r = session.get(url + path, allow_redirects=False) self.assertEqual(200, r.status_code, path) - with MISPSetting(self.admin_misp_connector, "Security.allow_self_registration", True): + with self.__setting("Security.allow_self_registration", True): r = session.get(url + "/users/register", allow_redirects=False) self.assertEqual(200, r.status_code, path) - with MISPSetting(self.admin_misp_connector, "Security.allow_self_registration", False): + with self.__setting("Security.allow_self_registration", False): r = session.get(url + "/users/register", allow_redirects=False) self.assertEqual(302, r.status_code) self.assertEqual(url + "/users/login", r.headers['Location']) @@ -343,14 +323,14 @@ class TestSecurity(unittest.TestCase): PyMISP(url, self.test_usr.authkey) def test_disabled_misp(self): - with MISPSetting(self.admin_misp_connector, "MISP.live", False): + with self.__setting("MISP.live", False): self.assertFalse(login(url, self.test_usr.email, self.test_usr_password)) # Check if user can login with given password self.assertTrue(login(url, self.test_usr.email, self.test_usr_password)) def test_disabled_misp_api_access(self): - with MISPSetting(self.admin_misp_connector, "MISP.live", False): + with self.__setting("MISP.live", False): # Try to login with self.assertRaises(PyMISPError): PyMISP(url, self.test_usr.authkey) @@ -359,7 +339,7 @@ class TestSecurity(unittest.TestCase): PyMISP(url, self.test_usr.authkey) def test_advanced_authkeys(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id) @@ -370,7 +350,7 @@ class TestSecurity(unittest.TestCase): self.__delete_advanced_authkey(auth_key["id"]) def test_advanced_authkeys_expired(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create expired advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id, { "expiration": "1990-01-05", @@ -383,7 +363,7 @@ class TestSecurity(unittest.TestCase): self.__delete_advanced_authkey(auth_key["id"]) def test_advanced_authkeys_invalid_start_end_correct(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id) @@ -395,7 +375,7 @@ class TestSecurity(unittest.TestCase): self.__delete_advanced_authkey(auth_key["id"]) def test_advanced_authkeys_reset_own(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id) @@ -419,7 +399,7 @@ class TestSecurity(unittest.TestCase): # TODO: Delete new key def test_advanced_authkeys_reset_for_different_user(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id) @@ -429,7 +409,7 @@ class TestSecurity(unittest.TestCase): # Reset auth key for different user new_auth_key = send(logged_in, "POST", "users/resetauthkey/1", check_errors=False) - assert_error_response(new_auth_key) + self.__assertErrorResponse(new_auth_key) # Try to login again logged_in = PyMISP(url, auth_key["authkey_raw"]) @@ -438,7 +418,7 @@ class TestSecurity(unittest.TestCase): self.__delete_advanced_authkey(auth_key["id"]) def test_advanced_authkeys_reset_org_admin(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): # Create advanced authkey auth_key = self.__create_advanced_authkey(self.test_usr.id) @@ -462,7 +442,7 @@ class TestSecurity(unittest.TestCase): # TODO: Delete new key def test_advanced_authkeys_view(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): auth_key = self.__create_advanced_authkey(self.test_usr.id) auth_key_id = auth_key["id"] auth_key = send(self.admin_misp_connector, "GET", f'authKeys/view/{auth_key_id}') @@ -470,7 +450,7 @@ class TestSecurity(unittest.TestCase): self.assertNotIn("authkey", auth_key["AuthKey"], "Response should not contain hashed authkey") def test_advanced_authkeys_index(self): - with MISPSetting(self.admin_misp_connector, "Security.advanced_authkeys", True): + with self.__setting("Security.advanced_authkeys", True): auth_key_id = self.__create_advanced_authkey(self.test_usr.id)["id"] auth_keys = send(self.admin_misp_connector, "GET", 'authKeys/index/') self.__delete_advanced_authkey(auth_key_id) @@ -479,6 +459,28 @@ class TestSecurity(unittest.TestCase): for auth_key in auth_keys: self.assertNotIn("authkey", auth_key["AuthKey"], "Response should not contain hashed authkey") + def test_advanced_authkeys_user_disabled(self): + with self.__setting("Security.advanced_authkeys", True): + auth_key = self.__create_advanced_authkey(self.test_usr.id) + + updated_user = self.admin_misp_connector.update_user({'disabled': True}, self.test_usr) + check_response(updated_user) + self.assertTrue(updated_user.disabled) + + # Try to login + with self.assertRaises(PyMISPError): + PyMISP(url, auth_key["authkey_raw"]) + + # Enable user + updated_user = self.admin_misp_connector.update_user({'disabled': False}, self.test_usr) + check_response(updated_user) + self.assertFalse(updated_user.disabled) + + # Try to login + PyMISP(url, auth_key["authkey_raw"]) + + self.__delete_advanced_authkey(auth_key["id"]) + def test_change_login(self): new_email = 'testusr@user' + random() + '.local' @@ -496,7 +498,7 @@ class TestSecurity(unittest.TestCase): self.assertEqual(self.test_usr.email, updated_user.email) def test_change_login_disabled(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_login_change", True): + with self.__setting("MISP.disable_user_login_change", True): new_email = 'testusr@user' + random() + '.local' logged_in = PyMISP(url, self.test_usr.authkey) @@ -524,14 +526,14 @@ class TestSecurity(unittest.TestCase): self.assertEqual(self.test_usr.email, updated_user.email) def test_change_login_disabled_org_admin(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_login_change", True): + with self.__setting("MISP.disable_user_login_change", True): # Try to change email as org admin new_email = 'testusr@user' + random() + '.local' updated_user = self.org_admin_misp_connector.update_user({'email': new_email}, self.test_usr) - assert_error_response(updated_user) + self.__assertErrorResponse(updated_user) def test_change_pw_disabled(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_password_change", True): + with self.__setting("MISP.disable_user_password_change", True): logged_in = PyMISP(url, self.test_usr.authkey) logged_in.global_pythonify = True logged_in.change_user_password(str(uuid.uuid4())) @@ -540,7 +542,7 @@ class TestSecurity(unittest.TestCase): self.assertTrue(login(url, self.test_usr.email, self.test_usr_password)) def test_change_pw_disabled_different_way(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_password_change", True): + with self.__setting("MISP.disable_user_password_change", True): logged_in = PyMISP(url, self.test_usr.authkey) logged_in.global_pythonify = True logged_in.update_user({"password": str(uuid.uuid4())}, self.test_usr.id) @@ -549,7 +551,7 @@ class TestSecurity(unittest.TestCase): self.assertTrue(login(url, self.test_usr.email, self.test_usr_password)) def test_change_pw_disabled_by_org_admin(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_password_change", True): + with self.__setting("MISP.disable_user_password_change", True): self.org_admin_misp_connector.update_user({"password": str(uuid.uuid4())}, self.test_usr.id) # Password should be still the same @@ -581,13 +583,13 @@ class TestSecurity(unittest.TestCase): check_response(deleted) def test_add_user_by_org_admin_disabled(self): - with MISPSetting(self.admin_misp_connector, "MISP.disable_user_add", True): + with self.__setting("MISP.disable_user_add", True): user = MISPUser() user.email = 'testusr@user' + random() + '.local' # make name always unique user.org_id = self.test_org.id user.role_id = 3 created_user = self.org_admin_misp_connector.add_user(user) - assert_error_response(created_user) + self.__assertErrorResponse(created_user) def test_change_user_org_by_org_admin_different_org(self): updated_user = self.org_admin_misp_connector.update_user({'org_id': 1}, self.test_usr) @@ -605,7 +607,7 @@ class TestSecurity(unittest.TestCase): self.assertEqual(updated_user.org_id, self.test_usr.org_id) def test_shibb_existing_user(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = self.test_usr.email session.headers["Federation-Tag"] = self.test_org.name @@ -620,7 +622,7 @@ class TestSecurity(unittest.TestCase): self.assertEqual(session.headers["Federation-Tag"], json_response["Organisation"]["name"]) def test_shibb_new_user(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external@user" + random() + ".local" session.headers["Federation-Tag"] = self.test_org.name @@ -637,7 +639,7 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_user(json_response["User"]["id"]) def test_shibb_new_user_multiple_groups(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external@user" + random() + ".local" session.headers["Federation-Tag"] = self.test_org.name @@ -654,7 +656,7 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_user(json_response["User"]["id"]) def test_shibb_new_user_non_exists_org(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external@user" + random() + ".local" session.headers["Federation-Tag"] = "Non exists org " + random() @@ -672,7 +674,7 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_organisation(json_response["User"]["org_id"]) def test_shibb_new_user_org_uuid(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): r = self.__shibb_login({ "Email-Tag": "external@user" + random() + ".local", "Federation-Tag": self.test_org.uuid, @@ -689,7 +691,7 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_organisation(json_response["User"]["org_id"]) def test_shibb_new_user_non_exists_org_uuid(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): r = self.__shibb_login({ "Email-Tag": "external@user" + random() + ".local", "Federation-Tag": str(uuid.uuid4()), @@ -700,7 +702,7 @@ class TestSecurity(unittest.TestCase): self.fail() def test_shibb_new_user_no_org_provided(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external@user" + random() + ".local" session.headers["Group-Tag"] = "user" @@ -716,7 +718,7 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_user(json_response["User"]["id"]) def test_shibb_invalid_group(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external@user" + random() + ".local" session.headers["Federation-Tag"] = self.test_org.name @@ -729,7 +731,7 @@ class TestSecurity(unittest.TestCase): self.fail() def test_shibb_invalid_email(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = "external.user" + random() + ".local" session.headers["Federation-Tag"] = self.test_org.name @@ -744,7 +746,7 @@ class TestSecurity(unittest.TestCase): def test_shibb_change_role(self): org_admin = self.__create_user(self.test_org.id, ROLE.ORG_ADMIN) - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = org_admin.email session.headers["Federation-Tag"] = self.test_org.name @@ -762,7 +764,7 @@ class TestSecurity(unittest.TestCase): def test_shibb_change_org(self): user = self.__create_user(self.test_org.id, ROLE.USER) - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): session = requests.Session() session.headers["Email-Tag"] = user.email session.headers["Federation-Tag"] = "Non exists org " + random() @@ -779,18 +781,18 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_organisation(json_response["User"]["org_id"]) def test_shibb_form_login(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): # Form login should still works when no header provided self.assertTrue(login(url, self.test_usr.email, self.test_usr_password)) def test_shibb_api_login(self): - with MISPComplexSetting(self.__default_shibb_config()): + with self.__setting(self.__default_shibb_config()): PyMISP(url, self.test_usr.authkey) def test_shibb_enforced_existing_user(self): config = self.__default_shibb_config() config["Security"]["auth_enforced"] = True - with MISPComplexSetting(config): + with self.__setting(config): r = self.__shibb_login({ "Email-Tag": self.test_usr.email, "Federation-Tag": self.test_org.name, @@ -805,7 +807,7 @@ class TestSecurity(unittest.TestCase): def test_shibb_enforced_form_login(self): config = self.__default_shibb_config() config["Security"]["auth_enforced"] = True - with MISPComplexSetting(config): + with self.__setting(config): # Form login should not work when shibb is enforced, because form doesn't exists with self.assertRaises(IndexError): login(url, self.test_usr.email, self.test_usr_password) @@ -813,9 +815,145 @@ class TestSecurity(unittest.TestCase): def test_shibb_enforced_api_login(self): config = self.__default_shibb_config() config["Security"]["auth_enforced"] = True - with MISPComplexSetting(config): + with self.__setting(config): PyMISP(url, self.test_usr.authkey) + def test_sg_index_user_cannot_see(self): + org = self.__create_org() + hidden_sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(hidden_sg, org.uuid)) + + logged_in = PyMISP(url, self.test_usr.authkey) + logged_in.global_pythonify = True + sgs = logged_in.sharing_groups() + check_response(sgs) + + self.admin_misp_connector.delete_sharing_group(hidden_sg) + self.admin_misp_connector.delete_organisation(org) + + for sg in sgs: + self.failIf(sg.uuid == hidden_sg.uuid) + + def test_sg_index_user_can_see(self): + visible_sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(visible_sg, self.test_org.uuid)) + + logged_in = PyMISP(url, self.test_usr.authkey) + logged_in.global_pythonify = True + sgs = logged_in.sharing_groups() + check_response(sgs) + + self.admin_misp_connector.delete_sharing_group(visible_sg) + + sg_found = False + for sg in sgs: + if sg.uuid == visible_sg.uuid: + sg_found = True + self.assertTrue(sg_found) + + def test_sg_view_user_cannot_see(self): + org = self.__create_org() + hidden_sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(hidden_sg, org.uuid)) + + logged_in = PyMISP(url, self.test_usr.authkey) + logged_in.global_pythonify = True + + with self.assertRaises(Exception): + send(logged_in, "GET", f"/sharingGroups/view/{hidden_sg.id}") + + with self.assertRaises(Exception): + send(logged_in, "GET", f"/sharingGroups/view/{hidden_sg.uuid}") + + with self.assertRaises(Exception): + send(logged_in, "POST", f"/sharingGroups/edit/{hidden_sg.id}", {"name": "New name1"}) + + with self.assertRaises(Exception): + send(logged_in, "POST", f"/sharingGroups/edit/{hidden_sg.uuid}", {"name": "New name2"}) + + self.__assertErrorResponse(logged_in.add_org_to_sharing_group(hidden_sg, self.test_org.uuid)) + self.__assertErrorResponse(logged_in.remove_org_from_sharing_group(hidden_sg, org.uuid)) + self.__assertErrorResponse(logged_in.delete_sharing_group(hidden_sg)) + + self.admin_misp_connector.delete_sharing_group(hidden_sg) + self.admin_misp_connector.delete_organisation(org) + + def test_sg_view_user_can_see(self): + org1 = self.__create_org() + sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(sg, org1.uuid)) + check_response(self.admin_misp_connector.add_org_to_sharing_group(sg, self.test_org.uuid)) + + logged_in = PyMISP(url, self.test_usr.authkey) + send(logged_in, "GET", f"/sharingGroups/view/{sg.id}") + send(logged_in, "GET", f"/sharingGroups/view/{sg.uuid}") + + self.admin_misp_connector.delete_sharing_group(sg) + self.admin_misp_connector.delete_organisation(org1) + + def test_sg_view_user_can_see_cannot_edit(self): + org = self.__create_org() + sync_user = self.__create_user(org.id, ROLE.SYNC_USER) + sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(sg, org.uuid)) + + logged_in = PyMISP(url, sync_user.authkey) + self.assertTrue(logged_in._current_role.perm_sharing_group, "Sync user should have permission to edit sharing groups") + + send(logged_in, "GET", f"/sharingGroups/view/{sg.id}") + + with self.assertRaises(Exception): + send(logged_in, "POST", f"/sharingGroups/edit/{sg.id}", {"name": "New name1"}) + self.assertEqual(sg.name, send(logged_in, "GET", f"/sharingGroups/view/{sg.id}")["SharingGroup"]["name"]) + + with self.assertRaises(Exception): + send(logged_in, "POST", f"/sharingGroups/edit/{sg.uuid}", {"name": "New name2"}) + self.assertEqual(sg.name, send(logged_in, "GET", f"/sharingGroups/view/{sg.id}")["SharingGroup"]["name"]) + + self.__assertErrorResponse(logged_in.add_org_to_sharing_group(sg, self.test_org.uuid)) + self.__assertErrorResponse(logged_in.remove_org_from_sharing_group(sg, org.uuid)) + self.__assertErrorResponse(logged_in.delete_sharing_group(sg)) + + self.admin_misp_connector.delete_sharing_group(sg) + self.admin_misp_connector.delete_user(sync_user) + self.admin_misp_connector.delete_organisation(org) + + def test_sg_view_user_can_see_can_edit(self): + org = self.__create_org() + sync_user = self.__create_user(org.id, ROLE.SYNC_USER) + sg = self.__create_sharing_group() + check_response(self.admin_misp_connector.add_org_to_sharing_group(sg, org.uuid, True)) + + logged_in = PyMISP(url, sync_user.authkey) + self.assertTrue(logged_in._current_role.perm_sharing_group, "Sync user should have permission to edit sharing groups") + + send(logged_in, "GET", f"/sharingGroups/view/{sg.id}") + after_edit = send(logged_in, "POST", f"/sharingGroups/edit/{sg.id}", {"name": "New name1"}) + self.assertEqual("New name1", after_edit["SharingGroup"]["name"]) + after_edit = send(logged_in, "POST", f"/sharingGroups/edit/{sg.uuid}", {"name": "New name2"}) + self.assertEqual("New name2", after_edit["SharingGroup"]["name"]) + + self.__assertErrorResponse(logged_in.delete_sharing_group(sg)) + + self.admin_misp_connector.delete_sharing_group(sg) + self.admin_misp_connector.delete_user(sync_user) + self.admin_misp_connector.delete_organisation(org) + + def __create_org(self) -> MISPOrganisation: + organisation = MISPOrganisation() + organisation.name = 'Test Org ' + random() # make name always unique + org = self.admin_misp_connector.add_organisation(organisation) + check_response(org) + return org + + def __create_sharing_group(self) -> MISPSharingGroup: + sg = MISPSharingGroup() + sg.name = 'Testcases SG ' + random() # make name always unique + sg.releasability = 'Nic' + sg = self.admin_misp_connector.add_sharing_group(sg) + check_response(sg) + return sg + def __shibb_login(self, headers: dict) -> requests.Response: session = requests.Session() session.headers.update(headers) @@ -854,6 +992,17 @@ class TestSecurity(unittest.TestCase): def __delete_advanced_authkey(self, key_id: int): return send(self.admin_misp_connector, "POST", f'authKeys/delete/{key_id}') + def __setting(self, key, value=None) -> MISPSetting: + if not isinstance(key, dict): + new_setting = {key: value} + else: + new_setting = key + return MISPSetting(self.admin_misp_connector, new_setting) + + def __assertErrorResponse(self, response): + if "errors" not in response: + self.fail(response) + def __default_shibb_config(self) -> dict: return { "ApacheShibbAuth": { @@ -868,7 +1017,7 @@ class TestSecurity(unittest.TestCase): "user": 3, } }, - "Security": { + "Security": { "auth": ["ShibbAuth.ApacheShibb"], } } From a3364f66c3d1bb381ed48d0a4c8b3984c3505b61 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 20:24:06 +0100 Subject: [PATCH 011/385] chg: [UI] Use standardised view for sharging group --- app/View/SharingGroups/view.ctp | 76 ++++++++++++++------------------- app/webroot/css/main.css | 2 +- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/app/View/SharingGroups/view.ctp b/app/View/SharingGroups/view.ctp index d4e5b7526..fa9132c30 100644 --- a/app/View/SharingGroups/view.ctp +++ b/app/View/SharingGroups/view.ctp @@ -1,36 +1,26 @@
-

+

+
__('ID'), 'value' => $sg['SharingGroup']['id']], + ['key' => __('UUID'), 'value' => $sg['SharingGroup']['uuid'], 'value_class' => 'quickSelect'], + ['key' => __('Name'), 'value' => $sg['SharingGroup']['name']], + ['key' => __('Releasability'), 'value' => $sg['SharingGroup']['releasability']], + ['key' => __('Description'), 'value' => $sg['SharingGroup']['description']], + ['key' => __('Selectable'), 'boolean' => $sg['SharingGroup']['active']], + ['key' => __('Created by'), 'html' => $this->OrgImg->getNameWithImg($sg)], +]; +if ($sg['SharingGroup']['sync_user_id']) { + $tableData[] = [ + 'key' => __('Synced by'), + 'html' => $this->OrgImg->getNameWithImg($sg), + ]; +} +echo $this->element('genericElements/viewMetaTable', ['table_data' => $tableData]); ?> -
- -
-
- '; - ?>  -
- -
-
- -
-
- -

+
+
- - - + + + - - + + Instances
- - - + + + - - - + + + -element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'viewSG')); -?> +element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'viewSG')); diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index cfe147606..df874e8c6 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -2680,6 +2680,6 @@ a.orgImg { background-repeat: no-repeat; background-size: 20px; padding-left: 25px; - padding-top: 2px; + padding-top: 3px; padding-bottom: 2px; } From 6a463d85d60a5d94d58c277cba75ba9ba09fe74a Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 20:30:00 +0100 Subject: [PATCH 012/385] chg: [internal] Load orgs just when it is necessary --- app/Controller/SharingGroupsController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index 43aa8c478..ece00623d 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -46,12 +46,6 @@ class SharingGroupsController extends AppController if (!$this->userRole['perm_sharing_group']) { throw new MethodNotAllowedException('You don\'t have the required privileges to do that.'); } - $orgs = $this->SharingGroup->Organisation->find('all', array( - 'conditions' => array('local' => 1), - 'recursive' => -1, - 'fields' => array('id', 'name', 'uuid') - )); - if ($this->request->is('post')) { if ($this->_isRest()) { if (isset($this->request->data['SharingGroup'])) { @@ -132,6 +126,12 @@ class SharingGroupsController extends AppController } elseif ($this->_isRest()) { return $this->RestResponse->describe('SharingGroup', 'add', false, $this->response->type()); } + + $orgs = $this->SharingGroup->Organisation->find('all', array( + 'conditions' => array('local' => 1), + 'recursive' => -1, + 'fields' => array('id', 'name', 'uuid') + )); $this->set('orgs', $orgs); $this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl')); // We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit From 0948c7652c0ca10d28785cd0ac823afb86173c4a Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 20:31:33 +0100 Subject: [PATCH 013/385] fix: [UI] Show delete and edit button for SG just when user has permission --- app/Controller/SharingGroupsController.php | 62 ++++++++++++---------- app/View/SharingGroups/index.ctp | 6 ++- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index ece00623d..38dbaea7b 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -14,31 +14,29 @@ class SharingGroupsController extends AppController if (!empty($this->request->params['admin']) && !$this->_isSiteAdmin()) { $this->redirect('/'); } - $sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user()); - $this->paginate = Set::merge($this->paginate, array('conditions' => array('SharingGroup.id' => $sgs))); } public $paginate = array( - 'limit' => 60, - 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page. - 'order' => array( - 'SharingGroup.name' => 'ASC' + 'limit' => 60, + 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page. + 'order' => array( + 'SharingGroup.name' => 'ASC' + ), + 'fields' => array('SharingGroup.id', 'SharingGroup.uuid', 'SharingGroup.name', 'SharingGroup.description', 'SharingGroup.releasability', 'SharingGroup.local', 'SharingGroup.active'), + 'contain' => array( + 'SharingGroupOrg' => array( + 'Organisation' => array('fields' => array('Organisation.name', 'Organisation.id', 'Organisation.uuid')) ), - 'fields' => array('SharingGroup.id', 'SharingGroup.uuid', 'SharingGroup.name', 'SharingGroup.description', 'SharingGroup.releasability', 'SharingGroup.local', 'SharingGroup.active'), - 'contain' => array( - 'SharingGroupOrg' => array( - 'Organisation' => array('fields' => array('Organisation.name', 'Organisation.id', 'Organisation.uuid')) - ), - 'Organisation' => array( - 'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'), - ), - 'SharingGroupServer' => array( - 'fields' => array('SharingGroupServer.all_orgs'), - 'Server' => array( - 'fields' => array('Server.name', 'Server.id') - ) - ) + 'Organisation' => array( + 'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'), ), + 'SharingGroupServer' => array( + 'fields' => array('SharingGroupServer.all_orgs'), + 'Server' => array( + 'fields' => array('Server.name', 'Server.id') + ) + ) + ), ); public function add() @@ -266,34 +264,43 @@ class SharingGroupsController extends AppController if ($passive === 'true') { $passive = true; } - if ($passive === true) { - $this->paginate['conditions'][] = array('SharingGroup.active' => 0); - } else { - $this->paginate['conditions'][] = array('SharingGroup.active' => 1); - } + $sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user()); + $this->paginate['conditions'][] = array('SharingGroup.id' => $sgs); + $this->paginate['conditions'][] = array('SharingGroup.active' => $passive === true ? 0 : 1); $result = $this->paginate(); + // check if the current user can modify or delete the SG + $userOrganisationUuid = $this->Auth->user()['Organisation']['uuid']; foreach ($result as $k => $sg) { - if ($sg['Organisation']['uuid'] == $this->Auth->user('Organisation')['uuid'] && $this->userRole['perm_sharing_group']) { + if (!$this->userRole['perm_sharing_group']) { + $result[$k]['editable'] = false; + $result[$k]['deletable'] = false; + continue; + } + if ($sg['Organisation']['uuid'] === $userOrganisationUuid) { $result[$k]['editable'] = true; + $result[$k]['deletable'] = true; } else { $result[$k]['editable'] = false; + $result[$k]['deletable'] = false; if (!empty($sg['SharingGroupOrg'])) { foreach ($sg['SharingGroupOrg'] as $sgo) { if ($sgo['org_id'] == $this->Auth->user('org_id') && $sgo['extend']) { $result[$k]['editable'] = true; + break; } } } } } - $this->set('passive', $passive); if ($this->_isRest()) { $this->set('response', $result); $this->set('_serialize', array('response')); } else { + $this->set('passive', $passive); $this->set('sharingGroups', $result); } + $this->set('title', __('Sharing Groups')); } public function view($id) @@ -349,6 +356,7 @@ class SharingGroupsController extends AppController $this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sg['SharingGroup']['id'])); $this->set('id', $sg['SharingGroup']['id']); $this->set('sg', $sg); + $this->set('title', __('Sharing Group %s', $sg['SharingGroup']['name'])); } private function __initialiseSGQuickEdit($id, $request) diff --git a/app/View/SharingGroups/index.ctp b/app/View/SharingGroups/index.ctp index 02c17c8e6..db14f75b5 100644 --- a/app/View/SharingGroups/index.ctp +++ b/app/View/SharingGroups/index.ctp @@ -84,9 +84,11 @@ foreach ($sharingGroups as $k => $sharingGroup): From 5dd7f68560fbfa243914eb2f9214ca7304d7cb8e Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 6 Dec 2020 15:34:49 +0100 Subject: [PATCH 014/385] chg: [internal] Simplified SharingGroup::checkIfOwner method --- app/Model/SharingGroup.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/Model/SharingGroup.php b/app/Model/SharingGroup.php index f984fb6eb..bfed95128 100644 --- a/app/Model/SharingGroup.php +++ b/app/Model/SharingGroup.php @@ -451,24 +451,28 @@ class SharingGroup extends AppModel return false; } - public function checkIfOwner($user, $id) + /** + * @param array $user + * @param string|int $id Sharing group ID or UUID + * @return bool False if sharing group doesn't exists or user org is not sharing group owner + */ + public function checkIfOwner(array $user, $id) { if (!isset($user['id'])) { throw new MethodNotAllowedException('Invalid user.'); } - $this->id = $id; - if (!$this->exists()) { + $sg = $this->find('first', array( + 'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id], + 'recursive' => -1, + 'fields' => array('org_id'), + )); + if (empty($sg)) { return false; } if ($user['Role']['perm_site_admin']) { return true; } - $sg = $this->find('first', array( - 'conditions' => array('SharingGroup.id' => $id), - 'recursive' => -1, - 'fields' => array('id', 'org_id'), - )); - return ($sg['SharingGroup']['org_id'] == $user['org_id']); + return $sg['SharingGroup']['org_id'] == $user['org_id']; } // Get all organisation ids that can see a SG From 45fc1cb2bfc0d1f29b7517463daf09809034da2f Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 6 Dec 2020 17:52:07 +0100 Subject: [PATCH 015/385] new: [UI] Allow to search in sharing group list --- app/Controller/SharingGroupsController.php | 73 ++++-- .../IndexTable/Fields/custom.ctp | 1 + app/View/SharingGroups/index.ctp | 228 ++++++++++-------- 3 files changed, 177 insertions(+), 125 deletions(-) create mode 100644 app/View/Elements/genericElements/IndexTable/Fields/custom.ctp diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index 38dbaea7b..6f5f45ba8 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -261,50 +261,73 @@ class SharingGroupsController extends AppController public function index($passive = false) { - if ($passive === 'true') { - $passive = true; - } - $sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user()); - $this->paginate['conditions'][] = array('SharingGroup.id' => $sgs); + $passive = $passive === 'true'; + $authorizedSgIds = $this->SharingGroup->fetchAllAuthorised($this->Auth->user()); + $this->paginate['conditions'][] = array('SharingGroup.id' => $authorizedSgIds); $this->paginate['conditions'][] = array('SharingGroup.active' => $passive === true ? 0 : 1); + + if (isset($this->params['named']['value'])) { + $term = '%' . strtolower($this->params['named']['value']) . '%'; + $sgIds = $this->SharingGroup->SharingGroupOrg->find('list', [ + 'conditions' => [ + 'OR' => [ + 'Organisation.uuid LIKE' => $term, + 'LOWER(Organisation.name) LIKE' => $term, + ], + 'SharingGroupOrg.sharing_group_id' => $authorizedSgIds, + ], + 'contain' => ['Organisation'], + 'fields' => ['SharingGroupOrg.sharing_group_id'], + ]); + $this->paginate['conditions'][]['OR'] = [ + 'SharingGroup.id' => $sgIds, + 'SharingGroup.uuid LIKE' => $term, + 'LOWER(SharingGroup.name) LIKE' => $term, + 'LOWER(SharingGroup.description) LIKE' => $term, + 'LOWER(SharingGroup.releasability) LIKE' => $term, + 'LOWER(Organisation.name) LIKE' => $term, + ]; + } $result = $this->paginate(); // check if the current user can modify or delete the SG $userOrganisationUuid = $this->Auth->user()['Organisation']['uuid']; foreach ($result as $k => $sg) { - if (!$this->userRole['perm_sharing_group']) { - $result[$k]['editable'] = false; - $result[$k]['deletable'] = false; - continue; - } - if ($sg['Organisation']['uuid'] === $userOrganisationUuid) { - $result[$k]['editable'] = true; - $result[$k]['deletable'] = true; - } else { - $result[$k]['editable'] = false; - $result[$k]['deletable'] = false; + $editable = false; + $deletable = false; + + if ($this->userRole['perm_site_admin'] || ($this->userRole['perm_sharing_group'] && $sg['Organisation']['uuid'] === $userOrganisationUuid)) { + $editable = true; + $deletable = true; + } else if ($this->userRole['perm_sharing_group']) { if (!empty($sg['SharingGroupOrg'])) { foreach ($sg['SharingGroupOrg'] as $sgo) { - if ($sgo['org_id'] == $this->Auth->user('org_id') && $sgo['extend']) { - $result[$k]['editable'] = true; + if ($sgo['extend'] && $sgo['org_id'] == $this->Auth->user('org_id')) { + $editable = true; break; } } } } + + $result[$k]['editable'] = $editable; + $result[$k]['deletable'] = $deletable; } if ($this->_isRest()) { - $this->set('response', $result); - $this->set('_serialize', array('response')); - } else { - $this->set('passive', $passive); - $this->set('sharingGroups', $result); + return $this->RestResponse->viewData(['response' => $result], $this->response->type()); // 'response' to keep BC } - $this->set('title', __('Sharing Groups')); + $this->set('passive', $passive); + $this->set('sharingGroups', $result); + $this->set('passedArgs', $passive ? 'true' : '[]'); + $this->set('title_for_layout', __('Sharing Groups')); } public function view($id) { + if ($this->request->is('head')) { // Just check if sharing group exists and user can access it + $exists = $this->SharingGroup->checkIfAuthorised($this->Auth->user(), $id); + return new CakeResponse(['status' => $exists ? 200 : 404]); + } if (!$this->SharingGroup->checkIfAuthorised($this->Auth->user(), $id)) { throw new MethodNotAllowedException('Sharing group doesn\'t exist or you do not have permission to access it.'); } @@ -356,7 +379,7 @@ class SharingGroupsController extends AppController $this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sg['SharingGroup']['id'])); $this->set('id', $sg['SharingGroup']['id']); $this->set('sg', $sg); - $this->set('title', __('Sharing Group %s', $sg['SharingGroup']['name'])); + $this->set('title_for_layout', __('Sharing Group %s', $sg['SharingGroup']['name'])); } private function __initialiseSGQuickEdit($id, $request) diff --git a/app/View/Elements/genericElements/IndexTable/Fields/custom.ctp b/app/View/Elements/genericElements/IndexTable/Fields/custom.ctp new file mode 100644 index 000000000..989676685 --- /dev/null +++ b/app/View/Elements/genericElements/IndexTable/Fields/custom.ctp @@ -0,0 +1 @@ + -

- - element('/genericElements/IndexTable/index_table', array( + 'data' => array( + 'title' => __('Sharing Groups'), + 'data' => $sharingGroups, + 'top_bar' => array( 'children' => array( array( + 'type' => 'simple', 'children' => array( array( 'url' => $baseurl . '/sharing_groups/index', 'text' => __('Active Sharing Groups'), - 'active' => !$passive + 'active' => !$passive, ), array( 'url' => $baseurl . '/sharing_groups/index/true', 'text' => __('Passive Sharing Groups'), - 'active' => $passive + 'active' => $passive, ) ) + ), + array( + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'searchKey' => 'value', ) ) - ); - echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data)); - ?> -
- + Html->link('', '/SharingGroups/edit/' . $sharingGroup['SharingGroup']['id'], array('class' => 'black fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit'))); ?> - Form->postLink('', '/SharingGroups/delete/' . $sharingGroup['SharingGroup']['id'], array('class' => 'black fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete %s?', h($sharingGroup['SharingGroup']['name']))); ?> + + + Form->postLink('', '/SharingGroups/delete/' . $sharingGroup['SharingGroup']['id'], array('class' => 'black fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete %s?', h($sharingGroup['SharingGroup']['name']))); ?>
- - - - - - - - - - $sharingGroup): + ), + 'fields' => array( + array( + 'name' => __('ID'), + 'sort' => 'SharingGroup.id', + 'element' => 'links', + 'class' => 'short', + 'data_path' => 'SharingGroup.id', + 'url' => $baseurl . '/sharing_groups/view/%s' + ), + array( + 'name' => __('UUID'), + 'data_path' => 'SharingGroup.uuid', + 'sort' => 'SharingGroup.uuid', + 'class' => 'short quickSelect', + ), + array( + 'name' => __('Name'), + 'data_path' => 'SharingGroup.name', + 'sort' => 'SharingGroup.name', + 'class' => 'short', + ), + array( + 'name' => __('Creator'), + 'sort' => 'Organisation.name', + 'element' => 'org', + 'data_path' => 'Organisation', + 'class' => 'short', + ), + array( + 'name' => __('Description'), + 'data_path' => 'SharingGroup.description', + ), + array( + 'name' => __('Org count'), + 'element' => 'custom', + 'class' => 'short', + 'function' => function (array $sharingGroup) { + echo count($sharingGroup['SharingGroupOrg']); + } + ), + array( + 'name' => __('Releasable to'), + 'element' => 'custom', + 'function' => function (array $sharingGroup) use ($baseurl) { + $combined = __("Organisations:"); + if (empty($sharingGroup['SharingGroupOrg'])) $combined .= "
N/A"; + foreach ($sharingGroup['SharingGroupOrg'] as $sge) { + if (!empty($sge['Organisation'])) { + $combined .= "
" . h($sge['Organisation']['name']) . ""; + if ($sge['extend']) $combined .= ' (can extend)'; + } + } + $combined .= '

Instances:'; + if (empty($sharingGroup['SharingGroupServer'])) $combined .= "
N/A"; + foreach ($sharingGroup['SharingGroupServer'] as $sgs) { + if ($sgs['server_id'] != 0) { + $combined .= "
" . h($sgs['Server']['name']) . ""; + } else { + $combined .= "
This instance"; + } + if ($sgs['all_orgs']) { + $combined .= ' (all organisations)'; + } else { + $combined .= ' (as defined above)'; + } + } ?> + + ' . __('Not defined') . '' : + h($sharingGroup['SharingGroup']['releasability']) + ?> + + array( + array( + 'url' => $baseurl . '/sharing_groups/view', + 'url_params_data_paths' => ['SharingGroup.id'], + 'icon' => 'eye', + 'dbclickAction' => true, + 'title' => __('View Sharing Group'), + ), + array( + 'url' => '/sharing_groups/edit', + 'url_params_data_paths' => ['SharingGroup.id'], + 'icon' => 'edit', + 'complex_requirement' => [ + 'function' => function (array $sharingGroup) { + return $sharingGroup['editable']; + } + ], + 'title' => __('Edit Sharing Group'), + ), + array( + 'url' => '/sharing_groups/delete', + 'url_params_data_paths' => ['SharingGroup.id'], + 'postLink' => true, + 'postLinkConfirm' => __('Are you sure you want to delete the sharing group?'), + 'icon' => 'trash', + 'complex_requirement' => [ + 'function' => function (array $sharingGroup) { + return $sharingGroup['deletable']; + } + ], + 'title' => __('Delete Sharing Group'), + ), + ) + ) +)); ?> - - - - - - - N/A"; - foreach ($sharingGroup['SharingGroupOrg'] as $k2 => $sge) { - if (!empty($sge['Organisation'])) { - $combined .= "
" . h($sge['Organisation']['name']) . ""; - if ($sge['extend']) $combined .= (' (can extend)'); - } - } - $combined .= "

Instances:"; - if (count($sharingGroup['SharingGroupServer']) == 0) $combined .= "
N/A"; - foreach ($sharingGroup['SharingGroupServer'] as $k3 => $sgs) { - if ($sgs['server_id'] != 0) { - $combined .= "
" . h($sgs['Server']['name']) . ""; - } else { - $combined .= "
This instance"; - } - if ($sgs['all_orgs']) $combined .= (' (all organisations)'); - else $combined .= (' (as defined above)'); - } - ?> - - - - -
Paginator->sort('id', __('ID'));?>Paginator->sort('name');?>Paginator->sort('Creator');?>
- - - - - - Html->link('', '/SharingGroups/edit/' . $sharingGroup['SharingGroup']['id'], array('class' => 'black fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit'))); ?> - - - Form->postLink('', '/SharingGroups/delete/' . $sharingGroup['SharingGroup']['id'], array('class' => 'black fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete %s?', h($sharingGroup['SharingGroup']['name']))); ?> - - -
-

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

-
-element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'indexSG')); +element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'indexSG')); From 0f1da20235fb6def79daa21b698e10c1c7c3d7d7 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 6 Dec 2020 20:58:09 +0100 Subject: [PATCH 016/385] new: [test] View org page --- .github/workflows/main.yml | 5 +- tests/testlive_security.py | 99 +++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a6fdced62..7d7dfb746 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -220,11 +220,12 @@ jobs: sudo chown -R $USER:www-data `pwd`/app/Config sudo chmod -R 777 `pwd`/app/Config - python3 tests/testlive_security.py -v - pushd tests ./curl_tests_GH.sh $AUTH $HOST popd + + python3 tests/testlive_security.py -v + pushd PyMISP poetry install -E fileobjects -E openioc -E virustotal -E docs -E pdfexport -E email poetry run python tests/testlive_comprehensive.py diff --git a/tests/testlive_security.py b/tests/testlive_security.py index 48abd76fc..f4e2323a9 100644 --- a/tests/testlive_security.py +++ b/tests/testlive_security.py @@ -14,7 +14,7 @@ from lxml.html import fromstring from enum import Enum try: - from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPRole, MISPSharingGroup + from pymisp import PyMISP, MISPOrganisation, MISPUser, MISPRole, MISPSharingGroup, MISPEvent from pymisp.exceptions import PyMISPError, NoKey, MISPServerError except ImportError: if sys.version_info < (3, 6): @@ -940,6 +940,101 @@ class TestSecurity(unittest.TestCase): self.admin_misp_connector.delete_user(sync_user) self.admin_misp_connector.delete_organisation(org) + def test_org_user_can_see(self): + org = self.__create_org() + + logged_in = PyMISP(url, self.test_usr.authkey) + for key in (org.id, org.uuid, org.name): + fetched_org = logged_in.get_organisation(key) + check_response(fetched_org) + self.assertNotIn("created_by", fetched_org["Organisation"]) + self.assertNotIn("created_by_email", fetched_org["Organisation"]) + + self.admin_misp_connector.delete_organisation(org) + + def test_org_hide_org_cannot_set(self): + org = self.__create_org() + with self.__setting("Security.hide_organisation_index_from_users", True): + logged_in = PyMISP(url, self.test_usr.authkey) + self.__assertErrorResponse(logged_in.get_organisation(org.id)) + self.__assertErrorResponse(logged_in.get_organisation(org.uuid)) + + self.admin_misp_connector.delete_organisation(org) + + def test_org_hide_org_can_see_after_contribution(self): + org = self.__create_org() + user = self.__create_user(org.id, ROLE.USER) + logged_in = PyMISP(url, user.authkey) + event = logged_in.add_event(self.__generate_event()) + check_response(event) + + with self.__setting("Security.hide_organisation_index_from_users", True): + logged_in = PyMISP(url, self.test_usr.authkey) + for key in (org.id, org.uuid, org.name): + fetched_org = logged_in.get_organisation(key) + check_response(fetched_org) + self.assertNotIn("created_by", fetched_org["Organisation"]) + self.assertNotIn("created_by_email", fetched_org["Organisation"]) + + self.admin_misp_connector.delete_event(event) + self.admin_misp_connector.delete_user(user) + self.admin_misp_connector.delete_organisation(org) + + def test_get_org_as_site_admin(self): + org = self.admin_misp_connector.get_organisation(self.test_org) + check_response(org) + self.assertIn("created_by", org.to_dict()) + self.assertIn("created_by_email", org.to_dict()) + + def test_get_org_as_org_admin(self): + org = self.org_admin_misp_connector.get_organisation(self.test_org) + check_response(org) + self.assertIn("created_by", org.to_dict()) + self.assertIn("created_by_email", org.to_dict()) + + def test_get_org_as_org_admin_different_org(self): + org = self.__create_org() + org = self.org_admin_misp_connector.get_organisation(org) + check_response(org) + self.assertNotIn("created_by", org.to_dict()) + self.assertNotIn("created_by_email", org.to_dict()) + self.admin_misp_connector.delete_organisation(org) + + def test_org_index_site_admin(self): + created_org = self.__create_org() + orgs = self.admin_misp_connector.organisations(created_org) + check_response(orgs) + contains = False + for org in orgs: + if org.id == created_org.id: + contains = True + self.assertIn("created_by", org.to_dict()) + self.assertIn("created_by_email", org.to_dict()) + self.assertTrue(contains) + self.admin_misp_connector.delete_organisation(created_org) + + def test_org_index_org_admin(self): + created_org = self.__create_org() + orgs = self.org_admin_misp_connector.organisations(created_org) + check_response(orgs) + contains = False + for org in orgs: + if org.id == created_org.id: + contains = True + self.assertNotIn("created_by", org.to_dict()) + self.assertNotIn("created_by_email", org.to_dict()) + self.assertTrue(contains) + self.admin_misp_connector.delete_organisation(created_org) + + def __generate_event(self) -> MISPEvent: + mispevent = MISPEvent() + mispevent.info = 'This is a super simple test' + mispevent.distribution = 1 + mispevent.threat_level_id = 1 + mispevent.analysis = 1 + mispevent.add_attribute('text', "Ahoj") + return mispevent + def __create_org(self) -> MISPOrganisation: organisation = MISPOrganisation() organisation.name = 'Test Org ' + random() # make name always unique @@ -969,7 +1064,7 @@ class TestSecurity(unittest.TestCase): return r - def __create_user(self, org_id: int = None, role_id: Union[int, ROLE] = None) -> MISPUser: + def __create_user(self, org_id: int, role_id: Union[int, ROLE]) -> MISPUser: if isinstance(role_id, ROLE): role_id = role_id.value From 837dbce85e97c601b2ba87f00c39bb4b6b091a74 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 6 Dec 2020 22:07:55 +0100 Subject: [PATCH 017/385] chg: [internal] HEAD check if org exists --- app/Controller/OrganisationsController.php | 168 ++++++++++++--------- 1 file changed, 93 insertions(+), 75 deletions(-) diff --git a/app/Controller/OrganisationsController.php b/app/Controller/OrganisationsController.php index 9e9cd509f..01a427aa7 100644 --- a/app/Controller/OrganisationsController.php +++ b/app/Controller/OrganisationsController.php @@ -47,26 +47,26 @@ class OrganisationsController extends AppController $searchall = $this->passedArgs['searchall']; } - if (isset($searchall) && !empty($searchall)) { $passedArgs['searchall'] = $searchall; $allSearchFields = array('name', 'description', 'nationality', 'sector', 'type', 'contacts', 'restricted_to_domain', 'uuid'); + $searchTerm = '%' . strtolower($passedArgs['searchall']) . '%'; foreach ($allSearchFields as $field) { - $conditions['OR'][] = array('LOWER(Organisation.' . $field . ') LIKE' => '%' . strtolower($passedArgs['searchall']) . '%'); + $conditions['OR'][] = array('LOWER(Organisation.' . $field . ') LIKE' => $searchTerm); } } - $this->set('passedArgs', json_encode($passedArgs)); + $this->paginate['conditions'] = $conditions; $usersPerOrg = $this->User->getMembersCount(); if ($this->_isRest()) { unset($this->paginate['limit']); $orgs = $this->Organisation->find('all', $this->paginate); } else { - if (isset($this->params['named']['viewall']) && $this->params['named']['viewall']) { - $orgCount = $this->Organisation->find('count'); - $this->paginate['limit'] = $orgCount; + $viewAll = isset($this->params['named']['viewall']) && $this->params['named']['viewall']; + if ($viewAll) { + unset($this->paginate['limit']); } - $this->set('viewall', isset($this->params['named']['viewall']) ? $this->params['named']['viewall'] : false); + $this->set('viewall', $viewAll); $orgs = $this->paginate(); } $this->loadModel('User'); @@ -76,28 +76,34 @@ class OrganisationsController extends AppController $orgs[$k]['Organisation']['user_count'] = $usersPerOrg[$org['Organisation']['id']]; } if ($this->_isSiteAdmin()) { - if (!in_array($org['Organisation']['created_by'], array_keys($org_creator_ids))) { - $email = $this->User->find('first', array('recursive' => -1, 'fields' => array('id', 'email'), 'conditions' => array('id' => $org['Organisation']['created_by']))); + if (!isset($org_creator_ids[$org['Organisation']['created_by']])) { + $email = $this->User->find('first', array( + 'recursive' => -1, + 'fields' => array('id', 'email'), + 'conditions' => array('id' => $org['Organisation']['created_by'])) + ); if (!empty($email)) { $org_creator_ids[$org['Organisation']['created_by']] = $email['User']['email']; } else { - $org_creator_ids[$org['Organisation']['created_by']] = 'Unknown'; + $org_creator_ids[$org['Organisation']['created_by']] = __('Unknown'); } } $orgs[$k]['Organisation']['created_by_email'] = $org_creator_ids[$org['Organisation']['created_by']]; + } else { + unset($orgs[$k]['Organisation']['created_by']); } } if ($this->_isRest()) { return $this->RestResponse->viewData($orgs, $this->response->type()); - } else { - foreach ($orgs as &$org) { - $org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']); - } - - $this->set('named', $this->params['named']); - $this->set('scope', $scope); - $this->set('orgs', $orgs); } + foreach ($orgs as &$org) { + $org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']); + } + + $this->set('named', $this->params['named']); + $this->set('scope', $scope); + $this->set('orgs', $orgs); + $this->set('passedArgs', json_encode($passedArgs)); } public function admin_add() @@ -308,76 +314,58 @@ class OrganisationsController extends AppController public function view($id) { - if (Validation::uuid($id)) { - $temp = $this->Organisation->find('first', array('recursive' => -1, 'fields' => array('Organisation.id'), 'conditions' => array('Organisation.uuid' => $id))); - if (empty($temp)) { - throw new NotFoundException(__('Invalid organisation.')); - } - $id = $temp['Organisation']['id']; - } elseif (!is_numeric($id)) { - $temp = $this->Organisation->find('first', array('recursive' => -1, 'fields' => array('Organisation.id'), 'conditions' => array('Organisation.name' => urldecode($id)))); - if (empty($temp)) { - throw new NotFoundException(__('Invalid organisation.')); - } - $id = $temp['Organisation']['id']; + if (is_numeric($id)) { + $conditions = ['Organisation.id' => $id]; + } else if (Validation::uuid($id)) { + $conditions = ['Organisation.uuid' => $id]; + } else { + $conditions = ['Organisation.name' => urldecode($id)]; } - $this->Organisation->id = $id; - if (!$this->Organisation->exists()) { + + if ($this->request->is('head')) { // Just check if org exists and user can access it + $org = $this->Organisation->find('first', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'fields' => ['id'], + )); + $exists = $org && $this->__canSeeOrg($this->Auth->user(), $org['Organisation']['id']); + return new CakeResponse(['status' => $exists ? 200 : 404]); + } + + $org = $this->Organisation->find('first', array( + 'conditions' => $conditions, + 'recursive' => -1, + 'fields' => ['id', 'name', 'date_created', 'date_modified', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid', 'restricted_to_domain', 'created_by'], + )); + if (!$org || !$this->__canSeeOrg($this->Auth->user(), $org['Organisation']['id'])) { throw new NotFoundException(__('Invalid organisation')); } - $fullAccess = false; - $fields = array('id', 'name', 'date_created', 'date_modified', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid', 'restricted_to_domain'); - if ($this->_isSiteAdmin() || ($this->_isAdmin() && $this->Auth->user('Organisation')['id'] == $id)) { - $fullAccess = true; - $fields = array_merge($fields, array('created_by')); - } - $org = $this->Organisation->find('first', array( - 'conditions' => array('id' => $id), - 'fields' => $fields, - 'recursive' => -1 - )); - if (!$this->Auth->user('Role')['perm_sharing_group'] && Configure::read('Security.hide_organisation_index_from_users')) { - $this->loadModel('Event'); - $event = $this->Event->find('first', array( - 'fields' => array('Event.id'), - 'recursive' => -1, - 'conditions' => array('Event.orgc_id' => $org['Organisation']['id']) - )); - if (empty($event)) { - $proposal = $this->Event->ShadowAttribute->find('first', array( - 'fields' => array('ShadowAttribute.id'), - 'recursive' => -1, - 'conditions' => array('ShadowAttribute.org_id' => $org['Organisation']['id']) - )); - if (empty($proposal)) { - throw new NotFoundException(__('Invalid organisation')); - } - } - } - $this->set('local', $org['Organisation']['local']); + $fullAccess = $this->_isSiteAdmin() || ($this->_isAdmin() && $this->Auth->user('Organisation')['id'] == $org['Organisation']['id']); if ($fullAccess) { - $creator = $this->Organisation->User->find( - 'first', - array( - 'conditions' => array('User.id' => $org['Organisation']['created_by']), - 'fields' => array('email'), - 'recursive' => -1 - ) - ); + $creator = $this->Organisation->User->find('first', array( + 'conditions' => array('User.id' => $org['Organisation']['created_by']), + 'fields' => array('email'), + 'recursive' => -1 + )); if (!empty($creator)) { $org['Organisation']['created_by_email'] = $creator['User']['email']; } + } else { + unset($org['Organisation']['created_by']); } + if ($this->_isRest()) { $org['Organisation']['user_count'] = $this->Organisation->User->getMembersCount($org['Organisation']['id']); return $this->RestResponse->viewData($org, $this->response->type()); - } else { - $org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']); - $this->set('fullAccess', $fullAccess); - $this->set('org', $org); - $this->set('id', $id); } + + $org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']); + $this->set('local', $org['Organisation']['local']); + $this->set('fullAccess', $fullAccess); + $this->set('org', $org); + $this->set('id', $org['Organisation']['id']); + $this->set('title_for_layout', __('Organisation %s', $org['Organisation']['name'])); } public function fetchOrgsForSG($idList = '{}', $type) @@ -487,4 +475,34 @@ class OrganisationsController extends AppController $this->render('ajax/merge'); } } + + /** + * Hide organisation view from users if they haven't yet contributed data and Security.hide_organisation_index_from_users is enabled + * + * @param array $user + * @param int $orgId + * @return bool + */ + private function __canSeeOrg(array $user, $orgId) + { + if (!$user['Role']['perm_sharing_group'] && Configure::read('Security.hide_organisation_index_from_users')) { + $this->loadModel('Event'); + $event = $this->Event->find('first', array( + 'fields' => array('Event.id'), + 'recursive' => -1, + 'conditions' => array('Event.orgc_id' => $orgId) + )); + if (empty($event)) { + $proposal = $this->Event->ShadowAttribute->find('first', array( + 'fields' => array('ShadowAttribute.id'), + 'recursive' => -1, + 'conditions' => array('ShadowAttribute.org_id' => $orgId) + )); + if (empty($proposal)) { + return false; + } + } + } + return true; + } } From dd04b664406e29912cf135be12ca97b988f55323 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 7 Dec 2020 12:47:35 +0100 Subject: [PATCH 018/385] fix: [UI] Link to role edit --- app/View/Roles/admin_index.ctp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/View/Roles/admin_index.ctp b/app/View/Roles/admin_index.ctp index 757f95894..d967ee99b 100644 --- a/app/View/Roles/admin_index.ctp +++ b/app/View/Roles/admin_index.ctp @@ -1,7 +1,7 @@ __('Id'), + 'name' => __('ID'), 'sort' => 'Role.id', 'data_path' => 'Role.id' ], @@ -44,7 +44,7 @@ 'sort' => 'Role.max_execution_time', 'data_path' => 'Role.max_execution_time', 'decorator' => function($value) use ($default_max_execution_time) { - return (empty($value) ? $default_max_execution_time : h($value)) . 's'; + return (empty($value) ? $default_max_execution_time : h($value)) . ' s'; } ]; @@ -54,7 +54,7 @@ 'data_path' => 'Role.rate_limit_count', 'decorator' => function($value) { - return (empty($value) ? __('N/A') : h($value)); + return (empty($value) ? __('Unlimited') : h($value)); } ]; @@ -86,7 +86,6 @@ 'type' => 'search', 'button' => __('Filter'), 'placeholder' => __('Enter value to search'), - 'data' => '', 'searchKey' => 'quickFilter' ] ] @@ -96,7 +95,7 @@ 'description' => empty($ajax) ? __('Instance specific permission roles.') : false, 'actions' => [ [ - 'url' => $baseurl . '/admin/roles/edit/', + 'url' => $baseurl . '/admin/roles/edit', 'url_params_data_paths' => array( 'Role.id' ), @@ -114,5 +113,3 @@ ] ] ]); - -?> From bd19c72d00d8d7a0a21e96a7e4b0dd7b31ea686c Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 7 Dec 2020 22:30:16 +0100 Subject: [PATCH 019/385] fix: [UI] Do not mention that STIX 2 export require library This information can be useful just for site administrators, but not for users --- app/Controller/EventsController.php | 151 ++++++++++++++-------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 0ee5546f4..88cf32fe4 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -3857,104 +3857,105 @@ class EventsController extends AppController if (empty($event)) { throw new NotFoundException(__('Event not found or you are not authorised to view it.')); } + $id = $event['Event']['id']; // #TODO i18n $exports = array( 'xml' => array( - 'url' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '.xml', - 'text' => 'MISP XML (metadata + all attributes)', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Encode Attachments', - 'checkbox_set' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '/withAttachments:1.xml', - 'checkbox_default' => true + 'url' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '.xml', + 'text' => 'MISP XML (metadata + all attributes)', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Encode Attachments', + 'checkbox_set' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '/withAttachments:1.xml', + 'checkbox_default' => true ), 'json' => array( - 'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json', - 'text' => 'MISP JSON (metadata + all attributes)', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Encode Attachments', - 'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json', - 'checkbox_default' => true + 'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json', + 'text' => 'MISP JSON (metadata + all attributes)', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Encode Attachments', + 'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json', + 'checkbox_default' => true ), 'openIOC' => array( - 'url' => $this->baseurl . '/events/restSearch/openioc/to_ids:1/published:1/eventid:' . $id . '.json', - 'text' => 'OpenIOC (all indicators marked to IDS)', - 'requiresPublished' => false, - 'checkbox' => false, + 'url' => $this->baseurl . '/events/restSearch/openioc/to_ids:1/published:1/eventid:' . $id . '.json', + 'text' => 'OpenIOC (all indicators marked to IDS)', + 'requiresPublished' => false, + 'checkbox' => false, ), 'csv' => array( - 'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:0/eventid:' . $id, - 'text' => 'CSV', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Include non-IDS marked attributes', - 'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:0/eventid:' . $id + 'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:0/eventid:' . $id, + 'text' => 'CSV', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Include non-IDS marked attributes', + 'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:0/eventid:' . $id ), 'csv_with_context' => array( - 'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:1/eventid:' . $id, - 'text' => 'CSV with additional context', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Include non-IDS marked attributes', - 'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:1/eventid:' . $id + 'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:1/eventid:' . $id, + 'text' => 'CSV with additional context', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Include non-IDS marked attributes', + 'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:1/eventid:' . $id ), 'stix_xml' => array( - 'url' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id, - 'text' => 'STIX XML (metadata + all attributes)', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Encode Attachments', - 'checkbox_set' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id . '/withAttachments:1' + 'url' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id, + 'text' => 'STIX 1 XML (metadata + all attributes)', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Encode Attachments', + 'checkbox_set' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id . '/withAttachments:1' ), 'stix_json' => array( - 'url' => $this->baseurl . '/events/restSearch/stix-json/eventid:' . $id, - 'text' => 'STIX JSON (metadata + all attributes)', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Encode Attachments', - 'checkbox_set' => $this->baseurl . '/events/restSearch/stix-json/withAttachments:1/eventid:' . $id + 'url' => $this->baseurl . '/events/restSearch/stix-json/eventid:' . $id, + 'text' => 'STIX 1 JSON (metadata + all attributes)', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Encode Attachments', + 'checkbox_set' => $this->baseurl . '/events/restSearch/stix-json/withAttachments:1/eventid:' . $id ), 'stix2_json' => array( - 'url' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id, - 'text' => 'STIX2 (requires the STIX 2 library)', - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Encode Attachments', - 'checkbox_set' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id . '/withAttachments:1' + 'url' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id, + 'text' => 'STIX 2', + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Encode Attachments', + 'checkbox_set' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id . '/withAttachments:1' ), 'rpz' => array( - 'url' => $this->baseurl . '/attributes/restSearch/returnFormat:rpz/published:1||0/eventid:' . $id, - 'text' => 'RPZ Zone file', - 'requiresPublished' => false, - 'checkbox' => false, + 'url' => $this->baseurl . '/attributes/restSearch/returnFormat:rpz/published:1||0/eventid:' . $id, + 'text' => 'RPZ Zone file', + 'requiresPublished' => false, + 'checkbox' => false, ), 'suricata' => array( - 'url' => $this->baseurl . '/events/restSearch/returnFormat:suricata/published:1||0/eventid:' . $id, - 'text' => 'Download Suricata rules', - 'requiresPublished' => false, - 'checkbox' => false, + 'url' => $this->baseurl . '/events/restSearch/returnFormat:suricata/published:1||0/eventid:' . $id, + 'text' => 'Download Suricata rules', + 'requiresPublished' => false, + 'checkbox' => false, ), 'snort' => array( - 'url' => $this->baseurl . '/events/restSearch/returnFormat:snort/published:1||0/eventid:' . $id, - 'text' => 'Download Snort rules', - 'requiresPublished' => false, - 'checkbox' => false, + 'url' => $this->baseurl . '/events/restSearch/returnFormat:snort/published:1||0/eventid:' . $id, + 'text' => 'Download Snort rules', + 'requiresPublished' => false, + 'checkbox' => false, ), 'bro' => array( - 'url' => $this->baseurl . '/attributes/bro/download/all/false/' . $id, - // 'url' => '/attributes/restSearch/returnFormat:bro/published:1||0/eventid:' . $id, - 'text' => 'Download Bro rules', - 'requiresPublished' => false, - 'checkbox' => false + 'url' => $this->baseurl . '/attributes/bro/download/all/false/' . $id, + // 'url' => '/attributes/restSearch/returnFormat:bro/published:1||0/eventid:' . $id, + 'text' => 'Download Bro rules', + 'requiresPublished' => false, + 'checkbox' => false ), 'text' => array( - 'text' => 'Export all attribute values as a text file', - 'url' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/eventid:' . $id, - 'requiresPublished' => false, - 'checkbox' => true, - 'checkbox_text' => 'Include non-IDS marked attributes', - 'checkbox_set' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/to_ids:1||0/eventid:' . $id + 'text' => 'Export all attribute values as a text file', + 'url' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/eventid:' . $id, + 'requiresPublished' => false, + 'checkbox' => true, + 'checkbox_text' => 'Include non-IDS marked attributes', + 'checkbox_set' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/to_ids:1||0/eventid:' . $id ), ); if ($event['Event']['published'] == 0) { @@ -3975,10 +3976,10 @@ class EventsController extends AppController if (is_array($modules) && !empty($modules)) { foreach ($modules['modules'] as $module) { $exports[$module['name']] = array( - 'url' => $this->baseurl . '/events/exportModule/' . $module['name'] . '/' . $id, - 'text' => Inflector::humanize($module['name']), - 'requiresPublished' => true, - 'checkbox' => false, + 'url' => $this->baseurl . '/events/exportModule/' . $module['name'] . '/' . $id, + 'text' => Inflector::humanize($module['name']), + 'requiresPublished' => true, + 'checkbox' => false, ); } } From 5215d29731ee51131ddc16fcbed7631f0b60189e Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 22:06:39 +0100 Subject: [PATCH 020/385] fix: [rest-client] Do not raise exception for not site admin --- app/Controller/Component/RestResponseComponent.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Controller/Component/RestResponseComponent.php b/app/Controller/Component/RestResponseComponent.php index 43faa2339..f4d690de3 100644 --- a/app/Controller/Component/RestResponseComponent.php +++ b/app/Controller/Component/RestResponseComponent.php @@ -1,5 +1,8 @@ __setup(); $result = array(); foreach ($this->__scopedFieldsConstraint as $controller => $actions) { - $controller = Inflector::tableize($controller); + // EventGraph controller has different rule + $controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller); foreach ($actions as $action => $data) { if ($this->ACL->canUserAccess($user, $controller, $action)) { $admin_routing = ''; @@ -364,7 +368,8 @@ class RestResponseComponent extends Component $this->__setup(); $result = array(); foreach ($this->__descriptions as $controller => $actions) { - $controller = Inflector::tableize($controller); + // EventGraph controller has different rule + $controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller); foreach ($actions as $action => $data) { if ($this->ACL->canUserAccess($user, $controller, $action)) { $admin_routing = ''; From 305605cd3fad14b6cd745ce9dd98f4126a31fc50 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 1 Dec 2020 11:00:42 +0100 Subject: [PATCH 021/385] fix: [sighting] Make sure that correct columns are processed --- app/Model/Sighting.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php index c19c77de3..b5303a870 100644 --- a/app/Model/Sighting.php +++ b/app/Model/Sighting.php @@ -231,7 +231,7 @@ class Sighting extends AppModel $objectElement = ucfirst($context) . 'Tag'; foreach ($sightings as $sighting) { $tagId = $sighting[$objectElement]['tag_id']; - $date = $sighting['Sighting']['date_sighting']; + $date = $sighting['Sighting']['date']; $count = (int)$sighting['Sighting']['sighting_count']; if (isset($sparklineData[$tagId][$date]['sighting'])) { @@ -324,18 +324,18 @@ class Sighting extends AppModel } // Returns date in `Y-m-d` format - $this->virtualFields['date_sighting'] = $this->dateVirtualColumn(); + $this->virtualFields['date'] = $this->dateVirtualColumn(); $this->virtualFields['sighting_count'] = 'COUNT(id)'; $this->virtualFields['last_timestamp'] = 'MAX(date_sighting)'; $groupedSightings = $this->find('all', array( 'conditions' => $conditions, - 'fields' => ['org_id', 'attribute_id', 'type', 'date_sighting', 'last_timestamp', 'sighting_count'], + 'fields' => ['org_id', 'attribute_id', 'type', 'date', 'last_timestamp', 'sighting_count'], 'recursive' => -1, - 'group' => ['org_id', 'attribute_id', 'type', 'date_sighting'], + 'group' => ['org_id', 'attribute_id', 'type', 'date'], 'order' => ['date_sighting'], // from oldest )); unset( - $this->virtualFields['date_sighting'], + $this->virtualFields['date'], $this->virtualFields['sighting_count'], $this->virtualFields['last_timestamp'] ); @@ -361,17 +361,17 @@ class Sighting extends AppModel ] ]); // Returns date in `Y-m-d` format - $this->virtualFields['date_sighting'] = $this->dateVirtualColumn(); + $this->virtualFields['date'] = $this->dateVirtualColumn(); $this->virtualFields['sighting_count'] = 'COUNT(Sighting.id)'; - $sightings = $this->find('all', array( + $sightings = $this->find('all', [ 'recursive' => -1, 'contain' => [ucfirst($context) . 'Tag'], 'conditions' => $conditions, - 'fields' => [ucfirst($context) . 'Tag.tag_id', 'date_sighting', 'sighting_count'], - 'group' => [ucfirst($context) . 'Tag.id', 'date_sighting'], + 'fields' => [ucfirst($context) . 'Tag.tag_id', 'date', 'sighting_count'], + 'group' => [ucfirst($context) . 'Tag.id', 'date'], 'order' => ['date_sighting'], // from oldest - )); - unset($this->virtualFields['date_sighting'], $this->virtualFields['sighting_count']); + ]); + unset($this->virtualFields['date'], $this->virtualFields['sighting_count']); return $sightings; } @@ -389,7 +389,7 @@ class Sighting extends AppModel $type = $this->type[$sighting['type']]; $orgName = isset($sighting['Organisation']['name']) ? $sighting['Organisation']['name'] : __('Others'); $count = (int)$sighting['sighting_count']; - $inRange = strtotime($sighting['date_sighting']) >= $range; + $inRange = strtotime($sighting['date']) >= $range; foreach ([$sighting['attribute_id'], 'all'] as $needle) { if (!isset($sightingsData[$needle][$type])) { @@ -407,10 +407,10 @@ class Sighting extends AppModel } if ($inRange) { - if (isset($sparklineData[$needle][$sighting['date_sighting']][$type])) { - $sparklineData[$needle][$sighting['date_sighting']][$type] += $count; + if (isset($sparklineData[$needle][$sighting['date']][$type])) { + $sparklineData[$needle][$sighting['date']][$type] += $count; } else { - $sparklineData[$needle][$sighting['date_sighting']][$type] = $count; + $sparklineData[$needle][$sighting['date']][$type] = $count; } } } From ce96003b8997e348ed3b0870162c590fb734e5b1 Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Tue, 8 Dec 2020 15:47:03 +0100 Subject: [PATCH 022/385] fix: [doc] Location typo fixed --- app/Plugin/ShibbAuth/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Plugin/ShibbAuth/README.md b/app/Plugin/ShibbAuth/README.md index 9e4d20920..829b0f7cd 100644 --- a/app/Plugin/ShibbAuth/README.md +++ b/app/Plugin/ShibbAuth/README.md @@ -68,7 +68,7 @@ Edit your MISP apache configuration by adding the below (location depends on you ```Apache SetHandler shib - + ``` Enable the plugin at bootstrap.php: From 754f5fcc8b6162fc3be38a592843f5c4f923bf69 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 4 Dec 2020 22:35:36 +0100 Subject: [PATCH 023/385] chg: [UI] Hide some fields from user profile and use better description --- app/View/Users/admin_add.ctp | 4 ++-- app/View/Users/admin_edit.ctp | 4 ++-- app/View/Users/edit.ctp | 4 ++-- app/View/Users/view.ctp | 30 +++++++++++++++++++----------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/View/Users/admin_add.ctp b/app/View/Users/admin_add.ctp index 02db9ad91..97afe5f00 100644 --- a/app/View/Users/admin_add.ctp +++ b/app/View/Users/admin_add.ctp @@ -77,12 +77,12 @@ Form->input('autoalert', array( - 'label' => __('Receive alerts when events are published'), + 'label' => __('Receive email alerts when events are published'), 'type' => 'checkbox', 'checked' => isset($this->request->data['User']['autoalert']) ? $this->request->data['User']['autoalert'] : $default_publish_alert )); echo $this->Form->input('contactalert', array( - 'label' => __('Receive alerts from "contact reporter" requests'), + 'label' => __('Receive email alerts from "Contact reporter" requests'), 'type' => 'checkbox', 'checked' => isset($this->request->data['User']['contactalert']) ? $this->request->data['User']['contactalert'] : true )); diff --git a/app/View/Users/admin_edit.ctp b/app/View/Users/admin_edit.ctp index d687707ae..fe8a03729 100644 --- a/app/View/Users/admin_edit.ctp +++ b/app/View/Users/admin_edit.ctp @@ -85,8 +85,8 @@ 'disabled' => !$canChangePassword, 'data-disabled-reason' => !$canChangePassword ? __('User password change is disabled on this instance') : '', ]); - echo $this->Form->input('autoalert', array('label' => __('Receive alerts when events are published'), 'type' => 'checkbox')); - echo $this->Form->input('contactalert', array('label' => __('Receive alerts from "contact reporter" requests'), 'type' => 'checkbox')); + echo $this->Form->input('autoalert', array('label' => __('Receive email alerts when events are published'), 'type' => 'checkbox')); + echo $this->Form->input('contactalert', array('label' => __('Receive email alerts from "Contact reporter" requests'), 'type' => 'checkbox')); echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Disable this user account'))); echo '
'; ?> diff --git a/app/View/Users/edit.ctp b/app/View/Users/edit.ctp index 77d25f1c1..ae6fbd63d 100755 --- a/app/View/Users/edit.ctp +++ b/app/View/Users/edit.ctp @@ -30,8 +30,8 @@ echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge')); } echo '
'; - echo $this->Form->input('autoalert', array('label' => __('Receive alerts when events are published'), 'type' => 'checkbox')); - echo $this->Form->input('contactalert', array('label' => __('Receive alerts from "contact reporter" requests'), 'type' => 'checkbox')); + echo $this->Form->input('autoalert', array('label' => __('Receive email alerts when events are published'), 'type' => 'checkbox')); + echo $this->Form->input('contactalert', array('label' => __('Receive email alerts from "Contact reporter" requests'), 'type' => 'checkbox')); echo '
'; ?> diff --git a/app/View/Users/view.ctp b/app/View/Users/view.ctp index 0b60a6fd1..63e00c26a 100755 --- a/app/View/Users/view.ctp +++ b/app/View/Users/view.ctp @@ -16,8 +16,8 @@ 'html' => $this->OrgImg->getNameWithImg($user), ); $table_data[] = array('key' => __('Role'), 'html' => $this->Html->link($user['Role']['name'], array('controller' => 'roles', 'action' => 'view', $user['Role']['id']))); - $table_data[] = array('key' => __('Autoalert'), 'boolean' => $user['User']['autoalert']); - $table_data[] = array('key' => __('Contactalert'), 'boolean' => $user['User']['contactalert']); + $table_data[] = array('key' => __('Event alert enabled'), 'boolean' => $user['User']['autoalert']); + $table_data[] = array('key' => __('Contact alert enabled'), 'boolean' => $user['User']['contactalert']); if (!$admin_view && !$user['Role']['perm_auth']) { $table_data[] = array( @@ -73,8 +73,10 @@ $table_data[] = array('key' => __('Org admin'), 'html' => implode('
', $org_admin_data)); } $table_data[] = array('key' => __('NIDS Start SID'), 'value' => $user['User']['nids_sid']); - $table_data[] = array('key' => __('Terms accepted'), 'boolean' => $user['User']['termsaccepted']); - $table_data[] = array('key' => __('Must change password'), 'boolean' => $user['User']['change_pw']); + if ($admin_view) { + $table_data[] = array('key' => __('Terms accepted'), 'boolean' => $user['User']['termsaccepted']); + $table_data[] = array('key' => __('Must change password'), 'boolean' => $user['User']['change_pw']); + } $table_data[] = array( 'key' => __('PGP key'), 'element' => 'genericElements/key', @@ -100,14 +102,20 @@ ); } $table_data[] = array( - 'key' => __('News read at'), - 'value' => $user['User']['newsread'] ? date('Y-m-d H:i:s', $user['User']['newsread']) : __('N/A') - ); - $table_data[] = array( - 'key' => __('Disabled'), - 'class' => empty($user['User']['disabled']) ? '' : 'background-red', - 'boolean' => $user['User']['disabled'] + 'key' => __('Created'), + 'value' => $user['User']['date_created'] ? date('Y-m-d H:i:s', $user['User']['date_created']) : __('N/A') ); + if ($admin_view) { + $table_data[] = array( + 'key' => __('News read at'), + 'value' => $user['User']['newsread'] ? date('Y-m-d H:i:s', $user['User']['newsread']) : __('N/A') + ); + $table_data[] = array( + 'key' => __('Disabled'), + 'class' => empty($user['User']['disabled']) ? '' : 'background-red', + 'boolean' => $user['User']['disabled'] + ); + } echo $this->element('genericElements/assetLoader', array( 'css' => array('vis', 'distribution-graph'), 'js' => array('vis', 'network-distribution-graph') From 6950f55c2944c22dc3e51233120ddb64cacb2d85 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 6 Dec 2020 19:52:21 +0100 Subject: [PATCH 024/385] chg: [UI] Use PGP instead of GnuGP, GnuPG is implementation --- app/View/Users/admin_add.ctp | 4 ++-- app/View/Users/admin_edit.ctp | 4 ++-- app/View/Users/admin_email.ctp | 2 +- app/View/Users/ajax/fetchpgpkey.ctp | 4 ++-- app/View/Users/edit.ctp | 4 ++-- app/View/Users/view.ctp | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/View/Users/admin_add.ctp b/app/View/Users/admin_add.ctp index 97afe5f00..6aa435e81 100644 --- a/app/View/Users/admin_add.ctp +++ b/app/View/Users/admin_add.ctp @@ -67,9 +67,9 @@ ?> Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.'))); + echo $this->Form->input('gpgkey', array('label' => __('PGP key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s PGP key here or try to retrieve it from the CIRCL key server by clicking on "Fetch PGP key" below.'))); ?> -
+
Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s S/MIME public key in PEM format here.'))); ?> diff --git a/app/View/Users/admin_edit.ctp b/app/View/Users/admin_edit.ctp index fe8a03729..36d984c13 100644 --- a/app/View/Users/admin_edit.ctp +++ b/app/View/Users/admin_edit.ctp @@ -70,9 +70,9 @@ ?> Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.'))); + echo $this->Form->input('gpgkey', array('label' => __('PGP key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s PGP key here or try to retrieve it from the CIRCL key server by clicking on "Fetch PGP key" below.'))); ?> -
+
Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s S/MIME public key in PEM format here.'))); diff --git a/app/View/Users/admin_email.ctp b/app/View/Users/admin_email.ctp index 67ac9f291..f2acef5bb 100644 --- a/app/View/Users/admin_email.ctp +++ b/app/View/Users/admin_email.ctp @@ -9,7 +9,7 @@
  • -
  • +
  • diff --git a/app/View/Users/ajax/fetchpgpkey.ctp b/app/View/Users/ajax/fetchpgpkey.ctp index 62a3b052b..3307484fc 100644 --- a/app/View/Users/ajax/fetchpgpkey.ctp +++ b/app/View/Users/ajax/fetchpgpkey.ctp @@ -13,7 +13,7 @@ - +
    @@ -26,7 +26,7 @@
    diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 563249c43..dcbc435b1 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -2207,26 +2207,38 @@ function runIndexFilter(element) { } function runIndexQuickFilter(preserveParams, url, target) { - if (!passedArgsArray) { + if (typeof passedArgsArray === "undefined") { var passedArgsArray = []; } - var searchKey = 'searchall'; - if ($('#quickFilterField').data('searchkey')) { - searchKey = $('#quickFilterField').data('searchkey'); + var $quickFilterField = $('#quickFilterField'); + var searchKey; + if ($quickFilterField.data('searchkey')) { + searchKey = $quickFilterField.data('searchkey'); + } else { + searchKey = 'searchall'; } - if ( $('#quickFilterField').val().trim().length > 0){ - passedArgsArray[searchKey] = encodeURIComponent($('#quickFilterField').val().trim()); + if ($quickFilterField.val().trim().length > 0) { + passedArgsArray[searchKey] = encodeURIComponent($quickFilterField.val().trim()); } if (typeof url === "undefined") { url = here; } - if (typeof preserveParams !== "undefined") { + if (typeof preserveParams === "string") { preserveParams = String(preserveParams); if (!preserveParams.startsWith('/')) { preserveParams = '/' + preserveParams; } url += preserveParams; + } else if (typeof preserveParams === "object") { + for (var key in preserveParams) { + if (typeof key == 'number') { + url += "/" + preserveParams[key]; + } else if (key !== 'page') { + url += "/" + key + ":" + preserveParams[key]; + } + } } + for (var key in passedArgsArray) { if (typeof key == 'number') { url += "/" + passedArgsArray[key]; @@ -2234,6 +2246,7 @@ function runIndexQuickFilter(preserveParams, url, target) { url += "/" + key + ":" + passedArgsArray[key]; } } + if (target !== undefined) { $.ajax({ beforeSend: function () { From 31563db2faab41a6be6af89c064d144f5367b83f Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 13:53:05 +0100 Subject: [PATCH 050/385] chg: [UI] Nicer title when creating event report --- app/View/EventReports/add.ctp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/View/EventReports/add.ctp b/app/View/EventReports/add.ctp index 76db1d91c..c79eb81aa 100644 --- a/app/View/EventReports/add.ctp +++ b/app/View/EventReports/add.ctp @@ -3,7 +3,7 @@ echo $this->element('genericElements/Form/genericForm', array( 'form' => $this->Form, 'data' => array( - 'title' => $action == 'add' ? __('Add Event Report (event %s)', h($event_id)) : __('Edit Event Report %s (event %s)', h($id), h($event_id)), + 'title' => $action == 'add' ? __('Add Event Report for Event #%s', h($event_id)) : __('Edit Event Report %s (event #%s)', h($id), h($event_id)), 'model' => 'EventReport', 'fields' => array( array( @@ -41,7 +41,6 @@ ) )); ?> - element('/genericElements/SideMenu/side_menu', array('menuList' => 'eventReports', 'menuItem' => $this->request->params['action'])); @@ -49,7 +48,7 @@ ?> diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index dcbc435b1..1e57653fc 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -2206,6 +2206,12 @@ function runIndexFilter(element) { window.location.href = url; } +function cancelSearch() +{ + $('#quickFilterField').val(''); + runIndexQuickFilter(); +} + function runIndexQuickFilter(preserveParams, url, target) { if (typeof passedArgsArray === "undefined") { var passedArgsArray = []; From 840bdbb956323ba1a8834bfad52c5e42cedefadc Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 14:26:38 +0100 Subject: [PATCH 055/385] fix: [UI] Show correct menu for Contact Reporter page --- app/Controller/AttributesController.php | 4 +-- app/Controller/EventsController.php | 26 +++++++++---------- app/Controller/ObjectsController.php | 4 +-- .../genericElements/SideMenu/side_menu.ctp | 9 +++---- app/View/Events/contact.ctp | 9 +------ 5 files changed, 21 insertions(+), 31 deletions(-) diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index f90a51243..35806586d 100644 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -115,7 +115,7 @@ class AttributesController extends AppController if (!$this->userRole['perm_add']) { throw new MethodNotAllowedException(__('You do not have permissions to create attributes')); } - $event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId); + $event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]); if (!$event) { throw new NotFoundException(__('Invalid event')); } @@ -355,7 +355,7 @@ class AttributesController extends AppController public function add_attachment($eventId = null) { - $event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId); + $event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]); if (empty($event)) { throw new NotFoundException(__('Invalid Event.')); } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 5acda8441..b8fb39b89 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -2179,7 +2179,7 @@ class EventsController extends AppController } } } - $target_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $target_id); + $target_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $target_id, ['contain' => ['Orgc']); if (empty($target_event)) { throw new NotFoundException(__('Invalid target event.')); } @@ -2271,7 +2271,7 @@ class EventsController extends AppController if ($this->request->is('get') && $this->_isRest()) { return $this->RestResponse->describe('Events', 'edit', false, $this->response->type()); } - $event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id); + $event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id, ['contain' => ['Orgc']]); if (!$event) { throw new NotFoundException(__('Invalid event')); } @@ -2735,8 +2735,8 @@ class EventsController extends AppController // Users with a GnuPG key will get the mail encrypted, other users will get the mail unencrypted public function contact($id = null) { - $events = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id)); - if (empty($events)) { + $event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id, ['contain' => ['Orgc']]); + if (empty($event)) { throw new NotFoundException(__('Invalid event')); } // User has filled in his contact form, send out the email. @@ -2751,7 +2751,7 @@ class EventsController extends AppController throw new MethodNotAllowedException($error); } else { $this->Flash->error($error); - $this->redirect(array('action' => 'contact', $id)); + $this->redirect(array('action' => 'contact', $event['Event']['id'])); } } @@ -2762,31 +2762,29 @@ class EventsController extends AppController $user = $this->Auth->user(); $user = $this->Event->User->fillKeysToUser($user); - $success = $this->Event->sendContactEmailRouter($id, $message, $creator_only, $user); + $success = $this->Event->sendContactEmailRouter($event['Event']['id'], $message, $creator_only, $user); if ($success) { $return_message = __('Email sent to the reporter.'); if ($this->_isRest()) { - return $this->RestResponse->saveSuccessResponse('Events', 'contact', $id, $this->response->type(), $return_message); + return $this->RestResponse->saveSuccessResponse('Events', 'contact', $event['Event']['id'], $this->response->type(), $return_message); } else { $this->Flash->success($return_message); // redirect to the view event page - $this->redirect(array('action' => 'view', $id)); + $this->redirect(array('action' => 'view', $event['Event']['id'])); } } else { $return_message = __('Sending of email failed.'); if ($this->_isRest()) { - return $this->RestResponse->saveFailResponse('Events', 'contact', $id, $return_message, $this->response->type()); + return $this->RestResponse->saveFailResponse('Events', 'contact', $event['Event']['id'], $return_message, $this->response->type()); } else { $this->Flash->error($return_message, 'default', array(), 'error'); // redirect to the view event page - $this->redirect(array('action' => 'view', $id)); + $this->redirect(array('action' => 'view', $event['Event']['id'])); } } } - // User didn't see the contact form yet. Present it to him. - if (empty($this->data)) { - $this->data = $events[0]; - } + $this->set('event', $event); + $this->set('mayModify', $this->__canModifyEvent($event)); } public function automation($legacy = false) diff --git a/app/Controller/ObjectsController.php b/app/Controller/ObjectsController.php index 1a22c6caf..8cc806065 100644 --- a/app/Controller/ObjectsController.php +++ b/app/Controller/ObjectsController.php @@ -39,7 +39,7 @@ class ObjectsController extends AppController 'ObjectTemplateElement' ) )); - $event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $event_id); + $event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $event_id, ['contain' => ['Orgc']]); if (empty($event)) { throw new NotFoundException(__('Invalid event.')); } @@ -169,7 +169,7 @@ class ObjectsController extends AppController } } // Find the event that is to be updated - $event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $eventId); + $event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]); if (empty($event)) { throw new NotFoundException(__('Invalid event.')); } diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index a6ce7435f..995a9cfca 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -62,21 +62,20 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); $mayModify = true; if ($isAclPublish) $mayPublish = true; } - if (($menuItem === 'template_populate_results')) { + + if ($menuItem === 'template_populate_results') { echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'template_populate_results', 'url' => $baseurl . '/templates/index', 'text' => __('Populate From Template') )); - } - if ($menuItem === 'enrichmentResults') { + } else if ($menuItem === 'enrichmentResults') { echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'enrichmentResults', 'text' => __('Enrichment Module Result') )); echo $divider; - } - if ($menuItem === 'freetextResults') { + } else if ($menuItem === 'freetextResults') { echo $this->element('/genericElements/SideMenu/side_menu_link', array( 'element_id' => 'freetextResults', 'text' => __('Freetext Import Result') diff --git a/app/View/Events/contact.ctp b/app/View/Events/contact.ctp index c5ca92dca..cc9ab1e52 100644 --- a/app/View/Events/contact.ctp +++ b/app/View/Events/contact.ctp @@ -1,7 +1,3 @@ -request->data['Event']['user_id'] == $me['id']) || ($isAclModifyOrg && $this->request->data['Event']['orgc_id'] == $me['org_id'])); -$mayPublish = ($isAclPublish && $this->request->data['Event']['orgc_id'] == $me['org_id']); -?>
    Form->create('Event');?>
    @@ -32,7 +28,4 @@ $mayPublish = ($isAclPublish && $this->request->data['Event']['orgc_id'] == $me[
    -data; - echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event', 'menuItem' => 'contact', 'event' => $event)); -?> +element('/genericElements/SideMenu/side_menu', array('menuList' => 'event', 'menuItem' => 'contact', 'event' => $event)); From eb42490b9f359aa00e262512f03604e7517d3c00 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 18:54:27 +0100 Subject: [PATCH 056/385] fix: [UI] For Taxonomies show actions just when user can permission to use them --- app/View/Elements/genericElements/SideMenu/side_menu.ctp | 4 ++-- app/View/Taxonomies/index.ctp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index 995a9cfca..59163dea4 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -1091,13 +1091,13 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); 'element_id' => 'delete', 'onClick' => array( 'function' => 'deleteObject', - 'params' => array('taxonomies', 'delete', h($id), h($id)) + 'params' => array('taxonomies', 'delete', h($id)) ), 'text' => __('Delete Taxonomy') )); } } - if ($isSiteAdmin) { + if ($canAccess('taxonomies', 'update')) { echo $this->element('/genericElements/SideMenu/side_menu_post_link', array( 'event_id' => 'update', 'url' => $baseurl . '/taxonomies/update', diff --git a/app/View/Taxonomies/index.ctp b/app/View/Taxonomies/index.ctp index 9ca96566d..f260eb0a6 100644 --- a/app/View/Taxonomies/index.ctp +++ b/app/View/Taxonomies/index.ctp @@ -18,7 +18,7 @@ - + @@ -34,7 +34,7 @@ foreach ($taxonomies as $item): ?> - + From de1ce7f6c01bf123f69123f163a327f61778158f Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 5 Dec 2020 18:58:43 +0100 Subject: [PATCH 057/385] fix: [UI] For tags show actions just when user can permission to use them --- app/View/Tags/index.ctp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/View/Tags/index.ctp b/app/View/Tags/index.ctp index 723f21e62..2c35fca6b 100644 --- a/app/View/Tags/index.ctp +++ b/app/View/Tags/index.ctp @@ -31,7 +31,7 @@ ], 'fields' => [ [ - 'name' => __('Id'), + 'name' => __('ID'), 'sort' => 'Tag.id', 'class' => 'short', 'data_path' => 'Tag.id', @@ -132,7 +132,8 @@ 'Tag.id' ], 'icon' => 'edit', - 'title' => __('Edit') + 'title' => __('Edit'), + 'requirement' => $isSiteAdmin, ], [ 'url' => $baseurl . '/tags/delete', @@ -142,7 +143,8 @@ 'postLink' => '', 'postLinkConfirm' => __('Are you sure you want to delete the Tag?'), 'icon' => 'trash', - 'title' => __('View graph') + 'title' => __('Delete tag'), + 'requirement' => $isSiteAdmin, ], ] ] @@ -163,7 +165,7 @@ ?> diff --git a/app/View/AuthKeys/view.ctp b/app/View/AuthKeys/view.ctp index 17aa4263a..b2ccacc4e 100644 --- a/app/View/AuthKeys/view.ctp +++ b/app/View/AuthKeys/view.ctp @@ -27,20 +27,10 @@ echo $this->element( 'path' => 'AuthKey.uuid', ], [ - 'key' => __('Auth key'), + 'key' => __('Auth Key'), 'path' => 'AuthKey', 'type' => 'authkey' ], - [ - 'key' => __('Created'), - 'path' => 'AuthKey.created', - 'type' => 'datetime' - ], - [ - 'key' => __('Expiration'), - 'path' => 'AuthKey.expiration', - 'type' => 'expiration' - ], [ 'key' => __('User'), 'path' => 'User.id', @@ -52,6 +42,16 @@ echo $this->element( 'key' => __('Comment'), 'path' => 'AuthKey.comment' ], + [ + 'key' => __('Created'), + 'path' => 'AuthKey.created', + 'type' => 'datetime' + ], + [ + 'key' => __('Expiration'), + 'path' => 'AuthKey.expiration', + 'type' => 'expiration' + ], [ 'key' => __('Key usage'), 'type' => 'sparkline', diff --git a/app/View/genericTemplates/delete.ctp b/app/View/genericTemplates/delete.ctp index 332d6a39f..c33404aa8 100644 --- a/app/View/genericTemplates/delete.ctp +++ b/app/View/genericTemplates/delete.ctp @@ -1,12 +1,15 @@ +params['controller']))); +?> - + @@ -102,7 +99,6 @@ foreach($tabs as $tabName => $column): empty($local) ? '0' : '1' ); - echo $this->Form->create('Galaxy', array('url' => $url, 'style' => 'margin:0px;')); echo $this->Form->input('target_ids', array('type' => 'text')); echo $this->Form->end(); From 1255f073a640b06aee2a3333f5e27471145db445 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Fri, 18 Dec 2020 16:48:52 +0100 Subject: [PATCH 135/385] fix: [UI] Empty field for galaxy 'Forked From' and 'Forked By' --- app/View/GalaxyClusters/view.ctp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/View/GalaxyClusters/view.ctp b/app/View/GalaxyClusters/view.ctp index 1ef8e02ae..0f1a9c066 100755 --- a/app/View/GalaxyClusters/view.ctp +++ b/app/View/GalaxyClusters/view.ctp @@ -2,7 +2,6 @@ echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'galaxies', 'menuItem' => 'view_cluster')); $extendedFromHtml = ''; - $extendFromLinks = array(); if (!empty($cluster['GalaxyCluster']['extended_from'])) { $element = $this->element('genericElements/IndexTable/Fields/links', array( 'url' => $baseurl . '/galaxy_clusters/view/', @@ -12,9 +11,8 @@ 'title' => sprintf(__('%s (version: %s)'), $cluster['GalaxyCluster']['extended_from']['GalaxyCluster']['value'], $cluster['GalaxyCluster']['extends_version']) ), )); - $extendFromLinks[] = sprintf('
  • %s
  • ', $element); + $extendedFromHtml = sprintf('
    • %s
    ', $element); } - $extendedFromHtml = sprintf('
      %s
    ', implode('', $extendFromLinks)); if ($newVersionAvailable) { $extendedFromHtml .= sprintf('
    %s
    ', sprintf(__('New version available! Update cluster to version %s'), '/galaxy_clusters/updateCluster/' . $cluster['GalaxyCluster']['id'], @@ -24,7 +22,7 @@ $extendedByHtml = ''; $extendByLinks = array(); - foreach($cluster['GalaxyCluster']['extended_by'] as $extendCluster) { + foreach ($cluster['GalaxyCluster']['extended_by'] as $extendCluster) { $element = $this->element('genericElements/IndexTable/Fields/links', array( 'url' => '/galaxy_clusters/view/', 'row' => $extendCluster, @@ -35,7 +33,10 @@ )); $extendByLinks[] = sprintf('
  • %s
  • ', $element); } - $extendedByHtml = sprintf('
      %s
    ', implode('', $extendByLinks)); + if (!empty($extendByLinks)) { + $extendedByHtml = sprintf('
      %s
    ', implode('', $extendByLinks)); + } + $table_data = array(); $table_data[] = array('key' => __('Cluster ID'), 'value' => $cluster['GalaxyCluster']['id']); $table_data[] = array('key' => __('Name'), 'value' => $cluster['GalaxyCluster']['value']); @@ -71,7 +72,7 @@ sprintf('%s/events/index/searchtag:%s', $baseurl, h($cluster['GalaxyCluster']['tag_id'])), __n('%s event', '%s events', $cluster['GalaxyCluster']['tag_count'], h($cluster['GalaxyCluster']['tag_count'])) ): - '0' + '0' ); if (!empty($extendedFromHtml)) { $table_data[] = array('key' => __('Forked From'), 'html' => $extendedFromHtml); @@ -80,7 +81,6 @@ $table_data[] = array('key' => __('Forked By'), 'html' => $extendedByHtml); } ?> -
    @@ -102,13 +102,13 @@
    0): ?>
    Paginator->sort('id');?>Paginator->sort('id', __('ID'));?> Paginator->sort('namespace');?> Paginator->sort('description');?> Paginator->sort('version');?>'">  '">  '">Yes' : 'No'; ?>  id="TaxonomyRequired"> id="TaxonomyRequired"> / Form->postLink(__('enable all'), array('action' => 'addTag', h($item['Taxonomy']['id'])), array('title' => __('Enable all tags')), (__('Are you sure you want to enable every tag associated to this taxonomy?'))) . ')'; ?>
    diff --git a/app/View/Taxonomies/index.ctp b/app/View/Taxonomies/index.ctp index f260eb0a6..0851fb19f 100644 --- a/app/View/Taxonomies/index.ctp +++ b/app/View/Taxonomies/index.ctp @@ -1,83 +1,148 @@
    -

    - - - - - - - - - - - - - - - - - - - - - - -
    Paginator->sort('id', __('ID'));?>Paginator->sort('namespace');?>Paginator->sort('description');?>Paginator->sort('version');?>Paginator->sort('enabled');?>Paginator->sort('required', __('Required'));?>
    '"> '"> '"> '"> '">Yes' : 'No'; ?>  id="TaxonomyRequired"> / Form->postLink(__('enable all'), array('action' => 'addTag', h($item['Taxonomy']['id'])), array('title' => __('Enable all tags')), (__('Are you sure you want to enable every tag associated to this taxonomy?'))) . ')'; ?>
    -

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

    - -
    -element('/genericElements/SideMenu/side_menu', array('menuList' => 'taxonomies', 'menuItem' => 'index')); + return $content; + } + ), + ), + 'actions' => array( + array( + 'title' => __('Enable'), + 'icon' => 'play', + 'postLink' => true, + 'url' => $baseurl . '/taxonomies/enable', + 'url_params_data_paths' => ['Taxonomy.id'], + 'postLinkConfirm' => __('Are you sure you want to enable this taxonomy library?'), + 'complex_requirement' => array( + 'function' => function ($row, $options) use ($isSiteAdmin) { + return $isSiteAdmin && !$options['datapath']['enabled']; + }, + 'options' => array( + 'datapath' => array( + 'enabled' => 'Taxonomy.enabled' + ) + ) + ), + ), + array( + 'title' => __('Disable'), + 'icon' => 'stop', + 'postLink' => true, + 'url' => $baseurl . '/taxonomies/disable', + 'url_params_data_paths' => ['Taxonomy.id'], + 'postLinkConfirm' => __('Are you sure you want to disable this taxonomy library?'), + 'complex_requirement' => array( + 'function' => function ($row, $options) use ($isSiteAdmin) { + return $isSiteAdmin && $options['datapath']['enabled']; + }, + 'options' => array( + 'datapath' => array( + 'enabled' => 'Taxonomy.enabled' + ) + ) + ), + ), + array( + 'onclick' => "deleteObject('taxonomies', 'delete', '[onclick_params_data_path]', '[onclick_params_data_path]');", + 'onclick_params_data_path' => 'Taxonomy.id', + 'icon' => 'trash', + 'title' => __('Delete taxonomy'), + 'requirement' => $isSiteAdmin, + ), + array( + 'url' => $baseurl . '/taxonomies/view', + 'url_params_data_paths' => array( + 'Taxonomy.id' + ), + 'icon' => 'eye', + 'title' => __('View taxonomy'), + 'dbclickAction' => true, + ) + ) +) +]); ?> - + +element('/genericElements/SideMenu/side_menu', array('menuList' => 'taxonomies', 'menuItem' => 'index')); diff --git a/app/View/Warninglists/index.ctp b/app/View/Warninglists/index.ctp index 769b1ae5e..9f5946389 100644 --- a/app/View/Warninglists/index.ctp +++ b/app/View/Warninglists/index.ctp @@ -91,7 +91,7 @@ ), ), array( - 'title' => __('Disabled'), + 'title' => __('Disable'), 'icon' => 'stop', 'onclick' => sprintf('toggleSetting(%s, \'%s\', \'%s\')', 'event', 'warninglist_enable', '[onclick_params_data_path]'), 'onclick_params_data_path' => 'Warninglist.id', diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 32563d3a0..c74191c49 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -5147,27 +5147,6 @@ function submit_feed_overlap_tool(feedId) { }); } -function changeTaxonomyRequiredState(checkbox) { - var checkbox_state = $(checkbox).is(":checked"); - var taxonomy_id = $(checkbox).data('taxonomy-id'); - fetchFormDataAjax(baseurl + '/taxonomies/toggleRequired/' + taxonomy_id, function(formData) { - $.ajax({ - data: $(formData).serialize(), - success:function (data, textStatus) { - handleGenericAjaxResponse({'saved':true, 'success':['Taxonomy\'s required state toggled.']}); - }, - error:function() { - $(checkbox).prop('checked', !$(checkbox).prop('checked')); - handleGenericAjaxResponse({'saved':false, 'errors':['Could not toggle the required state of the taxonomy.']}); - }, - async:"false", - type:"post", - cache: false, - url: baseurl + '/taxonomies/toggleRequired/' + taxonomy_id, - }); - }); -} - function fetchFormDataAjax(url, callback, errorCallback) { var formData = false; $.ajax({ From c6c34564ca94db5420abf4039fc54a01476204a6 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Mon, 21 Dec 2020 13:17:06 +0900 Subject: [PATCH 148/385] fix: [installer] Typo --- docs/generic/misp-modules-debian.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generic/misp-modules-debian.md b/docs/generic/misp-modules-debian.md index 18070e9d1..4b0f27b62 100644 --- a/docs/generic/misp-modules-debian.md +++ b/docs/generic/misp-modules-debian.md @@ -24,7 +24,7 @@ mispmodules () { cd build $SUDO_CMD cmake .. && $SUDO_CMD make sudo make install - cd /usr/loca/src/faup + cd /usr/local/src/faup # Install faup $SUDO_CMD mkdir -p build cd build From 62571cc27fdc3511b909dfed7e43d65dbd7d6061 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Mon, 21 Dec 2020 13:24:02 +0900 Subject: [PATCH 149/385] chg: [installer] Latest update --- INSTALL/INSTALL.sh | 2 +- INSTALL/INSTALL.sh.sfv | 6 +++--- INSTALL/INSTALL.sh.sha1 | 2 +- INSTALL/INSTALL.sh.sha256 | 2 +- INSTALL/INSTALL.sh.sha384 | 2 +- INSTALL/INSTALL.sh.sha512 | 2 +- docs/Changelog.md | 23 +++++++++++++++++++++++ 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/INSTALL/INSTALL.sh b/INSTALL/INSTALL.sh index d57d14f8a..58a893280 100755 --- a/INSTALL/INSTALL.sh +++ b/INSTALL/INSTALL.sh @@ -1749,7 +1749,7 @@ mispmodules () { cd build $SUDO_CMD cmake .. && $SUDO_CMD make sudo make install - cd /usr/loca/src/faup + cd /usr/local/src/faup # Install faup $SUDO_CMD mkdir -p build cd build diff --git a/INSTALL/INSTALL.sh.sfv b/INSTALL/INSTALL.sh.sfv index fb2cc262a..4538402ad 100644 --- a/INSTALL/INSTALL.sh.sfv +++ b/INSTALL/INSTALL.sh.sfv @@ -1,5 +1,5 @@ -; Generated by RHash v1.3.9 on 2020-12-17 at 18:19.38 +; Generated by RHash v1.3.9 on 2020-12-21 at 13:17.32 ; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/ ; -; 136949 18:19.37 2020-12-17 INSTALL.sh -INSTALL.sh 820CE340D40A593709B8E7F59A308ABD73096375 5CC0A05CD42CC364C6C015C8699739A3FEDA920F0865A96C25BF4D0C3C3D270A F156D957B70BA034DD50997067E632A05C0B31C2078EE89E8ABC11E85944F30C323BF405B90F06A51E467F7B7E75A9C1 56FA1930541A6DAE16A03CF2AADE3CC8BFCF4C48E35CB229BF04393389A5A64E2A4013036B235C1812885FB563B15BFACB1DB02C8DAEDACE73F18E390281C557 +; 136950 13:17.32 2020-12-21 INSTALL.sh +INSTALL.sh 8E401EC63F5A98AC610B56C831A949A9B494D9E8 94F6F2F0B8333274CFDC7443DC5275F71966CF4BC50B93A852F2FDCC173C4457 7A860CA5535CEBF450D71F141185E5A12767BE9A475F1E78276C442FE73C5A37F75927AF6F85FA5584FC641AC2259DE2 5A24D0523F2994DD2A5D2C4B831D1CFD66F9CD44DB7168709AEADC47CBF6ED4B9D3485639713AE97C23C3AAE4408C4BD4D39E03B2AEDABCF3B574C30ECF14CD6 diff --git a/INSTALL/INSTALL.sh.sha1 b/INSTALL/INSTALL.sh.sha1 index 80ce6b685..674448b1a 100644 --- a/INSTALL/INSTALL.sh.sha1 +++ b/INSTALL/INSTALL.sh.sha1 @@ -1 +1 @@ -820ce340d40a593709b8e7f59a308abd73096375 INSTALL.sh +8e401ec63f5a98ac610b56c831a949a9b494d9e8 INSTALL.sh diff --git a/INSTALL/INSTALL.sh.sha256 b/INSTALL/INSTALL.sh.sha256 index 6d2705a05..8ec3d3569 100644 --- a/INSTALL/INSTALL.sh.sha256 +++ b/INSTALL/INSTALL.sh.sha256 @@ -1 +1 @@ -5cc0a05cd42cc364c6c015c8699739a3feda920f0865a96c25bf4d0c3c3d270a INSTALL.sh +94f6f2f0b8333274cfdc7443dc5275f71966cf4bc50b93a852f2fdcc173c4457 INSTALL.sh diff --git a/INSTALL/INSTALL.sh.sha384 b/INSTALL/INSTALL.sh.sha384 index 694d62569..106aca84c 100644 --- a/INSTALL/INSTALL.sh.sha384 +++ b/INSTALL/INSTALL.sh.sha384 @@ -1 +1 @@ -f156d957b70ba034dd50997067e632a05c0b31c2078ee89e8abc11e85944f30c323bf405b90f06a51e467f7b7e75a9c1 INSTALL.sh +7a860ca5535cebf450d71f141185e5a12767be9a475f1e78276c442fe73c5a37f75927af6f85fa5584fc641ac2259de2 INSTALL.sh diff --git a/INSTALL/INSTALL.sh.sha512 b/INSTALL/INSTALL.sh.sha512 index d957655f8..8691c679d 100644 --- a/INSTALL/INSTALL.sh.sha512 +++ b/INSTALL/INSTALL.sh.sha512 @@ -1 +1 @@ -56fa1930541a6dae16a03cf2aade3cc8bfcf4c48e35cb229bf04393389a5a64e2a4013036b235c1812885fb563b15bfacb1db02c8daedace73f18e390281c557 INSTALL.sh +5a24d0523f2994dd2a5d2c4b831d1cfd66f9cd44db7168709aeadc47cbf6ed4b9d3485639713ae97c23c3aae4408c4bd4d39e03b2aedabcf3b574c30ecf14cd6 INSTALL.sh diff --git a/docs/Changelog.md b/docs/Changelog.md index 00a3e867d..88d560b13 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -4,8 +4,17 @@ Changelog v2.4 aka 2.4 for ever (current changelog) ------------------------ +New +--- +- [Statistics shell] Added new statistics shell. [iglocska] + + - (R)etrieval (o)f (m)etrics (m)atrix (e)xtended (f)or (s)tatistics + + - run it via /var/www/MISP/app/Console/cake Statistics rommelfs + Changes ------- +- [misp-objects] updated to the latest version. [Alexandre Dulaunoy] - [installer] update to latest. [Steve Clement] - [fix] typo. [Steve Clement] - [doc] OpenBSD 6.8 update. [Steve Clement] @@ -14,11 +23,25 @@ Changes Fix --- +- [installer] Typo. [Steve Clement] +- [statistics] Local org flag fixed to show the correct count. + [iglocska] +- [mistake in a comment fixed] [iglocska] +- [internal] sharing_group graph missing org_ids - throwing notices. + [iglocska] +- [internal] further promises removed from the galaxy model. [iglocska] + + - easier than getting people to stop using EOL software +- [installer] type in php-bcmath package. [Steve Clement] - [installer] forgot to add sfv. [Steve Clement] +- [internal] removed function promises in crud component. [iglocska] + + - to appease EOL php versions... - [delegation] invalid user call. [iglocska] Other ----- +- Merge branch '2.4' of github.com:MISP/MISP into 2.4. [iglocska] - Merge pull request #6767 from SteveClement/guides. [Steve Clement] - Merge pull request #6764 from Natsec/patch-1. [Andras Iklody] From fb7c98ebcf24bfa52e1b7179b6a86764e5b5034a Mon Sep 17 00:00:00 2001 From: "Fafner [_KeyZee_]" Date: Mon, 21 Dec 2020 14:39:25 +0100 Subject: [PATCH 150/385] Add the possibility to have a '-' in the baseurl With the actual regex in testBaseURL, we can not have a '-' inside the BaseURL, I did a quick fix --- app/Model/Server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/Server.php b/app/Model/Server.php index 909894228..6848251d9 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -3927,7 +3927,7 @@ class Server extends AppModel if ($this->testForEmpty($value) !== true) { return $this->testForEmpty($value); } - $regex = "/^(?https?):\/\/(?([\w,\.]+))(?::(?[0-9]+))?(?\/[a-z0-9_\-\.]+)?$/i"; + $regex = "/^(?https?):\/\/(?([\w,\-,\.]+))(?::(?[0-9]+))?(?\/[a-z0-9_\-\.]+)?$/i"; if ( !preg_match($regex, $value, $matches) || strtolower($matches['proto']) != strtolower($this->getProto()) || From 7cfcd3acdf886861d26a8523031c31d3576343e5 Mon Sep 17 00:00:00 2001 From: StefanKelm Date: Mon, 21 Dec 2020 17:34:54 +0100 Subject: [PATCH 151/385] Update importReportFromUrl.ctp --- app/View/EventReports/ajax/importReportFromUrl.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/EventReports/ajax/importReportFromUrl.ctp b/app/View/EventReports/ajax/importReportFromUrl.ctp index 79df0f799..7c6cb4fc6 100644 --- a/app/View/EventReports/ajax/importReportFromUrl.ctp +++ b/app/View/EventReports/ajax/importReportFromUrl.ctp @@ -10,7 +10,7 @@ 'field' => 'url', 'class' => 'input span6', 'div' => 'text', - 'label' => sprintf('%s: ', __('URL')) . __('Content for this URL will be downloaded and converted to Mardown') + 'label' => sprintf('%s: ', __('URL')) . __('Content for this URL will be downloaded and converted to Markdown') ), ), 'submit' => array( @@ -18,4 +18,4 @@ 'ajaxSubmit' => sprintf('submitPopoverForm(\'%s\', \'addEventReport\', 0, 1)', h($event_id)) ) ) - )); \ No newline at end of file + )); From adb37f6980d68f04aa714e79365f6fb10becbda6 Mon Sep 17 00:00:00 2001 From: StefanKelm Date: Mon, 21 Dec 2020 17:35:20 +0100 Subject: [PATCH 152/385] Update indexForEvent.ctp --- app/View/EventReports/ajax/indexForEvent.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/View/EventReports/ajax/indexForEvent.ctp b/app/View/EventReports/ajax/indexForEvent.ctp index 34a8cb77f..a224b4413 100644 --- a/app/View/EventReports/ajax/indexForEvent.ctp +++ b/app/View/EventReports/ajax/indexForEvent.ctp @@ -27,7 +27,7 @@ 'onClickParams' => [$baseurl . '/eventReports/importReportFromUrl/' . h($event_id)], 'active' => true, 'text' => __('Import from URL'), - 'title' => __('Content for this URL will be downloaded and converted to Mardown'), + 'title' => __('Content for this URL will be downloaded and converted to Markdown'), 'fa-icon' => 'link', 'requirement' => $canModify && $importModuleEnabled, ), From b770e46efbf17900ced3cd3a3393a15f0b8fe2e2 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 18:38:59 +0100 Subject: [PATCH 153/385] fix: [sighting] Order must contain group for some mysql servers --- app/Model/Sighting.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php index 67afc3cf9..bbda35428 100644 --- a/app/Model/Sighting.php +++ b/app/Model/Sighting.php @@ -332,7 +332,7 @@ class Sighting extends AppModel 'fields' => ['org_id', 'attribute_id', 'type', 'date', 'last_timestamp', 'sighting_count'], 'recursive' => -1, 'group' => ['org_id', 'attribute_id', 'type', 'date'], - 'order' => ['date_sighting'], // from oldest + 'order' => ['date'], // from oldest )); unset( $this->virtualFields['date'], @@ -369,7 +369,7 @@ class Sighting extends AppModel 'conditions' => $conditions, 'fields' => [ucfirst($context) . 'Tag.tag_id', 'date', 'sighting_count'], 'group' => [ucfirst($context) . 'Tag.id', 'date'], - 'order' => ['date_sighting'], // from oldest + 'order' => ['date'], // from oldest ]); unset($this->virtualFields['date'], $this->virtualFields['sighting_count']); return $sightings; From 0e31996be357fac0f12419c1b39eca7ccd98d855 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 18:54:26 +0100 Subject: [PATCH 154/385] chg: [sighting] Faster and memory efficient rest search --- app/Model/Sighting.php | 109 +++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 64 deletions(-) diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php index 67afc3cf9..778e37d0a 100644 --- a/app/Model/Sighting.php +++ b/app/Model/Sighting.php @@ -140,7 +140,13 @@ class Sighting extends AppModel return $this->save($sighting); } - public function getSighting($id, $user) + /** + * @param int $id + * @param array $user + * @param bool $withEvent + * @return array + */ + public function getSighting($id, array $user, $withEvent = true) { $sighting = $this->find('first', array( 'recursive' => -1, @@ -149,10 +155,7 @@ class Sighting extends AppModel 'fields' => array('Attribute.value', 'Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.to_ids') ), 'Event' => array( - 'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'), - 'Orgc' => array( - 'fields' => array('Orgc.name') - ) + 'fields' => $withEvent ? ['Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'] : ['Event.org_id'], ) ), 'conditions' => array('Sighting.id' => $id) @@ -161,11 +164,7 @@ class Sighting extends AppModel return array(); } - if (!isset($event)) { - $event = array('Event' => $sighting['Event']); - } - - $ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id']; + $ownEvent = $user['Role']['perm_site_admin'] || $sighting['Event']['org_id'] == $user['org_id']; if (!$ownEvent) { $sightingPolicy = $this->sightingsPolicy(); // if sighting policy == 0 then return false if the sighting doesn't belong to the user @@ -181,26 +180,24 @@ class Sighting extends AppModel } } } + + // Put event organisation name from cache + if ($withEvent) { + $sighting['Event']['Orgc']['name'] = $this->getOrganisationById($sighting['Event']['orgc_id'])['name']; + } + $anonymise = Configure::read('Plugin.Sightings_anonymise'); - if ($anonymise) { - if ($sighting['Sighting']['org_id'] != $user['org_id']) { - unset($sighting['Sighting']['org_id']); - unset($sighting['Organisation']); - } + if ($anonymise && $sighting['Sighting']['org_id'] != $user['org_id']) { + unset($sighting['Sighting']['org_id']); } // rearrange it to match the event format of fetchevent - if (isset($sighting['Organisation'])) { - $sighting['Sighting']['Organisation'] = $sighting['Organisation']; - unset($sighting['Organisation']); - } $result = array( 'Sighting' => $sighting['Sighting'] ); - $result['Sighting']['Event'] = $sighting['Event']; - $result['Sighting']['Attribute'] = $sighting['Attribute']; - if (!empty($sighting['Organisation'])) { - $result['Sighting']['Organisation'] = $sighting['Organisation']; + if ($withEvent) { + $result['Sighting']['Event'] = $sighting['Event']; } + $result['Sighting']['Attribute'] = $sighting['Attribute']; return $result; } @@ -659,6 +656,10 @@ class Sighting extends AppModel return $result; } + /** + * @return bool + * @deprecated + */ public function addUuids() { $sightings = $this->find('all', array( @@ -864,48 +865,27 @@ class Sighting extends AppModel } // fetch sightings matching the query - $sightings = $this->find('list', array( + $sightingIds = $this->find('list', array( 'recursive' => -1, 'conditions' => $conditions, 'fields' => array('id'), 'contain' => $contain, )); - $filters['requested_attributes'] = array('id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type'); - - // apply ACL and sighting policies - $allowedSightings = array(); - $additional_attribute_added = false; - $additional_event_added = false; - foreach ($sightings as $sid) { - $sight = $this->getSighting($sid, $user); - if (!empty($sight)) { - $sight['Sighting']['value'] = $sight['Sighting']['Attribute']['value']; - // by default, do not include event and attribute - if (!isset($filters['includeAttribute']) || !$filters['includeAttribute']) { - unset($sight["Sighting"]["Attribute"]); - } else if (!$additional_attribute_added) { - $filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value')); - $additional_attribute_added = true; - } - - if (!isset($filters['includeEvent']) || !$filters['includeEvent']) { - unset($sight["Sighting"]["Event"]); - } else if (!$additional_event_added) { - $filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name')); - $additional_event_added = true; - } - $allowedSightings[] = $sight; - } + $includeAttribute = isset($filters['includeAttribute']) && $filters['includeAttribute']; + $includeEvent = isset($filters['includeEvent']) && $filters['includeEvent']; + $requestedAttributes = ['id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type']; + if ($includeAttribute) { + $requestedAttributes = array_merge($requestedAttributes, ['attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value']); } - - $params = array( - 'conditions' => array(), //result already filtered - ); + if ($includeEvent) { + $requestedAttributes = array_merge($requestedAttributes, ['event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name']); + } + $filters['requested_attributes'] = $requestedAttributes; $exportToolParams = array( 'user' => $user, - 'params' => $params, + 'params' => ['conditions' => []], //result already filtered 'returnFormat' => $returnFormat, 'scope' => 'Sighting', 'filters' => $filters @@ -913,19 +893,20 @@ class Sighting extends AppModel $tmpfile = new TmpFileTool(); $tmpfile->write($exportTool->header($exportToolParams)); + $separator = $exportTool->separator($exportToolParams); - $temp = ''; - $i = 0; - foreach ($allowedSightings as $sighting) { - $temp .= $exportTool->handler($sighting, $exportToolParams); - if ($temp !== '') { - if ($i != count($allowedSightings) -1) { - $temp .= $exportTool->separator($exportToolParams); + foreach ($sightingIds as $sightingId) { + // apply ACL and sighting policies + $sighting = $this->getSighting($sightingId, $user, $includeEvent); + if (!empty($sighting)) { + $sighting['Sighting']['value'] = $sighting['Sighting']['Attribute']['value']; + if (!$includeAttribute) { + unset($sighting['Sighting']['Attribute']); } + $tmpfile->writeWithSeparator($exportTool->handler($sighting, $exportToolParams), $separator); } - $i++; } - $tmpfile->write($temp); + $tmpfile->write($exportTool->footer($exportToolParams)); return $tmpfile->finish(); } From 1a184ebbb5a5ec3cd185dca82358ad2273728ee3 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 25 Oct 2020 09:44:01 +0100 Subject: [PATCH 155/385] new: [internal] Allow to output directly TmpFileTool --- app/Controller/AppController.php | 2 +- .../Component/RestResponseComponent.php | 10 +- app/Lib/Tools/CakeResponseTmp.php | 40 ++++++++ app/Lib/Tools/ComplexTypeTool.php | 2 +- app/Lib/Tools/TmpFileTool.php | 93 ++++++++++++++++--- app/Model/Attribute.php | 2 +- app/Model/Event.php | 2 +- app/Model/Feed.php | 2 +- app/Model/MispObject.php | 2 +- app/Model/Sighting.php | 2 +- 10 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 app/Lib/Tools/CakeResponseTmp.php diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 27398bc06..05743d68e 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -1324,7 +1324,7 @@ class AppController extends Controller $final = $this->$scope->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView); if (!empty($renderView) && !empty($final)) { $this->layout = false; - $final = json_decode($final, true); + $final = json_decode($final->intoString(), true); foreach ($final as $key => $data) { $this->set($key, $data); } diff --git a/app/Controller/Component/RestResponseComponent.php b/app/Controller/Component/RestResponseComponent.php index 4d0551cd2..4e8418ac8 100644 --- a/app/Controller/Component/RestResponseComponent.php +++ b/app/Controller/Component/RestResponseComponent.php @@ -548,7 +548,15 @@ class RestResponseComponent extends Component } } } - $cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type)); + + App::uses('TmpFileTool', 'Tools'); + if ($response instanceof TmpFileTool) { + App::uses('CakeResponseTmp', 'Tools'); + $cakeResponse = new CakeResponseTmp(['status' => $code, 'type' => $type]); + $cakeResponse->file($response); + } else { + $cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type)); + } if (Configure::read('Security.allow_cors')) { $headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Authorization, Accept"; diff --git a/app/Lib/Tools/CakeResponseTmp.php b/app/Lib/Tools/CakeResponseTmp.php new file mode 100644 index 000000000..12952868b --- /dev/null +++ b/app/Lib/Tools/CakeResponseTmp.php @@ -0,0 +1,40 @@ +header('Content-Length', $path->size()); + $this->_clearBuffer(); + $this->_file = $path; + } else { + parent::file($path, $options); + } + } + + /** + * @param File|TmpFileTool $file + * @param array $range + * @return bool + * @throws Exception + */ + protected function _sendFile($file, $range) + { + if ($file instanceof TmpFileTool) { + set_time_limit(0); + session_write_close(); + + foreach ($file->intoChunks() as $chunk) { + if (!$this->_isActive()) { + $file->close(); + return false; + } + echo $chunk; + $this->_flushBuffer(); + } + return true; + } else { + return parent::_sendFile($file, $range); + } + } +} diff --git a/app/Lib/Tools/ComplexTypeTool.php b/app/Lib/Tools/ComplexTypeTool.php index 8ee39acce..71569b10c 100644 --- a/app/Lib/Tools/ComplexTypeTool.php +++ b/app/Lib/Tools/ComplexTypeTool.php @@ -177,7 +177,7 @@ class ComplexTypeTool unset($input); $iocArray = []; - foreach ($tmpFile->csv($delimiter) as $row) { + foreach ($tmpFile->intoParsedCsv($delimiter) as $row) { if (!empty($row[0][0]) && $row[0][0] === '#') { // Comment continue; } diff --git a/app/Lib/Tools/TmpFileTool.php b/app/Lib/Tools/TmpFileTool.php index 9b85aebf1..ce640a3d1 100644 --- a/app/Lib/Tools/TmpFileTool.php +++ b/app/Lib/Tools/TmpFileTool.php @@ -68,7 +68,7 @@ class TmpFileTool } /** - * Get one line from file parsed as CSV. + * Returns generator of parsed CSV line from file. * * @param string $delimiter * @param string $enclosure @@ -76,7 +76,7 @@ class TmpFileTool * @return Generator * @throws Exception */ - public function csv($delimiter = ',', $enclosure = '"', $escape = "\\") + public function intoParsedCsv($delimiter = ',', $enclosure = '"', $escape = "\\") { $this->rewind(); $line = 0; @@ -88,15 +88,16 @@ class TmpFileTool $line++; yield $result; } - fclose($this->tmpfile); - $this->tmpfile = null; + $this->close(); } /** + * Returns generator of line from file. + * * @return Generator * @throws Exception */ - public function lines() + public function intoLines() { $this->rewind(); while (!feof($this->tmpfile)) { @@ -106,24 +107,64 @@ class TmpFileTool } yield $result; } - fclose($this->tmpfile); - $this->tmpfile = null; + $this->close(); + } + + /** + * @param int $chunkSize In bytes + * @return Generator + * @throws Exception + */ + public function intoChunks($chunkSize = 8192) + { + $this->rewind(); + while (!feof($this->tmpfile)) { + $result = fread($this->tmpfile, $chunkSize); + if ($result === false) { + throw new Exception('Could not read from temporary file.'); + } + yield $result; + } + $this->close(); } /** * @return string * @throws Exception */ - public function finish() + public function intoString() { $this->rewind(); - $final = stream_get_contents($this->tmpfile); - if ($final === false) { + $string = stream_get_contents($this->tmpfile); + if ($string === false) { throw new Exception('Could not read from temporary file.'); } - fclose($this->tmpfile); - $this->tmpfile = null; - return $final; + $this->close(); + return $string; + } + + /** + * Pass data to output. + * + * @throws Exception + */ + public function intoOutput() + { + $this->rewind(); + if (fpassthru($this->tmpfile) === false) { + throw new Exception('Could not pass temporary file to output.'); + } + $this->close(); + } + + /** + * @return int + * @throws Exception + */ + public function size() + { + $this->isOpen(); + return fstat($this->tmpfile)['size']; } /** @@ -132,7 +173,30 @@ class TmpFileTool */ public function __toString() { - return $this->finish(); + return $this->intoString(); + } + + /** + * @return bool + */ + public function close() + { + if ($this->tmpfile) { + $result = fclose($this->tmpfile); + $this->tmpfile = null; + return $result; + } + return true; + } + + /** + * @throws Exception + */ + private function isOpen() + { + if ($this->tmpfile === null) { + throw new Exception('Temporary file is already closed.'); + } } /** @@ -142,6 +206,7 @@ class TmpFileTool */ private function rewind() { + $this->isOpen(); if (fseek($this->tmpfile, 0) === -1) { throw new Exception('Could not seek to start of temporary file.'); } diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index e7f543e24..6f1bf3a34 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -4697,7 +4697,7 @@ class Attribute extends AppModel $elementCounter = $this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams); } $tmpfile->write($exportTool->footer($exportToolParams)); - return $tmpfile->finish(); + return $tmpfile; } /** diff --git a/app/Model/Event.php b/app/Model/Event.php index 0e2293a60..be90ea790 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -6999,7 +6999,7 @@ class Event extends AppModel unset($result); unset($temp); $tmpfile->write($exportTool->footer($exportToolParams)); - return $tmpfile->finish(); + return $tmpfile; } /* diff --git a/app/Model/Feed.php b/app/Model/Feed.php index a24cfdc06..bb98e8e65 100644 --- a/app/Model/Feed.php +++ b/app/Model/Feed.php @@ -180,7 +180,7 @@ class Feed extends AppModel $tmpFile->write(trim($data)); unset($data); - return $tmpFile->csv(); + return $tmpFile->intoParsedCsv(); } /** diff --git a/app/Model/MispObject.php b/app/Model/MispObject.php index daf1790e9..f2fffb498 100644 --- a/app/Model/MispObject.php +++ b/app/Model/MispObject.php @@ -1443,7 +1443,7 @@ class MispObject extends AppModel } $this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams, $elementCounter); $tmpfile->write($exportTool->footer($exportToolParams)); - return $tmpfile->finish(); + return $tmpfile; } private function __iteratedFetch($user, &$params, &$loop, TmpFileTool $tmpfile, $exportTool, $exportToolParams, &$elementCounter = 0) diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php index 9b0e8c18d..33e2f92b7 100644 --- a/app/Model/Sighting.php +++ b/app/Model/Sighting.php @@ -908,7 +908,7 @@ class Sighting extends AppModel } $tmpfile->write($exportTool->footer($exportToolParams)); - return $tmpfile->finish(); + return $tmpfile; } /** From 8403bdee448d1308f16a5da481a2579ce9f7e2bf Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 22:43:27 +0100 Subject: [PATCH 156/385] chg: [UI] Make event report page nicer --- app/View/EventReports/view.ctp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/View/EventReports/view.ctp b/app/View/EventReports/view.ctp index 967506cf8..e2fe49cf8 100644 --- a/app/View/EventReports/view.ctp +++ b/app/View/EventReports/view.ctp @@ -1,7 +1,7 @@ __('ID'), 'value' => $report['EventReport']['id']); - $table_data[] = array('key' => __('UUID'), 'html' => '' . h($report['EventReport']['uuid']) . ''); + $table_data[] = array('key' => __('UUID'), 'value' => $report['EventReport']['uuid'], 'value_class' => 'quickSelect'); $table_data[] = array( 'key' => __('Event'), 'html' => sprintf( @@ -20,7 +20,13 @@ ); $table_data[] = array('key' => __('Last update'), 'value' => date('Y-m-d H:i:s', $report['EventReport']['timestamp'])); - $table_data[] = array('key' => __('Deleted'), 'boolean' => $report['EventReport']['deleted'], 'value_class' => $report['EventReport']['deleted'] ? 'red' : 'green'); + if ($report['EventReport']['deleted']) { + $table_data[] = array( + 'key' => __('Deleted'), + 'boolean' => $report['EventReport']['deleted'], + 'value_class' => 'red', + ); + } ?>
    '> @@ -30,7 +36,6 @@ element('genericElements/viewMetaTable', array('table_data' => $table_data)); ?>
    -

    element('markdownEditor/markdownEditor', [ From df5f65da255fdda5f63bef1cb8431fd455a0f907 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 22:45:29 +0100 Subject: [PATCH 157/385] chg: [UI] Go directly to edit mode after clicking to "Edit report" button --- .../Elements/markdownEditor/markdownEditor.ctp | 4 ++-- .../js/markdownEditor/markdownEditor.js | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/View/Elements/markdownEditor/markdownEditor.ctp b/app/View/Elements/markdownEditor/markdownEditor.ctp index 1e46c3c68..ea898d8dd 100644 --- a/app/View/Elements/markdownEditor/markdownEditor.ctp +++ b/app/View/Elements/markdownEditor/markdownEditor.ctp @@ -98,7 +98,7 @@ - + @@ -221,4 +221,4 @@ if (!empty($additionalMarkdownElements)) { echo $this->element($additionalMarkdownElements['path'], $additionalMarkdownElements['variables']); } -?> \ No newline at end of file +?> diff --git a/app/webroot/js/markdownEditor/markdownEditor.js b/app/webroot/js/markdownEditor/markdownEditor.js index 68d9ee5dc..ddb5bfb9e 100644 --- a/app/webroot/js/markdownEditor/markdownEditor.js +++ b/app/webroot/js/markdownEditor/markdownEditor.js @@ -40,7 +40,15 @@ $(document).ready(function() { initCodeMirror() toggleSaveButton(false) } - setMode(defaultMode) + var mode = defaultMode; + if (window.location.hash) { + // Switch to mode by using #[mode_name] in URL + var anchor = window.location.hash.substr(1); + if ((canEdit && (anchor === 'editor' || anchor === 'splitscreen')) || anchor === 'viewer' || anchor === 'raw') { + mode = anchor; + } + } + setMode(mode) if (canEdit) { setEditorData(originalRaw); @@ -337,20 +345,20 @@ function setMode(mode) { $mardownViewerToolbar.find('button[data-togglemode="' + mode + '"]').addClass('btn-inverse') hideAll() $editorContainer.css('width', ''); - if (mode == 'raw') { + if (mode === 'raw') { $rawContainer.show() } - if (mode == 'splitscreen') { + if (mode === 'splitscreen') { $resizableHandle.show() $splitContainer.addClass('split-actif') } else { $resizableHandle.hide() $splitContainer.removeClass('split-actif') } - if (mode == 'viewer' || mode == 'splitscreen') { + if (mode === 'viewer' || mode === 'splitscreen') { $viewerContainer.show() } - if (mode == 'editor' || mode == 'splitscreen') { + if (mode === 'editor' || mode === 'splitscreen') { $editorContainer.show({ duration: 0, complete: function() { From 402baa46771397d2f4ec6b2a401c32d219202231 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 22:51:04 +0100 Subject: [PATCH 158/385] fix: [UI] Show error message when galaxy info couldn't be loaded --- app/View/Galaxies/view.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Galaxies/view.ctp b/app/View/Galaxies/view.ctp index 0326f8378..194be1375 100644 --- a/app/View/Galaxies/view.ctp +++ b/app/View/Galaxies/view.ctp @@ -30,7 +30,7 @@
    From aa27703d02b56e2781e5c3f92509baa8f8303821 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 23:17:33 +0100 Subject: [PATCH 164/385] fix: [UI] Correctly show contributors in event view --- app/View/Events/view.ctp | 12 +++++------- app/View/Helper/OrgImgHelper.php | 6 ++++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index 6a365033d..7428d7593 100644 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -74,17 +74,15 @@ } } if (!empty($contributors)) { - $contributorsContent = ''; + $contributorsContent = []; foreach ($contributors as $organisationId => $name) { - $contributorsContent .= sprintf( - '%s', - $baseurl . "/logs/event_index/" . $event['Event']['id'] . '/' . h($name), - $this->OrgImg->getOrgImg(array('name' => $name, 'id' => $organisationId, 'size' => 24), true, true) - ); + $org = ['Organisation' => ['id' => $organisationId, 'name' => $name]]; + $link = $baseurl . "/logs/event_index/" . $event['Event']['id'] . '/' . h($name); + $contributorsContent[] = $this->OrgImg->getNameWithImg($org, $link); } $table_data[] = array( 'key' => __('Contributors'), - 'html' => $contributorsContent + 'html' => implode("
    ", $contributorsContent), ); } if (isset($event['User']['email'])) { diff --git a/app/View/Helper/OrgImgHelper.php b/app/View/Helper/OrgImgHelper.php index a201e0a83..6da47d790 100644 --- a/app/View/Helper/OrgImgHelper.php +++ b/app/View/Helper/OrgImgHelper.php @@ -6,7 +6,7 @@ class OrgImgHelper extends AppHelper { const IMG_PATH = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS; - public function getNameWithImg(array $organisation) + public function getNameWithImg(array $organisation, $link = null) { if (!isset($organisation['Organisation'])) { return ''; @@ -14,7 +14,9 @@ class OrgImgHelper extends AppHelper $orgImgName = $this->findOrgImage($organisation['Organisation']); $baseurl = $this->_View->viewVars['baseurl']; - $link = $baseurl . '/organisations/view/' . (empty($organisation['Organisation']['id']) ? h($organisation['Organisation']['name']) : h($organisation['Organisation']['id'])); + if (!$link) { + $link = $baseurl . '/organisations/view/' . (empty($organisation['Organisation']['id']) ? h($organisation['Organisation']['name']) : h($organisation['Organisation']['id'])); + } if ($orgImgName) { $orgImgUrl = $baseurl . '/img/orgs/' . $orgImgName; return sprintf('%s', $link, $orgImgUrl, h($organisation['Organisation']['name'])); From 8e47092b463e4969aed798a589bedfc4aa6918ad Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 23:21:05 +0100 Subject: [PATCH 165/385] fix: [UI] Remove unnecessary padding from form --- app/View/Elements/genericElements/Form/genericForm.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/View/Elements/genericElements/Form/genericForm.ctp b/app/View/Elements/genericElements/Form/genericForm.ctp index bedcea1af..e27614500 100644 --- a/app/View/Elements/genericElements/Form/genericForm.ctp +++ b/app/View/Elements/genericElements/Form/genericForm.ctp @@ -129,7 +129,7 @@ ); } else { echo sprintf( - '
    %s
    %s%s
    %s
    %s
    %s%s%s
    ', + '
    %s
    %s%s
    %s
    %s
    %s%s%s
    ', empty($data['skip_side_menu']) ? 'form' : 'menuless-form', $formCreate, empty($data['title']) ? h(Inflector::humanize($this->request->params['action'])) . ' ' . $modelForForm : h($data['title']), @@ -144,7 +144,7 @@ ?> From 66586a8766af4158e1d1f7f66577e73b864896ed Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 23:31:45 +0100 Subject: [PATCH 166/385] chg: [UI] Set dbclickAction for user index --- app/View/Users/admin_index.ctp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/View/Users/admin_index.ctp b/app/View/Users/admin_index.ctp index f08c961df..a8a6428d2 100755 --- a/app/View/Users/admin_index.ctp +++ b/app/View/Users/admin_index.ctp @@ -226,7 +226,8 @@ 'User.id' ), 'icon' => 'eye', - 'title' => __('View') + 'title' => __('View'), + 'dbclickAction' => true, ) ) ) From be11c542e89526ae26553da6abddbacde00dbd0a Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 23:45:48 +0100 Subject: [PATCH 167/385] fix: [UI] Add Object works again for all databases --- app/Controller/ObjectTemplatesController.php | 3 ++- app/View/Elements/generic_picker.ctp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Controller/ObjectTemplatesController.php b/app/Controller/ObjectTemplatesController.php index 2a78dced1..7f8c2b8fb 100644 --- a/app/Controller/ObjectTemplatesController.php +++ b/app/Controller/ObjectTemplatesController.php @@ -21,7 +21,7 @@ class ObjectTemplatesController extends AppController public function objectMetaChoice($event_id) { - $metas = $this->ObjectTemplate->find('list', array( + $metas = $this->ObjectTemplate->find('all', array( 'recursive' => -1, 'conditions' => array('ObjectTemplate.active' => 1), 'fields' => array('meta-category'), @@ -35,6 +35,7 @@ class ObjectTemplatesController extends AppController 'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/0" ]]; foreach ($metas as $meta) { + $meta = $meta['ObjectTemplate']['meta-category']; $items[] = array( 'name' => $meta, 'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/" . h($meta) diff --git a/app/View/Elements/generic_picker.ctp b/app/View/Elements/generic_picker.ctp index e83f9115d..8d281eff0 100644 --- a/app/View/Elements/generic_picker.ctp +++ b/app/View/Elements/generic_picker.ctp @@ -231,7 +231,7 @@ function submitFunction(clicked, callback) { GenericPicker->add_select_params($defaults)); ?>> $param) { + foreach ($items as $param) { echo $this->GenericPicker->add_pill($param, $defaults); if (isset($param['additionalData'])) { $additionalData = $param['additionalData']; From 49185c48df7a317fac896647c538e977f14914fd Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 22 Dec 2020 12:54:54 +0100 Subject: [PATCH 168/385] chg: [UI] Site admin redirects from role index to admin index --- app/Controller/RolesController.php | 17 +++++++---------- app/View/Roles/admin_index.ctp | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/Controller/RolesController.php b/app/Controller/RolesController.php index 1a1e9d4a5..43b5a7596 100644 --- a/app/Controller/RolesController.php +++ b/app/Controller/RolesController.php @@ -9,8 +9,6 @@ App::uses('AppController', 'Controller'); */ class RolesController extends AppController { - public $options = array('0' => 'Read Only', '1' => 'Manage My Own Events', '2' => 'Manage Organization Events', '3' => 'Manage & Publish Organization Events'); // FIXME move this to Role Model - public $components = array( 'Security', 'Session', @@ -41,23 +39,19 @@ class RolesController extends AppController { $this->set('menuData', array('menuList' => 'admin', 'menuItem' => 'addRole')); $params = []; - $selectConditions = []; $this->CRUD->add($params); if ($this->IndexFilter->isRest()) { return $this->restResponsePayload; } $this->set('permFlags', $this->Role->permFlags); $dropdownData = [ - 'options' => $this->options + 'options' => $this->Role->premissionLevelName, ]; $this->set(compact('dropdownData')); } public function admin_edit($id = null) { - if (!$this->_isSiteAdmin()) { - $this->redirect(array('controller' => 'roles', 'action' => 'index', 'admin' => false)); - } $this->Role->id = $id; if (!$this->Role->exists() && !$this->request->is('get')) { throw new NotFoundException(__('Invalid Role')); @@ -94,7 +88,7 @@ class RolesController extends AppController $this->request->data['Role']['id'] = $id; $this->request->data = $this->Role->read(null, $id); } - $this->set('options', $this->options); + $this->set('options', $this->Role->premissionLevelName); $this->set('permFlags', $this->Role->permFlags); $this->set('id', $id); } @@ -132,18 +126,21 @@ class RolesController extends AppController public function index() { - $this->recursive = 0; if ($this->_isRest()) { $roles = $this->Role->find('all', array( 'recursive' => -1 )); return $this->RestResponse->viewData($roles, $this->response->type()); } else { + if ($this->_isSiteAdmin()) { + $this->redirect(array('controller' => 'roles', 'action' => 'admin_index')); + } + $this->recursive = 0; $this->set('list', $this->paginate()); $this->set('permFlags', $this->Role->permFlags); $this->loadModel('AdminSetting'); $this->set('default_role_id', $this->AdminSetting->getSetting('default_role')); - $this->set('options', $this->options); + $this->set('options', $this->Role->premissionLevelName); } } diff --git a/app/View/Roles/admin_index.ctp b/app/View/Roles/admin_index.ctp index d967ee99b..195842308 100644 --- a/app/View/Roles/admin_index.ctp +++ b/app/View/Roles/admin_index.ctp @@ -71,6 +71,7 @@ 'data' => [ 'type' => 'simple', 'text' => __('Add role'), + 'fa-icon' => 'plus', 'class' => 'btn btn-primary', 'onClick' => 'openGenericModal', 'onClickParams' => [ From 123fce3f1023664e0a42a443deb74b721f53df41 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 22 Dec 2020 13:05:41 +0100 Subject: [PATCH 169/385] chg: [UI] Rotate header for role index table --- app/Controller/RolesController.php | 1 + .../IndexTable/Fields/boolean.ctp | 16 +++++++-- .../genericElements/IndexTable/headers.ctp | 12 +++++-- app/View/Roles/admin_index.ctp | 13 ++++++- app/webroot/css/main.css | 36 +++++++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/app/Controller/RolesController.php b/app/Controller/RolesController.php index 43b5a7596..1e89c6764 100644 --- a/app/Controller/RolesController.php +++ b/app/Controller/RolesController.php @@ -112,6 +112,7 @@ class RolesController extends AppController if ($this->IndexFilter->isRest()) { return $this->restResponsePayload; } + $this->set('options', $this->Role->premissionLevelName); $this->set('permFlags', $this->Role->permFlags); $this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'roles')); } diff --git a/app/View/Elements/genericElements/IndexTable/Fields/boolean.ctp b/app/View/Elements/genericElements/IndexTable/Fields/boolean.ctp index 1505fe9c8..dc80e110e 100644 --- a/app/View/Elements/genericElements/IndexTable/Fields/boolean.ctp +++ b/app/View/Elements/genericElements/IndexTable/Fields/boolean.ctp @@ -43,9 +43,19 @@ } $rules_raw = implode('
    ', $rules_raw); } + + $classes = ['fa']; + $classes[] = !empty(Hash::extract($row, $field['data_path'])[0]) ? 'fa-check' : 'fa-times'; + + if (!empty($field['colors'])) { + $classes[] = !empty(Hash::extract($row, $field['data_path'])[0]) ? 'green' : 'grey'; + } else { + $classes[] = 'black'; + } + echo sprintf( - '%s', - (!empty(Hash::extract($row, $field['data_path'])[0])) ? 'check' : 'times', + '%s', + implode(' ', $classes), (!empty(Hash::extract($row, $field['data_path'])[0])) ? __('Yes') : __('No'), empty($rules_raw) ? '' : sprintf( @@ -55,4 +65,4 @@ __('Rules') ) ); -?> + diff --git a/app/View/Elements/genericElements/IndexTable/headers.ctp b/app/View/Elements/genericElements/IndexTable/headers.ctp index 16ee73b3d..6b542098c 100644 --- a/app/View/Elements/genericElements/IndexTable/headers.ctp +++ b/app/View/Elements/genericElements/IndexTable/headers.ctp @@ -19,11 +19,19 @@ } else { $header_data = h($header['name']); } - } + $classes = []; + if (!empty($header['sort'])) { + $classes[] = 'pagination_link'; + } + if (!empty($header['rotate_header'])) { + $classes[] = 'rotate'; + $header_data = "
    $header_data
    "; + } + $headersHtml .= sprintf( '%s', - !empty($header['sort']) ? ' class="pagination_link"' : '', + !empty($classes) ? ' class="' . implode(' ', $classes) .'"' : '', $header_data ); } diff --git a/app/View/Roles/admin_index.ctp b/app/View/Roles/admin_index.ctp index 195842308..37fb3e726 100644 --- a/app/View/Roles/admin_index.ctp +++ b/app/View/Roles/admin_index.ctp @@ -18,6 +18,14 @@ 'name' => __('Name'), 'sort' => 'Role.name', 'data_path' => 'Role.name' + ], + [ + 'name' => __('Permission'), + 'sort' => 'Role.permission', + 'element' => 'custom', + 'function' => function (array $row) use ($options) { + return $options[$row['Role']['permission']]; + } ] ]; @@ -26,7 +34,10 @@ 'name' => Inflector::Humanize(substr($k, 5)), 'sort' => 'Role.' . $k, 'data_path' => 'Role.' . $k, - 'element' => 'boolean' + 'element' => 'boolean', + 'rotate_header' => true, + 'class' => 'rotate', + 'colors' => true, ]; } diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 6f27efe5b..8211d9534 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -2687,3 +2687,39 @@ a.orgImg { .misp-error-container { margin: 0 20px; } + +th.rotate { + /* Something you can count on */ + height: 100px; + white-space: nowrap; + vertical-align: inherit !important; +} + +th.rotate > div { + transform: + /* Magic Numbers */ + translate(15px, 30px) + /* 45 is really 360 - 45 */ + rotate(315deg); + width: 25px; +} + +th.rotate > div > span { + padding: 5px 0; +} + +td.rotate { + text-align: center; +} + +th.rotate, td.rotate { + width: 25px; +} + +th.rotate + th:not(.rotate) { + padding-left: 30px; +} + +td.rotate + td:not(.rotate) { + padding-left: 30px; +} From 44ffccf5cb2e8ddecf138aacc9e03a4a2d49691a Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 22 Dec 2020 14:27:01 +0100 Subject: [PATCH 170/385] fix: [UI] Cancelling search didn't work for index table --- .../IndexTable/index_table.ctp | 6 +-- app/webroot/js/misp.js | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/app/View/Elements/genericElements/IndexTable/index_table.ctp b/app/View/Elements/genericElements/IndexTable/index_table.ctp index 45a61f103..49f182722 100644 --- a/app/View/Elements/genericElements/IndexTable/index_table.ctp +++ b/app/View/Elements/genericElements/IndexTable/index_table.ctp @@ -101,7 +101,7 @@ $url = $baseurl . '/' . $this->params['controller'] . '/' . $this->params['action']; ?> From 307a273e77eba5536dc04069dbe6560bf4a9285e Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 22 Dec 2020 17:10:28 +0100 Subject: [PATCH 174/385] chg: [role] Do not allow delete role when is still assigned to user --- app/Controller/Component/CRUDComponent.php | 16 +++++++++++++++- app/Controller/RolesController.php | 12 +++++++++++- app/View/genericTemplates/delete.ctp | 9 +++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/Controller/Component/CRUDComponent.php b/app/Controller/Component/CRUDComponent.php index c05b09ce0..f164c4889 100644 --- a/app/Controller/Component/CRUDComponent.php +++ b/app/Controller/Component/CRUDComponent.php @@ -2,6 +2,7 @@ class CRUDComponent extends Component { + /** @var AppController */ public $Controller = null; public function initialize(Controller $controller, $settings=array()) { @@ -230,7 +231,18 @@ class CRUDComponent extends Component if (empty($data)) { throw new NotFoundException(__('Invalid %s.', $modelName)); } - if ($this->Controller->request->is('post') || $this->Controller->request->is('delete')) { + $validationError = null; + if (isset($params['validate'])) { + try { + $params['validate']($data); + } catch (Exception $e) { + $validationError = $e->getMessage(); + if ($this->Controller->IndexFilter->isRest()) { + $this->Controller->restResponsePayload = $this->Controller->RestResponse->saveFailResponse($modelName, 'delete', $id, $validationError); + } + } + } + if ($validationError === null && $this->Controller->request->is('post') || $this->Controller->request->is('delete')) { if (!empty($params['modelFunction'])) { $result = $this->Controller->$modelName->{$params['modelFunction']}($id); } else { @@ -240,12 +252,14 @@ class CRUDComponent extends Component $message = __('%s deleted.', $modelName); if ($this->Controller->IndexFilter->isRest()) { $this->Controller->restResponsePayload = $this->Controller->RestResponse->saveSuccessResponse($modelName, 'delete', $id, 'json', $message); + return; } else { $this->Controller->Flash->success($message); $this->Controller->redirect($this->Controller->referer()); } } } + $this->Controller->set('validationError', $validationError); $this->Controller->set('id', $data[$modelName]['id']); $this->Controller->set('data', $data); $this->Controller->layout = 'ajax'; diff --git a/app/Controller/RolesController.php b/app/Controller/RolesController.php index f67b31dab..903c1a7d6 100644 --- a/app/Controller/RolesController.php +++ b/app/Controller/RolesController.php @@ -95,7 +95,17 @@ class RolesController extends AppController public function admin_delete($id = null) { - $this->CRUD->delete($id); + $this->CRUD->delete($id, [ + 'validate' => function (array $role) { + $usersWithRole = $this->User->find('count', [ + 'conditions' => ['role_id' => $role['Role']['id']], + 'recursive' => -1, + ]); + if ($usersWithRole) { + throw new Exception(__("It is not possible to delete role that is assigned to users.")); + } + } + ]); if ($this->IndexFilter->isRest()) { return $this->restResponsePayload; } diff --git a/app/View/genericTemplates/delete.ctp b/app/View/genericTemplates/delete.ctp index c33404aa8..1ea0482f5 100644 --- a/app/View/genericTemplates/delete.ctp +++ b/app/View/genericTemplates/delete.ctp @@ -8,6 +8,14 @@ $title = Inflector::singularize(Inflector::humanize(Inflector::underscore($this-

    + + + + @@ -20,6 +28,7 @@ $title = Inflector::singularize(Inflector::humanize(Inflector::underscore($this- ?> + diff --git a/app/View/Elements/genericElements/SideMenu/side_menu.ctp b/app/View/Elements/genericElements/SideMenu/side_menu.ctp index 148014caa..ab6357194 100644 --- a/app/View/Elements/genericElements/SideMenu/side_menu.ctp +++ b/app/View/Elements/genericElements/SideMenu/side_menu.ctp @@ -477,6 +477,24 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider'); } break; + case 'correlationExclusions': + if ($menuItem === 'view') { + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'element_id' => 'view', + 'text' => __('View Correlation Exclusion') + )); + } + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'element_id' => 'index', + 'url' => $baseurl . '/correlation_exclusions/index', + 'text' => __('List Correlation Exclusions') + )); + echo $this->element('/genericElements/SideMenu/side_menu_link', array( + 'element_id' => 'add', + 'url' => $baseurl . '/correlation_exclusions/add', + 'text' => __('Add Correlation Exclusion') + )); + break; case 'warninglist': if ($menuItem === 'view') { echo $this->element('/genericElements/SideMenu/side_menu_link', array( diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index baca9d583..f9234d1b3 100755 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -164,7 +164,11 @@ array( 'text' => __('List Noticelists'), 'url' => $baseurl . '/noticelists/index' - ) + ), + [ + 'text' => __('List Correlation Exclusions'), + 'url' => $baseurl . '/correlation_exclusions/index' + ] ) ), array( From 3f49bf0f3484a45de3ba762c6ae5c344928229e6 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Thu, 7 Jan 2021 14:00:55 +0100 Subject: [PATCH 235/385] chg: [sync] Optimise version compatibility checking to save sql queries --- app/Model/Event.php | 2 +- app/Model/GalaxyCluster.php | 4 ++-- app/Model/Server.php | 39 ++++++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index ab2ac7e7d..bc0150fae 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -1086,7 +1086,7 @@ class Event extends AppModel public function uploadEventToServer($event, $server, $HttpSocket = null, $scope = 'events') { $this->Server = ClassRegistry::init('Server'); - $push = $this->Server->checkVersionCompatibility($server['Server']['id'], false, $HttpSocket); + $push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket); if ($scope === 'events' && empty($push['canPush'])) { return 'The remote user is not a sync user - the upload of the event has been blocked.'; } elseif ($scope === 'sightings' && empty($push['canPush']) && empty($push['canSight'])) { diff --git a/app/Model/GalaxyCluster.php b/app/Model/GalaxyCluster.php index 97ffc5fe9..b9a323ce6 100644 --- a/app/Model/GalaxyCluster.php +++ b/app/Model/GalaxyCluster.php @@ -1574,7 +1574,7 @@ class GalaxyCluster extends AppModel { $this->Server = ClassRegistry::init('Server'); $this->Log = ClassRegistry::init('Log'); - $push = $this->Server->checkVersionCompatibility($server['Server']['id'], false, $HttpSocket); + $push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket); if (empty($push['canPush']) && empty($push['canPushGalaxyCluster'])) { return __('The remote user does not have the permission to manipulate galaxies - the upload of the galaxy clusters has been blocked.'); } @@ -1852,7 +1852,7 @@ class GalaxyCluster extends AppModel public function pullGalaxyClusters(array $user, array $server, $technique = 'full') { $this->Server = ClassRegistry::init('Server'); - $compatible = $this->Server->checkVersionCompatibility($server['Server']['id'], $user)['supportEditOfGalaxyCluster']; + $compatible = $this->Server->checkVersionCompatibility($server, $user)['supportEditOfGalaxyCluster']; if (!$compatible) { return 0; } diff --git a/app/Model/Server.php b/app/Model/Server.php index 053fdb88b..da93981dc 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -3225,7 +3225,7 @@ class Server extends AppModel } $this->Event = ClassRegistry::init('Event'); $url = $server['Server']['url']; - $push = $this->checkVersionCompatibility($id, $user); + $push = $this->checkVersionCompatibility($server, $user); if (is_array($push) && !$push['canPush'] && !$push['canSight']) { $push = 'Remote instance is outdated or no permission to push.'; } @@ -4770,14 +4770,13 @@ class Server extends AppModel return ['status' => 1, 'content-encoding' => $contentEncoding]; } - public function checkVersionCompatibility($id, $user = array(), $HttpSocket = false) + public function checkVersionCompatibility(array $server, $user = array(), $HttpSocket = false) { // for event publishing when we don't have a user. if (empty($user)) { $user = array('Organisation' => array('name' => 'SYSTEM'), 'email' => 'SYSTEM', 'id' => 0); } $localVersion = $this->checkMISPVersion(); - $server = $this->find('first', array('conditions' => array('Server.id' => $id))); $HttpSocket = $this->setupHttpSocket($server, $HttpSocket); $request = $this->setupSyncRequest($server); $uri = $server['Server']['url'] . '/servers/getVersion'; @@ -4792,12 +4791,12 @@ class Server extends AppModel if (isset($response->code)) { $title = 'Error: Connection to the server has failed.' . (isset($response->code) ? ' Returned response code: ' . $response->code : ''); } else { - $title = 'Error: Connection to the server has failed. The returned exception\'s error message was: ' . $e->getMessage(); + $title = 'Error: Connection to the server has failed. The returned exception\'s error message was: ' . $error; } $this->Log->save(array( 'org' => $user['Organisation']['name'], 'model' => 'Server', - 'model_id' => $id, + 'model_id' => $server['Server']['id'], 'email' => $user['email'], 'action' => 'error', 'user_id' => $user['id'], @@ -4805,7 +4804,7 @@ class Server extends AppModel )); return $title; } - $remoteVersion = json_decode($response->body, true); + $remoteVersion = $this->jsonDecode($response->body); $canPush = isset($remoteVersion['perm_sync']) ? $remoteVersion['perm_sync'] : false; $canSight = isset($remoteVersion['perm_sighting']) ? $remoteVersion['perm_sighting'] : false; $supportEditOfGalaxyCluster = isset($remoteVersion['perm_galaxy_editor']); @@ -4818,7 +4817,7 @@ class Server extends AppModel $this->Log->save(array( 'org' => $user['Organisation']['name'], 'model' => 'Server', - 'model_id' => $id, + 'model_id' => $server['Server']['id'], 'email' => $user['email'], 'action' => 'error', 'user_id' => $user['id'], @@ -4830,16 +4829,16 @@ class Server extends AppModel $success = false; $issueLevel = "warning"; if ($localVersion['major'] > $remoteVersion[0]) { - $response = "Sync to Server ('" . $id . "') aborted. The remote instance's MISP version is behind by a major version."; + $response = "Sync to Server ('{$server['Server']['id']}') aborted. The remote instance's MISP version is behind by a major version."; } if ($response === false && $localVersion['major'] < $remoteVersion[0]) { - $response = "Sync to Server ('" . $id . "') aborted. The remote instance is at least a full major version ahead - make sure you update your MISP instance!"; + $response = "Sync to Server ('{$server['Server']['id']}') aborted. The remote instance is at least a full major version ahead - make sure you update your MISP instance!"; } if ($response === false && $localVersion['minor'] > $remoteVersion[1]) { - $response = "Sync to Server ('" . $id . "') aborted. The remote instance's MISP version is behind by a minor version."; + $response = "Sync to Server ('{$server['Server']['id']}') aborted. The remote instance's MISP version is behind by a minor version."; } if ($response === false && $localVersion['minor'] < $remoteVersion[1]) { - $response = "Sync to Server ('" . $id . "') aborted. The remote instance is at least a full minor version ahead - make sure you update your MISP instance!"; + $response = "Sync to Server ('{$server['Server']['id']}') aborted. The remote instance is at least a full minor version ahead - make sure you update your MISP instance!"; } // if we haven't set a message yet, we're good to go. We are only behind by a hotfix version @@ -4849,13 +4848,13 @@ class Server extends AppModel $issueLevel = "error"; } if ($response === false && $localVersion['hotfix'] > $remoteVersion[2]) { - $response = "Sync to Server ('" . $id . "') initiated, but the remote instance is a few hotfixes behind."; + $response = "Sync to Server ('{$server['Server']['id']}') initiated, but the remote instance is a few hotfixes behind."; } if ($response === false && $localVersion['hotfix'] < $remoteVersion[2]) { - $response = "Sync to Server ('" . $id . "') initiated, but the remote instance is a few hotfixes ahead. Make sure you keep your instance up to date!"; + $response = "Sync to Server ('{$server['Server']['id']}') initiated, but the remote instance is a few hotfixes ahead. Make sure you keep your instance up to date!"; } if (empty($response) && $remoteVersion[2] < 111) { - $response = "Sync to Server ('" . $id . "') initiated, but version 2.4.111 is required in order to be able to pull proposals from the remote side."; + $response = "Sync to Server ('{$server['Server']['id']}') initiated, but version 2.4.111 is required in order to be able to pull proposals from the remote side."; } if ($response !== false) { @@ -4864,14 +4863,22 @@ class Server extends AppModel $this->Log->save(array( 'org' => $user['Organisation']['name'], 'model' => 'Server', - 'model_id' => $id, + 'model_id' => $server['Server']['id'], 'email' => $user['email'], 'action' => $issueLevel, 'user_id' => $user['id'], 'title' => ucfirst($issueLevel) . ': ' . $response, )); } - return array('success' => $success, 'response' => $response, 'canPush' => $canPush, 'canSight' => $canSight, 'canEditGalaxyCluster' => $canEditGalaxyCluster, 'supportEditOfGalaxyCluster' => $supportEditOfGalaxyCluster, 'version' => $remoteVersion); + return [ + 'success' => $success, + 'response' => $response, + 'canPush' => $canPush, + 'canSight' => $canSight, + 'canEditGalaxyCluster' => $canEditGalaxyCluster, + 'supportEditOfGalaxyCluster' => $supportEditOfGalaxyCluster, + 'version' => $remoteVersion, + ]; } public function isJson($string) From 1276a42309e0cc2ff4edd3166f20d53ff8e3f399 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Mon, 21 Dec 2020 18:38:59 +0100 Subject: [PATCH 236/385] fix: [sighting] Order must contain group for some mysql servers --- app/Model/Sighting.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php index b5303a870..b9778e7c6 100644 --- a/app/Model/Sighting.php +++ b/app/Model/Sighting.php @@ -332,7 +332,7 @@ class Sighting extends AppModel 'fields' => ['org_id', 'attribute_id', 'type', 'date', 'last_timestamp', 'sighting_count'], 'recursive' => -1, 'group' => ['org_id', 'attribute_id', 'type', 'date'], - 'order' => ['date_sighting'], // from oldest + 'order' => ['date'], // from oldest )); unset( $this->virtualFields['date'], @@ -369,7 +369,7 @@ class Sighting extends AppModel 'conditions' => $conditions, 'fields' => [ucfirst($context) . 'Tag.tag_id', 'date', 'sighting_count'], 'group' => [ucfirst($context) . 'Tag.id', 'date'], - 'order' => ['date_sighting'], // from oldest + 'order' => ['date'], // from oldest ]); unset($this->virtualFields['date'], $this->virtualFields['sighting_count']); return $sightings; From 695af2dab1379324d3d2b241deaa754b80e90f79 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Thu, 7 Jan 2021 16:57:26 +0100 Subject: [PATCH 237/385] fix: [rest] Allow to edit roaming mode of sharing group --- app/Model/SharingGroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Model/SharingGroup.php b/app/Model/SharingGroup.php index 9c7a42eb5..7245fb9d1 100644 --- a/app/Model/SharingGroup.php +++ b/app/Model/SharingGroup.php @@ -700,7 +700,7 @@ class SharingGroup extends AppModel if ($force) { $sgids = $existingSG['SharingGroup']['id']; $editedSG = $existingSG['SharingGroup']; - $attributes = array('name', 'releasability', 'description', 'created', 'modified', 'active'); + $attributes = ['name', 'releasability', 'description', 'created', 'modified', 'active', 'roaming']; foreach ($attributes as $a) { if (isset($sg[$a])) { $editedSG[$a] = $sg[$a]; From ca9b74c1a7f271724070c10f76ccbc95bb1ff803 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Thu, 7 Jan 2021 18:05:10 +0100 Subject: [PATCH 238/385] chg: [UI] Make server index view nicer --- app/View/Servers/index.ctp | 73 +++++++++++++++++--------------------- app/webroot/css/main.css | 2 +- 2 files changed, 34 insertions(+), 41 deletions(-) diff --git a/app/View/Servers/index.ctp b/app/View/Servers/index.ctp index d9950bf82..62ea2757a 100644 --- a/app/View/Servers/index.ctp +++ b/app/View/Servers/index.ctp @@ -17,8 +17,8 @@ - - + + @@ -31,9 +31,9 @@ - - - + + + @@ -41,9 +41,9 @@ - + $server): +foreach ($servers as $server): $rules = array(); $rules['push'] = json_decode($server['Server']['push_rules'], true); $rules['pull'] = json_decode($server['Server']['pull_rules'], true); @@ -61,7 +61,7 @@ foreach ($servers as $row_pos => $server): if ($syncOption === 'push') $temp = $collection[$fieldOption][$temp]; $ruleDescription[$syncOption] .= h($temp); } - $ruleDescription[$syncOption] .= '
    '; + $ruleDescription[$syncOption] .= '
    '; } } } @@ -81,7 +81,7 @@ foreach ($servers as $row_pos => $server): $arrows = ''; foreach (['up', 'down'] as $direction) { $arrows .= sprintf( - '', + '', $direction, $direction, $direction === 'up' ? __('Move server priority up') : __('Move server priority down'), @@ -98,9 +98,7 @@ foreach ($servers as $row_pos => $server): else echo h($server['Server']['url']); ?> - + - - - - - - + + + + + + - - - + + + - - - - + + + + -element('/genericElements/SideMenu/side_menu', array('menuList' => 'sync', 'menuItem' => 'index')); +element('/genericElements/SideMenu/side_menu', array('menuList' => 'sync', 'menuItem' => 'index')); diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 8211d9534..d3c6faff7 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -2678,7 +2678,7 @@ table tr:hover .down-expand-button { a.orgImg { background-repeat: no-repeat; - background-size: 20px; + background-size: auto 20px; padding-left: 25px; padding-top: 3px; padding-bottom: 3px; From 9f8cbeff542b7b126e23b2c40ec37c1086b5e49b Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Thu, 7 Jan 2021 11:32:10 +0100 Subject: [PATCH 239/385] new: [UI] Show sharing groups in org view --- app/Controller/SharingGroupsController.php | 51 +++++++++++++++++----- app/View/Organisations/view.ctp | 9 ++-- app/View/SharingGroups/index.ctp | 12 ++--- app/webroot/js/misp.js | 2 + 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/app/Controller/SharingGroupsController.php b/app/Controller/SharingGroupsController.php index d522b51e2..e9c50caae 100644 --- a/app/Controller/SharingGroupsController.php +++ b/app/Controller/SharingGroupsController.php @@ -268,17 +268,21 @@ class SharingGroupsController extends AppController if (isset($this->params['named']['value'])) { $term = '%' . strtolower($this->params['named']['value']) . '%'; - $sgIds = $this->SharingGroup->SharingGroupOrg->find('list', [ - 'conditions' => [ - 'OR' => [ - 'Organisation.uuid LIKE' => $term, - 'LOWER(Organisation.name) LIKE' => $term, + if ($this->__showOrgs()) { + $sgIds = $this->SharingGroup->SharingGroupOrg->find('column', [ + 'conditions' => [ + 'OR' => [ + 'Organisation.uuid LIKE' => $term, + 'LOWER(Organisation.name) LIKE' => $term, + ], + 'SharingGroupOrg.sharing_group_id' => $authorizedSgIds, ], - 'SharingGroupOrg.sharing_group_id' => $authorizedSgIds, - ], - 'contain' => ['Organisation'], - 'fields' => ['SharingGroupOrg.sharing_group_id'], - ]); + 'contain' => ['Organisation'], + 'fields' => ['SharingGroupOrg.sharing_group_id'], + ]); + } else { + $sgIds = []; + } $this->paginate['conditions'][]['OR'] = [ 'SharingGroup.id' => $sgIds, 'SharingGroup.uuid LIKE' => $term, @@ -289,6 +293,33 @@ class SharingGroupsController extends AppController ]; } + if ($this->__showOrgs() && isset($this->params['named']['searchorg'])) { + $orgs = explode('|', $this->params['named']['searchorg']); + $conditions = []; + foreach ($orgs as $org) { + $exclude = $org[0] === '!'; + if ($exclude) { + $org = substr($org, 1); + } + $org = $this->SharingGroup->Organisation->fetchOrg($org); + if ($org) { + if ($exclude) { + $conditions['AND'][] = ['org_id !=' => $org['id']]; + } else { + $conditions['OR'][] = ['org_id' => $org['id']]; + } + } + } + $sgIds = $this->SharingGroup->SharingGroupOrg->find('column', [ + 'conditions' => $conditions, + 'fields' => ['SharingGroupOrg.sharing_group_id'], + ]); + if (empty($sgIds)) { + $sgIds = -1; + } + $this->paginate['conditions'][] = ['SharingGroup.id' => $sgIds]; + } + // To allow sort sharing group by number of organisation and also show org count when user don't have permission ot see them $this->SharingGroup->addCountField('org_count', $this->SharingGroup->SharingGroupOrg, ['SharingGroupOrg.sharing_group_id = SharingGroup.id']); $this->paginate['fields'][] = 'SharingGroup.org_count'; diff --git a/app/View/Organisations/view.ctp b/app/View/Organisations/view.ctp index 866e9cc4a..542bf12a2 100644 --- a/app/View/Organisations/view.ctp +++ b/app/View/Organisations/view.ctp @@ -66,9 +66,12 @@ - - -

    + + + + + +

    Html->script('vis'); diff --git a/app/View/SharingGroups/index.ctp b/app/View/SharingGroups/index.ctp index e7f5cf0a1..e4d6773e8 100644 --- a/app/View/SharingGroups/index.ctp +++ b/app/View/SharingGroups/index.ctp @@ -1,10 +1,9 @@ -
    Paginator->sort('id');?>
    Paginator->sort('id', __('ID'));?> Paginator->sort('name');?> Paginator->sort('push_galaxy_clusters', 'Push Clusters');?> Paginator->sort('pull_galaxy_clusters', 'Pull Clusters');?> Paginator->sort('caching_enabled', 'Cache');?>Paginator->sort('unpublish_event (push event)');?>Paginator->sort('publish_without_email (pull event)');?>Paginator->sort('url');?>Paginator->sort('unpublish_event');?>Paginator->sort('publish_without_email');?>Paginator->sort('url', 'URL');?> Paginator->sort('cert_file');?> Paginator->sort('client_cert_file');?>Paginator->sort('skip_proxy');?> Paginator->sort('org');?>
    - - @@ -118,12 +116,12 @@ foreach ($servers as $row_pos => $server): ?> " data-toggle="popover" title="Distribution List" data-content=""> ()" data-toggle="popover" title="Distribution List" data-content=""> ()" data-toggle="popover" title="Distribution List" data-content=""> ()" data-toggle="popover" title="Distribution List" data-content=""> () $server): } } echo sprintf( - '%s%s%s %s', - __('Age: '), - $last, - $unit, - '' + '%s %s', + __('Age: %s%s', $last, $unit), + '' ); } else { echo sprintf( '%s %s', __('Not cached'), - '' + '' ); } } else { - echo ''; + echo ''; } ?>   ">   ">
    + Form->create('Sighting', ['id' => 'SightingForm', 'url' => $baseurl . '/sightings/add/', 'style' => 'display:none;']); + echo $this->Form->input('id', ['label' => false, 'type' => 'number']); + echo $this->Form->input('type', ['label' => false]); + echo $this->Form->end(); + ?>

    Paginator->counter(array( diff --git a/app/View/Elements/Events/View/sighting_field.ctp b/app/View/Elements/Events/View/sighting_field.ctp index 7b69c9298..4ca14d20d 100644 --- a/app/View/Elements/Events/View/sighting_field.ctp +++ b/app/View/Elements/Events/View/sighting_field.ctp @@ -24,13 +24,6 @@ if (isset($sightingsData['data'][$objectId])) { } ?>

    - - Form->create('Sighting', array('id' => 'Sighting_' . $objectId, 'url' => $baseurl . '/sightings/add/' . $objectId, 'style' => 'display:none;')); - echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $objectId . '_type')); - echo $this->Form->end(); - ?> - diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp index bc605e21c..30837073d 100644 --- a/app/View/Elements/eventattribute.ctp +++ b/app/View/Elements/eventattribute.ctp @@ -185,6 +185,13 @@ } ?>
    + Form->create('Sighting', ['id' => 'SightingForm', 'url' => $baseurl . '/sightings/add/', 'style' => 'display:none;']); + echo $this->Form->input('id', ['label' => false, 'type' => 'number']); + echo $this->Form->input('type', ['label' => false]); + echo $this->Form->end(); + ?>
    diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 875951012..c8764cd78 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -605,15 +605,18 @@ function quickEditHover(td, type, id, field, event) { } function addSighting(type, attribute_id, event_id) { - $('#Sighting_' + attribute_id + '_type').val(type); + var $sightingForm = $('#SightingForm'); + $('input[name="data[Sighting][type]"]', $sightingForm).val(type); + $('input[name="data[Sighting][id]"]', $sightingForm).val(attribute_id); $.ajax({ - data: $('#Sighting_' + attribute_id).closest("form").serialize(), + data: $sightingForm.serialize(), cache: false, - success: function (data, textStatus) { + success: function (data) { handleGenericAjaxResponse(data); var result = data; if (result.saved == true) { - $('.sightingsCounter').each(function( counter ) { + // Update global sighting counter + $('.sightingsCounter').each(function() { $(this).html(parseInt($(this).html()) + 1); }); updateIndex(event_id, 'event'); @@ -624,7 +627,7 @@ function addSighting(type, attribute_id, event_id) { updateIndex(event_id, 'event'); }, type: "post", - url: baseurl + "/sightings/add/" + attribute_id + url: baseurl + "/sightings/add/", }); } From 2218546944238123573fa4e5ac19ee6bfaaa7411 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 9 Jan 2021 21:12:57 +0100 Subject: [PATCH 246/385] chg: [internal] Generate type definitions just when required --- app/Model/Attribute.php | 595 ++++++++++++++++++---------------- app/Model/ShadowAttribute.php | 26 +- 2 files changed, 330 insertions(+), 291 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index f342383db..b53fe132f 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -12,6 +12,8 @@ App::uses('ComplexTypeTool', 'Tools'); /** * @property Event $Event * @property AttributeTag $AttributeTag + * @property-read array $typeDefinitions + * @property-read array $categoryDefinitions */ class Attribute extends AppModel { @@ -60,12 +62,9 @@ class Attribute extends AppModel public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' Sharing Group', 5 => 'Inherit'); - public $categoryDefinitions = array(); - - public $typeDefinitions = array(); - - public function __construct($id = false, $table = null, $ds = null) { + public function __construct($id = false, $table = null, $ds = null) + { parent::__construct($id, $table, $ds); $this->distributionLevels = array( @@ -76,283 +75,6 @@ class Attribute extends AppModel 4 => __('Sharing group'), 5 => __('Inherit event') ); - - // - // NOTE WHEN MODIFYING: please ensure to run the script 'tools/gen_misp_types_categories.py' to update the new definitions everywhere. (docu, website, RFC, ...) - // - $this->categoryDefinitions = array( - 'Internal reference' => array( - 'desc' => __('Reference used by the publishing party (e.g. ticket number)'), - 'types' => array('text', 'link', 'comment', 'other', 'hex', 'anonymised', 'git-commit-id') - ), - 'Targeting data' => array( - 'desc' => __('Internal Attack Targeting and Compromise Information'), - 'formdesc' => __('Targeting information to include recipient email, infected machines, department, and or locations.'), - 'types' => array('target-user', 'target-email', 'target-machine', 'target-org', 'target-location', 'target-external', 'comment', 'anonymised') - ), - 'Antivirus detection' => array( - 'desc' => __('All the info about how the malware is detected by the antivirus products'), - 'formdesc' => __('List of anti-virus vendors detecting the malware or information on detection performance (e.g. 13/43 or 67%). Attachment with list of detection or link to VirusTotal could be placed here as well.'), - 'types' => array('link', 'comment', 'text', 'hex', 'attachment', 'other', 'anonymised') - ), - 'Payload delivery' => array( - 'desc' => __('Information about how the malware is delivered'), - 'formdesc' => __('Information about the way the malware payload is initially delivered, for example information about the email or web-site, vulnerability used, originating IP etc. Malware sample itself should be attached here.'), - 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'pehash', 'tlsh', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash','filename|impfuzzy', 'filename|pehash', 'mac-address', 'mac-eui-64', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'hostname', 'domain', 'email', 'email-src', 'email-dst', 'email-subject', 'email-attachment', 'email-body', 'url', 'user-agent', 'AS', 'pattern-in-file', 'pattern-in-traffic', 'filename-pattern', 'stix2-pattern', 'yara', 'sigma', 'mime-type', 'attachment', 'malware-sample', 'link', 'malware-type', 'comment', 'text', 'hex', 'vulnerability', 'cpe', 'weakness', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'other', 'hostname|port', 'email-dst-display-name', 'email-src-display-name', 'email-header', 'email-reply-to', 'email-x-mailer', 'email-mime-boundary', 'email-thread-index', 'email-message-id', 'mobile-application-id', 'chrome-extension-id', 'whois-registrant-email', 'anonymised') - ), - 'Artifacts dropped' => array( - 'desc' => __('Any artifact (files, registry keys etc.) dropped by the malware or other modifications to the system'), - 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash', 'filename|impfuzzy','filename|pehash', 'regkey', 'regkey|value', 'pattern-in-file', 'pattern-in-memory', 'filename-pattern', 'pdb', 'stix2-pattern', 'yara', 'sigma', 'attachment', 'malware-sample', 'named pipe', 'mutex', 'process-state','windows-scheduled-task', 'windows-service-name', 'windows-service-displayname', 'comment', 'text', 'hex', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'other', 'cookie', 'gene', 'kusto-query', 'mime-type', 'anonymised', 'pgp-public-key', 'pgp-private-key') - ), - 'Payload installation' => array( - 'desc' => __('Info on where the malware gets installed in the system'), - 'formdesc' => __('Location where the payload was placed in the system and the way it was installed. For example, a filename|md5 type attribute can be added here like this: c:\\windows\\system32\\malicious.exe|41d8cd98f00b204e9800998ecf8427e.'), - 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'pehash', 'tlsh', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash', 'filename|impfuzzy', 'filename|pehash', 'pattern-in-file', 'pattern-in-traffic', 'pattern-in-memory', 'filename-pattern', 'stix2-pattern', 'yara', 'sigma', 'vulnerability', 'cpe','weakness', 'attachment', 'malware-sample', 'malware-type', 'comment', 'text', 'hex', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'mobile-application-id', 'chrome-extension-id', 'other', 'mime-type', 'anonymised') - ), - 'Persistence mechanism' => array( - 'desc' => __('Mechanisms used by the malware to start at boot'), - 'formdesc' => __('Mechanisms used by the malware to start at boot. This could be a registry key, legitimate driver modification, LNK file in startup'), - 'types' => array('filename', 'regkey', 'regkey|value', 'comment', 'text', 'other', 'hex', 'anonymised') - ), - 'Network activity' => array( - 'desc' => __('Information about network traffic generated by the malware'), - 'types' => array('ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'port', 'hostname', 'domain', 'domain|ip', 'mac-address', 'mac-eui-64', 'email', 'email-dst', 'email-src', 'eppn', 'url', 'uri', 'user-agent', 'http-method', 'AS', 'snort', 'pattern-in-file', 'filename-pattern','stix2-pattern', 'pattern-in-traffic', 'attachment', 'comment', 'text', 'x509-fingerprint-md5', 'x509-fingerprint-sha1', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'other', 'hex', 'cookie', 'hostname|port', 'bro', 'zeek', 'anonymised', 'community-id', 'email-subject', 'favicon-mmh3') - ), - 'Payload type' => array( - 'desc' => __('Information about the final payload(s)'), - 'formdesc' => __('Information about the final payload(s). Can contain a function of the payload, e.g. keylogger, RAT, or a name if identified, such as Poison Ivy.'), - 'types' => array('comment', 'text', 'other', 'anonymised') - ), - 'Attribution' => array( - 'desc' => __('Identification of the group, organisation, or country behind the attack'), - 'types' => array('threat-actor', 'campaign-name', 'campaign-id', 'whois-registrant-phone', 'whois-registrant-email', 'whois-registrant-name', 'whois-registrant-org', 'whois-registrar', 'whois-creation-date','comment', 'text', 'x509-fingerprint-sha1','x509-fingerprint-md5', 'x509-fingerprint-sha256', 'other', 'dns-soa-email', 'anonymised', 'email') - ), - 'External analysis' => array( - 'desc' => __('Any other result from additional analysis of the malware like tools output'), - 'formdesc' => __('Any other result from additional analysis of the malware like tools output Examples: pdf-parser output, automated sandbox analysis, reverse engineering report.'), - 'types' => array('md5', 'sha1', 'sha256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'mac-address', 'mac-eui-64', 'hostname', 'domain', 'domain|ip', 'url', 'user-agent', 'regkey', 'regkey|value', 'AS', 'snort', 'bro', 'zeek', 'pattern-in-file', 'pattern-in-traffic', 'pattern-in-memory', 'filename-pattern','vulnerability', 'cpe', 'weakness', 'attachment', 'malware-sample', 'link', 'comment', 'text', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'github-repository', 'other', 'cortex', 'anonymised', 'community-id') - ), - 'Financial fraud' => array( - 'desc' => __('Financial Fraud indicators'), - 'formdesc' => __('Financial Fraud indicators, for example: IBAN Numbers, BIC codes, Credit card numbers, etc.'), - 'types' => array('btc', 'dash', 'xmr', 'iban', 'bic', 'bank-account-nr', 'aba-rtn', 'bin', 'cc-number', 'prtn', 'phone-number', 'comment', 'text', 'other', 'hex', 'anonymised'), - ), - 'Support Tool' => array( - 'desc' => __('Tools supporting analysis or detection of the event'), - 'types' => array('link', 'text', 'attachment', 'comment', 'other', 'hex', 'anonymised') - ), - 'Social network' => array( - 'desc' => __('Social networks and platforms'), - // email-src and email-dst or should we go with a new email type that is neither / both? - 'types' => array('github-username', 'github-repository', 'github-organisation', 'jabber-id', 'twitter-id', 'email', 'email-src', 'email-dst', 'eppn','comment', 'text', 'other', 'whois-registrant-email', 'anonymised', 'pgp-public-key', 'pgp-private-key') - ), - 'Person' => array( - 'desc' => __('A human being - natural person'), - 'types' => array('first-name', 'middle-name', 'last-name', 'date-of-birth', 'place-of-birth', 'gender', 'passport-number', 'passport-country', 'passport-expiration', 'redress-number', 'nationality', 'visa-number', 'issue-date-of-the-visa', 'primary-residence', 'country-of-residence', 'special-service-request', 'frequent-flyer-number', 'travel-details', 'payment-details', 'place-port-of-original-embarkation', 'place-port-of-clearance', 'place-port-of-onward-foreign-destination', 'passenger-name-record-locator-number', 'comment', 'text', 'other', 'phone-number', 'identity-card-number', 'anonymised', 'email', 'pgp-public-key', 'pgp-private-key') - ), - 'Other' => array( - 'desc' => __('Attributes that are not part of any other category or are meant to be used as a component in MISP objects in the future'), - 'types' => array('comment', 'text', 'other', 'size-in-bytes', 'counter', 'datetime', 'cpe', 'port', 'float', 'hex', 'phone-number', 'boolean', 'anonymised', 'pgp-public-key', 'pgp-private-key') - ) - ); - - // - // NOTE WHEN MODIFYING: please ensure to run the script 'tools/gen_misp_types_categories.py' to update the new definitions everywhere. (docu, website, RFC, ...) - // - $this->typeDefinitions = array( - 'md5' => array('desc' => __('A checksum in md5 format'), 'formdesc' => __("You are encouraged to use filename|md5 instead. A checksum in md5 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha1' => array('desc' => __('A checksum in sha1 format'), 'formdesc' => __("You are encouraged to use filename|sha1 instead. A checksum in sha1 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha256' => array('desc' => __('A checksum in sha256 format'), 'formdesc' => __("You are encouraged to use filename|sha256 instead. A checksum in sha256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename' => array('desc' => __('Filename'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'pdb' => array('desc' => __('Microsoft Program database (PDB) path information'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'filename|md5' => array('desc' => __('A filename and an md5 hash separated by a |'), 'formdesc' => __("A filename and an md5 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha1' => array('desc' => __('A filename and an sha1 hash separated by a |'), 'formdesc' => __("A filename and an sha1 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha256' => array('desc' => __('A filename and an sha256 hash separated by a |'), 'formdesc' => __("A filename and an sha256 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'ip-src' => array('desc' => __("A source IP address of the attacker"), 'default_category' => 'Network activity', 'to_ids' => 1), - 'ip-dst' => array('desc' => __('A destination IP address of the attacker or C&C server'), 'formdesc' => __("A destination IP address of the attacker or C&C server. Also set the IDS flag on when this IP is hardcoded in malware"), 'default_category' => 'Network activity', 'to_ids' => 1), - 'hostname' => array('desc' => __('A full host/dnsname of an attacker'), 'formdesc' => __("A full host/dnsname of an attacker. Also set the IDS flag on when this hostname is hardcoded in malware"), 'default_category' => 'Network activity', 'to_ids' => 1), - 'domain' => array('desc' => __('A domain name used in the malware'), 'formdesc' => __("A domain name used in the malware. Use this instead of hostname when the upper domain is important or can be used to create links between events."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'domain|ip' => array('desc' => __('A domain name and its IP address (as found in DNS lookup) separated by a |'),'formdesc' => __("A domain name and its IP address (as found in DNS lookup) separated by a | (no spaces)"), 'default_category' => 'Network activity', 'to_ids' => 1), - 'email' => array('desc' => ('An e-mail address'), 'default_category' => 'Social network', 'to_ids' => 1), - 'email-src' => array('desc' => __("The source email address. Used to describe the sender when describing an e-mail."), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'eppn' => array('desc' => __("eduPersonPrincipalName - eppn - the NetId of the person for the purposes of inter-institutional authentication. Should be stored in the form of user@univ.edu, where univ.edu is the name of the local security domain."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'email-dst' => array('desc' => __("The destination email address. Used to describe the recipient when describing an e-mail."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'email-subject' => array('desc' => __("The subject of the email"), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-attachment' => array('desc' => __("File name of the email attachment."), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'email-body' => array('desc' => __('Email body'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'float' => array('desc' => __("A floating point value."), 'default_category' => 'Other', 'to_ids' => 0), - 'git-commit-id' => array('desc' => __("A git commit ID."), 'default_category' => 'Internal reference', 'to_ids' => 0), - 'url' => array('desc' => __('url'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'http-method' => array('desc' => __("HTTP method used by the malware (e.g. POST, GET, ...)."), 'default_category' => 'Network activity', 'to_ids' => 0), - 'user-agent' => array('desc' => __("The user-agent used by the malware in the HTTP request."), 'default_category' => 'Network activity', 'to_ids' => 0), - 'ja3-fingerprint-md5' => array('desc' => __("JA3 is a method for creating SSL/TLS client fingerprints that should be easy to produce on any platform and can be easily shared for threat intelligence."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'jarm-fingerprint' => array('desc' => __("JARM is a method for creating SSL/TLS server fingerprints."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'favicon-mmh3' => array('desc' => __("favicon-mmh3 is the murmur3 hash of a favicon as used in Shodan."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'hassh-md5' => array('desc' => __("hassh is a network fingerprinting standard which can be used to identify specific Client SSH implementations. The fingerprints can be easily stored, searched and shared in the form of an MD5 fingerprint."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'hasshserver-md5' => array('desc' => __("hasshServer is a network fingerprinting standard which can be used to identify specific Server SSH implementations. The fingerprints can be easily stored, searched and shared in the form of an MD5 fingerprint."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'regkey' => array('desc' => __("Registry key or value"), 'default_category' => 'Persistence mechanism', 'to_ids' => 1), - 'regkey|value' => array('desc' => __("Registry value + data separated by |"), 'default_category' => 'Persistence mechanism', 'to_ids' => 1), - 'AS' => array('desc' => __('Autonomous system'), 'default_category' => 'Network activity', 'to_ids' => 0), - 'snort' => array('desc' => __('An IDS rule in Snort rule-format'), 'formdesc' => __("An IDS rule in Snort rule-format. This rule will be automatically rewritten in the NIDS exports."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'bro' => array('desc' => __('An NIDS rule in the Bro rule-format'), 'formdesc' => __("An NIDS rule in the Bro rule-format."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'zeek' => array('desc' => __('An NIDS rule in the Zeek rule-format'), 'formdesc' => __("An NIDS rule in the Zeek rule-format."), 'default_category' => 'Network activity', 'to_ids' => 1), - 'community-id' => array('desc' => __('a community ID flow hashing algorithm to map multiple traffic monitors into common flow id'), 'formdesc' => __("a community ID flow hashing algorithm to map multiple traffic monitors into common flow id"), 'default_category' => 'Network activity', 'to_ids' => 1), - 'pattern-in-file' => array('desc' => __('Pattern in file that identifies the malware'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'pattern-in-traffic' => array('desc' => __('Pattern in network traffic that identifies the malware'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'pattern-in-memory' => array('desc' => __('Pattern in memory dump that identifies the malware'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'pattern-filename' => array('desc' => __('A pattern in the name of a file'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'pgp-public-key' => array('desc' => __('A PGP public key'), 'default_category' => 'Person', 'to_ids' => 0), - 'pgp-private-key' => array('desc' => __('A PGP private key'), 'default_category' => 'Person', 'to_ids' => 0), - 'yara' => array('desc' => __('Yara signature'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'stix2-pattern' => array('desc' => __('STIX 2 pattern'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'sigma' => array('desc' => __('Sigma - Generic Signature Format for SIEM Systems'), 'default_category' => 'Payload installation', 'to_ids' => 1), - 'gene' => array('desc' => __('GENE - Go Evtx sigNature Engine'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'kusto-query' => array('desc' => __('Kusto query - Kusto from Microsoft Azure is a service for storing and running interactive analytics over Big Data.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'mime-type' => array('desc' => __('A media type (also MIME type and content type) is a two-part identifier for file formats and format contents transmitted on the Internet'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'identity-card-number' => array('desc' => __('Identity card number'), 'default_category' => 'Person', 'to_ids' => 0), - 'cookie' => array('desc' => __('HTTP cookie as often stored on the user web client. This can include authentication cookie or session cookie.'), 'default_category' => 'Network activity', 'to_ids' => 0), - 'vulnerability' => array('desc' => __('A reference to the vulnerability used in the exploit'), 'default_category' => 'External analysis', 'to_ids' => 0), - 'cpe' => array('desc' => __('Common Platform Enumeration - structured naming scheme for information technology systems, software, and packages.'), 'default_category' => 'External analysis', 'to_ids' => 0), - 'weakness' => array('desc'=> __('A reference to the weakness used in the exploit'), 'default_category' => 'External analysis', 'to_ids' => 0), - 'attachment' => array('desc' => __('Attachment with external information'), 'formdesc' => __("Please upload files using the Upload Attachment button."), 'default_category' => 'External analysis', 'to_ids' => 0), - 'malware-sample' => array('desc' => __('Attachment containing encrypted malware sample'), 'formdesc' => __("Please upload files using the Upload Attachment button."), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'link' => array('desc' => __('Link to an external information'), 'default_category' => 'External analysis', 'to_ids' => 0), - 'comment' => array('desc' => __('Comment or description in a human language'), 'formdesc' => __('Comment or description in a human language. This will not be correlated with other attributes'), 'default_category' => 'Other', 'to_ids' => 0), - 'text' => array('desc' => __('Name, ID or a reference'), 'default_category' => 'Other', 'to_ids' => 0), - 'hex' => array('desc' => __('A value in hexadecimal format'), 'default_category' => 'Other', 'to_ids' => 0), - 'other' => array('desc' => __('Other attribute'), 'default_category' => 'Other', 'to_ids' => 0), - 'named pipe' => array('desc' => __('Named pipe, use the format \\.\pipe\'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'mutex' => array('desc' => __('Mutex, use the format \BaseNamedObjects\'), 'default_category' => 'Artifacts dropped', 'to_ids' => 1), - 'process-state' => array('desc' => __('State of a process'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'target-user' => array('desc' => __('Attack Targets Username(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'target-email' => array('desc' => __('Attack Targets Email(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'target-machine' => array('desc' => __('Attack Targets Machine Name(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'target-org' => array('desc' => __('Attack Targets Department or Organization(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'target-location' => array('desc' => __('Attack Targets Physical Location(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'target-external' => array('desc' => __('External Target Organizations Affected by this Attack'), 'default_category' => 'Targeting data', 'to_ids' => 0), - 'btc' => array('desc' => __('Bitcoin Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'dash' => array('desc' => __('Dash Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'xmr' => array('desc' => __('Monero Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'iban' => array('desc' => __('International Bank Account Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'bic' => array('desc' => __('Bank Identifier Code Number also known as SWIFT-BIC, SWIFT code or ISO 9362 code'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'bank-account-nr' => array('desc' => __('Bank account number without any routing number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'aba-rtn' => array('desc' => __('ABA routing transit number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'bin' => array('desc' => __('Bank Identification Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'cc-number' => array('desc' => __('Credit-Card Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'prtn' => array('desc' => __('Premium-Rate Telephone Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), - 'phone-number' => array('desc' => __('Telephone Number'), 'default_category' => 'Person', 'to_ids' => 0), - 'threat-actor' => array('desc' => __('A string identifying the threat actor'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'campaign-name' => array('desc' => __('Associated campaign name'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'campaign-id' => array('desc' => __('Associated campaign ID'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'malware-type' => array('desc' => '', 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'uri' => array('desc' => __('Uniform Resource Identifier'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'authentihash' => array('desc' => __('Authenticode executable signature hash'), 'formdesc' => __("You are encouraged to use filename|authentihash instead. Authenticode executable signature hash, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'vhash' => array('desc' => __('A VirusTotal checksum'), 'formdesc' => __("You are encouraged to use filename|vhash instead. A checksum from VirusTotal, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'ssdeep' => array('desc' => __('A checksum in ssdeep format'), 'formdesc' => __("You are encouraged to use filename|ssdeep instead. A checksum in the SSDeep format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'imphash' => array('desc' => __('Import hash - a hash created based on the imports in the sample.'), 'formdesc' => __("You are encouraged to use filename|imphash instead. A hash created based on the imports in the sample, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'telfhash' => array('desc' => __('telfhash is symbol hash for ELF files, just like imphash is imports hash for PE files.'), 'formdesc' => __("You are encouraged to use a file object with telfash"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'pehash' => array('desc' => __('PEhash - a hash calculated based of certain pieces of a PE executable file'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'impfuzzy' => array('desc' => __('A fuzzy hash of import table of Portable Executable format'), 'formdesc' => __("You are encouraged to use filename|impfuzzy instead. A fuzzy hash created based on the imports in the sample, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha224' => array('desc' => __('A checksum in sha-224 format'), 'formdesc' => __("You are encouraged to use filename|sha224 instead. A checksum in sha224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha384' => array('desc' => __('A checksum in sha-384 format'), 'formdesc' => __("You are encouraged to use filename|sha384 instead. A checksum in sha384 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha512' => array('desc' => __('A checksum in sha-512 format'), 'formdesc' => __("You are encouraged to use filename|sha512 instead. A checksum in sha512 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha512/224' => array('desc' => __('A checksum in the sha-512/224 format'), 'formdesc' => __("You are encouraged to use filename|sha512/224 instead. A checksum in sha512/224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha512/256' => array('desc' => __('A checksum in the sha-512/256 format'), 'formdesc' => __("You are encouraged to use filename|sha512/256 instead. A checksum in sha512/256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha3-224' => array('desc' => __('A checksum in sha3-224 format'), 'formdesc' => __("You are encouraged to use filename|sha3-224 instead. A checksum in sha3-224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha3-256' => array('desc' => __('A checksum in sha3-256 format'), 'formdesc' => __("You are encouraged to use filename|sha3-256 instead. A checksum in sha3-256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha3-384' => array('desc' => __('A checksum in sha3-384 format'), 'formdesc' => __("You are encouraged to use filename|sha3-384 instead. A checksum in sha3-384 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'sha3-512' => array('desc' => __('A checksum in sha3-512 format'), 'formdesc' => __("You are encouraged to use filename|sha3-512 instead. A checksum in sha3-512 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'tlsh' => array('desc' => __('A checksum in the Trend Micro Locality Sensitive Hash format'), 'formdesc' => __("You are encouraged to use filename|tlsh instead. A checksum in the Trend Micro Locality Sensitive Hash format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'cdhash' => array('desc' => __('An Apple Code Directory Hash, identifying a code-signed Mach-O executable file'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|authentihash' => array('desc' => __('A checksum in md5 format'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|vhash' => array('desc' => __('A filename and a VirusTotal hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|ssdeep' => array('desc' => __('A checksum in ssdeep format'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|imphash' => array('desc' => __('Import hash - a hash created based on the imports in the sample.'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|impfuzzy' => array('desc' => __('Import fuzzy hash - a fuzzy hash created based on the imports in the sample.'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|pehash' => array('desc' => __('A filename and a PEhash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha224' => array('desc' => __('A filename and a sha-224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha384' => array('desc' => __('A filename and a sha-384 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha512' => array('desc' => __('A filename and a sha-512 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha512/224' => array('desc' => __('A filename and a sha-512/224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha512/256' => array('desc' => __('A filename and a sha-512/256 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha3-224' => array('desc' => __('A filename and an sha3-224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha3-256' => array('desc' => __('A filename and an sha3-256 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha3-384' => array('desc' => __('A filename and an sha3-384 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|sha3-512' => array('desc' => __('A filename and an sha3-512 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'filename|tlsh' => array('desc' => __('A filename and a Trend Micro Locality Sensitive Hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'windows-scheduled-task' => array('desc' => __('A scheduled task in windows'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'windows-service-name' => array('desc' => __('A windows service name. This is the name used internally by windows. Not to be confused with the windows-service-displayname.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'windows-service-displayname' => array('desc' => __('A windows service\'s displayname, not to be confused with the windows-service-name. This is the name that applications will generally display as the service\'s name in applications.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), - 'whois-registrant-email' => array('desc' => __('The e-mail of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'whois-registrant-phone' => array('desc' => __('The phone number of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'whois-registrant-name' => array('desc' => __('The name of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'whois-registrant-org' => array('desc' => __('The org of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'whois-registrar' => array('desc' => __('The registrar of the domain, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'whois-creation-date' => array('desc' => __('The date of domain\'s creation, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), - // 'targeted-threat-index' => array('desc' => ''), // currently not mapped! - // 'mailslot' => array('desc' => 'MailSlot interprocess communication'), // currently not mapped! - // 'pipe' => array('desc' => 'Pipeline (for named pipes use the attribute type "named pipe")'), // currently not mapped! - // 'ssl-cert-attributes' => array('desc' => 'SSL certificate attributes'), // currently not mapped! - 'x509-fingerprint-sha1' => array('desc' => __('X509 fingerprint in SHA-1 format'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'x509-fingerprint-md5' => array('desc' => __('X509 fingerprint in MD5 format'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'x509-fingerprint-sha256' => array('desc' => __('X509 fingerprint in SHA-256 format'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'dns-soa-email' => array('desc' => __('RFC1035 mandates that DNS zones should have a SOA (Statement Of Authority) record that contains an email address where a PoC for the domain could be contacted. This can sometimes be used for attribution/linkage between different domains even if protected by whois privacy'), 'default_category' => 'Attribution', 'to_ids' => 0), - 'size-in-bytes' => array('desc' => __('Size expressed in bytes'), 'default_category' => 'Other', 'to_ids' => 0), - 'counter' => array('desc' => __('An integer counter, generally to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0), - 'datetime' => array('desc' => __('Datetime in the ISO 8601 format'), 'default_category' => 'Other', 'to_ids' => 0), - 'port' => array('desc' => __('Port number'), 'default_category' => 'Network activity', 'to_ids' => 0), - 'ip-dst|port' => array('desc' => __('IP destination and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'ip-src|port' => array('desc' => __('IP source and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'hostname|port' => array('desc' => __('Hostname and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), - 'mac-address' => array('desc' => __('Mac address'), 'default_category' => 'Network activity', 'to_ids' => 0), - 'mac-eui-64' => array('desc' => __('Mac EUI-64 address'), 'default_category' => 'Network activity', 'to_ids' => 0), - // verify IDS flag defaults for these - 'email-dst-display-name' => array('desc' => __('Email destination display name'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-src-display-name' => array('desc' => __('Email source display name'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-header' => array('desc' => __('Email header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-reply-to' => array('desc' => __('Email reply to header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-x-mailer' => array('desc' => __('Email x-mailer header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-mime-boundary' => array('desc' => __('The email mime boundary separating parts in a multipart email'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-thread-index' => array('desc' => __('The email thread index header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'email-message-id' => array('desc' => __('The email message ID'), 'default_category' => 'Payload delivery', 'to_ids' => 0), - 'github-username' => array('desc' => __('A github user name'), 'default_category' => 'Social network', 'to_ids' => 0), - 'github-repository' => array('desc' => __('A github repository'), 'default_category' => 'Social network', 'to_ids' => 0), - 'github-organisation' => array('desc' => __('A github organisation'), 'default_category' => 'Social network', 'to_ids' => 0), - 'jabber-id' => array('desc' => __('Jabber ID'), 'default_category' => 'Social network', 'to_ids' => 0), - 'twitter-id' => array('desc' => __('Twitter ID'), 'default_category' => 'Social network', 'to_ids' => 0), - 'first-name' => array('desc' => __('First name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'middle-name' => array('desc' => __('Middle name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'last-name' => array('desc' => __('Last name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'date-of-birth' => array('desc' => __('Date of birth of a natural person (in YYYY-MM-DD format)'), 'default_category' => 'Person', 'to_ids' => 0), - 'place-of-birth' => array('desc' => __('Place of birth of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'gender' => array('desc' => __('The gender of a natural person (Male, Female, Other, Prefer not to say)'), 'default_category' => 'Person', 'to_ids' => 0), - 'passport-number' => array('desc' => __('The passport number of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'passport-country' => array('desc' => __('The country in which the passport was issued'), 'default_category' => 'Person', 'to_ids' => 0), - 'passport-expiration' => array('desc' => __('The expiration date of a passport'), 'default_category' => 'Person', 'to_ids' => 0), - 'redress-number' => array('desc' => __('The Redress Control Number is the record identifier for people who apply for redress through the DHS Travel Redress Inquiry Program (DHS TRIP). DHS TRIP is for travelers who have been repeatedly identified for additional screening and who want to file an inquiry to have erroneous information corrected in DHS systems'), 'default_category' => 'Person', 'to_ids' => 0), - 'nationality' => array('desc' => __('The nationality of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'visa-number' => array('desc' => __('Visa number'), 'default_category' => 'Person', 'to_ids' => 0), - 'issue-date-of-the-visa' => array('desc' => __('The date on which the visa was issued'), 'default_category' => 'Person', 'to_ids' => 0), - 'primary-residence' => array('desc' => __('The primary residence of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'country-of-residence' => array('desc' => __('The country of residence of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), - 'special-service-request' => array('desc' => __('A Special Service Request is a function to an airline to provide a particular facility for A Passenger or passengers. '), 'default_category' => 'Person', 'to_ids' => 0), - 'frequent-flyer-number' => array('desc' => __('The frequent flyer number of a passenger'), 'default_category' => 'Person', 'to_ids' => 0), - // Do we really need remarks? Or just use comment/text for this? - //'remarks' => array('desc' => '', 'default_category' => 'Person', 'to_ids' => 0), - 'travel-details' => array('desc' => __('Travel details'), 'default_category' => 'Person', 'to_ids' => 0), - 'payment-details' => array('desc' => __('Payment details'), 'default_category' => 'Person', 'to_ids' => 0), - 'place-port-of-original-embarkation' => array('desc' => __('The orignal port of embarkation'), 'default_category' => 'Person', 'to_ids' => 0), - 'place-port-of-clearance' => array('desc' => __('The port of clearance'), 'default_category' => 'Person', 'to_ids' => 0), - 'place-port-of-onward-foreign-destination' => array('desc' => __('A Port where the passenger is transiting to'), 'default_category' => 'Person', 'to_ids' => 0), - 'passenger-name-record-locator-number' => array('desc' => __('The Passenger Name Record Locator is a key under which the reservation for a trip is stored in the system. The PNR contains, among other data, the name, flight segments and address of the passenger. It is defined by a combination of five or six letters and numbers.'), 'default_category' => 'Person', 'to_ids' => 0), - 'mobile-application-id' => array('desc' => __('The application id of a mobile application'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'chrome-extension-id' => array('desc' => __('Chrome extension id'), 'default_category' => 'Payload delivery', 'to_ids' => 1), - 'cortex' => array('desc' => __('Cortex analysis result'), 'default_category' => 'External analysis', 'to_ids' => 0), - 'boolean' => array('desc' => __('Boolean value - to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0), - 'anonymised' => array('desc' => __('Anonymised value - described with the anonymisation object via a relationship'), 'formdesc' => __('Anonymised value - described with the anonymisation object via a relationship.'), 'default_category' => 'Other', 'to_ids' => 0) - // Not convinced about this. - //'url-regex' => array('desc' => '', 'default_category' => 'Person', 'to_ids' => 0), - ); } @@ -1014,7 +736,7 @@ class Attribute extends AppModel { $category = $this->data['Attribute']['category']; if (isset($this->categoryDefinitions[$category]['types'])) { - return in_array($fields['type'], $this->categoryDefinitions[$category]['types']); + return in_array($fields['type'], $this->categoryDefinitions[$category]['types'], true); } return false; } @@ -4904,4 +4626,311 @@ class Attribute extends AppModel } return $typeCategoryMapping; } + + public function __isset($name) + { + if ($name === 'typeDefinitions' || $name === 'categoryDefinitions') { + return true; + } + return parent::__isset($name); + } + + public function __get($name) + { + if ($name === 'typeDefinitions') { + $this->typeDefinitions = $this->generateTypeDefinitions(); + return $this->typeDefinitions; + } else if ($name === 'categoryDefinitions') { + $this->categoryDefinitions = $this->generateCategoryDefintions(); + return $this->categoryDefinitions; + } + return parent::__get($name); + } + + /** + * Generate just when really need + * NOTE WHEN MODIFYING: please ensure to run the script 'tools/gen_misp_types_categories.py' to update the new definitions everywhere. (docu, website, RFC, ... ) + * @return array[] + */ + private function generateCategoryDefintions() + { + return array( + 'Internal reference' => array( + 'desc' => __('Reference used by the publishing party (e.g. ticket number)'), + 'types' => array('text', 'link', 'comment', 'other', 'hex', 'anonymised', 'git-commit-id') + ), + 'Targeting data' => array( + 'desc' => __('Internal Attack Targeting and Compromise Information'), + 'formdesc' => __('Targeting information to include recipient email, infected machines, department, and or locations.'), + 'types' => array('target-user', 'target-email', 'target-machine', 'target-org', 'target-location', 'target-external', 'comment', 'anonymised') + ), + 'Antivirus detection' => array( + 'desc' => __('All the info about how the malware is detected by the antivirus products'), + 'formdesc' => __('List of anti-virus vendors detecting the malware or information on detection performance (e.g. 13/43 or 67%). Attachment with list of detection or link to VirusTotal could be placed here as well.'), + 'types' => array('link', 'comment', 'text', 'hex', 'attachment', 'other', 'anonymised') + ), + 'Payload delivery' => array( + 'desc' => __('Information about how the malware is delivered'), + 'formdesc' => __('Information about the way the malware payload is initially delivered, for example information about the email or web-site, vulnerability used, originating IP etc. Malware sample itself should be attached here.'), + 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'pehash', 'tlsh', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash','filename|impfuzzy', 'filename|pehash', 'mac-address', 'mac-eui-64', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'hostname', 'domain', 'email', 'email-src', 'email-dst', 'email-subject', 'email-attachment', 'email-body', 'url', 'user-agent', 'AS', 'pattern-in-file', 'pattern-in-traffic', 'filename-pattern', 'stix2-pattern', 'yara', 'sigma', 'mime-type', 'attachment', 'malware-sample', 'link', 'malware-type', 'comment', 'text', 'hex', 'vulnerability', 'cpe', 'weakness', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'other', 'hostname|port', 'email-dst-display-name', 'email-src-display-name', 'email-header', 'email-reply-to', 'email-x-mailer', 'email-mime-boundary', 'email-thread-index', 'email-message-id', 'mobile-application-id', 'chrome-extension-id', 'whois-registrant-email', 'anonymised') + ), + 'Artifacts dropped' => array( + 'desc' => __('Any artifact (files, registry keys etc.) dropped by the malware or other modifications to the system'), + 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash', 'filename|impfuzzy','filename|pehash', 'regkey', 'regkey|value', 'pattern-in-file', 'pattern-in-memory', 'filename-pattern', 'pdb', 'stix2-pattern', 'yara', 'sigma', 'attachment', 'malware-sample', 'named pipe', 'mutex', 'process-state','windows-scheduled-task', 'windows-service-name', 'windows-service-displayname', 'comment', 'text', 'hex', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'other', 'cookie', 'gene', 'kusto-query', 'mime-type', 'anonymised', 'pgp-public-key', 'pgp-private-key') + ), + 'Payload installation' => array( + 'desc' => __('Info on where the malware gets installed in the system'), + 'formdesc' => __('Location where the payload was placed in the system and the way it was installed. For example, a filename|md5 type attribute can be added here like this: c:\\windows\\system32\\malicious.exe|41d8cd98f00b204e9800998ecf8427e.'), + 'types' => array('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'sha512/224', 'sha512/256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'ssdeep', 'imphash', 'telfhash', 'impfuzzy', 'authentihash', 'vhash', 'pehash', 'tlsh', 'cdhash', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha224', 'filename|sha256', 'filename|sha384', 'filename|sha512', 'filename|sha512/224', 'filename|sha512/256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'filename|authentihash', 'filename|vhash', 'filename|ssdeep', 'filename|tlsh', 'filename|imphash', 'filename|impfuzzy', 'filename|pehash', 'pattern-in-file', 'pattern-in-traffic', 'pattern-in-memory', 'filename-pattern', 'stix2-pattern', 'yara', 'sigma', 'vulnerability', 'cpe','weakness', 'attachment', 'malware-sample', 'malware-type', 'comment', 'text', 'hex', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'mobile-application-id', 'chrome-extension-id', 'other', 'mime-type', 'anonymised') + ), + 'Persistence mechanism' => array( + 'desc' => __('Mechanisms used by the malware to start at boot'), + 'formdesc' => __('Mechanisms used by the malware to start at boot. This could be a registry key, legitimate driver modification, LNK file in startup'), + 'types' => array('filename', 'regkey', 'regkey|value', 'comment', 'text', 'other', 'hex', 'anonymised') + ), + 'Network activity' => array( + 'desc' => __('Information about network traffic generated by the malware'), + 'types' => array('ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'port', 'hostname', 'domain', 'domain|ip', 'mac-address', 'mac-eui-64', 'email', 'email-dst', 'email-src', 'eppn', 'url', 'uri', 'user-agent', 'http-method', 'AS', 'snort', 'pattern-in-file', 'filename-pattern','stix2-pattern', 'pattern-in-traffic', 'attachment', 'comment', 'text', 'x509-fingerprint-md5', 'x509-fingerprint-sha1', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'other', 'hex', 'cookie', 'hostname|port', 'bro', 'zeek', 'anonymised', 'community-id', 'email-subject', 'favicon-mmh3') + ), + 'Payload type' => array( + 'desc' => __('Information about the final payload(s)'), + 'formdesc' => __('Information about the final payload(s). Can contain a function of the payload, e.g. keylogger, RAT, or a name if identified, such as Poison Ivy.'), + 'types' => array('comment', 'text', 'other', 'anonymised') + ), + 'Attribution' => array( + 'desc' => __('Identification of the group, organisation, or country behind the attack'), + 'types' => array('threat-actor', 'campaign-name', 'campaign-id', 'whois-registrant-phone', 'whois-registrant-email', 'whois-registrant-name', 'whois-registrant-org', 'whois-registrar', 'whois-creation-date','comment', 'text', 'x509-fingerprint-sha1','x509-fingerprint-md5', 'x509-fingerprint-sha256', 'other', 'dns-soa-email', 'anonymised', 'email') + ), + 'External analysis' => array( + 'desc' => __('Any other result from additional analysis of the malware like tools output'), + 'formdesc' => __('Any other result from additional analysis of the malware like tools output Examples: pdf-parser output, automated sandbox analysis, reverse engineering report.'), + 'types' => array('md5', 'sha1', 'sha256', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512', 'filename', 'filename|md5', 'filename|sha1', 'filename|sha256', 'filename|sha3-224', 'filename|sha3-256', 'filename|sha3-384', 'filename|sha3-512', 'ip-src', 'ip-dst', 'ip-dst|port', 'ip-src|port', 'mac-address', 'mac-eui-64', 'hostname', 'domain', 'domain|ip', 'url', 'user-agent', 'regkey', 'regkey|value', 'AS', 'snort', 'bro', 'zeek', 'pattern-in-file', 'pattern-in-traffic', 'pattern-in-memory', 'filename-pattern','vulnerability', 'cpe', 'weakness', 'attachment', 'malware-sample', 'link', 'comment', 'text', 'x509-fingerprint-sha1', 'x509-fingerprint-md5', 'x509-fingerprint-sha256', 'ja3-fingerprint-md5', 'jarm-fingerprint', 'hassh-md5', 'hasshserver-md5', 'github-repository', 'other', 'cortex', 'anonymised', 'community-id') + ), + 'Financial fraud' => array( + 'desc' => __('Financial Fraud indicators'), + 'formdesc' => __('Financial Fraud indicators, for example: IBAN Numbers, BIC codes, Credit card numbers, etc.'), + 'types' => array('btc', 'dash', 'xmr', 'iban', 'bic', 'bank-account-nr', 'aba-rtn', 'bin', 'cc-number', 'prtn', 'phone-number', 'comment', 'text', 'other', 'hex', 'anonymised'), + ), + 'Support Tool' => array( + 'desc' => __('Tools supporting analysis or detection of the event'), + 'types' => array('link', 'text', 'attachment', 'comment', 'other', 'hex', 'anonymised') + ), + 'Social network' => array( + 'desc' => __('Social networks and platforms'), + // email-src and email-dst or should we go with a new email type that is neither / both? + 'types' => array('github-username', 'github-repository', 'github-organisation', 'jabber-id', 'twitter-id', 'email', 'email-src', 'email-dst', 'eppn','comment', 'text', 'other', 'whois-registrant-email', 'anonymised', 'pgp-public-key', 'pgp-private-key') + ), + 'Person' => array( + 'desc' => __('A human being - natural person'), + 'types' => array('first-name', 'middle-name', 'last-name', 'date-of-birth', 'place-of-birth', 'gender', 'passport-number', 'passport-country', 'passport-expiration', 'redress-number', 'nationality', 'visa-number', 'issue-date-of-the-visa', 'primary-residence', 'country-of-residence', 'special-service-request', 'frequent-flyer-number', 'travel-details', 'payment-details', 'place-port-of-original-embarkation', 'place-port-of-clearance', 'place-port-of-onward-foreign-destination', 'passenger-name-record-locator-number', 'comment', 'text', 'other', 'phone-number', 'identity-card-number', 'anonymised', 'email', 'pgp-public-key', 'pgp-private-key') + ), + 'Other' => array( + 'desc' => __('Attributes that are not part of any other category or are meant to be used as a component in MISP objects in the future'), + 'types' => array('comment', 'text', 'other', 'size-in-bytes', 'counter', 'datetime', 'cpe', 'port', 'float', 'hex', 'phone-number', 'boolean', 'anonymised', 'pgp-public-key', 'pgp-private-key') + ) + ); + } + + /** + * Generate just when really need + * NOTE WHEN MODIFYING: please ensure to run the script 'tools/gen_misp_types_categories.py' to update the new definitions everywhere. (docu, website, RFC, ... ) + * @return array[] + */ + private function generateTypeDefinitions() + { + return array( + 'md5' => array('desc' => __('A checksum in md5 format'), 'formdesc' => __("You are encouraged to use filename|md5 instead. A checksum in md5 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha1' => array('desc' => __('A checksum in sha1 format'), 'formdesc' => __("You are encouraged to use filename|sha1 instead. A checksum in sha1 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha256' => array('desc' => __('A checksum in sha256 format'), 'formdesc' => __("You are encouraged to use filename|sha256 instead. A checksum in sha256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename' => array('desc' => __('Filename'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'pdb' => array('desc' => __('Microsoft Program database (PDB) path information'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'filename|md5' => array('desc' => __('A filename and an md5 hash separated by a |'), 'formdesc' => __("A filename and an md5 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha1' => array('desc' => __('A filename and an sha1 hash separated by a |'), 'formdesc' => __("A filename and an sha1 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha256' => array('desc' => __('A filename and an sha256 hash separated by a |'), 'formdesc' => __("A filename and an sha256 hash separated by a | (no spaces)"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'ip-src' => array('desc' => __("A source IP address of the attacker"), 'default_category' => 'Network activity', 'to_ids' => 1), + 'ip-dst' => array('desc' => __('A destination IP address of the attacker or C&C server'), 'formdesc' => __("A destination IP address of the attacker or C&C server. Also set the IDS flag on when this IP is hardcoded in malware"), 'default_category' => 'Network activity', 'to_ids' => 1), + 'hostname' => array('desc' => __('A full host/dnsname of an attacker'), 'formdesc' => __("A full host/dnsname of an attacker. Also set the IDS flag on when this hostname is hardcoded in malware"), 'default_category' => 'Network activity', 'to_ids' => 1), + 'domain' => array('desc' => __('A domain name used in the malware'), 'formdesc' => __("A domain name used in the malware. Use this instead of hostname when the upper domain is important or can be used to create links between events."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'domain|ip' => array('desc' => __('A domain name and its IP address (as found in DNS lookup) separated by a |'),'formdesc' => __("A domain name and its IP address (as found in DNS lookup) separated by a | (no spaces)"), 'default_category' => 'Network activity', 'to_ids' => 1), + 'email' => array('desc' => ('An e-mail address'), 'default_category' => 'Social network', 'to_ids' => 1), + 'email-src' => array('desc' => __("The source email address. Used to describe the sender when describing an e-mail."), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'eppn' => array('desc' => __("eduPersonPrincipalName - eppn - the NetId of the person for the purposes of inter-institutional authentication. Should be stored in the form of user@univ.edu, where univ.edu is the name of the local security domain."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'email-dst' => array('desc' => __("The destination email address. Used to describe the recipient when describing an e-mail."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'email-subject' => array('desc' => __("The subject of the email"), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-attachment' => array('desc' => __("File name of the email attachment."), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'email-body' => array('desc' => __('Email body'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'float' => array('desc' => __("A floating point value."), 'default_category' => 'Other', 'to_ids' => 0), + 'git-commit-id' => array('desc' => __("A git commit ID."), 'default_category' => 'Internal reference', 'to_ids' => 0), + 'url' => array('desc' => __('url'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'http-method' => array('desc' => __("HTTP method used by the malware (e.g. POST, GET, ...)."), 'default_category' => 'Network activity', 'to_ids' => 0), + 'user-agent' => array('desc' => __("The user-agent used by the malware in the HTTP request."), 'default_category' => 'Network activity', 'to_ids' => 0), + 'ja3-fingerprint-md5' => array('desc' => __("JA3 is a method for creating SSL/TLS client fingerprints that should be easy to produce on any platform and can be easily shared for threat intelligence."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'jarm-fingerprint' => array('desc' => __("JARM is a method for creating SSL/TLS server fingerprints."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'favicon-mmh3' => array('desc' => __("favicon-mmh3 is the murmur3 hash of a favicon as used in Shodan."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'hassh-md5' => array('desc' => __("hassh is a network fingerprinting standard which can be used to identify specific Client SSH implementations. The fingerprints can be easily stored, searched and shared in the form of an MD5 fingerprint."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'hasshserver-md5' => array('desc' => __("hasshServer is a network fingerprinting standard which can be used to identify specific Server SSH implementations. The fingerprints can be easily stored, searched and shared in the form of an MD5 fingerprint."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'regkey' => array('desc' => __("Registry key or value"), 'default_category' => 'Persistence mechanism', 'to_ids' => 1), + 'regkey|value' => array('desc' => __("Registry value + data separated by |"), 'default_category' => 'Persistence mechanism', 'to_ids' => 1), + 'AS' => array('desc' => __('Autonomous system'), 'default_category' => 'Network activity', 'to_ids' => 0), + 'snort' => array('desc' => __('An IDS rule in Snort rule-format'), 'formdesc' => __("An IDS rule in Snort rule-format. This rule will be automatically rewritten in the NIDS exports."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'bro' => array('desc' => __('An NIDS rule in the Bro rule-format'), 'formdesc' => __("An NIDS rule in the Bro rule-format."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'zeek' => array('desc' => __('An NIDS rule in the Zeek rule-format'), 'formdesc' => __("An NIDS rule in the Zeek rule-format."), 'default_category' => 'Network activity', 'to_ids' => 1), + 'community-id' => array('desc' => __('a community ID flow hashing algorithm to map multiple traffic monitors into common flow id'), 'formdesc' => __("a community ID flow hashing algorithm to map multiple traffic monitors into common flow id"), 'default_category' => 'Network activity', 'to_ids' => 1), + 'pattern-in-file' => array('desc' => __('Pattern in file that identifies the malware'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'pattern-in-traffic' => array('desc' => __('Pattern in network traffic that identifies the malware'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'pattern-in-memory' => array('desc' => __('Pattern in memory dump that identifies the malware'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'pattern-filename' => array('desc' => __('A pattern in the name of a file'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'pgp-public-key' => array('desc' => __('A PGP public key'), 'default_category' => 'Person', 'to_ids' => 0), + 'pgp-private-key' => array('desc' => __('A PGP private key'), 'default_category' => 'Person', 'to_ids' => 0), + 'yara' => array('desc' => __('Yara signature'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'stix2-pattern' => array('desc' => __('STIX 2 pattern'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'sigma' => array('desc' => __('Sigma - Generic Signature Format for SIEM Systems'), 'default_category' => 'Payload installation', 'to_ids' => 1), + 'gene' => array('desc' => __('GENE - Go Evtx sigNature Engine'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'kusto-query' => array('desc' => __('Kusto query - Kusto from Microsoft Azure is a service for storing and running interactive analytics over Big Data.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'mime-type' => array('desc' => __('A media type (also MIME type and content type) is a two-part identifier for file formats and format contents transmitted on the Internet'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'identity-card-number' => array('desc' => __('Identity card number'), 'default_category' => 'Person', 'to_ids' => 0), + 'cookie' => array('desc' => __('HTTP cookie as often stored on the user web client. This can include authentication cookie or session cookie.'), 'default_category' => 'Network activity', 'to_ids' => 0), + 'vulnerability' => array('desc' => __('A reference to the vulnerability used in the exploit'), 'default_category' => 'External analysis', 'to_ids' => 0), + 'cpe' => array('desc' => __('Common Platform Enumeration - structured naming scheme for information technology systems, software, and packages.'), 'default_category' => 'External analysis', 'to_ids' => 0), + 'weakness' => array('desc'=> __('A reference to the weakness used in the exploit'), 'default_category' => 'External analysis', 'to_ids' => 0), + 'attachment' => array('desc' => __('Attachment with external information'), 'formdesc' => __("Please upload files using the Upload Attachment button."), 'default_category' => 'External analysis', 'to_ids' => 0), + 'malware-sample' => array('desc' => __('Attachment containing encrypted malware sample'), 'formdesc' => __("Please upload files using the Upload Attachment button."), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'link' => array('desc' => __('Link to an external information'), 'default_category' => 'External analysis', 'to_ids' => 0), + 'comment' => array('desc' => __('Comment or description in a human language'), 'formdesc' => __('Comment or description in a human language. This will not be correlated with other attributes'), 'default_category' => 'Other', 'to_ids' => 0), + 'text' => array('desc' => __('Name, ID or a reference'), 'default_category' => 'Other', 'to_ids' => 0), + 'hex' => array('desc' => __('A value in hexadecimal format'), 'default_category' => 'Other', 'to_ids' => 0), + 'other' => array('desc' => __('Other attribute'), 'default_category' => 'Other', 'to_ids' => 0), + 'named pipe' => array('desc' => __('Named pipe, use the format \\.\pipe\'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'mutex' => array('desc' => __('Mutex, use the format \BaseNamedObjects\'), 'default_category' => 'Artifacts dropped', 'to_ids' => 1), + 'process-state' => array('desc' => __('State of a process'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'target-user' => array('desc' => __('Attack Targets Username(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'target-email' => array('desc' => __('Attack Targets Email(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'target-machine' => array('desc' => __('Attack Targets Machine Name(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'target-org' => array('desc' => __('Attack Targets Department or Organization(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'target-location' => array('desc' => __('Attack Targets Physical Location(s)'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'target-external' => array('desc' => __('External Target Organizations Affected by this Attack'), 'default_category' => 'Targeting data', 'to_ids' => 0), + 'btc' => array('desc' => __('Bitcoin Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'dash' => array('desc' => __('Dash Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'xmr' => array('desc' => __('Monero Address'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'iban' => array('desc' => __('International Bank Account Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'bic' => array('desc' => __('Bank Identifier Code Number also known as SWIFT-BIC, SWIFT code or ISO 9362 code'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'bank-account-nr' => array('desc' => __('Bank account number without any routing number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'aba-rtn' => array('desc' => __('ABA routing transit number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'bin' => array('desc' => __('Bank Identification Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'cc-number' => array('desc' => __('Credit-Card Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'prtn' => array('desc' => __('Premium-Rate Telephone Number'), 'default_category' => 'Financial fraud', 'to_ids' => 1), + 'phone-number' => array('desc' => __('Telephone Number'), 'default_category' => 'Person', 'to_ids' => 0), + 'threat-actor' => array('desc' => __('A string identifying the threat actor'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'campaign-name' => array('desc' => __('Associated campaign name'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'campaign-id' => array('desc' => __('Associated campaign ID'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'malware-type' => array('desc' => '', 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'uri' => array('desc' => __('Uniform Resource Identifier'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'authentihash' => array('desc' => __('Authenticode executable signature hash'), 'formdesc' => __("You are encouraged to use filename|authentihash instead. Authenticode executable signature hash, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'vhash' => array('desc' => __('A VirusTotal checksum'), 'formdesc' => __("You are encouraged to use filename|vhash instead. A checksum from VirusTotal, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'ssdeep' => array('desc' => __('A checksum in ssdeep format'), 'formdesc' => __("You are encouraged to use filename|ssdeep instead. A checksum in the SSDeep format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'imphash' => array('desc' => __('Import hash - a hash created based on the imports in the sample.'), 'formdesc' => __("You are encouraged to use filename|imphash instead. A hash created based on the imports in the sample, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'telfhash' => array('desc' => __('telfhash is symbol hash for ELF files, just like imphash is imports hash for PE files.'), 'formdesc' => __("You are encouraged to use a file object with telfash"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'pehash' => array('desc' => __('PEhash - a hash calculated based of certain pieces of a PE executable file'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'impfuzzy' => array('desc' => __('A fuzzy hash of import table of Portable Executable format'), 'formdesc' => __("You are encouraged to use filename|impfuzzy instead. A fuzzy hash created based on the imports in the sample, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha224' => array('desc' => __('A checksum in sha-224 format'), 'formdesc' => __("You are encouraged to use filename|sha224 instead. A checksum in sha224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha384' => array('desc' => __('A checksum in sha-384 format'), 'formdesc' => __("You are encouraged to use filename|sha384 instead. A checksum in sha384 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha512' => array('desc' => __('A checksum in sha-512 format'), 'formdesc' => __("You are encouraged to use filename|sha512 instead. A checksum in sha512 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha512/224' => array('desc' => __('A checksum in the sha-512/224 format'), 'formdesc' => __("You are encouraged to use filename|sha512/224 instead. A checksum in sha512/224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha512/256' => array('desc' => __('A checksum in the sha-512/256 format'), 'formdesc' => __("You are encouraged to use filename|sha512/256 instead. A checksum in sha512/256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha3-224' => array('desc' => __('A checksum in sha3-224 format'), 'formdesc' => __("You are encouraged to use filename|sha3-224 instead. A checksum in sha3-224 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha3-256' => array('desc' => __('A checksum in sha3-256 format'), 'formdesc' => __("You are encouraged to use filename|sha3-256 instead. A checksum in sha3-256 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha3-384' => array('desc' => __('A checksum in sha3-384 format'), 'formdesc' => __("You are encouraged to use filename|sha3-384 instead. A checksum in sha3-384 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'sha3-512' => array('desc' => __('A checksum in sha3-512 format'), 'formdesc' => __("You are encouraged to use filename|sha3-512 instead. A checksum in sha3-512 format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'tlsh' => array('desc' => __('A checksum in the Trend Micro Locality Sensitive Hash format'), 'formdesc' => __("You are encouraged to use filename|tlsh instead. A checksum in the Trend Micro Locality Sensitive Hash format, only use this if you don't know the correct filename"), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'cdhash' => array('desc' => __('An Apple Code Directory Hash, identifying a code-signed Mach-O executable file'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|authentihash' => array('desc' => __('A checksum in md5 format'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|vhash' => array('desc' => __('A filename and a VirusTotal hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|ssdeep' => array('desc' => __('A checksum in ssdeep format'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|imphash' => array('desc' => __('Import hash - a hash created based on the imports in the sample.'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|impfuzzy' => array('desc' => __('Import fuzzy hash - a fuzzy hash created based on the imports in the sample.'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|pehash' => array('desc' => __('A filename and a PEhash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha224' => array('desc' => __('A filename and a sha-224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha384' => array('desc' => __('A filename and a sha-384 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha512' => array('desc' => __('A filename and a sha-512 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha512/224' => array('desc' => __('A filename and a sha-512/224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha512/256' => array('desc' => __('A filename and a sha-512/256 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha3-224' => array('desc' => __('A filename and an sha3-224 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha3-256' => array('desc' => __('A filename and an sha3-256 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha3-384' => array('desc' => __('A filename and an sha3-384 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|sha3-512' => array('desc' => __('A filename and an sha3-512 hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'filename|tlsh' => array('desc' => __('A filename and a Trend Micro Locality Sensitive Hash separated by a |'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'windows-scheduled-task' => array('desc' => __('A scheduled task in windows'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'windows-service-name' => array('desc' => __('A windows service name. This is the name used internally by windows. Not to be confused with the windows-service-displayname.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'windows-service-displayname' => array('desc' => __('A windows service\'s displayname, not to be confused with the windows-service-name. This is the name that applications will generally display as the service\'s name in applications.'), 'default_category' => 'Artifacts dropped', 'to_ids' => 0), + 'whois-registrant-email' => array('desc' => __('The e-mail of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'whois-registrant-phone' => array('desc' => __('The phone number of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'whois-registrant-name' => array('desc' => __('The name of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'whois-registrant-org' => array('desc' => __('The org of a domain\'s registrant, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'whois-registrar' => array('desc' => __('The registrar of the domain, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'whois-creation-date' => array('desc' => __('The date of domain\'s creation, obtained from the WHOIS information.'), 'default_category' => 'Attribution', 'to_ids' => 0), + // 'targeted-threat-index' => array('desc' => ''), // currently not mapped! + // 'mailslot' => array('desc' => 'MailSlot interprocess communication'), // currently not mapped! + // 'pipe' => array('desc' => 'Pipeline (for named pipes use the attribute type "named pipe")'), // currently not mapped! + // 'ssl-cert-attributes' => array('desc' => 'SSL certificate attributes'), // currently not mapped! + 'x509-fingerprint-sha1' => array('desc' => __('X509 fingerprint in SHA-1 format'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'x509-fingerprint-md5' => array('desc' => __('X509 fingerprint in MD5 format'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'x509-fingerprint-sha256' => array('desc' => __('X509 fingerprint in SHA-256 format'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'dns-soa-email' => array('desc' => __('RFC1035 mandates that DNS zones should have a SOA (Statement Of Authority) record that contains an email address where a PoC for the domain could be contacted. This can sometimes be used for attribution/linkage between different domains even if protected by whois privacy'), 'default_category' => 'Attribution', 'to_ids' => 0), + 'size-in-bytes' => array('desc' => __('Size expressed in bytes'), 'default_category' => 'Other', 'to_ids' => 0), + 'counter' => array('desc' => __('An integer counter, generally to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0), + 'datetime' => array('desc' => __('Datetime in the ISO 8601 format'), 'default_category' => 'Other', 'to_ids' => 0), + 'port' => array('desc' => __('Port number'), 'default_category' => 'Network activity', 'to_ids' => 0), + 'ip-dst|port' => array('desc' => __('IP destination and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'ip-src|port' => array('desc' => __('IP source and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'hostname|port' => array('desc' => __('Hostname and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1), + 'mac-address' => array('desc' => __('Mac address'), 'default_category' => 'Network activity', 'to_ids' => 0), + 'mac-eui-64' => array('desc' => __('Mac EUI-64 address'), 'default_category' => 'Network activity', 'to_ids' => 0), + // verify IDS flag defaults for these + 'email-dst-display-name' => array('desc' => __('Email destination display name'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-src-display-name' => array('desc' => __('Email source display name'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-header' => array('desc' => __('Email header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-reply-to' => array('desc' => __('Email reply to header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-x-mailer' => array('desc' => __('Email x-mailer header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-mime-boundary' => array('desc' => __('The email mime boundary separating parts in a multipart email'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-thread-index' => array('desc' => __('The email thread index header'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'email-message-id' => array('desc' => __('The email message ID'), 'default_category' => 'Payload delivery', 'to_ids' => 0), + 'github-username' => array('desc' => __('A github user name'), 'default_category' => 'Social network', 'to_ids' => 0), + 'github-repository' => array('desc' => __('A github repository'), 'default_category' => 'Social network', 'to_ids' => 0), + 'github-organisation' => array('desc' => __('A github organisation'), 'default_category' => 'Social network', 'to_ids' => 0), + 'jabber-id' => array('desc' => __('Jabber ID'), 'default_category' => 'Social network', 'to_ids' => 0), + 'twitter-id' => array('desc' => __('Twitter ID'), 'default_category' => 'Social network', 'to_ids' => 0), + 'first-name' => array('desc' => __('First name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'middle-name' => array('desc' => __('Middle name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'last-name' => array('desc' => __('Last name of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'date-of-birth' => array('desc' => __('Date of birth of a natural person (in YYYY-MM-DD format)'), 'default_category' => 'Person', 'to_ids' => 0), + 'place-of-birth' => array('desc' => __('Place of birth of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'gender' => array('desc' => __('The gender of a natural person (Male, Female, Other, Prefer not to say)'), 'default_category' => 'Person', 'to_ids' => 0), + 'passport-number' => array('desc' => __('The passport number of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'passport-country' => array('desc' => __('The country in which the passport was issued'), 'default_category' => 'Person', 'to_ids' => 0), + 'passport-expiration' => array('desc' => __('The expiration date of a passport'), 'default_category' => 'Person', 'to_ids' => 0), + 'redress-number' => array('desc' => __('The Redress Control Number is the record identifier for people who apply for redress through the DHS Travel Redress Inquiry Program (DHS TRIP). DHS TRIP is for travelers who have been repeatedly identified for additional screening and who want to file an inquiry to have erroneous information corrected in DHS systems'), 'default_category' => 'Person', 'to_ids' => 0), + 'nationality' => array('desc' => __('The nationality of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'visa-number' => array('desc' => __('Visa number'), 'default_category' => 'Person', 'to_ids' => 0), + 'issue-date-of-the-visa' => array('desc' => __('The date on which the visa was issued'), 'default_category' => 'Person', 'to_ids' => 0), + 'primary-residence' => array('desc' => __('The primary residence of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'country-of-residence' => array('desc' => __('The country of residence of a natural person'), 'default_category' => 'Person', 'to_ids' => 0), + 'special-service-request' => array('desc' => __('A Special Service Request is a function to an airline to provide a particular facility for A Passenger or passengers. '), 'default_category' => 'Person', 'to_ids' => 0), + 'frequent-flyer-number' => array('desc' => __('The frequent flyer number of a passenger'), 'default_category' => 'Person', 'to_ids' => 0), + // Do we really need remarks? Or just use comment/text for this? + //'remarks' => array('desc' => '', 'default_category' => 'Person', 'to_ids' => 0), + 'travel-details' => array('desc' => __('Travel details'), 'default_category' => 'Person', 'to_ids' => 0), + 'payment-details' => array('desc' => __('Payment details'), 'default_category' => 'Person', 'to_ids' => 0), + 'place-port-of-original-embarkation' => array('desc' => __('The orignal port of embarkation'), 'default_category' => 'Person', 'to_ids' => 0), + 'place-port-of-clearance' => array('desc' => __('The port of clearance'), 'default_category' => 'Person', 'to_ids' => 0), + 'place-port-of-onward-foreign-destination' => array('desc' => __('A Port where the passenger is transiting to'), 'default_category' => 'Person', 'to_ids' => 0), + 'passenger-name-record-locator-number' => array('desc' => __('The Passenger Name Record Locator is a key under which the reservation for a trip is stored in the system. The PNR contains, among other data, the name, flight segments and address of the passenger. It is defined by a combination of five or six letters and numbers.'), 'default_category' => 'Person', 'to_ids' => 0), + 'mobile-application-id' => array('desc' => __('The application id of a mobile application'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'chrome-extension-id' => array('desc' => __('Chrome extension id'), 'default_category' => 'Payload delivery', 'to_ids' => 1), + 'cortex' => array('desc' => __('Cortex analysis result'), 'default_category' => 'External analysis', 'to_ids' => 0), + 'boolean' => array('desc' => __('Boolean value - to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0), + 'anonymised' => array('desc' => __('Anonymised value - described with the anonymisation object via a relationship'), 'formdesc' => __('Anonymised value - described with the anonymisation object via a relationship.'), 'default_category' => 'Other', 'to_ids' => 0) + // Not convinced about this. + //'url-regex' => array('desc' => '', 'default_category' => 'Person', 'to_ids' => 0), + ); + } } diff --git a/app/Model/ShadowAttribute.php b/app/Model/ShadowAttribute.php index 9189ca417..be7054a03 100644 --- a/app/Model/ShadowAttribute.php +++ b/app/Model/ShadowAttribute.php @@ -8,6 +8,8 @@ App::uses('ComplexTypeTool', 'Tools'); /** * @property Event $Event * @property Attribute $Attribute + * @property-read array $typeDefinitions + * @property-read array $categoryDefinitions */ class ShadowAttribute extends AppModel { @@ -73,9 +75,6 @@ class ShadowAttribute extends AppModel 'attachment' ); - // definitions of categories - public $categoryDefinitions; - public $order = array("ShadowAttribute.event_id" => "DESC", "ShadowAttribute.type" => "ASC"); public $validate = array( @@ -149,11 +148,22 @@ class ShadowAttribute extends AppModel ) ); - public function __construct($id = false, $table = null, $ds = null) + public function __isset($name) { - parent::__construct($id, $table, $ds); - $this->categoryDefinitions = $this->Attribute->categoryDefinitions; - $this->typeDefinitions = $this->Attribute->typeDefinitions; + if ($name === 'typeDefinitions' || $name === 'categoryDefinitions') { + return true; + } + return parent::__isset($name); + } + + public function __get($name) + { + if ($name === 'categoryDefinitions') { + return $this->Attribute->categoryDefinitions; + } else if ($name === 'typeDefinitions') { + return $this->Attribute->typeDefinitions; + } + return parent::__get($name); } // The Associations below have been created with all possible keys, those that are not needed can be removed @@ -354,7 +364,7 @@ class ShadowAttribute extends AppModel { $category = $this->data['ShadowAttribute']['category']; if (isset($this->categoryDefinitions[$category]['types'])) { - return in_array($fields['type'], $this->categoryDefinitions[$category]['types']); + return in_array($fields['type'], $this->categoryDefinitions[$category]['types'], true); } return false; } From 3574240e03591090fb2499a5d1358bb4deb992cf Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 9 Jan 2021 21:29:22 +0100 Subject: [PATCH 247/385] fix: [internal] Remove duplicate array definition --- app/Model/Event.php | 115 +------------------------------------------- 1 file changed, 1 insertion(+), 114 deletions(-) diff --git a/app/Model/Event.php b/app/Model/Event.php index bc0150fae..ffc9556e8 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -62,120 +62,7 @@ class Event extends AppModel public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group'); - public $export_types = array( - 'json' => array( - 'extension' => '.json', - 'type' => 'JSON', - 'scope' => 'Event', - 'requiresPublished' => 0, - 'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'), - 'description' => 'Click this to download all events and attributes that you have access to in MISP JSON format.', - ), - 'xml' => array( - 'extension' => '.xml', - 'type' => 'XML', - 'scope' => 'Event', - 'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'), - 'requiresPublished' => 0, - 'description' => 'Click this to download all events and attributes that you have access to in MISP XML format.', - ), - 'csv_sig' => array( - 'extension' => '.csv', - 'type' => 'CSV_Sig', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'), - 'description' => 'Click this to download all attributes that are indicators and that you have access to (except file attachments) in CSV format.', - ), - 'csv_all' => array( - 'extension' => '.csv', - 'type' => 'CSV_All', - 'scope' => 'Event', - 'requiresPublished' => 0, - 'params' => array('ignore' => 1, 'returnFormat' => 'csv'), - 'description' => 'Click this to download all attributes that you have access to (except file attachments) in CSV format.', - ), - 'suricata' => array( - 'extension' => '.rules', - 'type' => 'Suricata', - 'scope' => 'Attribute', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'suricata'), - 'description' => 'Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.', - ), - 'snort' => array( - 'extension' => '.rules', - 'type' => 'Snort', - 'scope' => 'Attribute', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'snort'), - 'description' => 'Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.', - ), - 'bro' => array( - 'extension' => '.intel', - 'type' => 'Bro', - 'scope' => 'Attribute', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'bro'), - 'description' => 'Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.', - ), - 'stix' => array( - 'extension' => '.xml', - 'type' => 'STIX', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1), - 'description' => 'Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.' - ), - 'stix-json' => array( - 'extension' => '.json', - 'type' => 'STIX', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1), - 'description' => 'Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.' - ), - 'stix2' => array( - 'extension' => '.json', - 'type' => 'STIX2', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1), - 'description' => 'Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.' - ), - 'rpz' => array( - 'extension' => '.txt', - 'type' => 'RPZ', - 'scope' => 'Attribute', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'rpz'), - 'description' => 'Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.' - ), - 'text' => array( - 'extension' => '.txt', - 'type' => 'TEXT', - 'scope' => 'Attribute', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'text', 'includeAttachments' => 1), - 'description' => 'Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.' - ), - 'yara' => array( - 'extension' => '.yara', - 'type' => 'Yara', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'yara'), - 'description' => 'Click this to download Yara rules generated from all relevant attributes.' - ), - 'yara-json' => array( - 'extension' => '.json', - 'type' => 'Yara', - 'scope' => 'Event', - 'requiresPublished' => 1, - 'params' => array('returnFormat' => 'yara-json'), - 'description' => 'Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.' - ), - ); + public $export_types = []; public $validFormats = array( 'attack' => array('html', 'AttackExport', 'html'), From b5bd72841428b4599ea1c328692bd15142dacc5c Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 9 Jan 2021 21:44:56 +0100 Subject: [PATCH 248/385] chg: [internal] Generate server settings just when need --- app/Model/Server.php | 4805 +++++++++++++++++++++--------------------- 1 file changed, 2412 insertions(+), 2393 deletions(-) diff --git a/app/Model/Server.php b/app/Model/Server.php index da93981dc..8a36ee23d 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -2,6 +2,9 @@ App::uses('AppModel', 'Model'); App::uses('GpgTool', 'Tools'); +/** + * @property-read array $serverSettings + */ class Server extends AppModel { const SETTING_CRITICAL = 0, @@ -180,2399 +183,6 @@ class Server extends AppModel 'header' => __('Managing the background workers') ) ); - - $this->serverSettings = array( - 'MISP' => array( - 'branch' => 1, - 'baseurl' => array( - 'level' => 0, - 'description' => __('The base url of the application (in the format https://www.mymispinstance.com or https://myserver.com/misp). Several features depend on this setting being correctly set to function.'), - 'value' => '', - 'errorMessage' => __('The currently set baseurl does not match the URL through which you have accessed the page. Disregard this if you are accessing the page via an alternate URL (for example via IP address).'), - 'test' => 'testBaseURL', - 'type' => 'string', - ), - 'external_baseurl' => array( - 'level' => 0, - 'description' => __('The base url of the application (in the format https://www.mymispinstance.com) as visible externally/by other MISPs. MISP will encode this URL in sharing groups when including itself. If this value is not set, the baseurl is used as a fallback.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testURL', - 'type' => 'string', - ), - 'live' => array( - 'level' => 0, - 'description' => __('Unless set to true, the instance will only be accessible by site admins.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testLive', - 'type' => 'boolean', - ), - 'language' => array( - 'level' => 0, - 'description' => __('Select the language MISP should use. The default is english.'), - 'value' => 'eng', - 'errorMessage' => '', - 'test' => 'testLanguage', - 'type' => 'string', - 'optionsSource' => 'AvailableLanguages', - 'afterHook' => 'cleanCacheFiles' - ), - 'default_attribute_memory_coefficient' => array( - 'level' => 1, - 'description' => __('This values controls the internal fetcher\'s memory envelope when it comes to attributes. The number provided is the amount of attributes that can be loaded for each MB of PHP memory available in one shot. Consider lowering this number if your instance has a lot of attribute tags / attribute galaxies attached.'), - 'value' => 80, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => true - ), - 'default_event_memory_divisor' => array( - 'level' => 1, - 'description' => __('This value controls the divisor for attribute weighting when it comes to loading full events. Meaning that it will load coefficient / divisor number of attributes per MB of memory available. Consider raising this number if you have a lot of correlations or highly contextualised events (large number of event level galaxies/tags).'), - 'value' => 3, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => true - ), - 'enable_advanced_correlations' => array( - 'level' => 0, - 'description' => __('Enable some performance heavy correlations (currently CIDR correlation)'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'server_settings_skip_backup_rotate' => array( - 'level' => 1, - 'description' => __('Enable this setting to directly save the config.php file without first creating a temporary file and moving it to avoid concurency issues. Generally not recommended, but useful when for example other tools modify/maintain the config.php file.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'python_bin' => array( - 'level' => 1, - 'description' => __('It is highly recommended to install all the python dependencies in a virtualenv. The recommended location is: %s/venv', ROOT), - 'value' => false, - 'errorMessage' => '', - 'null' => false, - 'test' => 'testForBinExec', - 'beforeHook' => 'beforeHookBinExec', - 'type' => 'string', - 'cli_only' => 1 - ), - 'ca_path' => array( - 'level' => 1, - 'description' => __('MISP will default to the bundled mozilla certificate bundle shipped with the framework, which is rather stale. If you wish to use an alternate bundle, just set this setting using the path to the bundle to use. This setting can only be modified via the CLI.'), - 'value' => APP . 'Lib/cakephp/lib/Cake/Config/cacert.pem', - 'errorMessage' => '', - 'null' => true, - 'test' => 'testForCABundle', - 'type' => 'string', - 'cli_only' => 1 - ), - 'disable_auto_logout' => array( - 'level' => 1, - 'description' => __('In some cases, a heavily used MISP instance can generate unwanted blackhole errors due to a high number of requests hitting the server. Disable the auto logout functionality to ease the burden on the system.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'ssdeep_correlation_threshold' => array( - 'level' => 1, - 'description' => __('Set the ssdeep score at which to consider two ssdeep hashes as correlating [1-100]'), - 'value' => 40, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - '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.'), - 'value' => 'Great things are happening! MISP is undergoing maintenance, but will return shortly. You can contact the administration at $email.', - 'errorMessage' => __('If this is not set the default value will be used.'), - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'name' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'version' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'disable_cached_exports' => array( - 'level' => 1, - 'description' => __('Cached exports can take up a considerable amount of space and can be disabled instance wide using this setting. Disabling the cached exports is not recommended as it\'s a valuable feature, however, if your server is having free space issues it might make sense to take this step.'), - 'value' => false, - 'null' => true, - 'errorMessage' => '', - 'test' => 'testDisableCache', - 'type' => 'boolean', - 'afterHook' => 'disableCacheAfterHook', - ), - 'disable_threat_level' => array( - 'level' => 1, - 'description' => __('Disable displaying / modifications to the threat level altogether on the instance (deprecated field).'), - 'value' => false, - 'null' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'header' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footermidleft' => array( - 'level' => 2, - 'description' => __('Footer text prepending the "Powered by MISP" text.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footermidright' => array( - 'level' => 2, - 'description' => __('Footer text following the "Powered by MISP" text.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footerpart1' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footerpart2' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footer' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footerversion' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'footer_logo' => array( - 'level' => 2 , - 'description' => __('If set, this setting allows you to display a logo on the right side of the footer. Upload it as a custom image in the file management tool.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCustomImage', - 'type' => 'string', - ), - 'home_logo' => array( - 'level' => 2 , - 'description' => __('If set, this setting allows you to display a logo as the home icon. Upload it as a custom image in the file management tool.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCustomImage', - 'type' => 'string', - ), - 'main_logo' => array( - 'level' => 2 , - 'description' => __('If set, the image specified here will replace the main MISP logo on the login screen. Upload it as a custom image in the file management tool.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCustomImage', - 'type' => 'string', - ), - 'org' => array( - 'level' => 1, - 'description' => __('The organisation tag of the hosting organisation. This is used in the e-mail subjects.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'host_org_id' => array( - 'level' => 0, - 'description' => __('The hosting organisation of this instance. If this is not selected then replication instances cannot be added.'), - 'value' => '0', - 'errorMessage' => '', - 'test' => 'testLocalOrg', - 'type' => 'numeric', - 'optionsSource' => 'LocalOrgs', - ), - 'uuid' => array( - 'level' => 0, - 'description' => __('The MISP instance UUID. This UUID is used to identify this instance.'), - 'value' => '0', - 'errorMessage' => __('No valid UUID set'), - 'test' => 'testUuid', - 'type' => 'string' - ), - 'logo' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'showorg' => array( - 'level' => 0, - 'description' => __('Setting this setting to \'false\' will hide all organisation names / logos.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'threatlevel_in_email_subject' => array( - 'level' => 2, - 'description' => __('Put the event threat level in the notification E-mail subject.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'email_subject_TLP_string' => array( - 'level' => 2, - 'description' => __('This is the TLP string for e-mails when email_subject_tag is not found.'), - 'value' => 'tlp:amber', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'email_subject_tag' => array( - 'level' => 2, - 'description' => __('If this tag is set on an event it\'s value will be sent in the E-mail subject. If the tag is not set the email_subject_TLP_string will be used.'), - 'value' => 'tlp', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'email_subject_include_tag_name' => array( - 'level' => 2, - 'description' => __('Include in name of the email_subject_tag in the subject. When false only the tag value is used.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'taxii_sync' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'taxii_client_path' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'background_jobs' => array( - 'level' => 1, - 'description' => __('Enables the use of MISP\'s background processing.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBoolTrue', - 'type' => 'boolean', - ), - 'attachments_dir' => array( - 'level' => 2, - 'description' => __('Directory where attachments are stored. MISP will NOT migrate the existing data if you change this setting. The only safe way to change this setting is in config.php, when MISP is not running, and after having moved/copied the existing data to the new location. This directory must already exist and be writable and readable by the MISP application.'), - 'value' => APP . '/files', # GUI display purpose only. - 'errorMessage' => '', - 'null' => false, - 'test' => 'testForWritableDir', - 'type' => 'string', - 'cli_only' => 1 - ), - 'cached_attachments' => array( - 'level' => 1, - 'description' => __('Allow the XML caches to include the encoded attachments.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'download_attachments_on_load' => array( - 'level' => 2, - 'description' => __('Always download attachments when loaded by a user in a browser'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'osuser' => array( - 'level' => 0, - 'description' => __('The Unix user MISP (php) is running as'), - 'value' => 'www-data', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'email' => array( - 'level' => 0, - 'description' => __('The e-mail address that MISP should use for all notifications'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'disable_emailing' => array( - 'level' => 0, - 'description' => __('You can disable all e-mailing using this setting. When enabled, no outgoing e-mails will be sent by MISP.'), - 'value' => false, - 'errorMessage' => '', - 'null' => true, - 'test' => 'testDisableEmail', - 'type' => 'boolean', - ), - 'contact' => array( - 'level' => 1, - 'description' => __('The e-mail address that MISP should include as a contact address for the instance\'s support team.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'dns' => array( - 'level' => 3, - 'description' => __('This setting is deprecated and can be safely removed.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'cveurl' => array( - 'level' => 1, - 'description' => __('Turn Vulnerability type attributes into links linking to the provided CVE lookup'), - 'value' => 'http://cve.circl.lu/cve/', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'cweurl' => array( - 'level' => 1, - 'description' => __('Turn Weakness type attributes into links linking to the provided CWE lookup'), - 'value' => 'http://cve.circl.lu/cwe/', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'disablerestalert' => array( - 'level' => 1, - 'description' => __('This setting controls whether notification e-mails will be sent when an event is created via the REST interface. It might be a good idea to disable this setting when first setting up a link to another instance to avoid spamming your users during the initial pull. Quick recap: True = Emails are NOT sent, False = Emails are sent on events published via sync / REST.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'extended_alert_subject' => array( - 'level' => 1, - 'description' => __('enabling this flag will allow the event description to be transmitted in the alert e-mail\'s subject. Be aware that this is not encrypted by GnuPG, so only enable it if you accept that part of the event description will be sent out in clear-text.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'default_event_distribution' => array( - 'level' => 0, - 'description' => __('The default distribution setting for events (0-3).'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'options' => array('0' => 'Your organisation only', '1' => 'This community only', '2' => 'Connected communities', '3' => 'All communities'), - ), - 'default_attribute_distribution' => array( - 'level' => 0, - 'description' => __('The default distribution setting for attributes, set it to \'event\' if you would like the attributes to default to the event distribution level. (0-3 or "event")'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'options' => array('0' => 'Your organisation only', '1' => 'This community only', '2' => 'Connected communities', '3' => 'All communities', 'event' => 'Inherit from event'), - ), - 'default_event_threat_level' => array( - 'level' => 1, - 'description' => __('The default threat level setting when creating events.'), - 'value' => 4, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'options' => array('1' => 'High', '2' => 'Medium', '3' => 'Low', '4' => 'undefined'), - ), - 'default_event_tag_collection' => array( - 'level' => 0, - 'description' => __('The tag collection to be applied to all events created manually.'), - 'value' => 0, - 'errorMessage' => '', - 'test' => 'testTagCollections', - 'type' => 'numeric', - 'optionsSource' => 'TagCollections', - ), - 'default_publish_alert' => array( - 'level' => 0, - 'description' => __('The default setting for publish alerts when creating users.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'tagging' => array( - 'level' => 1, - 'description' => __('Enable the tagging feature of MISP. This is highly recommended.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'full_tags_on_event_index' => array( - 'level' => 2, - 'description' => __('Show the full tag names on the event index.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'options' => array(0 => 'Minimal tags', 1 => 'Full tags', 2 => 'Shortened tags'), - ), - 'welcome_text_top' => array( - 'level' => 2, - 'description' => __('Used on the login page, before the MISP logo'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'welcome_text_bottom' => array( - 'level' => 2, - 'description' => __('Used on the login page, after the MISP logo'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'welcome_logo' => array( - 'level' => 2, - 'description' => __('Used on the login page, to the left of the MISP logo, upload it as a custom image in the file management tool.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCustomImage', - 'type' => 'string', - ), - 'welcome_logo2' => array( - 'level' => 2, - 'description' => __('Used on the login page, to the right of the MISP logo, upload it as a custom image in the file management tool.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCustomImage', - 'type' => 'string', - ), - 'title_text' => array( - 'level' => 2, - 'description' => __('Used in the page title, after the name of the page'), - 'value' => 'MISP', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'take_ownership_xml_import' => array( - 'level' => 2, - 'description' => __('Allows users to take ownership of an event uploaded via the "Add MISP XML" button. This allows spoofing the creator of a manually imported event, also breaking possibly breaking the original intended releasability. Synchronising with an instance that has a different creator for the same event can lead to unwanted consequences.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'terms_download' => array( - 'level' => 2, - 'description' => __('Choose whether the terms and conditions should be displayed inline (false) or offered as a download (true)'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'terms_file' => array( - 'level' => 2, - 'description' => __('The filename of the terms and conditions file. Make sure that the file is located in your MISP/app/files/terms directory'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForTermsFile', - 'type' => 'string' - ), - 'showorgalternate' => array( - 'level' => 2, - 'description' => __('True enables the alternate org fields for the event index (source org and member org) instead of the traditional way of showing only an org field. This allows users to see if an event was uploaded by a member organisation on their MISP instance, or if it originated on an interconnected instance.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'unpublishedprivate' => array( - 'level' => 2, - 'description' => __('True will deny access to unpublished events to users outside the organization of the submitter except site admins.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'newUserText' => array( - 'level' => 1, - 'bigField' => true, - 'description' => __('The message sent to the user after account creation (has to be sent manually from the administration interface). Use \\n for line-breaks. The following variables will be automatically replaced in the text: $password = a new temporary password that MISP generates, $username = the user\'s e-mail address, $misp = the url of this instance, $org = the organisation that the instance belongs to, as set in MISP.org, $contact = the e-mail address used to contact the support team, as set in MISP.contact. For example, "the password for $username is $password" would appear to a user with the e-mail address user@misp.org as "the password for user@misp.org is hNamJae81".'), - 'value' => 'Dear new MISP user,\n\nWe would hereby like to welcome you to the $org MISP community.\n\n Use the credentials below to log into MISP at $misp, where you will be prompted to manually change your password to something of your own choice.\n\nUsername: $username\nPassword: $password\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', - 'errorMessage' => '', - 'test' => 'testPasswordResetText', - 'type' => 'string' - ), - 'passwordResetText' => array( - 'level' => 1, - 'bigField' => true, - 'description' => __('The message sent to the users when a password reset is triggered. Use \\n for line-breaks. The following variables will be automatically replaced in the text: $password = a new temporary password that MISP generates, $username = the user\'s e-mail address, $misp = the url of this instance, $contact = the e-mail address used to contact the support team, as set in MISP.contact. For example, "the password for $username is $password" would appear to a user with the e-mail address user@misp.org as "the password for user@misp.org is hNamJae81".'), - 'value' => 'Dear MISP user,\n\nA password reset has been triggered for your account. Use the below provided temporary password to log into MISP at $misp, where you will be prompted to manually change your password to something of your own choice.\n\nUsername: $username\nYour temporary password: $password\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', - 'errorMessage' => '', - 'test' => 'testPasswordResetText', - 'type' => 'string' - ), - 'enableEventBlocklisting' => array( - 'level' => 1, - 'description' => __('Since version 2.3.107 you can start blocklisting event UUIDs to prevent them from being pushed to your instance. This functionality will also happen silently whenever an event is deleted, preventing a deleted event from being pushed back from another instance.'), - 'value' => true, - 'type' => 'boolean', - 'test' => 'testBool' - ), - 'enableOrgBlocklisting' => array( - 'level' => 1, - 'description' => __('Blocklisting organisation UUIDs to prevent the creation of any event created by the blocklisted organisation.'), - 'value' => true, - 'type' => 'boolean', - 'test' => 'testBool' - ), - 'log_client_ip' => array( - 'level' => 1, - 'description' => __('If enabled, all log entries will include the IP address of the user.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'beforeHook' => 'ipLogBeforeHook' - ), - 'log_client_ip_header' => array( - 'level' => 1, - 'description' => __('If log_client_ip is enabled, you can customize which header field contains the client\'s IP address. This is generally used when you have a reverse proxy infront of your MISP instance.'), - 'value' => 'REMOTE_ADDR', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true, - ), - 'log_auth' => array( - 'level' => 1, - 'description' => __('If enabled, MISP will log all successful authentications using API keys. The requested URLs are also logged.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'log_skip_db_logs_completely' => array( - 'level' => 0, - 'description' => __('This functionality allows you to completely disable any logs from being saved in your SQL backend. This is HIGHLY advised against, you lose all the functionalities provided by the audit log subsystem along with the event history (as these are built based on the logs on the fly). Only enable this if you understand and accept the associated risks.'), - 'value' => false, - 'errorMessage' => __('Logging has now been disabled - your audit logs will not capture failed authentication attempts, your event history logs are not being populated and no system maintenance messages are being logged.'), - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'log_paranoid' => array( - 'level' => 0, - 'description' => __('If this functionality is enabled all page requests will be logged. Keep in mind this is extremely verbose and will become a burden to your database.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'log_paranoid_skip_db' => array( - 'level' => 0, - 'description' => __('You can decide to skip the logging of the paranoid logs to the database.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testParanoidSkipDb', - 'type' => 'boolean', - 'null' => true - ), - 'log_paranoid_include_post_body' => array( - 'level' => 0, - 'description' => __('If paranoid logging is enabled, include the POST body in the entries.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'log_user_ips' => array( - 'level' => 0, - 'description' => __('Log user IPs on each request. 30 day retention for lookups by IP to get the last authenticated user ID for the given IP, whilst on the reverse, indefinitely stores all associated IPs for a user ID.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'log_user_ips_authkeys' => [ - 'level' => self::SETTING_RECOMMENDED, - 'description' => __('Log user IP and key usage on each API request. All logs for given keys are deleted after one year when this key is not used.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ], - 'delegation' => array( - 'level' => 1, - 'description' => __('This feature allows users to create org only events and ask another organisation to take ownership of the event. This allows organisations to remain anonymous by asking a partner to publish an event for them.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'showCorrelationsOnIndex' => array( - 'level' => 1, - 'description' => __('When enabled, the number of correlations visible to the currently logged in user will be visible on the event index UI. This comes at a performance cost but can be very useful to see correlating events at a glance.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'showProposalsCountOnIndex' => array( - 'level' => 1, - 'description' => __('When enabled, the number of proposals for the events are shown on the index.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'showSightingsCountOnIndex' => array( - 'level' => 1, - 'description' => __('When enabled, the aggregate number of attribute sightings within the event becomes visible to the currently logged in user on the event index UI.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'showDiscussionsCountOnIndex' => array( - 'level' => 1, - 'description' => __('When enabled, the aggregate number of discussion posts for the event becomes visible to the currently logged in user on the event index UI.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'disableUserSelfManagement' => array( - 'level' => 1, - 'description' => __('When enabled only Org and Site admins can edit a user\'s profile.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'disable_user_login_change' => array( - 'level' => self::SETTING_RECOMMENDED, - 'description' => __('When enabled only Site admins can change user email. This should be enabled if you manage user logins by external system.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'disable_user_password_change' => array( - 'level' => self::SETTING_RECOMMENDED, - 'description' => __('When enabled only Site admins can change user password. This should be enabled if you manage user passwords by external system.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'disable_user_add' => array( - 'level' => self::SETTING_RECOMMENDED, - 'description' => __('When enabled, Org Admins could not add new users. This should be enabled if you manage users by external system.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'block_event_alert' => array( - 'level' => 1, - 'description' => __('Enable this setting to start blocking alert e-mails for events with a certain tag. Define the tag in MISP.block_event_alert_tag.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'block_event_alert_tag' => array( - 'level' => 1, - 'description' => __('If the MISP.block_event_alert setting is set, alert e-mails for events tagged with the tag defined by this setting will be blocked.'), - 'value' => 'no-alerts="true"', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => false, - ), - 'org_alert_threshold' => array( - 'level' => 1, - 'description' => __('Set a value to limit the number of email alerts that events can generate per creator organisation (for example, if an organisation pushes out 2000 events in one shot, only alert on the first 20).'), - 'value' => 0, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => true, - ), - 'block_old_event_alert' => array( - 'level' => 1, - 'description' => __('Enable this setting to start blocking alert e-mails for old events. The exact timing of what constitutes an old event is defined by MISP.block_old_event_alert_age.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'block_old_event_alert_age' => array( - 'level' => 1, - 'description' => __('If the MISP.block_old_event_alert setting is set, this setting will control how old an event can be for it to be alerted on. The "timestamp" field of the event is used. Expected format: integer, in days'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => false, - ), - 'block_old_event_alert_by_date' => array( - 'level' => 1, - 'description' => __('If the MISP.block_old_event_alert setting is set, this setting will control the threshold for the event.date field, indicating how old an event can be for it to be alerted on. The "date" field of the event is used. Expected format: integer, in days'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => false, - ), - 'tmpdir' => array( - 'level' => 1, - 'description' => __('Please indicate the temp directory you wish to use for certain functionalities in MISP. By default this is set to /tmp and will be used among others to store certain temporary files extracted from imports during the import process.'), - 'value' => '/tmp', - 'errorMessage' => '', - 'test' => 'testForPath', - 'type' => 'string', - 'null' => true, - 'cli_only' => 1 - ), - 'custom_css' => array( - 'level' => 2, - 'description' => __('If you would like to customise the css, simply drop a css file in the /var/www/MISP/app/webroot/css directory and enter the name here.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForStyleFile', - 'type' => 'string', - 'null' => true, - ), - 'proposals_block_attributes' => array( - 'level' => 0, - 'description' => __('Enable this setting to allow blocking attributes from to_ids sensitive exports if a proposal has been made to it to remove the IDS flag or to remove the attribute altogether. This is a powerful tool to deal with false-positives efficiently.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false, - ), - 'incoming_tags_disabled_by_default' => array( - 'level' => 1, - 'description' => __('Enable this settings if new tags synced / added via incoming events from any source should not be selectable by users by default.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => false - ), - 'completely_disable_correlation' => array( - 'level' => 0, - 'description' => __('*WARNING* This setting will completely disable the correlation on this instance and remove any existing saved correlations. Enabling this will trigger a full recorrelation of all data which is an extremely long and costly procedure. Only enable this if you know what you\'re doing.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true, - 'afterHook' => 'correlationAfterHook', - ), - 'allow_disabling_correlation' => array( - 'level' => 0, - 'description' => __('*WARNING* This setting will give event creators the possibility to disable the correlation of individual events / attributes that they have created.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'redis_host' => array( - 'level' => 0, - 'description' => __('The host running the redis server to be used for generic MISP tasks such as caching. This is not to be confused by the redis server used by the background processing.'), - 'value' => '127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'redis_port' => array( - 'level' => 0, - 'description' => __('The port used by the redis server to be used for generic MISP tasks such as caching. This is not to be confused by the redis server used by the background processing.'), - 'value' => 6379, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric' - ), - 'redis_database' => array( - 'level' => 0, - 'description' => __('The database on the redis server to be used for generic MISP tasks. If you run more than one MISP instance, please make sure to use a different database on each instance.'), - 'value' => 13, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric' - ), - 'redis_password' => array( - 'level' => 0, - 'description' => __('The password on the redis server (if any) to be used for generic MISP tasks.'), - 'value' => '', - 'errorMessage' => '', - 'test' => null, - 'type' => 'string', - 'redacted' => true - ), - 'event_view_filter_fields' => array( - 'level' => 2, - 'description' => __('Specify which fields to filter on when you search on the event view. Default values are : "id, uuid, value, comment, type, category, Tag.name"'), - 'value' => 'id, uuid, value, comment, type, category, Tag.name', - 'errorMessage' => '', - 'test' => null, - 'type' => 'string', - ), - 'manage_workers' => array( - 'level' => 2, - 'description' => __('Set this to false if you would like to disable MISP managing its own worker processes (for example, if you are managing the workers with a systemd unit).'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'deadlock_avoidance' => array( - 'level' => 1, - 'description' => __('Only enable this if you have some tools using MISP with extreme high concurency. General performance will be lower as normal as certain transactional queries are avoided in favour of shorter table locks.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'updateTimeThreshold' => array( - 'level' => 1, - 'description' => __('Sets the minimum time before being able to re-trigger an update if the previous one failed. (safe guard to avoid starting the same update multiple time)'), - 'value' => '7200', - 'test' => 'testForNumeric', - 'type' => 'numeric', - 'null' => true - ), - 'attribute_filters_block_only' => array( - 'level' => 1, - 'description' => __('This is a performance tweak to change the behaviour of restSearch to use attribute filters solely for blocking. This means that a lookup on the event scope with for example the type field set will be ignored unless it\'s used to strip unwanted attributes from the results. If left disabled, passing [ip-src, ip-dst] for example will return any event with at least one ip-src or ip-dst attribute. This is generally not considered to be too useful and is a heavy burden on the database.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'attachment_scan_module' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('Name of enrichment module that will be used for attachment malware scanning. This module must return av-signature or sb-signature object.'), - 'value' => '', - 'errorMessage' => '', - 'type' => 'string', - 'null' => true, - ], - 'attachment_scan_hash_only' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('Send to attachment scan module just file hash. This can be useful if module sends attachment to remote service and you don\'t want to leak real data.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - ], - 'attachment_scan_timeout' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('How long to wait for scan results in seconds.'), - 'value' => 30, - 'errorMessage' => '', - 'test' => 'testForPositiveInteger', - 'type' => 'numeric', - 'null' => true, - ], - ), - 'GnuPG' => array( - 'branch' => 1, - 'binary' => array( - 'level' => 2, - 'description' => __('The location of the GnuPG executable. If you would like to use a different GnuPG executable than /usr/bin/gpg, you can set it here. If the default is fine, just keep the setting suggested by MISP.'), - 'value' => '/usr/bin/gpg', - 'errorMessage' => '', - 'test' => 'testForGPGBinary', - 'type' => 'string', - 'cli_only' => 1 - ), - 'onlyencrypted' => array( - 'level' => 0, - 'description' => __('Allow (false) unencrypted e-mails to be sent to users that don\'t have a GnuPG key.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'bodyonlyencrypted' => array( - 'level' => 2, - 'description' => __('Allow (false) the body of unencrypted e-mails to contain details about the event.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'sign' => array( - 'level' => 2, - 'description' => __('Enable the signing of GnuPG emails. By default, GnuPG emails are signed'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'email' => array( - 'level' => 0, - 'description' => __('The e-mail address that the instance\'s GnuPG key is tied to.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'password' => array( - 'level' => 1, - 'description' => __('The password (if it is set) of the GnuPG key of the instance.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'redacted' => true - ), - 'homedir' => array( - 'level' => 0, - 'description' => __('The location of the GnuPG homedir.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'obscure_subject' => array( - 'level' => 2, - 'description' => __('When enabled, subject in signed and encrypted e-mails will not send in unencrypted form.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ) - ), - 'SMIME' => array( - 'branch' => 1, - 'enabled' => array( - 'level' => 2, - 'description' => __('Enable SMIME encryption. The encryption posture of the GnuPG.onlyencrypted and GnuPG.bodyonlyencrypted settings are inherited if SMIME is enabled.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'email' => array( - 'level' => 2, - 'description' => __('The e-mail address that the instance\'s SMIME key is tied to.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'cert_public_sign' => array( - 'level' => 2, - 'description' => __('The location of the public half of the signing certificate.'), - 'value' => '/var/www/MISP/.smime/email@address.com.pem', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'key_sign' => array( - 'level' => 2, - 'description' => __('The location of the private half of the signing certificate.'), - 'value' => '/var/www/MISP/.smime/email@address.com.key', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'password' => array( - 'level' => 2, - 'description' => __('The password (if it is set) of the SMIME key of the instance.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'redacted' => true - ), - ), - 'Proxy' => array( - 'branch' => 1, - 'host' => array( - 'level' => 2, - 'description' => __('The hostname of an HTTP proxy for outgoing sync requests. Leave empty to not use a proxy.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'port' => array( - 'level' => 2, - 'description' => __('The TCP port for the HTTP proxy.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric', - ), - 'method' => array( - 'level' => 2, - 'description' => __('The authentication method for the HTTP proxy. Currently supported are Basic or Digest. Leave empty for no proxy authentication.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'user' => array( - 'level' => 2, - 'description' => __('The authentication username for the HTTP proxy.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'password' => array( - 'level' => 2, - 'description' => __('The authentication password for the HTTP proxy.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - ), - 'Security' => array( - 'branch' => 1, - 'disable_form_security' => array( - 'level' => 0, - 'description' => __('Disabling this setting will remove all form tampering protection. Do not set this setting pretty much ever. You were warned.'), - 'value' => false, - 'errorMessage' => 'This setting leaves your users open to CSRF attacks. Do not please consider disabling this setting.', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'salt' => array( - 'level' => 0, - 'description' => __('The salt used for the hashed passwords. You cannot reset this from the GUI, only manually from the settings.php file. Keep in mind, this will invalidate all passwords in the database.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testSalt', - 'type' => 'string', - 'editable' => false, - 'redacted' => true - ), - 'advanced_authkeys' => array( - 'level' => 0, - 'description' => __('Advanced authkeys will allow each user to create and manage a set of authkeys for themselves, each with individual expirations and comments. API keys are stored in a hashed state and can no longer be recovered from MISP. Users will be prompted to note down their key when creating a new authkey. You can generate a new set of API keys for all users on demand in the diagnostics page, or by triggering %s.', sprintf('%s', $this->baseurl, __('the advanced upgrade'))), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'advanced_authkeys_validity' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('Maximal key lifetime in days. Use can limit that validity even more. Just newly created keys will be affected. When not set, key validity is not limited.'), - 'value' => '', - 'errorMessage' => '', - 'type' => 'numeric', - 'test' => 'testForNumeric', - 'null' => true, - ], - 'authkey_keep_session' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('When enabled, session is kept between API requests.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - ], - 'auth_enforced' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('This optional can be enabled if external auth provider is used and when set to true, it will disable default form authentication.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ], - 'rest_client_enable_arbitrary_urls' => array( - 'level' => 0, - 'description' => __('Enable this setting if you wish for users to be able to query any arbitrary URL via the rest client. Keep in mind that queries are executed by the MISP server, so internal IPs in your MISP\'s network may be reachable.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'rest_client_baseurl' => array( - 'level' => 1, - 'description' => __('If left empty, the baseurl of your MISP is used. However, in some instances (such as port-forwarded VM installations) this will not work. You can override the baseurl with a url through which your MISP can reach itself (typically https://127.0.0.1 would work).'), - 'value' => false, - 'errorMessage' => '', - 'test' => null, - 'type' => 'string' - ), - 'syslog' => array( - 'level' => 0, - 'description' => __('Enable this setting to pass all audit log entries directly to syslog. Keep in mind, this is verbose and will include user, organisation, event data.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'syslog_to_stderr' => array( - 'level' => self::SETTING_OPTIONAL, - 'description' => __('Write syslog messages also to standard error output.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'syslog_ident' => array( - 'level' => self::SETTING_OPTIONAL, - 'description' => __('Syslog message identifier.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'do_not_log_authkeys' => array( - 'level' => 0, - 'description' => __('If enabled, any authkey will be replaced by asterisks in Audit log.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'disable_browser_cache' => array( - 'level' => 0, - 'description' => __('If enabled, HTTP headers that block browser cache will be send. Static files (like images or JavaScripts) will still be cached, but not generated pages.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - ), - 'check_sec_fetch_site_header' => [ - 'level' => 0, - 'description' => __('If enabled, any POST, PUT or AJAX request will be allow just when Sec-Fetch-Site header is not defined or contains "same-origin".'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - ], - 'email_otp_enabled' => array( - 'level'=> 2, - 'description' => __('Enable two step authentication with a OTP sent by email. Requires e-mailing to be enabled. Warning: You cannot use it in combination with external authentication plugins.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'beforeHook' => 'otpBeforeHook', - 'type' => 'boolean', - 'null' => true - ), - 'email_otp_length' => array ( - 'level' => 2, - 'description' => __('Define the length of the OTP code sent by email'), - 'value' => '6', - 'errorMessage' => '', - 'type' => 'numeric', - 'test' => 'testForNumeric', - 'null' => true, - ), - 'email_otp_validity' => array ( - 'level' => 2, - 'description' => __('Define the validity (in minutes) of the OTP code sent by email'), - 'value' => '5', - 'errorMessage' => '', - 'type' => 'numeric', - 'test' => 'testForNumeric', - 'null' => true, - ), - 'email_otp_text' => array( - 'level' => 2, - 'bigField' => true, - 'description' => __('The message sent to the user when a new OTP is requested. Use \\n for line-breaks. The following variables will be automatically replaced in the text: $otp = the new OTP generated by MISP, $username = the user\'s e-mail address, $org the Organisation managing the instance, $misp = the url of this instance, $contact = the e-mail address used to contact the support team (as set in MISP.contact), $ip the IP used to complete the first step of the login and $validity the validity time in minutes.'), - 'value' => 'Dear MISP user,\n\nYou have attempted to login to MISP ($misp) from $ip with username $username.\n\n Use the following OTP to log into MISP: $otp\n This code is valid for the next $validity minutes.\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true, - ), - 'email_otp_exceptions' => array( - 'level' => 2, - 'bigField' => true, - 'description' => __('A comma separated list of emails for which the OTP is disabled. Note that if you remove someone from this list, the OTP will only be asked at next login.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true, - ), - 'allow_self_registration' => array( - 'level' => 1, - 'description' => __('Enabling this setting will allow users to have access to the pre-auth registration form. This will create an inbox entry for administrators to review.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'self_registration_message' => array( - 'level' => 1, - 'bigField' => true, - 'description' => __('The message sent shown to anyone trying to self-register.'), - 'value' => 'If you would like to send us a registration request, please fill out the form below. Make sure you fill out as much information as possible in order to ease the task of the administrators.', - 'errorMessage' => '', - 'test' => false, - 'type' => 'string' - ), - 'password_policy_length' => array( - 'level' => 2, - 'description' => __('Password length requirement. If it is not set or it is set to 0, then the default value is assumed (12).'), - 'value' => '12', - 'errorMessage' => '', - 'test' => 'testPasswordLength', - 'type' => 'numeric', - ), - 'password_policy_complexity' => array( - 'level' => 2, - 'description' => __('Password complexity requirement. Leave it empty for the default setting (3 out of 4, with either a digit or a special char) or enter your own regex. Keep in mind that the length is checked in another key. Default (simple 3 out of 4 or minimum 16 characters): /^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/'), - 'value' => '/^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/', - 'errorMessage' => '', - 'test' => 'testPasswordRegex', - 'type' => 'string', - ), - 'require_password_confirmation' => array( - 'level' => 1, - 'description' => __('Enabling this setting will require users to submit their current password on any edits to their profile (including a triggered password change). For administrators, the confirmation will be required when changing the profile of any user. Could potentially mitigate an attacker trying to change a compromised user\'s password in order to establish persistance, however, enabling this feature will be highly annoying to users.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'sanitise_attribute_on_delete' => array( - 'level' => 1, - 'description' => __('Enabling this setting will sanitise the contents of an attribute on a soft delete'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'hide_organisation_index_from_users' => array( - 'level' => 1, - 'description' => __('Enabling this setting will block the organisation index from being visible to anyone besides site administrators on the current instance. Keep in mind that users can still see organisations that produce data via events, proposals, event history log entries, etc.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'hide_organisations_in_sharing_groups' => [ - 'level' => self::SETTING_RECOMMENDED, - 'description' => __('Enabling this setting will block the organisation list from being visible in sharing group besides user with sharing group permission.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ], - 'disable_local_feed_access' => array( - 'level' => 0, - 'description' => __('Disabling this setting will allow the creation/modification of local feeds (as opposed to network feeds). Enabling this setting will restrict feed sources to be network based only. When disabled, keep in mind that a malicious site administrator could get access to any arbitrary file on the system that the apache user has access to. Make sure that proper safe-guards are in place. This setting can only be modified via the CLI.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - 'cli_only' => 1 - ), - 'allow_unsafe_apikey_named_param' => array( - 'level' => 0, - 'description' => __('Allows passing the API key via the named url parameter "apikey" - highly recommended not to enable this, but if you have some dodgy legacy tools that cannot pass the authorization header it can work as a workaround. Again, only use this as a last resort.'), - 'value' => false, - 'errorMessage' => __('You have enabled the passing of API keys via URL parameters. This is highly recommended against, do you really want to reveal APIkeys in your logs?...'), - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'allow_cors' => array( - 'level' => 1, - 'description' => __('Allow cross-origin requests to this instance, matching origins given in Security.cors_origins. Set to false to totally disable'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'cors_origins' => array( - 'level' => 1, - 'description' => __('Set the origins from which MISP will allow cross-origin requests. Useful for external integration. Comma seperate if you need more than one.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'sync_audit' => array( - 'level' => 1, - 'description' => __('Enable this setting to create verbose logs of synced event data for debugging reasons. Logs are saved in your MISP directory\'s app/files/scripts/tmp/ directory.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - 'null' => true - ), - 'user_monitoring_enabled' => array( - 'level' => 1, - 'description' => __('Enables the functionality to monitor users - thereby enabling all logging functionalities for a single user. This functionality is intrusive and potentially heavy on the system - use it with care.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'username_in_response_header' => [ - 'level' => self::SETTING_OPTIONAL, - 'description' => __('When enabled, logged in username will be included in X-Username HTTP response header. This is useful for request logging on webserver/proxy side.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ] - ), - 'SecureAuth' => array( - 'branch' => 1, - 'amount' => array( - 'level' => 0, - 'description' => __('The number of tries a user can try to login and fail before the bruteforce protection kicks in.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'string', - ), - 'expire' => array( - 'level' => 0, - 'description' => __('The duration (in seconds) of how long the user will be locked out when the allowed number of login attempts are exhausted.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'string', - ), - ), - 'Session' => array( - 'branch' => 1, - 'autoRegenerate' => array( - 'level' => 0, - 'description' => __('Set to true to automatically regenerate sessions after x number of requests. This might lead to the user getting de-authenticated and is frustrating in general, so only enable it if you really need to regenerate sessions. (Not recommended)'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - ), - 'checkAgent' => array( - 'level' => 0, - 'description' => __('Set to true to check for the user agent string in each request. This can lead to occasional logouts (not recommended).'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBoolFalse', - 'type' => 'boolean', - ), - 'defaults' => array( - 'level' => 0, - 'description' => __('The session type used by MISP. The default setting is php, which will use the session settings configured in php.ini for the session data (supported options: php, database). The recommended option is php and setting your PHP up to use redis sessions via your php.ini. Just add \'session.save_handler = redis\' and "session.save_path = \'tcp://localhost:6379\'" (replace the latter with your redis connection) to '), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForSessionDefaults', - 'type' => 'string', - 'options' => array('php' => 'php', 'database' => 'database', 'cake' => 'cake', 'cache' => 'cache'), - ), - 'timeout' => array( - 'level' => 0, - 'description' => __('The timeout duration of sessions (in MINUTES). 0 does not mean infinite for the PHP session handler, instead sessions will invalidate immediately.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'string' - ), - 'cookieTimeout' => array( - 'level' => 0, - 'description' => __('The expiration of the cookie (in MINUTES). The session timeout gets refreshed frequently, however the cookies do not. Generally it is recommended to have a much higher cookie_timeout than timeout.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForCookieTimeout', - 'type' => 'numeric' - ) - ), - 'Plugin' => array( - 'branch' => 1, - 'RPZ_policy' => array( - 'level' => 2, - 'description' => __('The default policy action for the values added to the RPZ.'), - 'value' => 1, - 'errorMessage' => '', - 'test' => 'testForRPZBehaviour', - 'type' => 'numeric', - 'options' => array(0 => 'DROP', 1 => 'NXDOMAIN', 2 => 'NODATA', 3 => 'Local-Data', 4 => 'PASSTHRU', 5 => 'TCP-only' ), - ), - 'RPZ_walled_garden' => array( - 'level' => 2, - 'description' => __('The default walled garden used by the RPZ export if the Local-Data policy setting is picked for the export.'), - 'value' => '127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'RPZ_serial' => array( - 'level' => 2, - 'description' => __('The serial in the SOA portion of the zone file. (numeric, best practice is yyyymmddrr where rr is the two digit sub-revision of the file. $date will automatically get converted to the current yyyymmdd, so $date00 is a valid setting). Setting it to $time will give you an unixtime-based serial (good then you need more than 99 revisions per day).'), - 'value' => '$date00', - 'errorMessage' => '', - 'test' => 'testForRPZSerial', - 'type' => 'string', - ), - 'RPZ_refresh' => array( - 'level' => 2, - 'description' => __('The refresh specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), - 'value' => '2h', - 'errorMessage' => '', - 'test' => 'testForRPZDuration', - 'type' => 'string', - ), - 'RPZ_retry' => array( - 'level' => 2, - 'description' => __('The retry specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), - 'value' => '30m', - 'errorMessage' => '', - 'test' => 'testForRPZDuration', - 'type' => 'string', - ), - 'RPZ_expiry' => array( - 'level' => 2, - 'description' => __('The expiry specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), - 'value' => '30d', - 'errorMessage' => '', - 'test' => 'testForRPZDuration', - 'type' => 'string', - ), - 'RPZ_minimum_ttl' => array( - 'level' => 2, - 'description' => __('The minimum TTL specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), - 'value' => '1h', - 'errorMessage' => '', - 'test' => 'testForRPZDuration', - 'type' => 'string', - ), - 'RPZ_ttl' => array( - 'level' => 2, - 'description' => __('The TTL of the zone file. (in seconds, or shorthand duration such as 15m)'), - 'value' => '1w', - 'errorMessage' => '', - 'test' => 'testForRPZDuration', - 'type' => 'string', - ), - 'RPZ_ns' => array( - 'level' => 2, - 'description' => __('Nameserver'), - 'value' => 'localhost.', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'RPZ_ns_alt' => array( - 'level' => 2, - 'description' => __('Alternate nameserver'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'RPZ_email' => array( - 'level' => 2, - 'description' => __('The e-mail address specified in the SOA portion of the zone file.'), - 'value' => 'root.localhost', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'Kafka_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the Kafka pub feature of MISP. Make sure that you install the requirements for the plugin to work. Refer to the installation instructions for more information.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'Kafka_brokers' => array( - 'level' => 2, - 'description' => __('A comma separated list of Kafka bootstrap brokers'), - 'value' => 'kafka:9092', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'Kafka_rdkafka_config' => array( - 'level' => 2, - 'description' => __('A path to an ini file with configuration options to be passed to rdkafka. Section headers in the ini file will be ignored.'), - 'value' => '/etc/rdkafka.ini', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - ), - 'Kafka_include_attachments' => array( - 'level' => 2, - 'description' => __('Enable this setting to include the base64 encoded payloads of malware-samples/attachments in the output.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_event_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any event creations/edits/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_event_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing event creations/edits/deletions.'), - 'value' => 'misp_event', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_event_publish_notifications_enable' => array( - 'level' => 2, - 'description' => __('If enabled it will publish to Kafka the event at the time that the event gets published in MISP. Event actions (creation or edit) will not be published to Kafka.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_event_publish_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing event information on publish.'), - 'value' => 'misp_event_publish', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_object_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any object creations/edits/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_object_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing object creations/edits/deletions.'), - 'value' => 'misp_object', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_object_reference_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any object reference creations/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_object_reference_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing object reference creations/deletions.'), - 'value' => 'misp_object_reference', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_attribute_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any attribute creations/edits/soft deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_attribute_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing attribute creations/edits/soft deletions.'), - 'value' => 'misp_attribute', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_shadow_attribute_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any proposal creations/edits/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_shadow_attribute_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing proposal creations/edits/deletions.'), - 'value' => 'misp_shadow_attribute', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_tag_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_tag_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), - 'value' => 'misp_tag', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_sighting_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new sightings.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_sighting_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing sightings.'), - 'value' => 'misp_sighting', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_user_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new/modified users.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_user_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing new/modified users.'), - 'value' => 'misp_user', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_organisation_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new/modified organisations.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_organisation_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing new/modified organisations.'), - 'value' => 'misp_organisation', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Kafka_audit_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of log entries. Keep in mind, this can get pretty verbose depending on your logging settings.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Kafka_audit_notifications_topic' => array( - 'level' => 2, - 'description' => __('Topic for publishing log entries.'), - 'value' => 'misp_audit', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'ZeroMQ_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the pub/sub feature of MISP. Make sure that you install the requirements for the plugin to work. Refer to the installation instructions for more information.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_port' => array( - 'level' => 2, - 'description' => __('The port that the pub/sub feature will use.'), - 'value' => 50000, - 'errorMessage' => '', - 'test' => 'testForZMQPortNumber', - 'type' => 'numeric', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_username' => array( - 'level' => 2, - 'description' => __('The username that client need to use to connect to ZeroMQ.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_password' => array( - 'level' => 2, - 'description' => __('The password that client need to use to connect to ZeroMQ.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_redis_host' => array( - 'level' => 2, - 'description' => __('Location of the Redis db used by MISP and the Python PUB script to queue data to be published.'), - 'value' => 'localhost', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_redis_port' => array( - 'level' => 2, - 'description' => __('The port that Redis is listening on.'), - 'value' => 6379, - 'errorMessage' => '', - 'test' => 'testForPortNumber', - 'type' => 'numeric', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_redis_password' => array( - 'level' => 2, - 'description' => __('The password, if set for Redis.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_redis_database' => array( - 'level' => 2, - 'description' => __('The database to be used for queuing messages for the pub/sub functionality.'), - 'value' => 1, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_redis_namespace' => array( - 'level' => 2, - 'description' => __('The namespace to be used for queuing messages for the pub/sub functionality.'), - 'value' => 'mispq', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'afterHook' => 'zmqAfterHook', - ), - 'ZeroMQ_include_attachments' => array( - 'level' => 2, - 'description' => __('Enable this setting to include the base64 encoded payloads of malware-samples/attachments in the output.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_event_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any event creations/edits/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_object_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any object creations/edits/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_object_reference_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any object reference creations/deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_attribute_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any attribute creations/edits/soft deletions.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_tag_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of any tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_sighting_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new sightings to the ZMQ pubsub feed.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_user_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new/modified users to the ZMQ pubsub feed.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_organisation_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of new/modified organisations to the ZMQ pubsub feed.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ZeroMQ_audit_notifications_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables the publishing of log entries to the ZMQ pubsub feed. Keep in mind, this can get pretty verbose depending on your logging settings.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ElasticSearch_logging_enable' => array( - 'level' => 2, - 'description' => __('Enabled logging to an ElasticSearch instance'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'ElasticSearch_connection_string' => array( - 'level' => 2, - 'description' => __('The URL(s) at which to access ElasticSearch - comma separate if you want to have more than one.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'ElasticSearch_log_index' => array( - 'level' => 2, - 'description' => __('The index in which to place logs'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'S3_enable' => array( - 'level' => 2, - 'description' => __('Enables or disables uploading of malware samples to S3 rather than to disk (WARNING: Get permission from amazon first!)'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'S3_bucket_name' => array( - 'level' => 2, - 'description' => __('Bucket name to upload to'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'S3_region' => array( - 'level' => 2, - 'description' => __('Region in which your S3 bucket resides'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'S3_aws_access_key' => array( - 'level' => 2, - 'description' => __('AWS key to use when uploading samples (WARNING: It\' highly recommended that you use EC2 IAM roles if at all possible)'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'S3_aws_secret_key' => array( - 'level' => 2, - 'description' => __('AWS secret key to use when uploading samples'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Sightings_policy' => array( - 'level' => 1, - 'description' => __('This setting defines who will have access to seeing the reported sightings. The default setting is the event owner organisation alone (in addition to everyone seeing their own contribution) with the other options being Sighting reporters (meaning the event owner and any organisation that provided sighting data about the event) and Everyone (meaning anyone that has access to seeing the event / attribute).'), - 'value' => 0, - 'errorMessage' => '', - 'test' => 'testForSightingVisibility', - 'type' => 'numeric', - 'options' => array( - 0 => __('Event Owner Organisation'), - 1 => __('Sighting reporters'), - 2 => __('Everyone'), - 3 => __('Event Owner + host org sightings'), - ), - ), - 'Sightings_anonymise' => array( - 'level' => 1, - 'description' => __('Enabling the anonymisation of sightings will simply aggregate all sightings instead of showing the organisations that have reported a sighting. Users will be able to tell the number of sightings their organisation has submitted and the number of sightings for other organisations'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - ), - 'Sightings_anonymise_as' => array( - 'level' => 1, - 'description' => __('When pushing sightings to another server, report all sightings from this instance as this organisation. This effectively hides all sightings from this instance behind a single organisation to the outside world. Sightings pulled from this instance follow the Sightings_policy above.'), - 'value' => '0', - 'errorMessage' => '', - 'test' => 'testLocalOrg', - 'type' => 'numeric', - 'optionsSource' => 'LocalOrgs', - ), - 'Sightings_range' => array( - 'level' => 1, - 'description' => __('Set the range in which sightings will be taken into account when generating graphs. For example a sighting with a sighted_date of 7 years ago might not be relevant anymore. Setting given in number of days, default is 365 days'), - 'value' => 365, - 'errorMessage' => '', - 'test' => 'testForNumeric', - 'type' => 'numeric' - ), - 'Sightings_sighting_db_enable' => array( - 'level' => 1, - 'description' => __('Enable SightingDB integration.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'CustomAuth_enable' => array( - 'level' => 2, - 'description' => __('Enable this functionality if you would like to handle the authentication via an external tool and authenticate with MISP using a custom header.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true, - 'beforeHook' => 'customAuthBeforeHook' - ), - 'CustomAuth_header' => array( - 'level' => 2, - 'description' => __('Set the header that MISP should look for here. If left empty it will default to the Authorization header.'), - 'value' => 'Authorization', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_use_header_namespace' => array( - 'level' => 2, - 'description' => __('Use a header namespace for the auth header - default setting is enabled'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'CustomAuth_header_namespace' => array( - 'level' => 2, - 'description' => __('The default header namespace for the auth header - default setting is HTTP_'), - 'value' => 'HTTP_', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_required' => array( - 'level' => 2, - 'description' => __('If this setting is enabled then the only way to authenticate will be using the custom header. Alternatively, you can run in mixed mode that will log users in via the header if found, otherwise users will be redirected to the normal login page.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'CustomAuth_only_allow_source' => array( - 'level' => 2, - 'description' => __('If you are using an external tool to authenticate with MISP and would like to only allow the tool\'s url as a valid point of entry then set this field. '), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_name' => array( - 'level' => 2, - 'description' => __('The name of the authentication method, this is cosmetic only and will be shown on the user creation page and logs.'), - 'value' => 'External authentication', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_disable_logout' => array( - 'level' => 2, - 'description' => __('Disable the logout button for users authenticate with the external auth mechanism.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Enrichment_services_enable' => array( - 'level' => 0, - 'description' => __('Enable/disable the enrichment services'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Enrichment_timeout' => array( - 'level' => 1, - 'description' => __('Set a timeout for the enrichment services'), - 'value' => 10, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - 'Import_services_enable' => array( - 'level' => 0, - 'description' => __('Enable/disable the import services'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Import_timeout' => array( - 'level' => 1, - 'description' => __('Set a timeout for the import services'), - 'value' => 10, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - 'Import_services_url' => array( - 'level' => 1, - 'description' => __('The url used to access the import services. By default, it is accessible at http://127.0.0.1:6666'), - 'value' => 'http://127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Import_services_port' => array( - 'level' => 1, - 'description' => __('The port used to access the import services. By default, it is accessible at 127.0.0.1:6666'), - 'value' => '6666', - 'errorMessage' => '', - 'test' => 'testForPortNumber', - 'type' => 'numeric' - ), - 'Export_services_url' => array( - 'level' => 1, - 'description' => __('The url used to access the export services. By default, it is accessible at http://127.0.0.1:6666'), - 'value' => 'http://127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Export_services_port' => array( - 'level' => 1, - 'description' => __('The port used to access the export services. By default, it is accessible at 127.0.0.1:6666'), - 'value' => '6666', - 'errorMessage' => '', - 'test' => 'testForPortNumber', - 'type' => 'numeric' - ), - 'Export_services_enable' => array( - 'level' => 0, - 'description' => __('Enable/disable the export services'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Export_timeout' => array( - 'level' => 1, - 'description' => __('Set a timeout for the export services'), - 'value' => 10, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - 'Enrichment_hover_enable' => array( - 'level' => 0, - 'description' => __('Enable/disable the hover over information retrieved from the enrichment modules'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Enrichment_hover_popover_only' => array( - 'level' => 0, - 'description' => __('When enabled, users have to click on the magnifier icon to show the enrichment'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Enrichment_hover_timeout' => array( - 'level' => 1, - 'description' => __('Set a timeout for the hover services'), - 'value' => 5, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - 'Enrichment_services_url' => array( - 'level' => 1, - 'description' => __('The url used to access the enrichment services. By default, it is accessible at http://127.0.0.1:6666'), - 'value' => 'http://127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Enrichment_services_port' => array( - 'level' => 1, - 'description' => __('The port used to access the enrichment services. By default, it is accessible at 127.0.0.1:6666'), - 'value' => 6666, - 'errorMessage' => '', - 'test' => 'testForPortNumber', - 'type' => 'numeric' - ), - 'Cortex_services_url' => array( - 'level' => 1, - 'description' => __('The url used to access Cortex. By default, it is accessible at http://cortex-url'), - 'value' => 'http://127.0.0.1', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string' - ), - 'Cortex_services_port' => array( - 'level' => 1, - 'description' => __('The port used to access Cortex. By default, this is port 9000'), - 'value' => 9000, - 'errorMessage' => '', - 'test' => 'testForPortNumber', - 'type' => 'numeric' - ), - 'Cortex_services_enable' => array( - 'level' => 0, - 'description' => __('Enable/disable the Cortex services'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean' - ), - 'Cortex_authkey' => array( - 'level' => 1, - 'description' => __('Set an authentication key to be passed to Cortex'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'Cortex_timeout' => array( - 'level' => 1, - 'description' => __('Set a timeout for the Cortex services'), - 'value' => 120, - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'numeric' - ), - 'Cortex_ssl_verify_peer' => array( - 'level' => 1, - 'description' => __('Set to false to disable SSL verification. This is not recommended.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'Cortex_ssl_verify_host' => array( - 'level' => 1, - 'description' => __('Set to false if you wish to ignore hostname match errors when validating certificates.'), - 'value' => true, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'Cortex_ssl_allow_self_signed' => array( - 'level' => 1, - 'description' => __('Set to true to enable self-signed certificates to be accepted. This requires Cortex_ssl_verify_peer to be enabled.'), - 'value' => false, - 'errorMessage' => '', - 'test' => 'testBool', - 'type' => 'boolean', - 'null' => true - ), - 'Cortex_ssl_cafile' => array( - 'level' => 1, - 'description' => __('Set to the absolute path of the Certificate Authority file that you wish to use for verifying SSL certificates.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_custom_password_reset' => array( - 'level' => 2, - 'description' => __('Provide your custom authentication users with an external URL to the authentication system to reset their passwords.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ), - 'CustomAuth_custom_logout' => array( - 'level' => 2, - 'description' => __('Provide a custom logout URL for your users that will log them out using the authentication system you use.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testForEmpty', - 'type' => 'string', - 'null' => true - ) - ), - 'debug' => array( - 'level' => 0, - 'description' => __('The debug level of the instance, always use 0 for production instances.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testDebug', - 'type' => 'numeric', - 'options' => array(0 => 'Debug off', 1 => 'Debug on', 2 => 'Debug + SQL dump'), - ), - 'site_admin_debug' => array( - 'level' => 0, - 'description' => __('The debug level of the instance for site admins. This feature allows site admins to run debug mode on a live instance without exposing it to other users. The most verbose option of debug and site_admin_debug is used for site admins.'), - 'value' => '', - 'errorMessage' => '', - 'test' => 'testDebugAdmin', - 'type' => 'boolean', - 'null' => true - ), - ); } private $__settingTabMergeRules = array( @@ -6689,4 +4299,2413 @@ class Server extends AppModel return $response->code; } } + + public function __get($name) + { + if ($name === 'serverSettings') { + $this->serverSettings = $this->generateServerSettings(); + return $this->serverSettings; + } + return parent::__get($name); + } + + /** + * Generate just when required + * @return array[] + */ + private function generateServerSettings() + { + return array( + 'MISP' => array( + 'branch' => 1, + 'baseurl' => array( + 'level' => 0, + 'description' => __('The base url of the application (in the format https://www.mymispinstance.com or https://myserver.com/misp). Several features depend on this setting being correctly set to function.'), + 'value' => '', + 'errorMessage' => __('The currently set baseurl does not match the URL through which you have accessed the page. Disregard this if you are accessing the page via an alternate URL (for example via IP address).'), + 'test' => 'testBaseURL', + 'type' => 'string', + ), + 'external_baseurl' => array( + 'level' => 0, + 'description' => __('The base url of the application (in the format https://www.mymispinstance.com) as visible externally/by other MISPs. MISP will encode this URL in sharing groups when including itself. If this value is not set, the baseurl is used as a fallback.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testURL', + 'type' => 'string', + ), + 'live' => array( + 'level' => 0, + 'description' => __('Unless set to true, the instance will only be accessible by site admins.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testLive', + 'type' => 'boolean', + ), + 'language' => array( + 'level' => 0, + 'description' => __('Select the language MISP should use. The default is english.'), + 'value' => 'eng', + 'errorMessage' => '', + 'test' => 'testLanguage', + 'type' => 'string', + 'optionsSource' => 'AvailableLanguages', + 'afterHook' => 'cleanCacheFiles' + ), + 'default_attribute_memory_coefficient' => array( + 'level' => 1, + 'description' => __('This values controls the internal fetcher\'s memory envelope when it comes to attributes. The number provided is the amount of attributes that can be loaded for each MB of PHP memory available in one shot. Consider lowering this number if your instance has a lot of attribute tags / attribute galaxies attached.'), + 'value' => 80, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => true + ), + 'default_event_memory_divisor' => array( + 'level' => 1, + 'description' => __('This value controls the divisor for attribute weighting when it comes to loading full events. Meaning that it will load coefficient / divisor number of attributes per MB of memory available. Consider raising this number if you have a lot of correlations or highly contextualised events (large number of event level galaxies/tags).'), + 'value' => 3, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => true + ), + 'enable_advanced_correlations' => array( + 'level' => 0, + 'description' => __('Enable some performance heavy correlations (currently CIDR correlation)'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'server_settings_skip_backup_rotate' => array( + 'level' => 1, + 'description' => __('Enable this setting to directly save the config.php file without first creating a temporary file and moving it to avoid concurency issues. Generally not recommended, but useful when for example other tools modify/maintain the config.php file.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'python_bin' => array( + 'level' => 1, + 'description' => __('It is highly recommended to install all the python dependencies in a virtualenv. The recommended location is: %s/venv', ROOT), + 'value' => false, + 'errorMessage' => '', + 'null' => false, + 'test' => 'testForBinExec', + 'beforeHook' => 'beforeHookBinExec', + 'type' => 'string', + 'cli_only' => 1 + ), + 'ca_path' => array( + 'level' => 1, + 'description' => __('MISP will default to the bundled mozilla certificate bundle shipped with the framework, which is rather stale. If you wish to use an alternate bundle, just set this setting using the path to the bundle to use. This setting can only be modified via the CLI.'), + 'value' => APP . 'Lib/cakephp/lib/Cake/Config/cacert.pem', + 'errorMessage' => '', + 'null' => true, + 'test' => 'testForCABundle', + 'type' => 'string', + 'cli_only' => 1 + ), + 'disable_auto_logout' => array( + 'level' => 1, + 'description' => __('In some cases, a heavily used MISP instance can generate unwanted blackhole errors due to a high number of requests hitting the server. Disable the auto logout functionality to ease the burden on the system.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'ssdeep_correlation_threshold' => array( + 'level' => 1, + 'description' => __('Set the ssdeep score at which to consider two ssdeep hashes as correlating [1-100]'), + 'value' => 40, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + '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.'), + 'value' => 'Great things are happening! MISP is undergoing maintenance, but will return shortly. You can contact the administration at $email.', + 'errorMessage' => __('If this is not set the default value will be used.'), + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'name' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'version' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'disable_cached_exports' => array( + 'level' => 1, + 'description' => __('Cached exports can take up a considerable amount of space and can be disabled instance wide using this setting. Disabling the cached exports is not recommended as it\'s a valuable feature, however, if your server is having free space issues it might make sense to take this step.'), + 'value' => false, + 'null' => true, + 'errorMessage' => '', + 'test' => 'testDisableCache', + 'type' => 'boolean', + 'afterHook' => 'disableCacheAfterHook', + ), + 'disable_threat_level' => array( + 'level' => 1, + 'description' => __('Disable displaying / modifications to the threat level altogether on the instance (deprecated field).'), + 'value' => false, + 'null' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'header' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footermidleft' => array( + 'level' => 2, + 'description' => __('Footer text prepending the "Powered by MISP" text.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footermidright' => array( + 'level' => 2, + 'description' => __('Footer text following the "Powered by MISP" text.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footerpart1' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footerpart2' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footer' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footerversion' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'footer_logo' => array( + 'level' => 2 , + 'description' => __('If set, this setting allows you to display a logo on the right side of the footer. Upload it as a custom image in the file management tool.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCustomImage', + 'type' => 'string', + ), + 'home_logo' => array( + 'level' => 2 , + 'description' => __('If set, this setting allows you to display a logo as the home icon. Upload it as a custom image in the file management tool.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCustomImage', + 'type' => 'string', + ), + 'main_logo' => array( + 'level' => 2 , + 'description' => __('If set, the image specified here will replace the main MISP logo on the login screen. Upload it as a custom image in the file management tool.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCustomImage', + 'type' => 'string', + ), + 'org' => array( + 'level' => 1, + 'description' => __('The organisation tag of the hosting organisation. This is used in the e-mail subjects.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'host_org_id' => array( + 'level' => 0, + 'description' => __('The hosting organisation of this instance. If this is not selected then replication instances cannot be added.'), + 'value' => '0', + 'errorMessage' => '', + 'test' => 'testLocalOrg', + 'type' => 'numeric', + 'optionsSource' => 'LocalOrgs', + ), + 'uuid' => array( + 'level' => 0, + 'description' => __('The MISP instance UUID. This UUID is used to identify this instance.'), + 'value' => '0', + 'errorMessage' => __('No valid UUID set'), + 'test' => 'testUuid', + 'type' => 'string' + ), + 'logo' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'showorg' => array( + 'level' => 0, + 'description' => __('Setting this setting to \'false\' will hide all organisation names / logos.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'threatlevel_in_email_subject' => array( + 'level' => 2, + 'description' => __('Put the event threat level in the notification E-mail subject.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'email_subject_TLP_string' => array( + 'level' => 2, + 'description' => __('This is the TLP string for e-mails when email_subject_tag is not found.'), + 'value' => 'tlp:amber', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'email_subject_tag' => array( + 'level' => 2, + 'description' => __('If this tag is set on an event it\'s value will be sent in the E-mail subject. If the tag is not set the email_subject_TLP_string will be used.'), + 'value' => 'tlp', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'email_subject_include_tag_name' => array( + 'level' => 2, + 'description' => __('Include in name of the email_subject_tag in the subject. When false only the tag value is used.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'taxii_sync' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'taxii_client_path' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'background_jobs' => array( + 'level' => 1, + 'description' => __('Enables the use of MISP\'s background processing.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBoolTrue', + 'type' => 'boolean', + ), + 'attachments_dir' => array( + 'level' => 2, + 'description' => __('Directory where attachments are stored. MISP will NOT migrate the existing data if you change this setting. The only safe way to change this setting is in config.php, when MISP is not running, and after having moved/copied the existing data to the new location. This directory must already exist and be writable and readable by the MISP application.'), + 'value' => APP . '/files', # GUI display purpose only. + 'errorMessage' => '', + 'null' => false, + 'test' => 'testForWritableDir', + 'type' => 'string', + 'cli_only' => 1 + ), + 'cached_attachments' => array( + 'level' => 1, + 'description' => __('Allow the XML caches to include the encoded attachments.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'download_attachments_on_load' => array( + 'level' => 2, + 'description' => __('Always download attachments when loaded by a user in a browser'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'osuser' => array( + 'level' => 0, + 'description' => __('The Unix user MISP (php) is running as'), + 'value' => 'www-data', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'email' => array( + 'level' => 0, + 'description' => __('The e-mail address that MISP should use for all notifications'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'disable_emailing' => array( + 'level' => 0, + 'description' => __('You can disable all e-mailing using this setting. When enabled, no outgoing e-mails will be sent by MISP.'), + 'value' => false, + 'errorMessage' => '', + 'null' => true, + 'test' => 'testDisableEmail', + 'type' => 'boolean', + ), + 'contact' => array( + 'level' => 1, + 'description' => __('The e-mail address that MISP should include as a contact address for the instance\'s support team.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'dns' => array( + 'level' => 3, + 'description' => __('This setting is deprecated and can be safely removed.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'cveurl' => array( + 'level' => 1, + 'description' => __('Turn Vulnerability type attributes into links linking to the provided CVE lookup'), + 'value' => 'http://cve.circl.lu/cve/', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'cweurl' => array( + 'level' => 1, + 'description' => __('Turn Weakness type attributes into links linking to the provided CWE lookup'), + 'value' => 'http://cve.circl.lu/cwe/', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'disablerestalert' => array( + 'level' => 1, + 'description' => __('This setting controls whether notification e-mails will be sent when an event is created via the REST interface. It might be a good idea to disable this setting when first setting up a link to another instance to avoid spamming your users during the initial pull. Quick recap: True = Emails are NOT sent, False = Emails are sent on events published via sync / REST.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'extended_alert_subject' => array( + 'level' => 1, + 'description' => __('enabling this flag will allow the event description to be transmitted in the alert e-mail\'s subject. Be aware that this is not encrypted by GnuPG, so only enable it if you accept that part of the event description will be sent out in clear-text.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'default_event_distribution' => array( + 'level' => 0, + 'description' => __('The default distribution setting for events (0-3).'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'options' => array('0' => 'Your organisation only', '1' => 'This community only', '2' => 'Connected communities', '3' => 'All communities'), + ), + 'default_attribute_distribution' => array( + 'level' => 0, + 'description' => __('The default distribution setting for attributes, set it to \'event\' if you would like the attributes to default to the event distribution level. (0-3 or "event")'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'options' => array('0' => 'Your organisation only', '1' => 'This community only', '2' => 'Connected communities', '3' => 'All communities', 'event' => 'Inherit from event'), + ), + 'default_event_threat_level' => array( + 'level' => 1, + 'description' => __('The default threat level setting when creating events.'), + 'value' => 4, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'options' => array('1' => 'High', '2' => 'Medium', '3' => 'Low', '4' => 'undefined'), + ), + 'default_event_tag_collection' => array( + 'level' => 0, + 'description' => __('The tag collection to be applied to all events created manually.'), + 'value' => 0, + 'errorMessage' => '', + 'test' => 'testTagCollections', + 'type' => 'numeric', + 'optionsSource' => 'TagCollections', + ), + 'default_publish_alert' => array( + 'level' => 0, + 'description' => __('The default setting for publish alerts when creating users.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'tagging' => array( + 'level' => 1, + 'description' => __('Enable the tagging feature of MISP. This is highly recommended.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'full_tags_on_event_index' => array( + 'level' => 2, + 'description' => __('Show the full tag names on the event index.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'options' => array(0 => 'Minimal tags', 1 => 'Full tags', 2 => 'Shortened tags'), + ), + 'welcome_text_top' => array( + 'level' => 2, + 'description' => __('Used on the login page, before the MISP logo'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'welcome_text_bottom' => array( + 'level' => 2, + 'description' => __('Used on the login page, after the MISP logo'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'welcome_logo' => array( + 'level' => 2, + 'description' => __('Used on the login page, to the left of the MISP logo, upload it as a custom image in the file management tool.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCustomImage', + 'type' => 'string', + ), + 'welcome_logo2' => array( + 'level' => 2, + 'description' => __('Used on the login page, to the right of the MISP logo, upload it as a custom image in the file management tool.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCustomImage', + 'type' => 'string', + ), + 'title_text' => array( + 'level' => 2, + 'description' => __('Used in the page title, after the name of the page'), + 'value' => 'MISP', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'take_ownership_xml_import' => array( + 'level' => 2, + 'description' => __('Allows users to take ownership of an event uploaded via the "Add MISP XML" button. This allows spoofing the creator of a manually imported event, also breaking possibly breaking the original intended releasability. Synchronising with an instance that has a different creator for the same event can lead to unwanted consequences.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'terms_download' => array( + 'level' => 2, + 'description' => __('Choose whether the terms and conditions should be displayed inline (false) or offered as a download (true)'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'terms_file' => array( + 'level' => 2, + 'description' => __('The filename of the terms and conditions file. Make sure that the file is located in your MISP/app/files/terms directory'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForTermsFile', + 'type' => 'string' + ), + 'showorgalternate' => array( + 'level' => 2, + 'description' => __('True enables the alternate org fields for the event index (source org and member org) instead of the traditional way of showing only an org field. This allows users to see if an event was uploaded by a member organisation on their MISP instance, or if it originated on an interconnected instance.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'unpublishedprivate' => array( + 'level' => 2, + 'description' => __('True will deny access to unpublished events to users outside the organization of the submitter except site admins.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'newUserText' => array( + 'level' => 1, + 'bigField' => true, + 'description' => __('The message sent to the user after account creation (has to be sent manually from the administration interface). Use \\n for line-breaks. The following variables will be automatically replaced in the text: $password = a new temporary password that MISP generates, $username = the user\'s e-mail address, $misp = the url of this instance, $org = the organisation that the instance belongs to, as set in MISP.org, $contact = the e-mail address used to contact the support team, as set in MISP.contact. For example, "the password for $username is $password" would appear to a user with the e-mail address user@misp.org as "the password for user@misp.org is hNamJae81".'), + 'value' => 'Dear new MISP user,\n\nWe would hereby like to welcome you to the $org MISP community.\n\n Use the credentials below to log into MISP at $misp, where you will be prompted to manually change your password to something of your own choice.\n\nUsername: $username\nPassword: $password\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', + 'errorMessage' => '', + 'test' => 'testPasswordResetText', + 'type' => 'string' + ), + 'passwordResetText' => array( + 'level' => 1, + 'bigField' => true, + 'description' => __('The message sent to the users when a password reset is triggered. Use \\n for line-breaks. The following variables will be automatically replaced in the text: $password = a new temporary password that MISP generates, $username = the user\'s e-mail address, $misp = the url of this instance, $contact = the e-mail address used to contact the support team, as set in MISP.contact. For example, "the password for $username is $password" would appear to a user with the e-mail address user@misp.org as "the password for user@misp.org is hNamJae81".'), + 'value' => 'Dear MISP user,\n\nA password reset has been triggered for your account. Use the below provided temporary password to log into MISP at $misp, where you will be prompted to manually change your password to something of your own choice.\n\nUsername: $username\nYour temporary password: $password\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', + 'errorMessage' => '', + 'test' => 'testPasswordResetText', + 'type' => 'string' + ), + 'enableEventBlocklisting' => array( + 'level' => 1, + 'description' => __('Since version 2.3.107 you can start blocklisting event UUIDs to prevent them from being pushed to your instance. This functionality will also happen silently whenever an event is deleted, preventing a deleted event from being pushed back from another instance.'), + 'value' => true, + 'type' => 'boolean', + 'test' => 'testBool' + ), + 'enableOrgBlocklisting' => array( + 'level' => 1, + 'description' => __('Blocklisting organisation UUIDs to prevent the creation of any event created by the blocklisted organisation.'), + 'value' => true, + 'type' => 'boolean', + 'test' => 'testBool' + ), + 'log_client_ip' => array( + 'level' => 1, + 'description' => __('If enabled, all log entries will include the IP address of the user.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'beforeHook' => 'ipLogBeforeHook' + ), + 'log_client_ip_header' => array( + 'level' => 1, + 'description' => __('If log_client_ip is enabled, you can customize which header field contains the client\'s IP address. This is generally used when you have a reverse proxy infront of your MISP instance.'), + 'value' => 'REMOTE_ADDR', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true, + ), + 'log_auth' => array( + 'level' => 1, + 'description' => __('If enabled, MISP will log all successful authentications using API keys. The requested URLs are also logged.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'log_skip_db_logs_completely' => array( + 'level' => 0, + 'description' => __('This functionality allows you to completely disable any logs from being saved in your SQL backend. This is HIGHLY advised against, you lose all the functionalities provided by the audit log subsystem along with the event history (as these are built based on the logs on the fly). Only enable this if you understand and accept the associated risks.'), + 'value' => false, + 'errorMessage' => __('Logging has now been disabled - your audit logs will not capture failed authentication attempts, your event history logs are not being populated and no system maintenance messages are being logged.'), + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'log_paranoid' => array( + 'level' => 0, + 'description' => __('If this functionality is enabled all page requests will be logged. Keep in mind this is extremely verbose and will become a burden to your database.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'log_paranoid_skip_db' => array( + 'level' => 0, + 'description' => __('You can decide to skip the logging of the paranoid logs to the database.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testParanoidSkipDb', + 'type' => 'boolean', + 'null' => true + ), + 'log_paranoid_include_post_body' => array( + 'level' => 0, + 'description' => __('If paranoid logging is enabled, include the POST body in the entries.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'log_user_ips' => array( + 'level' => 0, + 'description' => __('Log user IPs on each request. 30 day retention for lookups by IP to get the last authenticated user ID for the given IP, whilst on the reverse, indefinitely stores all associated IPs for a user ID.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'log_user_ips_authkeys' => [ + 'level' => self::SETTING_RECOMMENDED, + 'description' => __('Log user IP and key usage on each API request. All logs for given keys are deleted after one year when this key is not used.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ], + 'delegation' => array( + 'level' => 1, + 'description' => __('This feature allows users to create org only events and ask another organisation to take ownership of the event. This allows organisations to remain anonymous by asking a partner to publish an event for them.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'showCorrelationsOnIndex' => array( + 'level' => 1, + 'description' => __('When enabled, the number of correlations visible to the currently logged in user will be visible on the event index UI. This comes at a performance cost but can be very useful to see correlating events at a glance.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'showProposalsCountOnIndex' => array( + 'level' => 1, + 'description' => __('When enabled, the number of proposals for the events are shown on the index.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'showSightingsCountOnIndex' => array( + 'level' => 1, + 'description' => __('When enabled, the aggregate number of attribute sightings within the event becomes visible to the currently logged in user on the event index UI.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'showDiscussionsCountOnIndex' => array( + 'level' => 1, + 'description' => __('When enabled, the aggregate number of discussion posts for the event becomes visible to the currently logged in user on the event index UI.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'disableUserSelfManagement' => array( + 'level' => 1, + 'description' => __('When enabled only Org and Site admins can edit a user\'s profile.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'disable_user_login_change' => array( + 'level' => self::SETTING_RECOMMENDED, + 'description' => __('When enabled only Site admins can change user email. This should be enabled if you manage user logins by external system.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'disable_user_password_change' => array( + 'level' => self::SETTING_RECOMMENDED, + 'description' => __('When enabled only Site admins can change user password. This should be enabled if you manage user passwords by external system.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'disable_user_add' => array( + 'level' => self::SETTING_RECOMMENDED, + 'description' => __('When enabled, Org Admins could not add new users. This should be enabled if you manage users by external system.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'block_event_alert' => array( + 'level' => 1, + 'description' => __('Enable this setting to start blocking alert e-mails for events with a certain tag. Define the tag in MISP.block_event_alert_tag.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'block_event_alert_tag' => array( + 'level' => 1, + 'description' => __('If the MISP.block_event_alert setting is set, alert e-mails for events tagged with the tag defined by this setting will be blocked.'), + 'value' => 'no-alerts="true"', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => false, + ), + 'org_alert_threshold' => array( + 'level' => 1, + 'description' => __('Set a value to limit the number of email alerts that events can generate per creator organisation (for example, if an organisation pushes out 2000 events in one shot, only alert on the first 20).'), + 'value' => 0, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => true, + ), + 'block_old_event_alert' => array( + 'level' => 1, + 'description' => __('Enable this setting to start blocking alert e-mails for old events. The exact timing of what constitutes an old event is defined by MISP.block_old_event_alert_age.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'block_old_event_alert_age' => array( + 'level' => 1, + 'description' => __('If the MISP.block_old_event_alert setting is set, this setting will control how old an event can be for it to be alerted on. The "timestamp" field of the event is used. Expected format: integer, in days'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => false, + ), + 'block_old_event_alert_by_date' => array( + 'level' => 1, + 'description' => __('If the MISP.block_old_event_alert setting is set, this setting will control the threshold for the event.date field, indicating how old an event can be for it to be alerted on. The "date" field of the event is used. Expected format: integer, in days'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => false, + ), + 'tmpdir' => array( + 'level' => 1, + 'description' => __('Please indicate the temp directory you wish to use for certain functionalities in MISP. By default this is set to /tmp and will be used among others to store certain temporary files extracted from imports during the import process.'), + 'value' => '/tmp', + 'errorMessage' => '', + 'test' => 'testForPath', + 'type' => 'string', + 'null' => true, + 'cli_only' => 1 + ), + 'custom_css' => array( + 'level' => 2, + 'description' => __('If you would like to customise the css, simply drop a css file in the /var/www/MISP/app/webroot/css directory and enter the name here.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForStyleFile', + 'type' => 'string', + 'null' => true, + ), + 'proposals_block_attributes' => array( + 'level' => 0, + 'description' => __('Enable this setting to allow blocking attributes from to_ids sensitive exports if a proposal has been made to it to remove the IDS flag or to remove the attribute altogether. This is a powerful tool to deal with false-positives efficiently.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false, + ), + 'incoming_tags_disabled_by_default' => array( + 'level' => 1, + 'description' => __('Enable this settings if new tags synced / added via incoming events from any source should not be selectable by users by default.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => false + ), + 'completely_disable_correlation' => array( + 'level' => 0, + 'description' => __('*WARNING* This setting will completely disable the correlation on this instance and remove any existing saved correlations. Enabling this will trigger a full recorrelation of all data which is an extremely long and costly procedure. Only enable this if you know what you\'re doing.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true, + 'afterHook' => 'correlationAfterHook', + ), + 'allow_disabling_correlation' => array( + 'level' => 0, + 'description' => __('*WARNING* This setting will give event creators the possibility to disable the correlation of individual events / attributes that they have created.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'redis_host' => array( + 'level' => 0, + 'description' => __('The host running the redis server to be used for generic MISP tasks such as caching. This is not to be confused by the redis server used by the background processing.'), + 'value' => '127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'redis_port' => array( + 'level' => 0, + 'description' => __('The port used by the redis server to be used for generic MISP tasks such as caching. This is not to be confused by the redis server used by the background processing.'), + 'value' => 6379, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric' + ), + 'redis_database' => array( + 'level' => 0, + 'description' => __('The database on the redis server to be used for generic MISP tasks. If you run more than one MISP instance, please make sure to use a different database on each instance.'), + 'value' => 13, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric' + ), + 'redis_password' => array( + 'level' => 0, + 'description' => __('The password on the redis server (if any) to be used for generic MISP tasks.'), + 'value' => '', + 'errorMessage' => '', + 'test' => null, + 'type' => 'string', + 'redacted' => true + ), + 'event_view_filter_fields' => array( + 'level' => 2, + 'description' => __('Specify which fields to filter on when you search on the event view. Default values are : "id, uuid, value, comment, type, category, Tag.name"'), + 'value' => 'id, uuid, value, comment, type, category, Tag.name', + 'errorMessage' => '', + 'test' => null, + 'type' => 'string', + ), + 'manage_workers' => array( + 'level' => 2, + 'description' => __('Set this to false if you would like to disable MISP managing its own worker processes (for example, if you are managing the workers with a systemd unit).'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'deadlock_avoidance' => array( + 'level' => 1, + 'description' => __('Only enable this if you have some tools using MISP with extreme high concurency. General performance will be lower as normal as certain transactional queries are avoided in favour of shorter table locks.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'updateTimeThreshold' => array( + 'level' => 1, + 'description' => __('Sets the minimum time before being able to re-trigger an update if the previous one failed. (safe guard to avoid starting the same update multiple time)'), + 'value' => '7200', + 'test' => 'testForNumeric', + 'type' => 'numeric', + 'null' => true + ), + 'attribute_filters_block_only' => array( + 'level' => 1, + 'description' => __('This is a performance tweak to change the behaviour of restSearch to use attribute filters solely for blocking. This means that a lookup on the event scope with for example the type field set will be ignored unless it\'s used to strip unwanted attributes from the results. If left disabled, passing [ip-src, ip-dst] for example will return any event with at least one ip-src or ip-dst attribute. This is generally not considered to be too useful and is a heavy burden on the database.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'attachment_scan_module' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('Name of enrichment module that will be used for attachment malware scanning. This module must return av-signature or sb-signature object.'), + 'value' => '', + 'errorMessage' => '', + 'type' => 'string', + 'null' => true, + ], + 'attachment_scan_hash_only' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('Send to attachment scan module just file hash. This can be useful if module sends attachment to remote service and you don\'t want to leak real data.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + ], + 'attachment_scan_timeout' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('How long to wait for scan results in seconds.'), + 'value' => 30, + 'errorMessage' => '', + 'test' => 'testForPositiveInteger', + 'type' => 'numeric', + 'null' => true, + ], + ), + 'GnuPG' => array( + 'branch' => 1, + 'binary' => array( + 'level' => 2, + 'description' => __('The location of the GnuPG executable. If you would like to use a different GnuPG executable than /usr/bin/gpg, you can set it here. If the default is fine, just keep the setting suggested by MISP.'), + 'value' => '/usr/bin/gpg', + 'errorMessage' => '', + 'test' => 'testForGPGBinary', + 'type' => 'string', + 'cli_only' => 1 + ), + 'onlyencrypted' => array( + 'level' => 0, + 'description' => __('Allow (false) unencrypted e-mails to be sent to users that don\'t have a GnuPG key.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'bodyonlyencrypted' => array( + 'level' => 2, + 'description' => __('Allow (false) the body of unencrypted e-mails to contain details about the event.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'sign' => array( + 'level' => 2, + 'description' => __('Enable the signing of GnuPG emails. By default, GnuPG emails are signed'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'email' => array( + 'level' => 0, + 'description' => __('The e-mail address that the instance\'s GnuPG key is tied to.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'password' => array( + 'level' => 1, + 'description' => __('The password (if it is set) of the GnuPG key of the instance.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'redacted' => true + ), + 'homedir' => array( + 'level' => 0, + 'description' => __('The location of the GnuPG homedir.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'obscure_subject' => array( + 'level' => 2, + 'description' => __('When enabled, subject in signed and encrypted e-mails will not send in unencrypted form.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ) + ), + 'SMIME' => array( + 'branch' => 1, + 'enabled' => array( + 'level' => 2, + 'description' => __('Enable SMIME encryption. The encryption posture of the GnuPG.onlyencrypted and GnuPG.bodyonlyencrypted settings are inherited if SMIME is enabled.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'email' => array( + 'level' => 2, + 'description' => __('The e-mail address that the instance\'s SMIME key is tied to.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'cert_public_sign' => array( + 'level' => 2, + 'description' => __('The location of the public half of the signing certificate.'), + 'value' => '/var/www/MISP/.smime/email@address.com.pem', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'key_sign' => array( + 'level' => 2, + 'description' => __('The location of the private half of the signing certificate.'), + 'value' => '/var/www/MISP/.smime/email@address.com.key', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'password' => array( + 'level' => 2, + 'description' => __('The password (if it is set) of the SMIME key of the instance.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'redacted' => true + ), + ), + 'Proxy' => array( + 'branch' => 1, + 'host' => array( + 'level' => 2, + 'description' => __('The hostname of an HTTP proxy for outgoing sync requests. Leave empty to not use a proxy.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'port' => array( + 'level' => 2, + 'description' => __('The TCP port for the HTTP proxy.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric', + ), + 'method' => array( + 'level' => 2, + 'description' => __('The authentication method for the HTTP proxy. Currently supported are Basic or Digest. Leave empty for no proxy authentication.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'user' => array( + 'level' => 2, + 'description' => __('The authentication username for the HTTP proxy.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'password' => array( + 'level' => 2, + 'description' => __('The authentication password for the HTTP proxy.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + ), + 'Security' => array( + 'branch' => 1, + 'disable_form_security' => array( + 'level' => 0, + 'description' => __('Disabling this setting will remove all form tampering protection. Do not set this setting pretty much ever. You were warned.'), + 'value' => false, + 'errorMessage' => 'This setting leaves your users open to CSRF attacks. Do not please consider disabling this setting.', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'salt' => array( + 'level' => 0, + 'description' => __('The salt used for the hashed passwords. You cannot reset this from the GUI, only manually from the settings.php file. Keep in mind, this will invalidate all passwords in the database.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testSalt', + 'type' => 'string', + 'editable' => false, + 'redacted' => true + ), + 'advanced_authkeys' => array( + 'level' => 0, + 'description' => __('Advanced authkeys will allow each user to create and manage a set of authkeys for themselves, each with individual expirations and comments. API keys are stored in a hashed state and can no longer be recovered from MISP. Users will be prompted to note down their key when creating a new authkey. You can generate a new set of API keys for all users on demand in the diagnostics page, or by triggering %s.', sprintf('%s', $this->baseurl, __('the advanced upgrade'))), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'advanced_authkeys_validity' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('Maximal key lifetime in days. Use can limit that validity even more. Just newly created keys will be affected. When not set, key validity is not limited.'), + 'value' => '', + 'errorMessage' => '', + 'type' => 'numeric', + 'test' => 'testForNumeric', + 'null' => true, + ], + 'authkey_keep_session' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('When enabled, session is kept between API requests.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + ], + 'auth_enforced' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('This optional can be enabled if external auth provider is used and when set to true, it will disable default form authentication.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ], + 'rest_client_enable_arbitrary_urls' => array( + 'level' => 0, + 'description' => __('Enable this setting if you wish for users to be able to query any arbitrary URL via the rest client. Keep in mind that queries are executed by the MISP server, so internal IPs in your MISP\'s network may be reachable.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'rest_client_baseurl' => array( + 'level' => 1, + 'description' => __('If left empty, the baseurl of your MISP is used. However, in some instances (such as port-forwarded VM installations) this will not work. You can override the baseurl with a url through which your MISP can reach itself (typically https://127.0.0.1 would work).'), + 'value' => false, + 'errorMessage' => '', + 'test' => null, + 'type' => 'string' + ), + 'syslog' => array( + 'level' => 0, + 'description' => __('Enable this setting to pass all audit log entries directly to syslog. Keep in mind, this is verbose and will include user, organisation, event data.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'syslog_to_stderr' => array( + 'level' => self::SETTING_OPTIONAL, + 'description' => __('Write syslog messages also to standard error output.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'syslog_ident' => array( + 'level' => self::SETTING_OPTIONAL, + 'description' => __('Syslog message identifier.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'do_not_log_authkeys' => array( + 'level' => 0, + 'description' => __('If enabled, any authkey will be replaced by asterisks in Audit log.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'disable_browser_cache' => array( + 'level' => 0, + 'description' => __('If enabled, HTTP headers that block browser cache will be send. Static files (like images or JavaScripts) will still be cached, but not generated pages.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + ), + 'check_sec_fetch_site_header' => [ + 'level' => 0, + 'description' => __('If enabled, any POST, PUT or AJAX request will be allow just when Sec-Fetch-Site header is not defined or contains "same-origin".'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + ], + 'email_otp_enabled' => array( + 'level'=> 2, + 'description' => __('Enable two step authentication with a OTP sent by email. Requires e-mailing to be enabled. Warning: You cannot use it in combination with external authentication plugins.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'beforeHook' => 'otpBeforeHook', + 'type' => 'boolean', + 'null' => true + ), + 'email_otp_length' => array ( + 'level' => 2, + 'description' => __('Define the length of the OTP code sent by email'), + 'value' => '6', + 'errorMessage' => '', + 'type' => 'numeric', + 'test' => 'testForNumeric', + 'null' => true, + ), + 'email_otp_validity' => array ( + 'level' => 2, + 'description' => __('Define the validity (in minutes) of the OTP code sent by email'), + 'value' => '5', + 'errorMessage' => '', + 'type' => 'numeric', + 'test' => 'testForNumeric', + 'null' => true, + ), + 'email_otp_text' => array( + 'level' => 2, + 'bigField' => true, + 'description' => __('The message sent to the user when a new OTP is requested. Use \\n for line-breaks. The following variables will be automatically replaced in the text: $otp = the new OTP generated by MISP, $username = the user\'s e-mail address, $org the Organisation managing the instance, $misp = the url of this instance, $contact = the e-mail address used to contact the support team (as set in MISP.contact), $ip the IP used to complete the first step of the login and $validity the validity time in minutes.'), + 'value' => 'Dear MISP user,\n\nYou have attempted to login to MISP ($misp) from $ip with username $username.\n\n Use the following OTP to log into MISP: $otp\n This code is valid for the next $validity minutes.\n\nIf you have any questions, don\'t hesitate to contact us at: $contact.\n\nBest regards,\nYour $org MISP support team', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true, + ), + 'email_otp_exceptions' => array( + 'level' => 2, + 'bigField' => true, + 'description' => __('A comma separated list of emails for which the OTP is disabled. Note that if you remove someone from this list, the OTP will only be asked at next login.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true, + ), + 'allow_self_registration' => array( + 'level' => 1, + 'description' => __('Enabling this setting will allow users to have access to the pre-auth registration form. This will create an inbox entry for administrators to review.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'self_registration_message' => array( + 'level' => 1, + 'bigField' => true, + 'description' => __('The message sent shown to anyone trying to self-register.'), + 'value' => 'If you would like to send us a registration request, please fill out the form below. Make sure you fill out as much information as possible in order to ease the task of the administrators.', + 'errorMessage' => '', + 'test' => false, + 'type' => 'string' + ), + 'password_policy_length' => array( + 'level' => 2, + 'description' => __('Password length requirement. If it is not set or it is set to 0, then the default value is assumed (12).'), + 'value' => '12', + 'errorMessage' => '', + 'test' => 'testPasswordLength', + 'type' => 'numeric', + ), + 'password_policy_complexity' => array( + 'level' => 2, + 'description' => __('Password complexity requirement. Leave it empty for the default setting (3 out of 4, with either a digit or a special char) or enter your own regex. Keep in mind that the length is checked in another key. Default (simple 3 out of 4 or minimum 16 characters): /^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/'), + 'value' => '/^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/', + 'errorMessage' => '', + 'test' => 'testPasswordRegex', + 'type' => 'string', + ), + 'require_password_confirmation' => array( + 'level' => 1, + 'description' => __('Enabling this setting will require users to submit their current password on any edits to their profile (including a triggered password change). For administrators, the confirmation will be required when changing the profile of any user. Could potentially mitigate an attacker trying to change a compromised user\'s password in order to establish persistance, however, enabling this feature will be highly annoying to users.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'sanitise_attribute_on_delete' => array( + 'level' => 1, + 'description' => __('Enabling this setting will sanitise the contents of an attribute on a soft delete'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'hide_organisation_index_from_users' => array( + 'level' => 1, + 'description' => __('Enabling this setting will block the organisation index from being visible to anyone besides site administrators on the current instance. Keep in mind that users can still see organisations that produce data via events, proposals, event history log entries, etc.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'hide_organisations_in_sharing_groups' => [ + 'level' => self::SETTING_RECOMMENDED, + 'description' => __('Enabling this setting will block the organisation list from being visible in sharing group besides user with sharing group permission.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ], + 'disable_local_feed_access' => array( + 'level' => 0, + 'description' => __('Disabling this setting will allow the creation/modification of local feeds (as opposed to network feeds). Enabling this setting will restrict feed sources to be network based only. When disabled, keep in mind that a malicious site administrator could get access to any arbitrary file on the system that the apache user has access to. Make sure that proper safe-guards are in place. This setting can only be modified via the CLI.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + 'cli_only' => 1 + ), + 'allow_unsafe_apikey_named_param' => array( + 'level' => 0, + 'description' => __('Allows passing the API key via the named url parameter "apikey" - highly recommended not to enable this, but if you have some dodgy legacy tools that cannot pass the authorization header it can work as a workaround. Again, only use this as a last resort.'), + 'value' => false, + 'errorMessage' => __('You have enabled the passing of API keys via URL parameters. This is highly recommended against, do you really want to reveal APIkeys in your logs?...'), + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'allow_cors' => array( + 'level' => 1, + 'description' => __('Allow cross-origin requests to this instance, matching origins given in Security.cors_origins. Set to false to totally disable'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'cors_origins' => array( + 'level' => 1, + 'description' => __('Set the origins from which MISP will allow cross-origin requests. Useful for external integration. Comma seperate if you need more than one.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'sync_audit' => array( + 'level' => 1, + 'description' => __('Enable this setting to create verbose logs of synced event data for debugging reasons. Logs are saved in your MISP directory\'s app/files/scripts/tmp/ directory.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + 'null' => true + ), + 'user_monitoring_enabled' => array( + 'level' => 1, + 'description' => __('Enables the functionality to monitor users - thereby enabling all logging functionalities for a single user. This functionality is intrusive and potentially heavy on the system - use it with care.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'username_in_response_header' => [ + 'level' => self::SETTING_OPTIONAL, + 'description' => __('When enabled, logged in username will be included in X-Username HTTP response header. This is useful for request logging on webserver/proxy side.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ] + ), + 'SecureAuth' => array( + 'branch' => 1, + 'amount' => array( + 'level' => 0, + 'description' => __('The number of tries a user can try to login and fail before the bruteforce protection kicks in.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'string', + ), + 'expire' => array( + 'level' => 0, + 'description' => __('The duration (in seconds) of how long the user will be locked out when the allowed number of login attempts are exhausted.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'string', + ), + ), + 'Session' => array( + 'branch' => 1, + 'autoRegenerate' => array( + 'level' => 0, + 'description' => __('Set to true to automatically regenerate sessions after x number of requests. This might lead to the user getting de-authenticated and is frustrating in general, so only enable it if you really need to regenerate sessions. (Not recommended)'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + ), + 'checkAgent' => array( + 'level' => 0, + 'description' => __('Set to true to check for the user agent string in each request. This can lead to occasional logouts (not recommended).'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBoolFalse', + 'type' => 'boolean', + ), + 'defaults' => array( + 'level' => 0, + 'description' => __('The session type used by MISP. The default setting is php, which will use the session settings configured in php.ini for the session data (supported options: php, database). The recommended option is php and setting your PHP up to use redis sessions via your php.ini. Just add \'session.save_handler = redis\' and "session.save_path = \'tcp://localhost:6379\'" (replace the latter with your redis connection) to '), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForSessionDefaults', + 'type' => 'string', + 'options' => array('php' => 'php', 'database' => 'database', 'cake' => 'cake', 'cache' => 'cache'), + ), + 'timeout' => array( + 'level' => 0, + 'description' => __('The timeout duration of sessions (in MINUTES). 0 does not mean infinite for the PHP session handler, instead sessions will invalidate immediately.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'string' + ), + 'cookieTimeout' => array( + 'level' => 0, + 'description' => __('The expiration of the cookie (in MINUTES). The session timeout gets refreshed frequently, however the cookies do not. Generally it is recommended to have a much higher cookie_timeout than timeout.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForCookieTimeout', + 'type' => 'numeric' + ) + ), + 'Plugin' => array( + 'branch' => 1, + 'RPZ_policy' => array( + 'level' => 2, + 'description' => __('The default policy action for the values added to the RPZ.'), + 'value' => 1, + 'errorMessage' => '', + 'test' => 'testForRPZBehaviour', + 'type' => 'numeric', + 'options' => array(0 => 'DROP', 1 => 'NXDOMAIN', 2 => 'NODATA', 3 => 'Local-Data', 4 => 'PASSTHRU', 5 => 'TCP-only' ), + ), + 'RPZ_walled_garden' => array( + 'level' => 2, + 'description' => __('The default walled garden used by the RPZ export if the Local-Data policy setting is picked for the export.'), + 'value' => '127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'RPZ_serial' => array( + 'level' => 2, + 'description' => __('The serial in the SOA portion of the zone file. (numeric, best practice is yyyymmddrr where rr is the two digit sub-revision of the file. $date will automatically get converted to the current yyyymmdd, so $date00 is a valid setting). Setting it to $time will give you an unixtime-based serial (good then you need more than 99 revisions per day).'), + 'value' => '$date00', + 'errorMessage' => '', + 'test' => 'testForRPZSerial', + 'type' => 'string', + ), + 'RPZ_refresh' => array( + 'level' => 2, + 'description' => __('The refresh specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), + 'value' => '2h', + 'errorMessage' => '', + 'test' => 'testForRPZDuration', + 'type' => 'string', + ), + 'RPZ_retry' => array( + 'level' => 2, + 'description' => __('The retry specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), + 'value' => '30m', + 'errorMessage' => '', + 'test' => 'testForRPZDuration', + 'type' => 'string', + ), + 'RPZ_expiry' => array( + 'level' => 2, + 'description' => __('The expiry specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), + 'value' => '30d', + 'errorMessage' => '', + 'test' => 'testForRPZDuration', + 'type' => 'string', + ), + 'RPZ_minimum_ttl' => array( + 'level' => 2, + 'description' => __('The minimum TTL specified in the SOA portion of the zone file. (in seconds, or shorthand duration such as 15m)'), + 'value' => '1h', + 'errorMessage' => '', + 'test' => 'testForRPZDuration', + 'type' => 'string', + ), + 'RPZ_ttl' => array( + 'level' => 2, + 'description' => __('The TTL of the zone file. (in seconds, or shorthand duration such as 15m)'), + 'value' => '1w', + 'errorMessage' => '', + 'test' => 'testForRPZDuration', + 'type' => 'string', + ), + 'RPZ_ns' => array( + 'level' => 2, + 'description' => __('Nameserver'), + 'value' => 'localhost.', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'RPZ_ns_alt' => array( + 'level' => 2, + 'description' => __('Alternate nameserver'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'RPZ_email' => array( + 'level' => 2, + 'description' => __('The e-mail address specified in the SOA portion of the zone file.'), + 'value' => 'root.localhost', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'Kafka_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the Kafka pub feature of MISP. Make sure that you install the requirements for the plugin to work. Refer to the installation instructions for more information.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'Kafka_brokers' => array( + 'level' => 2, + 'description' => __('A comma separated list of Kafka bootstrap brokers'), + 'value' => 'kafka:9092', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'Kafka_rdkafka_config' => array( + 'level' => 2, + 'description' => __('A path to an ini file with configuration options to be passed to rdkafka. Section headers in the ini file will be ignored.'), + 'value' => '/etc/rdkafka.ini', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + ), + 'Kafka_include_attachments' => array( + 'level' => 2, + 'description' => __('Enable this setting to include the base64 encoded payloads of malware-samples/attachments in the output.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_event_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any event creations/edits/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_event_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing event creations/edits/deletions.'), + 'value' => 'misp_event', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_event_publish_notifications_enable' => array( + 'level' => 2, + 'description' => __('If enabled it will publish to Kafka the event at the time that the event gets published in MISP. Event actions (creation or edit) will not be published to Kafka.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_event_publish_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing event information on publish.'), + 'value' => 'misp_event_publish', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_object_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any object creations/edits/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_object_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing object creations/edits/deletions.'), + 'value' => 'misp_object', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_object_reference_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any object reference creations/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_object_reference_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing object reference creations/deletions.'), + 'value' => 'misp_object_reference', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_attribute_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any attribute creations/edits/soft deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_attribute_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing attribute creations/edits/soft deletions.'), + 'value' => 'misp_attribute', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_shadow_attribute_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any proposal creations/edits/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_shadow_attribute_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing proposal creations/edits/deletions.'), + 'value' => 'misp_shadow_attribute', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_tag_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_tag_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), + 'value' => 'misp_tag', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_sighting_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new sightings.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_sighting_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing sightings.'), + 'value' => 'misp_sighting', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_user_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new/modified users.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_user_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing new/modified users.'), + 'value' => 'misp_user', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_organisation_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new/modified organisations.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_organisation_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing new/modified organisations.'), + 'value' => 'misp_organisation', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Kafka_audit_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of log entries. Keep in mind, this can get pretty verbose depending on your logging settings.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Kafka_audit_notifications_topic' => array( + 'level' => 2, + 'description' => __('Topic for publishing log entries.'), + 'value' => 'misp_audit', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'ZeroMQ_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the pub/sub feature of MISP. Make sure that you install the requirements for the plugin to work. Refer to the installation instructions for more information.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_port' => array( + 'level' => 2, + 'description' => __('The port that the pub/sub feature will use.'), + 'value' => 50000, + 'errorMessage' => '', + 'test' => 'testForZMQPortNumber', + 'type' => 'numeric', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_username' => array( + 'level' => 2, + 'description' => __('The username that client need to use to connect to ZeroMQ.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_password' => array( + 'level' => 2, + 'description' => __('The password that client need to use to connect to ZeroMQ.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_redis_host' => array( + 'level' => 2, + 'description' => __('Location of the Redis db used by MISP and the Python PUB script to queue data to be published.'), + 'value' => 'localhost', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_redis_port' => array( + 'level' => 2, + 'description' => __('The port that Redis is listening on.'), + 'value' => 6379, + 'errorMessage' => '', + 'test' => 'testForPortNumber', + 'type' => 'numeric', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_redis_password' => array( + 'level' => 2, + 'description' => __('The password, if set for Redis.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_redis_database' => array( + 'level' => 2, + 'description' => __('The database to be used for queuing messages for the pub/sub functionality.'), + 'value' => 1, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_redis_namespace' => array( + 'level' => 2, + 'description' => __('The namespace to be used for queuing messages for the pub/sub functionality.'), + 'value' => 'mispq', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'afterHook' => 'zmqAfterHook', + ), + 'ZeroMQ_include_attachments' => array( + 'level' => 2, + 'description' => __('Enable this setting to include the base64 encoded payloads of malware-samples/attachments in the output.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_event_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any event creations/edits/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_object_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any object creations/edits/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_object_reference_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any object reference creations/deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_attribute_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any attribute creations/edits/soft deletions.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_tag_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of any tag creations/edits/deletions as well as tags being attached to / detached from various MISP elements.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_sighting_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new sightings to the ZMQ pubsub feed.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_user_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new/modified users to the ZMQ pubsub feed.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_organisation_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of new/modified organisations to the ZMQ pubsub feed.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ZeroMQ_audit_notifications_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables the publishing of log entries to the ZMQ pubsub feed. Keep in mind, this can get pretty verbose depending on your logging settings.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ElasticSearch_logging_enable' => array( + 'level' => 2, + 'description' => __('Enabled logging to an ElasticSearch instance'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'ElasticSearch_connection_string' => array( + 'level' => 2, + 'description' => __('The URL(s) at which to access ElasticSearch - comma separate if you want to have more than one.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'ElasticSearch_log_index' => array( + 'level' => 2, + 'description' => __('The index in which to place logs'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'S3_enable' => array( + 'level' => 2, + 'description' => __('Enables or disables uploading of malware samples to S3 rather than to disk (WARNING: Get permission from amazon first!)'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'S3_bucket_name' => array( + 'level' => 2, + 'description' => __('Bucket name to upload to'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'S3_region' => array( + 'level' => 2, + 'description' => __('Region in which your S3 bucket resides'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'S3_aws_access_key' => array( + 'level' => 2, + 'description' => __('AWS key to use when uploading samples (WARNING: It\' highly recommended that you use EC2 IAM roles if at all possible)'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'S3_aws_secret_key' => array( + 'level' => 2, + 'description' => __('AWS secret key to use when uploading samples'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Sightings_policy' => array( + 'level' => 1, + 'description' => __('This setting defines who will have access to seeing the reported sightings. The default setting is the event owner organisation alone (in addition to everyone seeing their own contribution) with the other options being Sighting reporters (meaning the event owner and any organisation that provided sighting data about the event) and Everyone (meaning anyone that has access to seeing the event / attribute).'), + 'value' => 0, + 'errorMessage' => '', + 'test' => 'testForSightingVisibility', + 'type' => 'numeric', + 'options' => array( + 0 => __('Event Owner Organisation'), + 1 => __('Sighting reporters'), + 2 => __('Everyone'), + 3 => __('Event Owner + host org sightings'), + ), + ), + 'Sightings_anonymise' => array( + 'level' => 1, + 'description' => __('Enabling the anonymisation of sightings will simply aggregate all sightings instead of showing the organisations that have reported a sighting. Users will be able to tell the number of sightings their organisation has submitted and the number of sightings for other organisations'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + ), + 'Sightings_anonymise_as' => array( + 'level' => 1, + 'description' => __('When pushing sightings to another server, report all sightings from this instance as this organisation. This effectively hides all sightings from this instance behind a single organisation to the outside world. Sightings pulled from this instance follow the Sightings_policy above.'), + 'value' => '0', + 'errorMessage' => '', + 'test' => 'testLocalOrg', + 'type' => 'numeric', + 'optionsSource' => 'LocalOrgs', + ), + 'Sightings_range' => array( + 'level' => 1, + 'description' => __('Set the range in which sightings will be taken into account when generating graphs. For example a sighting with a sighted_date of 7 years ago might not be relevant anymore. Setting given in number of days, default is 365 days'), + 'value' => 365, + 'errorMessage' => '', + 'test' => 'testForNumeric', + 'type' => 'numeric' + ), + 'Sightings_sighting_db_enable' => array( + 'level' => 1, + 'description' => __('Enable SightingDB integration.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'CustomAuth_enable' => array( + 'level' => 2, + 'description' => __('Enable this functionality if you would like to handle the authentication via an external tool and authenticate with MISP using a custom header.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true, + 'beforeHook' => 'customAuthBeforeHook' + ), + 'CustomAuth_header' => array( + 'level' => 2, + 'description' => __('Set the header that MISP should look for here. If left empty it will default to the Authorization header.'), + 'value' => 'Authorization', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_use_header_namespace' => array( + 'level' => 2, + 'description' => __('Use a header namespace for the auth header - default setting is enabled'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'CustomAuth_header_namespace' => array( + 'level' => 2, + 'description' => __('The default header namespace for the auth header - default setting is HTTP_'), + 'value' => 'HTTP_', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_required' => array( + 'level' => 2, + 'description' => __('If this setting is enabled then the only way to authenticate will be using the custom header. Alternatively, you can run in mixed mode that will log users in via the header if found, otherwise users will be redirected to the normal login page.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'CustomAuth_only_allow_source' => array( + 'level' => 2, + 'description' => __('If you are using an external tool to authenticate with MISP and would like to only allow the tool\'s url as a valid point of entry then set this field. '), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_name' => array( + 'level' => 2, + 'description' => __('The name of the authentication method, this is cosmetic only and will be shown on the user creation page and logs.'), + 'value' => 'External authentication', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_disable_logout' => array( + 'level' => 2, + 'description' => __('Disable the logout button for users authenticate with the external auth mechanism.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Enrichment_services_enable' => array( + 'level' => 0, + 'description' => __('Enable/disable the enrichment services'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Enrichment_timeout' => array( + 'level' => 1, + 'description' => __('Set a timeout for the enrichment services'), + 'value' => 10, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + 'Import_services_enable' => array( + 'level' => 0, + 'description' => __('Enable/disable the import services'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Import_timeout' => array( + 'level' => 1, + 'description' => __('Set a timeout for the import services'), + 'value' => 10, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + 'Import_services_url' => array( + 'level' => 1, + 'description' => __('The url used to access the import services. By default, it is accessible at http://127.0.0.1:6666'), + 'value' => 'http://127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Import_services_port' => array( + 'level' => 1, + 'description' => __('The port used to access the import services. By default, it is accessible at 127.0.0.1:6666'), + 'value' => '6666', + 'errorMessage' => '', + 'test' => 'testForPortNumber', + 'type' => 'numeric' + ), + 'Export_services_url' => array( + 'level' => 1, + 'description' => __('The url used to access the export services. By default, it is accessible at http://127.0.0.1:6666'), + 'value' => 'http://127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Export_services_port' => array( + 'level' => 1, + 'description' => __('The port used to access the export services. By default, it is accessible at 127.0.0.1:6666'), + 'value' => '6666', + 'errorMessage' => '', + 'test' => 'testForPortNumber', + 'type' => 'numeric' + ), + 'Export_services_enable' => array( + 'level' => 0, + 'description' => __('Enable/disable the export services'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Export_timeout' => array( + 'level' => 1, + 'description' => __('Set a timeout for the export services'), + 'value' => 10, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + 'Enrichment_hover_enable' => array( + 'level' => 0, + 'description' => __('Enable/disable the hover over information retrieved from the enrichment modules'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Enrichment_hover_popover_only' => array( + 'level' => 0, + 'description' => __('When enabled, users have to click on the magnifier icon to show the enrichment'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Enrichment_hover_timeout' => array( + 'level' => 1, + 'description' => __('Set a timeout for the hover services'), + 'value' => 5, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + 'Enrichment_services_url' => array( + 'level' => 1, + 'description' => __('The url used to access the enrichment services. By default, it is accessible at http://127.0.0.1:6666'), + 'value' => 'http://127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Enrichment_services_port' => array( + 'level' => 1, + 'description' => __('The port used to access the enrichment services. By default, it is accessible at 127.0.0.1:6666'), + 'value' => 6666, + 'errorMessage' => '', + 'test' => 'testForPortNumber', + 'type' => 'numeric' + ), + 'Cortex_services_url' => array( + 'level' => 1, + 'description' => __('The url used to access Cortex. By default, it is accessible at http://cortex-url'), + 'value' => 'http://127.0.0.1', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string' + ), + 'Cortex_services_port' => array( + 'level' => 1, + 'description' => __('The port used to access Cortex. By default, this is port 9000'), + 'value' => 9000, + 'errorMessage' => '', + 'test' => 'testForPortNumber', + 'type' => 'numeric' + ), + 'Cortex_services_enable' => array( + 'level' => 0, + 'description' => __('Enable/disable the Cortex services'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean' + ), + 'Cortex_authkey' => array( + 'level' => 1, + 'description' => __('Set an authentication key to be passed to Cortex'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'Cortex_timeout' => array( + 'level' => 1, + 'description' => __('Set a timeout for the Cortex services'), + 'value' => 120, + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'numeric' + ), + 'Cortex_ssl_verify_peer' => array( + 'level' => 1, + 'description' => __('Set to false to disable SSL verification. This is not recommended.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'Cortex_ssl_verify_host' => array( + 'level' => 1, + 'description' => __('Set to false if you wish to ignore hostname match errors when validating certificates.'), + 'value' => true, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'Cortex_ssl_allow_self_signed' => array( + 'level' => 1, + 'description' => __('Set to true to enable self-signed certificates to be accepted. This requires Cortex_ssl_verify_peer to be enabled.'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'Cortex_ssl_cafile' => array( + 'level' => 1, + 'description' => __('Set to the absolute path of the Certificate Authority file that you wish to use for verifying SSL certificates.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_custom_password_reset' => array( + 'level' => 2, + 'description' => __('Provide your custom authentication users with an external URL to the authentication system to reset their passwords.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ), + 'CustomAuth_custom_logout' => array( + 'level' => 2, + 'description' => __('Provide a custom logout URL for your users that will log them out using the authentication system you use.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true + ) + ), + 'debug' => array( + 'level' => 0, + 'description' => __('The debug level of the instance, always use 0 for production instances.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testDebug', + 'type' => 'numeric', + 'options' => array(0 => 'Debug off', 1 => 'Debug on', 2 => 'Debug + SQL dump'), + ), + 'site_admin_debug' => array( + 'level' => 0, + 'description' => __('The debug level of the instance for site admins. This feature allows site admins to run debug mode on a live instance without exposing it to other users. The most verbose option of debug and site_admin_debug is used for site admins.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testDebugAdmin', + 'type' => 'boolean', + 'null' => true + ), + ); + } } From 20aa97f486f9c945113e235d5acc57dd0f4d7859 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sat, 9 Jan 2021 22:50:05 +0100 Subject: [PATCH 249/385] fix: [internal] Remove unused method isOwnedByOrg --- app/Model/Attribute.php | 7 ------- app/Model/Server.php | 6 ------ 2 files changed, 13 deletions(-) diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php index b53fe132f..37a4d52f3 100644 --- a/app/Model/Attribute.php +++ b/app/Model/Attribute.php @@ -1435,13 +1435,6 @@ class Attribute extends AppModel return $compositeTypes; } - public function isOwnedByOrg($attributeId, $org) - { - $this->id = $attributeId; - $this->read(); - return $this->data['Event']['org_id'] === $org; - } - public function getRelatedAttributes($user, $attribute, $fields=array(), $includeEventData = false) { // LATER getRelatedAttributes($attribute) this might become a performance bottleneck diff --git a/app/Model/Server.php b/app/Model/Server.php index 8a36ee23d..3fa2b3a27 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -193,14 +193,8 @@ class Server extends AppModel 'Session' => 'Security' ); - public $validEventIndexFilters = array('searchall', 'searchpublished', 'searchorg', 'searchtag', 'searcheventid', 'searchdate', 'searcheventinfo', 'searchthreatlevel', 'searchdistribution', 'searchanalysis', 'searchattribute'); - public function isOwnedByOrg($serverid, $org) - { - return $this->field('id', array('id' => $serverid, 'org' => $org)) === $serverid; - } - public function beforeSave($options = array()) { $this->data['Server']['url'] = rtrim($this->data['Server']['url'], '/'); From c1119283dc35336dc38cfe8c44f71c34595442c8 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Sun, 10 Jan 2021 12:03:04 +0100 Subject: [PATCH 250/385] chg: [UI] Faster event paginator --- app/View/Attributes/index.ctp | 7 ------- app/View/Elements/eventattribute.ctp | 14 ++------------ app/View/Events/index.ctp | 7 ------- app/webroot/js/misp.js | 23 +++++++++++++++++++++++ 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/app/View/Attributes/index.ctp b/app/View/Attributes/index.ctp index a6172130d..90156df4c 100755 --- a/app/View/Attributes/index.ctp +++ b/app/View/Attributes/index.ctp @@ -29,13 +29,6 @@ -