From c103d10856bb2f7ae709bb773f595622905230b0 Mon Sep 17 00:00:00 2001 From: Jakub Onderka Date: Tue, 15 Oct 2019 17:41:29 +0200 Subject: [PATCH 01/23] fix: [UI] Proposal attachment downloading --- app/View/Elements/Events/View/value_field.ctp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/View/Elements/Events/View/value_field.ctp b/app/View/Elements/Events/View/value_field.ctp index 0b5900c0a..7726d195b 100644 --- a/app/View/Elements/Events/View/value_field.ctp +++ b/app/View/Elements/Events/View/value_field.ctp @@ -24,7 +24,8 @@ switch ($object['type']) { $filename = $filenameHash[0]; } - $url = array('controller' => 'attributes', 'action' => 'download', $object['id']); + $controller = isset($object['objectType']) && $object['objectType'] === 'proposal' ? 'shadow_attributes' : 'attributes'; + $url = array('controller' => $controller, 'action' => 'download', $object['id']); echo $this->Html->link($filename, $url, array('class' => $linkClass)); if (isset($filenameHash[1])) { echo '
' . $filenameHash[1]; From 103355c67a10f9f007eb930dc1b856bde7469587 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 22 Oct 2019 13:40:25 +0200 Subject: [PATCH 02/23] fix: Bumped latest stix2 python version + Updated expected version in the diagnostic --- app/Model/Server.php | 2 +- cti-python-stix2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Model/Server.php b/app/Model/Server.php index 4e1a99269..04e8368ff 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -4336,7 +4336,7 @@ class Server extends AppModel public function stixDiagnostics(&$diagnostic_errors, &$stixVersion, &$cyboxVersion, &$mixboxVersion, &$maecVersion, &$stix2Version, &$pymispVersion) { $result = array(); - $expected = array('stix' => '>1.2.0.6', 'cybox' => '>2.1.0.18.dev0', 'mixbox' => '1.0.3', 'maec' => '>4.1.0.14', 'stix2' => '1.2.0', 'pymisp' => '>2.4.93'); + $expected = array('stix' => '>1.2.0.6', 'cybox' => '>2.1.0.18.dev0', 'mixbox' => '1.0.3', 'maec' => '>4.1.0.14', 'stix2' => '>1.2.0', 'pymisp' => '>2.4.93'); // check if the STIX and Cybox libraries are working using the test script stixtest.py $scriptResult = shell_exec($this->getPythonVersion() . ' ' . APP . 'files' . DS . 'scripts' . DS . 'stixtest.py'); $scriptResult = json_decode($scriptResult, true); diff --git a/cti-python-stix2 b/cti-python-stix2 index 4f1d68065..e2a4129ad 160000 --- a/cti-python-stix2 +++ b/cti-python-stix2 @@ -1 +1 @@ -Subproject commit 4f1d68065a866e3c2984bb5a1384a05972dc127d +Subproject commit e2a4129ad343eb8d98bd8bda36411486fe3a8d63 From e9e22bea17eb91c74778ce646e6b9e2072a737e6 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Tue, 22 Oct 2019 14:54:52 +0200 Subject: [PATCH 03/23] fix: [stix2 import] Importing directory patterns from external stix --- app/files/scripts/stix2/stix2misp.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/files/scripts/stix2/stix2misp.py b/app/files/scripts/stix2/stix2misp.py index 6b1e9f19b..c5103126c 100644 --- a/app/files/scripts/stix2/stix2misp.py +++ b/app/files/scripts/stix2/stix2misp.py @@ -1133,7 +1133,8 @@ class ExternalStixParser(StixParser): ('url',): self.parse_url_observable, ('user-account',): self.parse_user_account_observable, ('windows-registry-key',): self.parse_regkey_observable} - self.pattern_mapping = {('directory', 'file'): self.parse_file_pattern, + self.pattern_mapping = {('directory',): self.parse_file_pattern, + ('directory', 'file'): self.parse_file_pattern, ('domain-name',): self.parse_domain_ip_port_pattern, ('domain-name', 'ipv4-addr', 'url'): self.parse_domain_ip_port_pattern, ('domain-name', 'ipv6-addr', 'url'): self.parse_domain_ip_port_pattern, @@ -1365,7 +1366,10 @@ class ExternalStixParser(StixParser): def parse_file_pattern(self, pattern, marking=None, uuid=None): pattern_types, pattern_values = self.get_types_and_values_from_pattern(pattern) attributes = self.attributes_from_file_pattern(pattern_types, pattern_values) - self.handle_import_case(attributes, 'file', marking, uuid) + if any((attribute['object_relation'] == 'path' for attribute in attributes)): + self.object_case_import(attributes, 'file', uuid) + else: + self.handle_import_case(attributes, 'file', marking, uuid) def parse_file_object_observable(self, objects, marking, uuid): file, data = self.split_file_observable(objects) From 0ef0cf49cbca9e5f25cd198759f9a419e2e595c6 Mon Sep 17 00:00:00 2001 From: Jan Skalny Date: Tue, 22 Oct 2019 20:13:11 +0200 Subject: [PATCH 04/23] new: [cli] server connectivity test --- app/Console/Command/ServerShell.php | 26 ++++++++++++++++++++++++++ app/Model/Server.php | 4 +++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/Console/Command/ServerShell.php b/app/Console/Command/ServerShell.php index 215404032..52da69563 100644 --- a/app/Console/Command/ServerShell.php +++ b/app/Console/Command/ServerShell.php @@ -6,6 +6,32 @@ class ServerShell extends AppShell { public $uses = array('Server', 'Task', 'Job', 'User', 'Feed'); + public function list() { + $res = ['servers'=>[]]; + + $servers = $this->Server->find('all', [ + 'fields' => ['Server.id', 'Server.name', 'Server.url'], + 'recursive' => 0 + ]); + foreach ($servers as $server) + $res['servers'][] = $server['Server']; + + echo json_encode($res) . PHP_EOL; + } + + public function test() { + if (empty($this->args[0])) { + die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Test'] . PHP_EOL); + } + + $serverId = intval($this->args[0]); + $res = @$this->Server->runConnectionTest($serverId); + if (!empty($res['message'])) + $res['message'] = json_decode($res['message']); + + echo json_encode($res) . PHP_EOL; + } + public function pull() { if (empty($this->args[0]) || empty($this->args[1])) { die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['pull'] . PHP_EOL); diff --git a/app/Model/Server.php b/app/Model/Server.php index 4e1a99269..ff5fe72ed 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -134,7 +134,9 @@ class Server extends AppModel 'Push' => 'MISP/app/Console/cake Server push [user_id] [server_id]', 'Cache feeds for quick lookups' => 'MISP/app/Console/cake Server cacheFeed [user_id] [feed_id|all|csv|text|misp]', 'Fetch feeds as local data' => 'MISP/app/Console/cake Server fetchFeed [user_id] [feed_id|all|csv|text|misp]', - 'Run enrichment' => 'MISP/app/Console/cake Event enrichEvent [user_id] [event_id] [json_encoded_module_list]' + 'Run enrichment' => 'MISP/app/Console/cake Event enrichEvent [user_id] [event_id] [json_encoded_module_list]', + 'Test' => 'MISP/app/Console/cake Server test [server_id]', + 'List' => 'MISP/app/Console/cake Server list' ), 'description' => __('If you would like to automate tasks such as caching feeds or pulling from server instances, you can do it using the following command line tools. Simply execute the given commands via the command line / create cron jobs easily out of them.'), 'header' => __('Automating certain console tasks') From abe0e440d4934b5401339129c09dcb2cb0aac151 Mon Sep 17 00:00:00 2001 From: RuneBergh Date: Wed, 23 Oct 2019 10:56:52 +0200 Subject: [PATCH 05/23] Adding commenting for key to use with ldap Commenting in the PHP_AUTH_USER key which is set by basic auth if using ldap or AD authentication. --- app/Config/config.default.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Config/config.default.php b/app/Config/config.default.php index 9f190e3f5..d41ceb911 100644 --- a/app/Config/config.default.php +++ b/app/Config/config.default.php @@ -141,7 +141,7 @@ $config = array( /* 'ApacheSecureAuth' => // Configuration for kerberos authentication array( - 'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER + 'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER, If BasicAuth ldap = PHP_AUTH_USER 'ldapServer' => 'ldap://example.com', // FQDN or IP 'ldapProtocol' => 3, 'ldapNetworkTimeout' => -1, // use -1 for unlimited network timeout From 4b023f7235f0ba5e7d2ac27d2276424650448de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Vinot?= Date: Wed, 23 Oct 2019 11:22:22 +0200 Subject: [PATCH 06/23] fix: MIssing quotes in test cases setup Fix https://github.com/MISP/PyMISP/issues/484 --- docs/generic/supportFunctions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/generic/supportFunctions.md b/docs/generic/supportFunctions.md index 94d3e38b7..561147a9c 100644 --- a/docs/generic/supportFunctions.md +++ b/docs/generic/supportFunctions.md @@ -830,8 +830,8 @@ genRCLOCAL () { # Run PyMISP tests runTests () { - echo "url = ${MISP_BASEURL} -key = ${AUTH_KEY}" |sudo tee ${PATH_TO_MISP}/PyMISP/tests/keys.py + echo "url = '${MISP_BASEURL}' +key = '${AUTH_KEY}'" |sudo tee ${PATH_TO_MISP}/PyMISP/tests/keys.py sudo chown -R $WWW_USER:$WWW_USER $PATH_TO_MISP/PyMISP/ sudo -H -u $WWW_USER sh -c "cd $PATH_TO_MISP/PyMISP && git submodule foreach git pull origin master" From 004141f504d5b4e09a24b6e907a1d56b106e8a72 Mon Sep 17 00:00:00 2001 From: Steve Clement Date: Wed, 23 Oct 2019 19:45:20 +0900 Subject: [PATCH 07/23] chg: [doc] Various updates for RHEL8/CentOS8/Debian10.1 --- docs/INSTALL.rhel7.md | 2 -- docs/INSTALL.rhel8.md | 38 ++++++++++++++++++++++---------------- docs/xINSTALL.debian10.md | 4 ++-- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/docs/INSTALL.rhel7.md b/docs/INSTALL.rhel7.md index ebf78094c..463917a5f 100644 --- a/docs/INSTALL.rhel7.md +++ b/docs/INSTALL.rhel7.md @@ -198,7 +198,6 @@ sudo systemctl enable --now haveged.service # installCoreRHEL () { # Download MISP using git in the $PATH_TO_MISP directory. - PATH_TO_MISP="/var/www/MISP" sudo mkdir -p $(dirname $PATH_TO_MISP) sudo chown $WWW_USER:$WWW_USER $(dirname $PATH_TO_MISP) cd $(dirname $PATH_TO_MISP) @@ -257,7 +256,6 @@ installCoreRHEL () { # lief needs manual compilation sudo yum install devtoolset-7 cmake3 cppcheck -y - # FIXME: This does not work! cd $PATH_TO_MISP/app/files/scripts/lief $SUDO_WWW mkdir build cd build diff --git a/docs/INSTALL.rhel8.md b/docs/INSTALL.rhel8.md index f5193c303..885380f1f 100644 --- a/docs/INSTALL.rhel8.md +++ b/docs/INSTALL.rhel8.md @@ -29,7 +29,9 @@ Make sure you are reading the parsed version of this Document. When in doubt [cl The core MISP team cannot verify if this guide is working or not. Please help us in keeping it up to date and accurate. Thus we also have difficulties in supporting RHEL issues but will do a best effort on a similar yet slightly different setup. -This document details the steps to install MISP on Red Hat Enterprise Linux 8.x (RHEL 8.x). +!!! notice + Maintenance for CentOS 8 will end on: May 31st, 2029 [Source[0]](https://wiki.centos.org/About/Product) [Source[1]](https://linuxlifecycle.com/) + CentOS 8 [NetInstallURL](http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=BaseOS) This document details the steps to install MISP on Red Hat Enterprise Linux 8.x (RHEL 8.x) and CentOS 8.x. At time of this writing it was tested on versions 8.0 for RHEL. @@ -104,12 +106,9 @@ sudo yum update -y ## 1.6/ **[RHEL]** Install the EPEL repo -!!! note - There is no epel-releas-latest-8 yet, but the RHEL 7 seems to work for testing. - ```bash # -sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y +sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm -y # ``` @@ -142,7 +141,7 @@ yumInstallCoreDeps () { sudo systemctl enable --now redis.service PHP_INI=/etc/php.ini - sudo yum install php php-fpm php-devel \ + sudo yum install php php-fpm php-devel php-pear \ php-mysqlnd \ php-mbstring \ php-xml \ @@ -238,7 +237,7 @@ installCoreRHEL () { # lief needs manual compilation sudo yum groupinstall "Development Tools" -y - sudo yum install cmake3 cppcheck -y + sudo yum install cmake3 -y cd $PATH_TO_MISP/app/files/scripts/lief $SUDO_WWW mkdir build @@ -252,14 +251,18 @@ installCoreRHEL () { .. $SUDO_WWW make -j3 pyLIEF - # In case you get "internal compiler error: Killed (program cc1plus)" - # You ran out of memory. - # Create some swap - # sudo dd if=/dev/zero of=/var/swap.img bs=1024k count=4000 - # sudo mkswap /var/swap.img - # sudo swapon /var/swap.img - # And compile again - # $SUDO_WWW make -j3 pyLIEF + if [ $? == 2 ]; then + # In case you get "internal compiler error: Killed (program cc1plus)" + # You ran out of memory. + # Create some swap + sudo dd if=/dev/zero of=/var/swap.img bs=1024k count=4000 + sudo mkswap /var/swap.img + sudo swapon /var/swap.img + # And compile again + $SUDO_WWW make -j3 pyLIEF + sudo swapoff /var/swap.img + sudo rm /var/swap.img + fi # The following adds a PYTHONPATH to where the pyLIEF module has been compiled echo /var/www/MISP/app/files/scripts/lief/build/api/python |$SUDO_WWW tee /var/www/MISP/venv/lib/python3.6/site-packages/lief.pth @@ -420,7 +423,7 @@ EOF sudo systemctl restart mariadb mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "CREATE DATABASE $DBNAME;" - mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT USAGE on *.* to $DBUSER@localhost IDENTIFIED by '$DBPASSWORD_MISP';" + mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT USAGE on *.* to $DBUSER_MISP@localhost IDENTIFIED by '$DBPASSWORD_MISP';" mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e "GRANT ALL PRIVILEGES on $DBNAME.* to '$DBUSER_MISP'@'localhost';" mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e 'FLUSH PRIVILEGES;' @@ -676,6 +679,9 @@ sudo systemctl enable --now misp-workers.service ``` ## 9.07/ misp-modules (Broken on RHEL8) + +Here are CentOS 8 packages of openjpeg2-devel: https://centos.pkgs.org/8/centos-powertools-x86_64/openjpeg2-devel-2.3.0-8.el8.x86_64.rpm.html + ```bash # some misp-modules dependencies sudo yum install openjpeg2-devel -y diff --git a/docs/xINSTALL.debian10.md b/docs/xINSTALL.debian10.md index e9bd8932c..6fa719b7f 100644 --- a/docs/xINSTALL.debian10.md +++ b/docs/xINSTALL.debian10.md @@ -1,12 +1,12 @@ # INSTALLATION INSTRUCTIONS -## for Debian 10 "buster" +## for Debian 10.1 "buster" ### 0/ MISP debian stable install - Status ------------------------------------ !!! notice This is mostly the install [@SteveClement](https://twitter.com/SteveClement) uses for testing, qc and random development. - Maintained and tested by @SteveClement on 20190707 + Maintained and tested by @SteveClement on 20191016 !!! warning PHP 7.3.4-2 is not working at the moment with the packaged composer.phar
From d448993e3e9dbd89fd9704433be41db271145d8d Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Fri, 25 Oct 2019 08:52:25 +0200 Subject: [PATCH 08/23] chg: [misp-galaxy] updated to the latest version --- app/files/misp-galaxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/misp-galaxy b/app/files/misp-galaxy index 078a9f576..158182787 160000 --- a/app/files/misp-galaxy +++ b/app/files/misp-galaxy @@ -1 +1 @@ -Subproject commit 078a9f5763ec65aab6acbe6b51eafc153947ef00 +Subproject commit 1581827875597abfc29cbfc237429e8ffa7ef5b5 From abfeb3af1064ef8f26c482e8f4bec646f098e13b Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Fri, 25 Oct 2019 11:47:22 +0200 Subject: [PATCH 09/23] chg: [misp-galaxy] updated to the latest version of ATT&CK October 2019 --- app/files/misp-galaxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/misp-galaxy b/app/files/misp-galaxy index 158182787..4ab9bbbfa 160000 --- a/app/files/misp-galaxy +++ b/app/files/misp-galaxy @@ -1 +1 @@ -Subproject commit 1581827875597abfc29cbfc237429e8ffa7ef5b5 +Subproject commit 4ab9bbbfa3da8c940ae68ae930797430ccff28d8 From c6c899d0e5b0644b57c6a93b308296c691964464 Mon Sep 17 00:00:00 2001 From: Richard van den Berg Date: Tue, 29 Oct 2019 08:20:41 +0100 Subject: [PATCH 10/23] Allow python scripts to write to exec-errors.log --- INSTALL/misplogrotate.te | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/INSTALL/misplogrotate.te b/INSTALL/misplogrotate.te index 4e3f35a7b..921989772 100644 --- a/INSTALL/misplogrotate.te +++ b/INSTALL/misplogrotate.te @@ -1,8 +1,9 @@ -module misplogrotate 1.1; +module misplogrotate 1.2; require { type httpd_t; type logrotate_t; type httpd_log_t; + type httpd_sys_script_t; type httpd_sys_content_t; type httpd_sys_rw_content_t; class dir { ioctl read getattr lock search open remove_name }; @@ -12,4 +13,4 @@ require { allow logrotate_t httpd_sys_content_t:dir { ioctl read getattr lock search open }; allow logrotate_t httpd_sys_rw_content_t:dir { ioctl read getattr lock search open }; allow httpd_t httpd_log_t:dir remove_name; -allow httpd_t httpd_log_t:file { unlink write }; +allow { httpd_t httpd_sys_script_t } httpd_log_t:file { unlink write }; From b38c09ae2a702c7521c83fe767c7489d520d744a Mon Sep 17 00:00:00 2001 From: Andras Iklody Date: Wed, 30 Oct 2019 16:15:33 +0100 Subject: [PATCH 11/23] Update eventattributetoolbar.ctp --- app/View/Elements/eventattributetoolbar.ctp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/View/Elements/eventattributetoolbar.ctp b/app/View/Elements/eventattributetoolbar.ctp index 57adc5438..279be0f29 100644 --- a/app/View/Elements/eventattributetoolbar.ctp +++ b/app/View/Elements/eventattributetoolbar.ctp @@ -100,7 +100,7 @@ 'id' => 'multi-accept-button', 'title' => __('Accept selected Proposals'), 'class' => 'mass-proposal-select hidden', - 'fa-icon' => 'check-circl', + 'fa-icon' => 'check-circle', 'onClick' => 'multiSelectAction', 'onClickParams' => array($event['Event']['id'], 'acceptProposals') ), From 75f993e524e5b22c8f4837cfb92675df3db23448 Mon Sep 17 00:00:00 2001 From: Koen Van Impe Date: Thu, 31 Oct 2019 08:48:00 +0100 Subject: [PATCH 12/23] Force botvrij.eu feed to use HTTPS --- INSTALL/MYSQL.sql | 2 +- INSTALL/POSTGRESQL-data-initial.sql | 2 +- app/Model/AppModel.php | 2 +- app/files/feed-metadata/defaults.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql index 0c26a8045..86f42f950 100644 --- a/INSTALL/MYSQL.sql +++ b/INSTALL/MYSQL.sql @@ -1358,7 +1358,7 @@ INSERT INTO `admin_settings` (`id`, `setting`, `value`) VALUES INSERT INTO `feeds` (`id`, `provider`, `name`, `url`, `distribution`, `default`, `enabled`) VALUES (1, 'CIRCL', 'CIRCL OSINT Feed', 'https://www.circl.lu/doc/misp/feed-osint', 3, 1, 0), -(2, 'Botvrij.eu', 'The Botvrij.eu Data', 'http://www.botvrij.eu/data/feed-osint', 3, 1, 0); +(2, 'Botvrij.eu', 'The Botvrij.eu Data', 'https://www.botvrij.eu/data/feed-osint', 3, 1, 0); INSERT INTO `regexp` (`id`, `regexp`, `replacement`, `type`) VALUES (1, '/.:.ProgramData./i', '%ALLUSERSPROFILE%\\\\', 'ALL'), diff --git a/INSTALL/POSTGRESQL-data-initial.sql b/INSTALL/POSTGRESQL-data-initial.sql index ebb35f845..ca95d6d33 100644 --- a/INSTALL/POSTGRESQL-data-initial.sql +++ b/INSTALL/POSTGRESQL-data-initial.sql @@ -118,7 +118,7 @@ COPY public.favourite_tags (id, tag_id, user_id) FROM stdin; COPY public.feeds (id, name, provider, url, rules, enabled, distribution, sharing_group_id, tag_id, "default", source_format, fixed_event, delta_merge, event_id, publish, override_ids, settings, input_source, delete_local_file, lookup_visible, headers, caching_enabled) FROM stdin; 1 CIRCL OSINT Feed CIRCL https://www.circl.lu/doc/misp/feed-osint \N f 3 0 0 t misp f f 0 f f \N network f f \N f -2 The Botvrij.eu Data Botvrij.eu http://www.botvrij.eu/data/feed-osint \N f 3 0 0 t misp f f 0 f f \N network f f \N f +2 The Botvrij.eu Data Botvrij.eu https://www.botvrij.eu/data/feed-osint \N f 3 0 0 t misp f f 0 f f \N network f f \N f \. diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index a0fd26313..9a268dab3 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -122,7 +122,7 @@ class AppModel extends Model break; case '2.4.27': $newFeeds = array( - array('provider' => 'Botvrij.eu', 'name' => 'The Botvrij.eu Data','url' => 'http://www.botvrij.eu/data/feed-osint', 'enabled' => 0) + array('provider' => 'Botvrij.eu', 'name' => 'The Botvrij.eu Data','url' => 'https://www.botvrij.eu/data/feed-osint', 'enabled' => 0) ); $this->__addNewFeeds($newFeeds); break; diff --git a/app/files/feed-metadata/defaults.json b/app/files/feed-metadata/defaults.json index f8256808c..c9497e4b2 100644 --- a/app/files/feed-metadata/defaults.json +++ b/app/files/feed-metadata/defaults.json @@ -29,7 +29,7 @@ "id": "2", "name": "The Botvrij.eu Data", "provider": "Botvrij.eu", - "url": "http://www.botvrij.eu/data/feed-osint", + "url": "https://www.botvrij.eu/data/feed-osint", "rules": "{\"tags\":{\"OR\":[],\"NOT\":[]},\"orgs\":{\"OR\":[],\"NOT\":[]}}", "enabled": true, "distribution": "3", From be86cbed5df0b250d302ed455e9516e2f37aa7dc Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Sun, 3 Nov 2019 08:53:31 +0100 Subject: [PATCH 13/23] chg: [misp-galaxy] updated to the latest version --- app/files/misp-galaxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/files/misp-galaxy b/app/files/misp-galaxy index 4ab9bbbfa..64a356980 160000 --- a/app/files/misp-galaxy +++ b/app/files/misp-galaxy @@ -1 +1 @@ -Subproject commit 4ab9bbbfa3da8c940ae68ae930797430ccff28d8 +Subproject commit 64a35698033c471f0aebb375fe4d08addede8227 From 213e8b50498528af5eb0095826daacd949e21bd1 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 4 Nov 2019 11:54:22 +0100 Subject: [PATCH 14/23] new: [tags] Preminilary support of exclusive tags based on taxonomy data --- app/Controller/EventsController.php | 29 +++++ app/Controller/TagsController.php | 3 + app/Controller/TaxonomiesController.php | 19 +++ app/Model/Event.php | 7 ++ app/Model/Taxonomy.php | 117 ++++++++++++++---- .../Elements/Events/View/row_attribute.ctp | 2 +- app/View/Elements/ajaxTags.ctp | 12 ++ app/View/Events/ajax/ajaxTags.ctp | 3 +- app/View/Events/view.ctp | 3 +- app/webroot/css/main.css | 9 ++ 10 files changed, 180 insertions(+), 24 deletions(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 9c30a28f8..6bb5cbed2 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -1150,6 +1150,7 @@ class EventsController extends AppController // remove galaxies tags $this->loadModel('GalaxyCluster'); + $this->loadModel('Taxonomy'); $cluster_names = $this->GalaxyCluster->find('list', array('fields' => array('GalaxyCluster.tag_name'), 'group' => array('GalaxyCluster.tag_name', 'GalaxyCluster.id'))); foreach ($event['Object'] as $k => $object) { if (isset($object['Attribute'])) { @@ -1159,6 +1160,8 @@ class EventsController extends AppController unset($event['Object'][$k]['Attribute'][$k2]['AttributeTag'][$k3]); } } + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + $event['Object'][$k]['Attribute'][$k2]['tagConflicts'] = $tagConflicts; } } } @@ -1168,6 +1171,8 @@ class EventsController extends AppController unset($event['Attribute'][$k]['AttributeTag'][$k2]); } } + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + $event['Attribute'][$k]['tagConflicts'] = $tagConflicts; } if (empty($this->passedArgs['sort'])) { $filters['sort'] = 'timestamp'; @@ -1258,6 +1263,7 @@ class EventsController extends AppController private function __viewUI($event, $continue, $fromEvent) { + $this->loadModel('Taxonomy'); $filterData = array( 'request' => $this->request, 'paramArray' => $this->acceptedFilteringNamedParams, @@ -1364,6 +1370,10 @@ class EventsController extends AppController unset($event['EventTag'][$k]); } } + + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($event['EventTag'], '{n}.Tag.name')); + $this->set('tagConflicts', $tagConflicts); + $startDate = null; $modificationMap = array(); foreach ($event['Attribute'] as $k => $attribute) { @@ -1380,6 +1390,8 @@ class EventsController extends AppController unset($event['Attribute'][$k]['AttributeTag'][$k2]); } } + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + $event['Attribute'][$k]['tagConflicts'] = $tagConflicts; } $attributeTagsName = $this->Event->Attribute->AttributeTag->extractAttributeTagsNameFromEvent($event, 'both'); $this->set('attributeTags', array_values($attributeTagsName['tags'])); @@ -1405,6 +1417,8 @@ class EventsController extends AppController unset($event['Object'][$k]['Attribute'][$k2]['AttributeTag'][$k3]); } } + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + $event['Object'][$k]['Attribute'][$k2]['tagConflicts'] = $tagConflicts; } } } @@ -3715,6 +3729,7 @@ class EventsController extends AppController public function addTag($id = false, $tag_id = false) { + $this->loadModel('Taxonomy'); $rearrangeRules = array( 'request' => false, 'Event' => false, @@ -3829,6 +3844,20 @@ class EventsController extends AppController $error = __('Tag is already attached to this event.'); continue; } + $tagsOnEvent = $this->Event->EventTag->find('all', array( + 'conditions' => array( + 'Tag.id' => $tag_id, + 'EventTag.event_id' => $id + ), + 'contain' => 'Tag', + 'fields' => array('Tag.name'), + 'recursive' => -1 + )); + $exclusive_test_passed = $this->Taxonomy->checkIfNewTagIsAllowedByTaxonomy($tag['Tag']['name'], $tagsOnEvent); + if (!empty($exclusive_test_passed)) { + $fail = __('Tag is not allowed due to taxonomy exclusivity settings'); + continue; + } $this->Event->EventTag->create(); if ($this->Event->EventTag->save(array('event_id' => $id, 'tag_id' => $tag_id, 'local' => $local))) { if (!$local) { diff --git a/app/Controller/TagsController.php b/app/Controller/TagsController.php index e75f36d0c..3d0675e63 100644 --- a/app/Controller/TagsController.php +++ b/app/Controller/TagsController.php @@ -459,6 +459,7 @@ class TagsController extends AppController public function showEventTag($id) { $this->loadModel('EventTag'); + $this->loadModel('Taxonomy'); if (!$this->EventTag->Event->checkIfAuthorised($this->Auth->user(), $id)) { throw new MethodNotAllowedException('Invalid event.'); } @@ -487,6 +488,8 @@ class TagsController extends AppController 'conditions' => array('Event.id' => $id) )); $this->set('required_taxonomies', $this->EventTag->Event->getRequiredTaxonomies()); + $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($tags, '{n}.Tag.name')); + $this->set('tagConflicts', $tagConflicts); $this->set('event', $event); $this->layout = 'ajax'; $this->render('/Events/ajax/ajaxTags'); diff --git a/app/Controller/TaxonomiesController.php b/app/Controller/TaxonomiesController.php index 21ae042ab..fdf18517b 100644 --- a/app/Controller/TaxonomiesController.php +++ b/app/Controller/TaxonomiesController.php @@ -239,6 +239,25 @@ class TaxonomiesController extends AppController } } + public function testExclusive() + { + $res = $this->Taxonomy->checkIfNewTagIsAllowedByTaxonomy('tlp:green', array('tlp:white1')); + $res2 = $this->Taxonomy->checkIfNewTagIsAllowedByTaxonomy('tlp:green', array('tlp:white')); + $res3 = $this->Taxonomy->checkIfNewTagIsAllowedByTaxonomy('estimative-language:confidence-in-analytic-judgment="high"', array('estimative-language:confidence-in-analytic-judgment="low"')); + return $this->RestResponse->viewData([$res, $res2, $res3], 'html'); + } + public function testInconsistencies() + { + $res = $this->Taxonomy->checkIfTagInconsistencies(array('tlp:green', 'tlp:white1')); + $res2 = $this->Taxonomy->checkIfTagInconsistencies(array('estimative-language:confidence-in-analytic-judgment="low"', 'estimative-language:confidence-in-analytic-judgment="high"')); + $res3 = $this->Taxonomy->checkIfTagInconsistencies(array( + 'fr-classif:classifiees-defense="CONFIDENTIEL_DEFENSE"', + 'fr-classif:classifiees-defense="TRES_SECRET_DEFENSE"', + 'fr-classif:non-classifiees-defense="CONFIDENTIEL"' + )); + return $this->RestResponse->viewData([$res, $res2, $res3], 'html'); + } + public function addTag($taxonomy_id = false) { if ((!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) || !$this->request->is('post')) { diff --git a/app/Model/Event.php b/app/Model/Event.php index fec7a2b2a..72d0f2b0c 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -6944,4 +6944,11 @@ class Event extends AppModel } return true; } + + public function checkIfTagAllowedByTaxonomy($tagName) + { + $this->Taxonomy = ClassRegistry::init('Taxonomy'); + // $taxonomy + $taxonomy = $this->Taxonomy->getTaxonomyForTag($tag['Tag']['name']); + } } diff --git a/app/Model/Taxonomy.php b/app/Model/Taxonomy.php index 5bc21a528..f640e040e 100644 --- a/app/Model/Taxonomy.php +++ b/app/Model/Taxonomy.php @@ -101,7 +101,7 @@ class Taxonomy extends AppModel } $this->deleteAll(array('Taxonomy.namespace' => $current['Taxonomy']['namespace'])); } - $taxonomy['Taxonomy'] = array('namespace' => $vocab['namespace'], 'description' => $vocab['description'], 'version' => $vocab['version'], 'enabled' => $enabled); + $taxonomy['Taxonomy'] = array('namespace' => $vocab['namespace'], 'description' => $vocab['description'], 'version' => $vocab['version'], 'exclusive' => $vocab['exclusive'], 'enabled' => $enabled); $predicateLookup = array(); foreach ($vocab['predicates'] as $k => $predicate) { $taxonomy['Taxonomy']['TaxonomyPredicate'][$k] = $predicate; @@ -489,27 +489,29 @@ class Taxonomy extends AppModel return $taxonomies; } - public function getTaxonomyForTag($tagName, $metaOnly = false) + public function getTaxonomyForTag($tagName, $metaOnly = false, $fullTaxonomy = False) { if (preg_match('/^[^:="]+:[^:="]+="[^:="]+"$/i', $tagName)) { $temp = explode(':', $tagName); $pieces = array_merge(array($temp[0]), explode('=', $temp[1])); $pieces[2] = trim($pieces[2], '"'); + $contain = array( + 'TaxonomyPredicate' => array( + 'TaxonomyEntry' => array() + ) + ); + if (!$fullTaxonomy) { + $contain['TaxonomyPredicate']['conditions'] = array( + 'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1]) + ); + $contain['TaxonomyPredicate']['TaxonomyEntry']['conditions'] = array( + 'LOWER(TaxonomyEntry.value)' => strtolower($pieces[2]) + ); + } $taxonomy = $this->find('first', array( 'recursive' => -1, 'conditions' => array('LOWER(Taxonomy.namespace)' => strtolower($pieces[0])), - 'contain' => array( - 'TaxonomyPredicate' => array( - 'conditions' => array( - 'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1]) - ), - 'TaxonomyEntry' => array( - 'conditions' => array( - 'LOWER(TaxonomyEntry.value)' => strtolower($pieces[2]) - ) - ) - ) - ) + 'contain' => $contain )); if ($metaOnly && !empty($taxonomy)) { return array('Taxonomy' => $taxonomy['Taxonomy']); @@ -517,16 +519,16 @@ class Taxonomy extends AppModel return $taxonomy; } elseif (preg_match('/^[^:="]+:[^:="]+$/i', $tagName)) { $pieces = explode(':', $tagName); + $contain = array('TaxonomyPredicate' => array()); + if (!$fullTaxonomy) { + $contain['TaxonomyPredicate']['conditions'] = array( + 'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1]) + ); + } $taxonomy = $this->find('first', array( 'recursive' => -1, 'conditions' => array('LOWER(Taxonomy.namespace)' => strtolower($pieces[0])), - 'contain' => array( - 'TaxonomyPredicate' => array( - 'conditions' => array( - 'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1]) - ) - ) - ) + 'contain' => $contain )); if ($metaOnly && !empty($taxonomy)) { return array('Taxonomy' => $taxonomy['Taxonomy']); @@ -536,4 +538,77 @@ class Taxonomy extends AppModel return false; } } + + // Remove the value for triple component tags or the predicate for double components tags + public function stripLastTagComponent($tagName) + { + $shortenedTag = ''; + if (preg_match('/^[^:="]+:[^:="]+="[^:="]+"$/i', $tagName)) { + $shortenedTag = explode('=', $tagName)[0]; + } elseif (preg_match('/^[^:="]+:[^:="]+$/i', $tagName)) { + $shortenedTag = explode(':', $tagName)[0]; + } + return $shortenedTag; + } + + public function checkIfNewTagIsAllowedByTaxonomy($newTagName, $tagNameList=array()) + { + $newTagShortened = $this->stripLastTagComponent($newTagName); + $prefixIsFree = true; + foreach ($tagNameList as $tagName) { + $tagShortened = $this->stripLastTagComponent($tagName); + if ($newTagShortened == $tagShortened) { + $prefixIsFree = false; + } + } + if (!$prefixIsFree) { + // at this point, we have a duplicated namespace(-predicate) + $taxonomy = $this->getTaxonomyForTag($newTagName); + if (isset($taxonomy['Taxonomy']['exclusive']) && $taxonomy['Taxonomy']['exclusive']) { + return false; // only one tag of this taxonomy is allowed + } elseif (isset($taxonomy['TaxonomyPredicate'][0]['exclusive']) && $taxonomy['TaxonomyPredicate'][0]['exclusive']) { + return false; // only one tag belonging to this predicate is allowed + } + } + return true; + } + + public function checkIfTagInconsistencies($tagNameList) + { + $potentiallyConflictingTaxonomy = array(); + $conflictingTaxonomy = array(); + foreach ($tagNameList as $tagName) { + $tagShortened = $this->stripLastTagComponent($tagName); + if (isset($potentiallyConflictingTaxonomy[$tagShortened])) { + $potentiallyConflictingTaxonomy[$tagShortened]['taxonomy'] = $this->getTaxonomyForTag($tagName); + $potentiallyConflictingTaxonomy[$tagShortened]['count']++; + } else { + $potentiallyConflictingTaxonomy[$tagShortened] = array( + 'count' => 1 + ); + } + $potentiallyConflictingTaxonomy[$tagShortened]['tagNames'][] = $tagName; + } + foreach ($potentiallyConflictingTaxonomy as $potTaxonomy) { + if ($potTaxonomy['count'] > 1) { + $taxonomy = $potTaxonomy['taxonomy']; + if (isset($taxonomy['Taxonomy']['exclusive']) && $taxonomy['Taxonomy']['exclusive']) { + $conflictingTaxonomy[] = array( + 'tags' => $potTaxonomy['tagNames'], + 'conflict' => sprintf(__('Taxonomy `%s` is an exclusive Taxonomy'), $taxonomy['Taxonomy']['namespace']) + ); + } elseif (isset($taxonomy['TaxonomyPredicate'][0]['exclusive']) && $taxonomy['TaxonomyPredicate'][0]['exclusive']) { + $conflictingTaxonomy[] = array( + 'tags' => $potTaxonomy['tagNames'], + 'conflict' => sprintf( + __('Predicate `%s` is exclusive from Taxonomy `%s` is exclusive'), + $taxonomy['TaxonomyPredicate'][0]['value'], + $taxonomy['Taxonomy']['namespace'] + ) + ); + } + } + } + return $conflictingTaxonomy; + } } diff --git a/app/View/Elements/Events/View/row_attribute.ctp b/app/View/Elements/Events/View/row_attribute.ctp index a9ef9f43c..26f64b478 100644 --- a/app/View/Elements/Events/View/row_attribute.ctp +++ b/app/View/Elements/Events/View/row_attribute.ctp @@ -156,7 +156,7 @@
- element('ajaxTags', array('attributeId' => $object['id'], 'tags' => $object['AttributeTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']), 'context' => $context, 'scope' => 'attribute')); ?> + element('ajaxTags', array('attributeId' => $object['id'], 'tags' => $object['AttributeTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['org_id']), 'context' => $context, 'scope' => 'attribute', 'tagConflicts' => $object['tagConflicts'])); ?>
%s', $tagData ); + $tagConflictData = ''; + if (!empty($tagConflicts)) { + foreach ($tagConflicts as $tagConflict) { + $tagConflictData .= sprintf( + '
%s | %s
', + h(implode(', ', $tagConflict['tags'])), + h($tagConflict['conflict']) + ); + } + } + $tagConflictData .= '
'; + echo $tagConflictData; ?> diff --git a/app/View/Events/ajax/ajaxTags.ctp b/app/View/Events/ajax/ajaxTags.ctp index 6001997d0..e56be1370 100644 --- a/app/View/Events/ajax/ajaxTags.ctp +++ b/app/View/Events/ajax/ajaxTags.ctp @@ -6,6 +6,7 @@ echo $this->element('ajaxTags', array( 'event' => $event, 'tags' => $tags, - 'tagAccess' => ($isSiteAdmin || $mayModify) + 'tagAccess' => ($isSiteAdmin || $mayModify), + 'tagConflicts' => $tagConflicts )); ?> diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index d3c4615f7..aebb01d62 100644 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -133,7 +133,8 @@ 'event' => $event, 'tags' => $event['EventTag'], 'tagAccess' => ($isSiteAdmin || $mayModify || $me['org_id'] == $event['Event']['orgc_id']), - 'required_taxonomies' => $required_taxonomies + 'required_taxonomies' => $required_taxonomies, + 'tagConflicts' => $tagConflicts ) ) ) diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index caf510376..c8243c893 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -2394,3 +2394,12 @@ table tr:hover .down-expand-button { #notice_message { margin: 10px; } + +.tag-conflict-notice { + margin: 5px 0px; +} + +.attributeTagContainer .tag-conflict-notice { + max-width: 350px; + overflow: auto; +} \ No newline at end of file From 983a58afbac7fa5f27e3c3bc3fad1cfea7367425 Mon Sep 17 00:00:00 2001 From: Alexandre Dulaunoy Date: Mon, 4 Nov 2019 12:41:52 +0100 Subject: [PATCH 15/23] chg: [default] old default 'TLP Amber' is now 'tlp:amber' to be consistent and use MISP taxonomy naming --- app/Config/config.default.php | 2 +- app/Model/Event.php | 2 +- app/Model/Post.php | 2 +- app/Model/Server.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Config/config.default.php b/app/Config/config.default.php index d41ceb911..dc55fb86e 100644 --- a/app/Config/config.default.php +++ b/app/Config/config.default.php @@ -17,7 +17,7 @@ $config = array( 'org' => 'ORGNAME', 'showorg' => true, 'threatlevel_in_email_subject' => true, - 'email_subject_TLP_string' => 'TLP Amber', + 'email_subject_TLP_string' => 'tlp:amber', 'email_subject_tag' => 'tlp', 'email_subject_include_tag_name' => true, 'background_jobs' => true, diff --git a/app/Model/Event.php b/app/Model/Event.php index fec7a2b2a..d951b582a 100755 --- a/app/Model/Event.php +++ b/app/Model/Event.php @@ -3173,7 +3173,7 @@ class Event extends AppModel $bodyevent = $temp[0]; $body = $temp[1]; $result = true; - $tplColorString = !empty(Configure::read('MISP.email_subject_TLP_string')) ? Configure::read('MISP.email_subject_TLP_string') : "TLP Amber"; + $tplColorString = !empty(Configure::read('MISP.email_subject_TLP_string')) ? Configure::read('MISP.email_subject_TLP_string') : "tlp:amber"; $subject = "[" . Configure::read('MISP.org') . " MISP] Need info about event " . $id . " - ".$tplColorString; $result = $this->User->sendEmail($reporter, $bodyevent, $body, $subject, $user) && $result; } diff --git a/app/Model/Post.php b/app/Model/Post.php index eac2a6b14..199450eec 100644 --- a/app/Model/Post.php +++ b/app/Model/Post.php @@ -118,7 +118,7 @@ class Post extends AppModel $bodyDetail .= "The following message was added: \n"; $bodyDetail .= "\n"; $bodyDetail .= $message . "\n"; - $tplColorString = !empty(Configure::read('MISP.email_subject_TLP_string')) ? Configure::read('MISP.email_subject_TLP_string') : "TLP Amber"; + $tplColorString = !empty(Configure::read('MISP.email_subject_TLP_string')) ? Configure::read('MISP.email_subject_TLP_string') : "tlp:amber"; $subject = "[" . Configure::read('MISP.org') . " MISP] New post in discussion " . $post['Post']['thread_id'] . " - ".$tplColorString; foreach ($orgMembers as $recipient) { $this->User->sendEmail($recipient, $bodyDetail, $body, $subject); diff --git a/app/Model/Server.php b/app/Model/Server.php index 8bf27755b..701d4bd75 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -430,7 +430,7 @@ class Server extends AppModel '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', + 'value' => 'tlp:amber', 'errorMessage' => '', 'test' => 'testForEmpty', 'type' => 'string', From 1aaa590e3001904c2dc6d8e0e822dcd032357775 Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 4 Nov 2019 13:41:53 +0100 Subject: [PATCH 16/23] chg: [event:view] Added notice and improved inconsistency text message --- app/Controller/EventsController.php | 11 +++++++++ app/Model/Taxonomy.php | 2 ++ app/View/Elements/ajaxTags.ctp | 11 +++++---- app/View/Events/view.ctp | 35 +++++++++++++++++++++++++++++ app/webroot/css/main.css | 5 +++++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 6bb5cbed2..be5987a9e 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -1270,6 +1270,7 @@ class EventsController extends AppController 'named_params' => $this->params['named'] ); $exception = false; + $warningTagConflict = array(); $filters = $this->_harvestParameters($filterData, $exception); $this->loadModel('GalaxyCluster'); @@ -1372,6 +1373,9 @@ class EventsController extends AppController } $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($event['EventTag'], '{n}.Tag.name')); + foreach ($tagConflicts as $tagConflict) { + $warningTagConflict[$tagConflict['taxonomy']['Taxonomy']['namespace']] = $tagConflict['taxonomy']; + } $this->set('tagConflicts', $tagConflicts); $startDate = null; @@ -1391,6 +1395,9 @@ class EventsController extends AppController } } $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + foreach ($tagConflicts as $tagConflict) { + $warningTagConflict[$tagConflict['taxonomy']['Taxonomy']['namespace']] = $tagConflict['taxonomy']; + } $event['Attribute'][$k]['tagConflicts'] = $tagConflicts; } $attributeTagsName = $this->Event->Attribute->AttributeTag->extractAttributeTagsNameFromEvent($event, 'both'); @@ -1418,10 +1425,14 @@ class EventsController extends AppController } } $tagConflicts = $this->Taxonomy->checkIfTagInconsistencies(Hash::extract($attribute['AttributeTag'], '{n}.Tag.name')); + foreach ($tagConflicts as $tagConflict) { + $warningTagConflict[$tagConflict['taxonomy']['Taxonomy']['namespace']] = $tagConflict['taxonomy']; + } $event['Object'][$k]['Attribute'][$k2]['tagConflicts'] = $tagConflicts; } } } + $this->set('warningTagConflict', $warningTagConflict); $filters['sort'] = 'timestamp'; $filters['direction'] = 'desc'; if (isset($filters['distribution'])) { diff --git a/app/Model/Taxonomy.php b/app/Model/Taxonomy.php index f640e040e..34e5a71f9 100644 --- a/app/Model/Taxonomy.php +++ b/app/Model/Taxonomy.php @@ -595,11 +595,13 @@ class Taxonomy extends AppModel if (isset($taxonomy['Taxonomy']['exclusive']) && $taxonomy['Taxonomy']['exclusive']) { $conflictingTaxonomy[] = array( 'tags' => $potTaxonomy['tagNames'], + 'taxonomy' => $taxonomy, 'conflict' => sprintf(__('Taxonomy `%s` is an exclusive Taxonomy'), $taxonomy['Taxonomy']['namespace']) ); } elseif (isset($taxonomy['TaxonomyPredicate'][0]['exclusive']) && $taxonomy['TaxonomyPredicate'][0]['exclusive']) { $conflictingTaxonomy[] = array( 'tags' => $potTaxonomy['tagNames'], + 'taxonomy' => $taxonomy, 'conflict' => sprintf( __('Predicate `%s` is exclusive from Taxonomy `%s` is exclusive'), $taxonomy['TaxonomyPredicate'][0]['value'], diff --git a/app/View/Elements/ajaxTags.ctp b/app/View/Elements/ajaxTags.ctp index b538ebbc0..bf00904fe 100644 --- a/app/View/Elements/ajaxTags.ctp +++ b/app/View/Elements/ajaxTags.ctp @@ -162,16 +162,19 @@ '%s', $tagData ); - $tagConflictData = ''; + $tagConflictData = ''; if (!empty($tagConflicts)) { + $tagConflictData .= '
'; foreach ($tagConflicts as $tagConflict) { $tagConflictData .= sprintf( - '
%s | %s
', - h(implode(', ', $tagConflict['tags'])), + '%s
', h($tagConflict['conflict']) ); + foreach ($tagConflict['tags'] as $tag) { + $tagConflictData .= sprintf('%s
', h($tag)); + } } + $tagConflictData .= '
'; } - $tagConflictData .= ''; echo $tagConflictData; ?> diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index aebb01d62..eb70b1b9f 100644 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -331,6 +331,41 @@ element('genericElements/viewMetaTable', array('table_data' => $table_data)); ?>