diff --git a/INSTALL/INSTALL.debian7.txt b/INSTALL/INSTALL.debian7.txt
index 151d55a05..7fedee09a 100644
--- a/INSTALL/INSTALL.debian7.txt
+++ b/INSTALL/INSTALL.debian7.txt
@@ -137,14 +137,8 @@ service apache2 reload
# We seriously recommend using only SSL !
# Check out the /var/www/MISP/INSTALL/apache.misp.ssl file for an example
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of its workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-cp INSTALL/misp.logrotate /etc/logrotate.d/misp
-
-9/ MISP configuration
+8/ MISP configuration
---------------------
# There are 4 sample configuration files in /var/www/MISP/app/Config that need to be copied
cd /var/www/MISP/app/Config
diff --git a/INSTALL/INSTALL.debian8.txt b/INSTALL/INSTALL.debian8.txt
index efeaa53ab..9a7fd981e 100644
--- a/INSTALL/INSTALL.debian8.txt
+++ b/INSTALL/INSTALL.debian8.txt
@@ -183,11 +183,6 @@ sudo a2ensite misp-ssl
# Restart apache
sudo systemctl restart apache2
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of its workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-
sudo cp INSTALL/misp.logrotate /etc/logrotate.d/misp
9/ MISP configuration
@@ -303,4 +298,3 @@ sudo ldconfig
## install pyzmq
sudo pip install pyzmq
-
diff --git a/INSTALL/INSTALL.ubuntu1404.txt b/INSTALL/INSTALL.ubuntu1404.txt
index b699371fc..715101aba 100644
--- a/INSTALL/INSTALL.ubuntu1404.txt
+++ b/INSTALL/INSTALL.ubuntu1404.txt
@@ -128,14 +128,8 @@ service apache2 reload
# We seriously recommend using only SSL !
# Check out the apache.misp.ssl file for an example
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of its workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-cp INSTALL/misp.logrotate /etc/logrotate.d/misp
-
-9/ MISP configuration
+8/ MISP configuration
---------------------
# There are 4 sample configuration files in /var/www/MISP/app/Config that need to be copied
cd /var/www/MISP/app/Config
diff --git a/INSTALL/INSTALL.ubuntu1604.txt b/INSTALL/INSTALL.ubuntu1604.txt
index c184ae862..6441f7c67 100644
--- a/INSTALL/INSTALL.ubuntu1604.txt
+++ b/INSTALL/INSTALL.ubuntu1604.txt
@@ -184,11 +184,6 @@ sudo a2ensite misp-ssl
# Restart apache
sudo systemctl restart apache2
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of its workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-
sudo cp INSTALL/misp.logrotate /etc/logrotate.d/misp
9/ MISP configuration
diff --git a/INSTALL/MYSQL.sql b/INSTALL/MYSQL.sql
index 4988e4053..ab49b8f10 100644
--- a/INSTALL/MYSQL.sql
+++ b/INSTALL/MYSQL.sql
@@ -29,12 +29,15 @@ CREATE TABLE IF NOT EXISTS `attributes` (
`timestamp` int(11) NOT NULL DEFAULT 0,
`distribution` tinyint(4) NOT NULL DEFAULT 0,
`sharing_group_id` int(11) NOT NULL,
- `comment` text COLLATE utf8_bin NOT NULL,
+ `comment` text COLLATE utf8_bin,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
+ `disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
INDEX `event_id` (`event_id`),
INDEX `value1` (`value1`(255)),
INDEX `value2` (`value2`(255)),
+ INDEX `type` (`type`),
+ INDEX `category` (`category`),
INDEX `sharing_group_id` (`sharing_group_id`),
UNIQUE INDEX `uuid` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@@ -137,6 +140,7 @@ CREATE TABLE IF NOT EXISTS `events` (
`locked` tinyint(1) NOT NULL DEFAULT 0,
`threat_level_id` int(11) NOT NULL,
`publish_timestamp` int(11) NOT NULL DEFAULT 0,
+ `disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `uuid` (`uuid`),
FULLTEXT INDEX `info` (`info`(255)),
@@ -206,14 +210,26 @@ CREATE TABLE IF NOT EXISTS `feeds` (
`provider` varchar(255) COLLATE utf8_bin NOT NULL,
`url` varchar(255) COLLATE utf8_bin NOT NULL,
`rules` text COLLATE utf8_bin DEFAULT NULL,
- `enabled` BOOLEAN NOT NULL,
- `distribution` tinyint(4) NOT NULL,
+ `enabled` tinyint(1) DEFAULT 0,
+ `distribution` tinyint(4) NOT NULL DEFAULT 0,
`sharing_group_id` int(11) NOT NULL DEFAULT 0,
`tag_id` int(11) NOT NULL DEFAULT 0,
- `default` tinyint(1) NOT NULL,
- PRIMARY KEY (`id`)
+ `default` tinyint(1) DEFAULT 0,
+ `source_format` varchar(255) COLLATE utf8_bin DEFAULT 'misp',
+ `fixed_event` tinyint(1) NOT NULL DEFAULT 0,
+ `delta_merge` tinyint(1) NOT NULL DEFAULT 0,
+ `event_id` int(11) NOT NULL DEFAULT 0,
+ `publish` tinyint(1) NOT NULL DEFAULT 0,
+ `override_ids` tinyint(1) NOT NULL DEFAULT 0,
+ `settings` text NOT NULL DEFAULT '',
+ `input_source` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT "network",
+ `delete_local_file` tinyint(1) DEFAULT 0,
+ PRIMARY KEY (`id`),
+ INDEX `input_source` (`input_source`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+
-- -------------------------------------------------------
--
@@ -330,9 +346,9 @@ CREATE TABLE IF NOT EXISTS `logs` (
`model_id` int(11) NOT NULL,
`action` varchar(20) COLLATE utf8_bin NOT NULL,
`user_id` int(11) NOT NULL,
- `change` text CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
- `email` varchar(255) COLLATE utf8_bin NOT NULL,
- `org` varchar(255) COLLATE utf8_bin NOT NULL,
+ `change` text COLLATE utf8_bin NOT NULL DEFAULT "",
+ `email` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT "",
+ `org` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT "",
`description` text CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@@ -473,12 +489,12 @@ CREATE TABLE IF NOT EXISTS `servers` (
-- --------------------------------------------------------
--
--- Table structure for table `shadow_attributes`
+-- Table structure for table ``)ributes`
--
CREATE TABLE IF NOT EXISTS `shadow_attributes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
- `old_id` int(11) NOT NULL,
+ `old_id` int(11) DEFAULT 0,
`event_id` int(11) NOT NULL,
`type` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`category` varchar(255) COLLATE utf8_bin NOT NULL,
@@ -494,6 +510,7 @@ CREATE TABLE IF NOT EXISTS `shadow_attributes` (
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`timestamp` int(11) NOT NULL DEFAULT 0,
`proposal_to_delete` BOOLEAN NOT NULL DEFAULT 0,
+ `disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
INDEX `event_id` (`event_id`),
INDEX `event_uuid` (`event_uuid`),
@@ -501,7 +518,9 @@ CREATE TABLE IF NOT EXISTS `shadow_attributes` (
INDEX `uuid` (`uuid`),
INDEX `old_id` (`old_id`),
INDEX `value1` (`value1`(255)),
- INDEX `value2` (`value2`(255))
+ INDEX `value2` (`value2`(255)),
+ INDEX `type` (`type`),
+ INDEX `category` (`category`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- --------------------------------------------------------
@@ -599,15 +618,21 @@ CREATE TABLE `sharing_groups` (
--
CREATE TABLE IF NOT EXISTS sightings (
- id int(11) NOT NULL AUTO_INCREMENT,
- attribute_id int(11) NOT NULL,
- event_id int(11) NOT NULL,
- org_id int(11) NOT NULL,
- date_sighting bigint(20) NOT NULL,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `attribute_id` int(11) NOT NULL,
+ `event_id` int(11) NOT NULL,
+ `org_id` int(11) NOT NULL,
+ `date_sighting` bigint(20) NOT NULL,
+ `uuid` varchar(255) COLLATE utf8_bin DEFAULT "",
+ `source` varchar(255) COLLATE utf8_bin DEFAULT "",
+ `type` int(11) DEFAULT 0,
PRIMARY KEY (id),
- INDEX attribute_id (attribute_id),
- INDEX event_id (event_id),
- INDEX org_id (org_id)
+ INDEX `attribute_id` (`attribute_id`),
+ INDEX `event_id` (`event_id`),
+ INDEX `org_id` (`org_id`),
+ INDEX `uuid` (`uuid`),
+ INDEX `source` (`source`),
+ INDEX `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- --------------------------------------------------------
@@ -622,6 +647,7 @@ CREATE TABLE IF NOT EXISTS `tags` (
`colour` varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`exportable` tinyint(1) NOT NULL,
`org_id` tinyint(1) NOT NULL DEFAULT 0,
+ `hide_tag` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
FULLTEXT INDEX `name` (`name`),
INDEX `org_id` (`org_id`)
@@ -922,7 +948,7 @@ CREATE TABLE IF NOT EXISTS `whitelist` (
--
INSERT INTO `admin_settings` (`id`, `setting`, `value`) VALUES
-(1, 'db_version', '2.4.51');
+(1, 'db_version', '2.4.66');
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),
diff --git a/INSTALL/misp.logrotate b/INSTALL/misp.logrotate
deleted file mode 100644
index 37ba7611d..000000000
--- a/INSTALL/misp.logrotate
+++ /dev/null
@@ -1,9 +0,0 @@
-/var/www/MISP/app/tmp/logs/resque-*-error.log {
- rotate 30
- dateext
- missingok
- notifempty
- compress
- weekly
- copytruncate
-}
diff --git a/INSTALL/misplogrotate.te b/INSTALL/misplogrotate.te
deleted file mode 100644
index 9d8d28ebc..000000000
--- a/INSTALL/misplogrotate.te
+++ /dev/null
@@ -1,8 +0,0 @@
-module misplogrotate 1.0;
-require {
- type logrotate_t;
- type httpd_sys_content_t;
- class dir { ioctl read getattr lock search open };
-}
-#============= logrotate_t ==============
-allow logrotate_t httpd_sys_content_t:dir { ioctl read getattr lock search open };
diff --git a/INSTALL/xINSTALL.centos6.txt b/INSTALL/xINSTALL.centos6.txt
index ad98b350c..0fb59e19b 100644
--- a/INSTALL/xINSTALL.centos6.txt
+++ b/INSTALL/xINSTALL.centos6.txt
@@ -176,14 +176,8 @@ service iptables save
# We seriously recommend using only SSL !
# Check out the apache.misp.ssl file for an example
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of its workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-cp INSTALL/misp.logrotate /etc/logrotate.d/misp
-
-9/ MISP configuration
+8/ MISP configuration
---------------------
# There are 4 sample configuration files in /var/www/MISP/app/Config that need to be copied
cd /var/www/MISP/app/Config
diff --git a/INSTALL/xINSTALL.centos7.txt b/INSTALL/xINSTALL.centos7.txt
index 56c95a8f5..55ff97882 100644
--- a/INSTALL/xINSTALL.centos7.txt
+++ b/INSTALL/xINSTALL.centos7.txt
@@ -193,24 +193,8 @@ firewall-cmd --reload
# Add SSL support by running: yum install mod_ssl
# Check out the apache.misp.ssl file for an example
-8/ Log rotation
----------------
-# MISP saves the stdout and stderr of it's workers in /var/www/MISP/app/tmp/logs
-# To rotate these logs install the supplied logrotate script:
-cp INSTALL/misp.logrotate /etc/logrotate.d/misp
-
-# Now make logrotate work under SELinux as well
-# Allow logrotate to modify the log files
-semanage fcontext -a -t httpd_log_t "/var/www/MISP/app/tmp/logs(/.*)?"
-chcon -R -t httpd_log_t /var/www/MISP/app/tmp/logs
-
-# Allow logrotate to read /var/www
-checkmodule -M -m -o /tmp/misplogrotate.mod INSTALL/misplogrotate.te
-semodule_package -o /tmp/misplogrotate.pp -m /tmp/misplogrotate.mod
-semodule -i /tmp/misplogrotate.pp
-
-9/ MISP configuration
+8/ MISP configuration
---------------------
# There are 4 sample configuration files in /var/www/MISP/app/Config that need to be copied
cd /var/www/MISP/app/Config
diff --git a/PyMISP b/PyMISP
index 26a8f4c66..a81f6b5c1 160000
--- a/PyMISP
+++ b/PyMISP
@@ -1 +1 @@
-Subproject commit 26a8f4c66230c0df10b2f9637e53ee1542a26f40
+Subproject commit a81f6b5c15e2effbc0b6118f1e1524b0950a576c
diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php
index f50c2f770..4f6e4e819 100755
--- a/app/Controller/AppController.php
+++ b/app/Controller/AppController.php
@@ -46,9 +46,9 @@ class AppController extends Controller {
public $helpers = array('Utility');
- private $__jsVersion = '2.4.62';
+ private $__jsVersion = '2.4.66';
public $pyMispVersion = '2.4.65';
- public $phpmin = '5.5.9';
+ public $phpmin = '5.6.5';
public $phprec = '7.0.0';
// Used for _isAutomation(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php
index ddd78d7aa..cb1d3fe2d 100644
--- a/app/Controller/Component/ACLComponent.php
+++ b/app/Controller/Component/ACLComponent.php
@@ -286,8 +286,12 @@ class ACLComponent extends Component {
),
'sightings' => array(
'add' => array('perm_add'),
+ 'advanced' => array('perm_add'),
'delete' => array('perm_add'),
- 'index' => array('*')
+ 'index' => array('*'),
+ 'listSightings' => array('perm_add'),
+ 'quickDelete' => array('perm_add'),
+ 'viewSightings' => array('perm_add')
),
'tags' => array(
'add' => array('perm_tag_editor'),
diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php
index 4684cbb50..2ef1bfa0d 100644
--- a/app/Controller/EventsController.php
+++ b/app/Controller/EventsController.php
@@ -748,6 +748,8 @@ class EventsController extends AppController {
$this->set('attributeFilter', isset($this->params['named']['attributeFilter']) ? $this->params['named']['attributeFilter'] : 'all');
$this->disableCache();
$this->layout = 'ajax';
+ $this->loadModel('Sighting');
+ $this->set('sightingTypes', $this->Sighting->type);
$this->set('currentUri', $this->params->here);
$this->render('/Elements/eventattribute');
}
@@ -859,6 +861,8 @@ class EventsController extends AppController {
}
$this->set('contributors', $contributors);
$this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings));
+ $this->loadModel('Sighting');
+ $this->set('sightingTypes', $this->Sighting->type);
}
public function view($id = null, $continue=false, $fromEvent=null) {
@@ -3093,8 +3097,6 @@ class EventsController extends AppController {
foreach (${$source} as $k => $attribute) {
if ($attribute['type'] == 'ip-src/ip-dst') {
$types = array('ip-src', 'ip-dst');
- } else if ($attribute['type'] == 'ip-src|port/ip-dst|port') {
- $types = array('ip-src|port', 'ip-dst|port');
} else if ($attribute['type'] == 'malware-sample') {
if (!isset($attribute['data_is_handled']) || !$attribute['data_is_handled']) {
App::uses('FileAccessTool', 'Tools');
diff --git a/app/Controller/SightingsController.php b/app/Controller/SightingsController.php
index b453e4204..1595e35d7 100644
--- a/app/Controller/SightingsController.php
+++ b/app/Controller/SightingsController.php
@@ -18,42 +18,110 @@ class SightingsController extends AppController {
// takes an attribute ID or UUID
public function add($id = false) {
if (!$this->userRole['perm_add']) throw new MethodNotAllowedException('You are not authorised to add sightings data as you don\'t have write access.');
- if (!$this->request->is('post')) throw new MethodNotAllowedException('This action can only be accessed via a post request.');
- $now = time();
- $values = false;
- $timestamp = false;
- $error = false;
- if ($id === 'stix') {
- $result = $this->Sighting->handleStixSighting(file_get_contents('php://input'));
- if ($result['success']) {
- $result['data'] = json_decode($result['data'], true);
- $timestamp = isset($result['data']['timestamp']) ? strtotime($result['data']['timestamp']) : $now;
- if (isset($result['data']['values'])) $values = $result['data']['values'];
- else $error = 'No valid values found could be extracted from the sightings document.';
- } $error = $result['message'];
- } else {
- if (isset($this->request->data['request'])) $this->request->data = $this->request->data['request'];
- if (isset($this->request->data['Sighting'])) $this->request->data = $this->request->data['Sighting'];
- $timestamp = isset($this->request->data['timestamp']) ? $this->request->data['timestamp'] : $now;
- if (isset($this->request->data['value'])) $this->request->data['values'] = array($this->request->data['value']);
- $values = isset($this->request->data['values']) ? $this->request->data['values'] : false;
- if (!$id && isset($this->request->data['id'])) $id = $this->request->data['id'];
- }
- if (!$error) $result = $this->Sighting->saveSightings($id, $values, $timestamp, $this->Auth->user());
- if ($result == 0) $error = 'No valid attributes found that would match the sighting criteria.';
-
- if ($this->request->is('ajax')) {
- if ($error) {
- $error_message = 'Could not add the Sighting. Reason: ' . $error;
- return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $error_message)), 'status' => 200));
+ if ($this->request->is('post')) {
+ $now = time();
+ $values = false;
+ $timestamp = false;
+ $error = false;
+ if ($id === 'stix') {
+ $result = $this->Sighting->handleStixSighting(file_get_contents('php://input'));
+ if ($result['success']) {
+ $result['data'] = json_decode($result['data'], true);
+ $timestamp = isset($result['data']['timestamp']) ? strtotime($result['data']['timestamp']) : $now;
+ $type = '0';
+ $source = '';
+ if (isset($result['data']['values'])) $values = $result['data']['values'];
+ else $error = 'No valid values found could be extracted from the sightings document.';
+ } $error = $result['message'];
} else {
- return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $result . ' sighting' . (($result == 1) ? '' : 's') . ' added.')), 'status' => 200));
+ if (isset($this->request->data['request'])) $this->request->data = $this->request->data['request'];
+ if (isset($this->request->data['Sighting'])) $this->request->data = $this->request->data['Sighting'];
+ if (!empty($this->request->data['date']) && !empty($this->request->data['time'])) {
+ $timestamp = DateTime::createFromFormat('Y-m-d:H:i:s', $this->request->data['date'] . ':' . $this->request->data['time']);
+ $timestamp = $timestamp->getTimestamp();
+ } else {
+ $timestamp = isset($this->request->data['timestamp']) ? $this->request->data['timestamp'] : $now;
+ }
+ if (isset($this->request->data['value'])) $this->request->data['values'] = array($this->request->data['value']);
+ $values = isset($this->request->data['values']) ? $this->request->data['values'] : false;
+ if (!$id && isset($this->request->data['id'])) $id = $this->request->data['id'];
+ $type = isset($this->request->data['type']) ? $this->request->data['type'] : '0';
+ $source = isset($this->request->data['source']) ? trim($this->request->data['source']) : '';
+ }
+ if (!$error) $result = $this->Sighting->saveSightings($id, $values, $timestamp, $this->Auth->user(), $type, $source);
+ if ($result == 0) $error = 'No valid attributes found that would match the sighting criteria.';
+
+ if ($this->request->is('ajax')) {
+ if ($error) {
+ $error_message = 'Could not add the Sighting. Reason: ' . $error;
+ return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $error_message)), 'status' => 200));
+ } else {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $result . ' ' . $this->Sighting->type[$type] . (($result == 1) ? '' : 's') . ' added.')), 'status' => 200));
+ }
+ } else {
+ if ($error) {
+ return $this->RestResponse->saveFailResponse('Sighting', 'add', $id, $error);
+ } else {
+ return $this->RestResponse->saveSuccessResponse('Sighting', 'add', $id, false, $result . ' ' . $this->Sighting->type[$type] . (($result == 1) ? '' : 's') . ' successfuly added.');
+ }
}
} else {
- if ($error) {
- return $this->RestResponse->saveFailResponse('Sighting', 'add', $id, $error);
+ if (!$this->request->is('ajax')) {
+ throw new MethodNotAllowedException('This method is only accessible via POST requests and ajax GET requests.');
} else {
- return $this->RestResponse->saveSuccessResponse('Sighting', 'add', $id, false, $result . ' sighting' . (($result == 1) ? '' : 's') . ' successfuly added.');
+ $this->layout = false;
+ $this->loadModel('Attribute');
+ $attributes = $this->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id)));
+ if (empty($attributes)) {
+ throw new MethodNotAllowedExeption('Invalid Attribute.');
+ }
+ $this->set('event_id', $attributes[0]['Attribute']['event_id']);
+ $this->set('id', $id);
+ $this->render('ajax/add_sighting');
+ }
+ }
+ }
+
+ public function advanced($id) {
+ if (empty($id)) {
+ throw new MethodNotAllowedException('Invalid attribute.');
+ }
+ $input_id = $id;
+ $id = $this->Sighting->explodeIdList($id);
+ $this->loadModel('Attribute');
+ $attributes = $this->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id)));
+ if (empty($attributes)) {
+ throw new MethodNotAllowedException('Invalid attribute.');
+ }
+ $this->set('id', $input_id);
+ $this->render('/Sightings/ajax/advanced');
+ }
+
+ public function quickDelete($id, $rawId, $context) {
+ if (!$this->userRole['perm_modify_org']) throw new MethodNotAllowedException('You are not authorised to remove sightings data as you don\'t have permission to modify your organisation\'s data.');
+ if (!$this->request->is('post')) {
+ $this->set('id', $id);
+ $sighting = $this->Sighting->find('first', array('conditions' => array('Sighting.id' => $id), 'recursive' => -1, 'fields' => array('Sighting.attribute_id')));
+ $this->set('rawId', $rawId);
+ $this->set('context', $context);
+ $this->render('ajax/quickDeleteConfirmationForm');
+ } else {
+ if (!isset($id)) {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'errors' => 'Invalid request.')), 'status' => 200));
+ } else {
+ $sighting = $this->Sighting->find('first', array('conditions' => array('Sighting.id' => $id), 'recursive' => -1));
+ if (empty($sighting)) {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'errors' => 'Invalid sighting.')), 'status' => 200));
+ }
+ if (!$this->_isSiteAdmin() && $sighting['Sighting']['org_id'] != $this->Auth->user('org_id')) {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'errors' => 'Invalid sighting.')), 'status' => 200));
+ }
+ $result = $this->Sighting->delete($id);
+ if ($result) {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Sighting deleted.')), 'status' => 200));
+ } else {
+ return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'errors' => 'Sighting could not be deleted')), 'status' => 200));
+ }
}
}
}
@@ -102,4 +170,107 @@ class SightingsController extends AppController {
}
return $this->RestResponse->viewData($sightings);
}
+
+ public function listSightings($id, $context = 'attribute', $org_id = false) {
+ $this->loadModel('Event');
+ $rawId = $id;
+ $id = $this->Sighting->explodeIdList($id);
+ if ($context === 'attribute') {
+ $object = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id, 'Attribute.deleted' => 0)));
+ } else {
+ // let's set the context to event here, since we reuse the variable later on for some additional lookups.
+ // Passing $context = 'org' could have interesting results otherwise...
+ $context = 'event';
+ $object = $this->Event->fetchEvent($this->Auth->user(), $options = array('eventid' => $id, 'metadata' => true));
+ }
+ if (empty($object)) {
+ throw new MethodNotAllowedException('Invalid object.');
+ }
+ $conditions = array(
+ 'Sighting.' . $context . '_id' => $id
+ );
+ if ($org_id) {
+ $conditions[] = array('Sighting.org_id' => $org_id);
+ }
+ $sightings = $this->Sighting->find('all', array(
+ 'conditions' => $conditions,
+ 'recursive' => -1,
+ 'contain' => array('Organisation.name'),
+ 'order' => array('Sighting.date_sighting DESC')
+ ));
+ $this->set('org_id', $org_id);
+ $this->set('rawId', $rawId);
+ $this->set('context', $context);
+ $this->set('types', array('Sighting', 'False-positive', 'Expiration'));
+ $this->set('sightings', $sightings);
+ $this->layout = false;
+ $this->render('ajax/list_sightings');
+ }
+
+ public function viewSightings($id, $context = 'attribute') {
+ $this->loadModel('Event');
+ $id = $this->Sighting->explodeIdList($id);
+ if ($context === 'attribute') {
+ $attribute_id = $id;
+ $object = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id, 'Attribute.deleted' => 0)));
+ if (empty($object)) {
+ throw new MethodNotAllowedException('Invalid object.');
+ }
+ $eventIds = array();
+ foreach ($object as $k => $v) {
+ $eventIds[] = $v['Attribute']['event_id'];
+ }
+ $events = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $eventIds));
+ } else {
+ $attribute_id = false;
+ // let's set the context to event here, since we reuse the variable later on for some additional lookups.
+ // Passing $context = 'org' could have interesting results otherwise...
+ $context = 'event';
+ $events = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id));
+
+ }
+ if (empty($events)) {
+ throw new MethodNotAllowedException('Invalid object.');
+ }
+ $results = array();
+ $raw = array();
+ foreach ($events as $event) {
+ $raw = array_merge($raw, $this->Sighting->attachToEvent($event, $this->Auth->user(), $attribute_id));
+ }
+ foreach ($raw as $sighting) {
+ $results[$sighting['type']][date('Ymd', $sighting['date_sighting'])][] = $sighting;
+ }
+ $tsv = 'date\tSighting\tFalse-positive\n';
+ $dataPoints = array();
+ $startDate = (date('Ymd') - 3);
+ $details = array();
+ foreach ($results as $type => $data) {
+ foreach ($data as $date => $sighting) {
+ if ($date < $startDate) {
+ $startDate = $date;
+ }
+ $temp = array();
+ foreach ($sighting as $sightingInstance) {
+ if (!isset($sightingInstance['Organisation']['name'])) {
+ $org = 'Anonymised';
+ } else {
+ $org = $sightingInstance['Organisation']['name'];
+ }
+ $temp[$org] = isset($temp[$org]) ? $temp[$org] + 1 : 1;
+ }
+ $dataPoints[$date][$type] = array('count' => count($sighting), 'details' => $temp);
+ }
+ }
+ for ($i = $startDate; $i < date('Ymd') + 1; $i++) {
+ if (checkdate(substr($i, 4, 2), substr($i, 6, 2), substr($i, 0, 4))) {
+ $tsv .= $i . '\t' . (isset($dataPoints[$i][0]['count']) ? $dataPoints[$i][0]['count'] : 0) . '\t' . (isset($dataPoints[$i][1]['count']) ? $dataPoints[$i][1]['count'] : 0) . '\n';
+ $details[$i][0] = isset($dataPoints[$i][0]['details']) ? $dataPoints[$i][0]['details'] : array();
+ $details[$i][1] = isset($dataPoints[$i][1]['details']) ? $dataPoints[$i][1]['details'] : array();
+ }
+ }
+ $this->set('tsv', $tsv);
+ $this->set('results', $results);
+ $this->layout = 'ajax';
+ $this->render('ajax/view_sightings');
+ }
}
diff --git a/app/Controller/ThreadsController.php b/app/Controller/ThreadsController.php
index d66ea5261..03fe8f594 100644
--- a/app/Controller/ThreadsController.php
+++ b/app/Controller/ThreadsController.php
@@ -22,21 +22,24 @@ class ThreadsController extends AppController {
$thread_id = false;
if ($result) {
$thread_id = $this->Thread->find('first', array('recursive' => -1, 'conditions' => array('Thread.event_id' => $id), 'fields' => array('Thread.id')));
- if ($thread_id) {
- if (!$this->_isRest()) {
- $this->redirect(array('action' => 'view', $thread_id['Thread']['id'], true));
+ if ($thread_id) {
+ $thread_id = $thread_id['Thread']['id'];
} else {
- return $this->__view($thread_id['Thread']['id'], false, false);
- }
- } else {
- if ($this->_isRest()) {
- return $this->RestResponse->viewData(array(), $this->response->type());
- } else {
- throw new NotFoundException('Invalid Thread.');
+ if ($this->_isRest()) {
+ return $this->RestResponse->viewData($array(), $this->response->type());
+ }
+ $thread_id = false;
}
+ }
+ if ($thread_id) {
+ $post_id = false;
+ if (isset($this->passedArgs['post_id'])) $post_id = $this->passedArgs['post_id'];
+ $response = $this->__view($thread_id, false, $post_id);
+ if ($this->_isRest()) {
+ return $response;
}
} else {
- throw new NotFoundException('Invalid Event.');
+ throw new NotFoundException('Invalid Thread.');
}
}
@@ -80,6 +83,7 @@ class ThreadsController extends AppController {
throw new NotFoundException('Invalid thread.');
}
$thread = $this->Thread->read();
+
// If the thread belongs to an event, we have to make sure that the event's distribution level hasn't changed.
// This is also a good time to update the thread's distribution level if that did happen.
if (!empty($thread['Thread']['event_id'])) {
diff --git a/app/Lib/Tools/ComplexTypeTool.php b/app/Lib/Tools/ComplexTypeTool.php
index d4220c0ba..002aac695 100644
--- a/app/Lib/Tools/ComplexTypeTool.php
+++ b/app/Lib/Tools/ComplexTypeTool.php
@@ -8,7 +8,6 @@ class ComplexTypeTool {
'/^h\[tt\]p/i' => 'http',
'/\[\.\]/' => '.',
'/\[dot\]/' => '.',
- '/\(dot\)/' => '.',
'/\\\\\./' => '.',
'/\.+/' => '.'
);
@@ -210,6 +209,7 @@ class ComplexTypeTool {
// input2 from here on is the variable containing the original input with the port removed. It is only used by url / domain name / hostname / ip
$comment = false;
if (preg_match('/(:[0-9]{2,5})$/', $inputRefanged, $port)) {
+ $comment = 'On port ' . substr($port[0], 1);
$inputRefangedNoPort = str_replace($port[0], '', $inputRefanged);
$port = substr($port[0], 1);
} else {
@@ -217,13 +217,7 @@ class ComplexTypeTool {
$inputRefangedNoPort = $inputRefanged;
}
// check for IP
- if (filter_var($inputRefangedNoPort, FILTER_VALIDATE_IP)) {
- if (isset($port)) {
- return array('types' => array('ip-dst|port', 'ip-src|port', 'ip-src|port/ip-dst|port'), 'to_ids' => true, 'default_type' => 'ip-dst|port', 'comment' => $comment, 'value' => $inputRefangedNoPort . '|' . $port);
- } else {
- return array('types' => array('ip-dst', 'ip-src', 'ip-src/ip-dst'), 'to_ids' => true, 'default_type' => 'ip-dst', 'comment' => $comment, 'value' => $inputRefangedNoPort);
- }
- }
+ if (filter_var($inputRefangedNoPort, FILTER_VALIDATE_IP)) return array('types' => array('ip-dst', 'ip-src', 'ip-src/ip-dst'), 'to_ids' => true, 'default_type' => 'ip-dst', 'comment' => $comment, 'value' => $inputRefangedNoPort);
if (strpos($inputRefangedNoPort, '/')) {
$temp = explode('/', $inputRefangedNoPort);
if (count($temp) == 2) {
diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php
index e82ec34df..1915ca347 100644
--- a/app/Model/AppModel.php
+++ b/app/Model/AppModel.php
@@ -41,7 +41,7 @@ class AppModel extends Model {
42 => false, 44 => false, 45 => false, 49 => true, 50 => false,
51 => false, 52 => false, 55 => true, 56 => true, 57 => true,
58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
- 63 => false, 64 => false, 65 => false
+ 63 => false, 64 => false, 65 => false, 66 => false
)
)
);
@@ -78,6 +78,11 @@ class AppModel extends Model {
case '2.4.55':
$this->updateDatabase('addSightings');
break;
+ case '2.4.64':
+ $this->updateDatabase('2.4.64');
+ $this->Sighting = Classregistry::init('Sighting');
+ $this->Sighting->addUuids();
+ break;
default:
$this->updateDatabase($command);
break;
@@ -557,8 +562,6 @@ class AppModel extends Model {
$sqlArray[] = 'CREATE INDEX idx_attribute_tags_event_id ON attribute_tags (event_id);';
$sqlArray[] = 'CREATE INDEX idx_attribute_tags_tag_id ON attribute_tags (tag_id);';
}
- $this->__dropIndex('attribute_tags', 'attribute_id');
- $this->__dropIndex('attribute_tags', 'tag_id');
break;
case '2.4.61':
$sqlArray[] = 'ALTER TABLE feeds ADD input_source varchar(255) COLLATE utf8_bin NOT NULL DEFAULT "network";';
@@ -603,7 +606,15 @@ class AppModel extends Model {
break;
case '2.4.66':
$sqlArray[] = 'ALTER TABLE shadow_attributes CHANGE old_id old_id int(11) DEFAULT 0;';
-
+ $sqlArray[] = 'ALTER TABLE sightings ADD COLUMN uuid varchar(255) COLLATE utf8_bin DEFAULT "";';
+ $sqlArray[] = 'ALTER TABLE sightings ADD COLUMN source varchar(255) COLLATE utf8_bin DEFAULT "";';
+ $sqlArray[] = 'ALTER TABLE sightings ADD COLUMN type int(11) DEFAULT 0;';
+ $indexArray[] = array('sightings', 'uuid');
+ $indexArray[] = array('sightings', 'source');
+ $indexArray[] = array('sightings', 'type');
+ $indexArray[] = array('attributes', 'category');
+ $indexArray[] = array('shadow_attributes', 'category');
+ $indexArray[] = array('shadow_attributes', 'type');
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
@@ -653,11 +664,14 @@ class AppModel extends Model {
));
}
}
- foreach ($indexArray as $iA) {
- if (isset($iA[2])) {
- $this->__addIndex($iA[0], $iA[1], $iA[2]);
- } else {
- $this->__addIndex($iA[0], $iA[1]);
+ if (!empty($indexArray)) {
+ if ($clean) $this->cleanCacheFiles();
+ foreach ($indexArray as $iA) {
+ if (isset($iA[2])) {
+ $this->__addIndex($iA[0], $iA[1], $iA[2]);
+ } else {
+ $this->__addIndex($iA[0], $iA[1]);
+ }
}
}
if ($clean) $this->cleanCacheFiles();
diff --git a/app/Model/Attribute.php b/app/Model/Attribute.php
index 5911c3759..de99e38d5 100644
--- a/app/Model/Attribute.php
+++ b/app/Model/Attribute.php
@@ -1336,14 +1336,14 @@ class Attribute extends AppModel {
}
}
}
+ $extraConditions = array();
if (!empty($ipValues)) {
$extraConditions = array('OR' => array(
'Attribute.value1' => $ipValues,
'Attribute.value2' => $ipValues
));
- return $extraConditions;
}
- return false;
+ return $extraConditions;
}
public function __afterSaveCorrelation($a, $full = false, $event = false) {
diff --git a/app/Model/Event.php b/app/Model/Event.php
index 9747f7fd0..9fba065d3 100644
--- a/app/Model/Event.php
+++ b/app/Model/Event.php
@@ -918,14 +918,8 @@ class Event extends AppModel {
// cleanup the array from things we do not want to expose
foreach (array('Org', 'org_id', 'orgc_id', 'proposal_email_lock', 'org', 'orgc') as $field) unset($event['Event'][$field]);
foreach ($event['Event']['EventTag'] as $kt => $tag) {
- if (!$tag['Tag']['exportable']) {
- unset($event['Event']['EventTag'][$kt]);
- } else {
- unset($tag['org_id']);
- $event['Event']['Tag'][] = $tag['Tag'];
- }
+ if (!$tag['Tag']['exportable']) unset($event['Event']['EventTag'][$kt]);
}
- unset($event['Event']['EventTag']);
// Add the local server to the list of instances in the SG
if (isset($event['Event']['SharingGroup']) && isset($event['Event']['SharingGroup']['SharingGroupServer'])) {
@@ -967,16 +961,6 @@ class Event extends AppModel {
}
}
}
- foreach ($attribute['AttributeTag'] as $kt => $tag) {
- if (!$tag['Tag']['exportable']) {
- unset($attribute['AttributeTag'][$kt]);
- } else {
- unset($tag['Tag']['org_id']);
- $attribute['Tag'][] = $tag['Tag'];
- }
- }
- unset($attribute['AttributeTag']);
-
// remove value1 and value2 from the output
unset($attribute['value1']);
@@ -1474,10 +1458,6 @@ class Event extends AppModel {
}
$event['ShadowAttribute'] = array_values($event['ShadowAttribute']);
}
- if ($event['Event']['orgc_id'] === $user['org_id'] && $user['Role']['perm_audit']) {
- $UserEmail = $this->User->getAuthUser($event['Event']['user_id'])['email'];
- $event['Event']['event_creator_email'] = $UserEmail;
- }
}
return $results;
}
@@ -2455,8 +2435,7 @@ class Event extends AppModel {
'fields' => array('id', 'url', 'name')
)
),
- ),
- 'AttributeTag' => array('Tag')
+ )
),
'EventTag' => array('Tag'),
'Org' => array('fields' => array('id', 'uuid', 'name', 'local')),
diff --git a/app/Model/Feed.php b/app/Model/Feed.php
index f9dd6367c..bf1743a56 100644
--- a/app/Model/Feed.php
+++ b/app/Model/Feed.php
@@ -655,20 +655,12 @@ class Feed extends AppModel {
if (empty($data)) {
return true;
}
- $prunedCopy = array();
foreach ($data as $key => $value) {
- foreach ($prunedCopy as $copy) {
- if ($copy['type'] == $value['type'] && $copy['category'] == $value['category'] && $copy['value'] == $value['value']) {
- continue 2;
- }
- }
$data[$key]['event_id'] = $event['Event']['id'];
$data[$key]['distribution'] = $feed['Feed']['distribution'];
$data[$key]['sharing_group_id'] = $feed['Feed']['sharing_group_id'];
$data[$key]['to_ids'] = $feed['Feed']['override_ids'] ? 0 : $data[$key]['to_ids'];
- $prunedCopy[] = $data[$key];
}
- $data = $prunedCopy;
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
diff --git a/app/Model/Sighting.php b/app/Model/Sighting.php
index db5b835b3..ac86735c2 100644
--- a/app/Model/Sighting.php
+++ b/app/Model/Sighting.php
@@ -28,19 +28,31 @@ class Sighting extends AppModel {
),
);
+ public $type = array(
+ 0 => 'sighting',
+ 1 => 'false-positive',
+ 2 => 'expiration'
+ );
+
public function beforeValidate($options = array()) {
parent::beforeValidate();
$date = date('Y-m-d H:i:s');
if (empty($this->data['Sighting']['id']) && empty($this->data['Sighting']['date_sighting'])) {
$this->data['Sighting']['date_sighting'] = $date;
}
+ if (empty($this->data['Sighting']['uuid'])) {
+ $this->data['Sighting']['uuid'] = CakeText::uuid();
+ }
return true;
}
- public function attachToEvent(&$event, $user) {
+ public function attachToEvent($event, $user, $attribute_id = false) {
$ownEvent = false;
if ($user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id']) $ownEvent = true;
$conditions = array('Sighting.event_id' => $event['Event']['id']);
+ if ($attribute_id) {
+ $conditions[] = array('Sighting.attribute_id' => $attribute_id);
+ }
if (!$ownEvent && (!Configure::read('Plugin.Sightings_policy') || Configure::read('Plugin.Sightings_policy') == 0)) {
$conditions['Sighting.org_id'] = $user['org_id'];
}
@@ -81,10 +93,11 @@ class Sighting extends AppModel {
return $sightings;
}
- public function saveSightings($id, $values, $timestamp, $user) {
+ public function saveSightings($id, $values, $timestamp, $user, $type = false, $source = false) {
$conditions = array();
if ($id && $id !== 'stix') {
- if (strlen($id) == 36) $conditions = array('Attribute.uuid' => $id);
+ $id = $this->explodeIdList($id);
+ if (!is_array($id) && strlen($id) == 36) $conditions = array('Attribute.uuid' => $id);
else $conditions = array('Attribute.id' => $id);
} else {
if (!$values) return 0;
@@ -100,12 +113,18 @@ class Sighting extends AppModel {
if (empty($attributes)) return 0;
$sightingsAdded = 0;
foreach ($attributes as $attribute) {
+ if ($type === '2') {
+ // remove existing expiration by the same org if it exists
+ $this->deleteAll(array('Sighting.org_id' => $user['org_id'], 'Sighting.type' => $type, 'Sighting.attribute_id' => $attribute['Attribute']['id']));
+ }
$this->create();
$sighting = array(
'attribute_id' => $attribute['Attribute']['id'],
'event_id' => $attribute['Attribute']['event_id'],
'org_id' => $user['org_id'],
'date_sighting' => $timestamp,
+ 'type' => $type,
+ 'source' => $source
);
$sightingsAdded += $this->save($sighting) ? 1 : 0;
}
@@ -139,4 +158,26 @@ class Sighting extends AppModel {
public function generateRandomFileName() {
return (new RandomTool())->random_str(FALSE, 12);
}
+
+ public function addUuids() {
+ $sightings = $this->find('all', array(
+ 'recursive' => -1,
+ 'conditions' => array('uuid' => '')
+ ));
+ $this->saveMany($sightings);
+ return true;
+ }
+
+ public function explodeIdList($id) {
+ if (strpos($id, '|')) {
+ $id = explode('|', $id);
+ foreach ($id as $k => $v) {
+ if (!is_numeric($v)) {
+ unset($id[$k]);
+ }
+ }
+ $id = array_values($id);
+ }
+ return $id;
+ }
}
diff --git a/app/Model/User.php b/app/Model/User.php
index f477ca9ba..b0c656f2e 100644
--- a/app/Model/User.php
+++ b/app/Model/User.php
@@ -900,9 +900,6 @@ App::uses('RandomTool', 'Tools');
'conditions' => $conditions
);
$orgs = $this->find($findType, $params);
- if (empty($orgs)) {
- return 0;
- }
if ($org_id !== false) {
return $orgs[0]['num_members'];
} else {
@@ -957,7 +954,7 @@ App::uses('RandomTool', 'Tools');
if ($fixedPassword) {
$password = $fixedPassword;
} else {
- $password = $this->generateRandomPassword();
+ $password = $this->generateRandomPassword();
}
$body = str_replace('$password', $password, $body);
$body = str_replace('$username', $user['User']['email'], $body);
diff --git a/app/View/Elements/eventattribute.ctp b/app/View/Elements/eventattribute.ctp
index a4ecb4368..b556cfc9d 100644
--- a/app/View/Elements/eventattribute.ctp
+++ b/app/View/Elements/eventattribute.ctp
@@ -15,49 +15,69 @@
$attributeSightings = array();
$attributeOwnSightings = array();
$attributeSightingsPopover = array();
- if (isset($event['Sighting']) && !empty($event['Sighting'])) {
+ $sightingsData = array();
+ $sparklineData = array();
+ $startDates = array();
+ if (!empty($event['Sighting'])) {
foreach ($event['Sighting'] as $sighting) {
- $attributeSightings[$sighting['attribute_id']][] = $sighting;
- if (isset($sighting['org_id']) && $sighting['org_id'] == $me['org_id']) {
- if (isset($attributeOwnSightings[$sighting['attribute_id']])) {
- $attributeOwnSightings[$sighting['attribute_id']]['count']++;
- if (!isset($attributeOwnSightings[$sighting['attribute_id']]['date']) || $attributeOwnSightings[$sighting['attribute_id']]['date'] < $sighting['date_sighting']) {
- $attributeOwnSightings[$sighting['attribute_id']]['date'] = $sighting['date_sighting'];
- }
- } else {
- $attributeOwnSightings[$sighting['attribute_id']]['count'] = 1;
- $attributeOwnSightings[$sighting['attribute_id']]['date'] = $sighting['date_sighting'];
+ $type = $sightingTypes[$sighting['type']];
+ if (!isset($sightingsData[$sighting['attribute_id']][$type])) {
+ $sightingsData[$sighting['attribute_id']][$type] = array('count' => 0);
+ }
+ $sightingsData[$sighting['attribute_id']][$type]['count']++;
+ $orgName = isset($sighting['Organisation']['name']) ? $sighting['Organisation']['name'] : 'Others';
+ if (!isset($startDates[$sighting['attribute_id']]) || $startDates[$sighting['attribute_id']] > $sighting['date_sighting']) {
+ $startDates[$sighting['attribute_id']] = $sighting['date_sighting'];
+ }
+ if (!isset($sightingsData[$sighting['attribute_id']][$type]['orgs'][$orgName])) {
+ $sightingsData[$sighting['attribute_id']][$type]['orgs'][$orgName] = array('count' => 1, 'date' => $sighting['date_sighting']);
+ } else {
+ $sightingsData[$sighting['attribute_id']][$type]['orgs'][$orgName]['count']++;
+ if ($sightingsData[$sighting['attribute_id']][$type]['orgs'][$orgName]['date'] < $sighting['date_sighting']) {
+ $sightingsData[$sighting['attribute_id']][$type]['orgs'][$orgName]['date'] = $sighting['date_sighting'];
}
}
- if (isset($sighting['org_id'])) {
- if (isset($attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']])) {
- $attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['count']++;
- if (!isset($attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['date']) || $attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['date'] < $sighting['date_sighting']) {
- $attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['date'] = $sighting['date_sighting'];
- }
- } else {
- $attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['count'] = 1;
- $attributeSightingsPopover[$sighting['attribute_id']][$sighting['Organisation']['name']]['date'] = $sighting['date_sighting'];
- }
+ $date = date("Ymd", $sighting['date_sighting']);
+ if (!isset($sparklineData[$sighting['attribute_id']][$type][$date])) {
+ $sparklineData[$sighting['attribute_id']][$type][$date] = 1;
} else {
- if (isset($attributeSightingsPopover[$sighting['attribute_id']]['Other organisations'])) {
- $attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['count']++;
- if (!isset($attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['date']) || $attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['date'] < $sighting['date_sighting']) {
- $attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['date'] = $sighting['date_sighting'];
+ $sparklineData[$sighting['attribute_id']][$type][$date]++;
+ }
+ }
+ $csv = array();
+ $to = new DateTime();
+ $from = new DateTime();
+ foreach ($sparklineData as $aid => $data) {
+ foreach ($data as $type => $sighting) {
+ $from->setTimestamp(($startDates[$aid] - 259200));
+ for ($date = clone $from; $date < $to; $date->modify('+1 day')) {
+ if (!isset($csv[$aid][$type])) {
+ $csv[$aid][$type] = 'Date,Close\n';
+ }
+ $currentDate = $date->format('Ymd');
+ if (isset($sighting[$currentDate])) {
+ $csv[$aid][$type] .= $currentDate . ',' . $sighting[$currentDate] . '\n';
+ } else {
+ $csv[$aid][$type] .= $currentDate . ',0\n';
}
- } else {
- $attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['count'] = 1;
- $attributeSightingsPopover[$sighting['attribute_id']]['Other organisations']['date'] = $sighting['date_sighting'];
}
}
}
- if (!empty($attributeSightingsPopover)) {
- $attributeSightingsPopoverText = array();
- foreach ($attributeSightingsPopover as $aid => &$attribute) {
- $attributeSightingsPopoverText[$aid] = '';
- foreach ($attribute as $org => $data) {
- $attributeSightingsPopoverText[$aid] .= '' . h($org) . ': ' . h($data['count']) . ' (' . date('Y-m-d H:i:s', $data['date']) . ')
';
+ unset($sparklineData);
+ foreach ($sightingsData as $aid => $data) {
+ $sightingsData[$aid]['html'] = '';
+ foreach ($data as $type => $typeData) {
+ $name = (($type != 'expiration') ? Inflector::pluralize($type) : $type);
+ $sightingsData[$aid]['html'] .= '' . ucfirst(h($name)) . '
';
+ foreach ($typeData['orgs'] as $org => $orgData) {
+ $extra = (($org == $me['Organisation']['name']) ? " class= 'bold'" : "");
+ if ($type == 'expiration') {
+ $sightingsData[$aid]['html'] .= '' . h($org) . ': ' . date('Y-m-d H:i:s', $orgData['date']) . '
';
+ } else {
+ $sightingsData[$aid]['html'] .= '' . h($org) . ': ' . h($orgData['count']) . ' (' . date('Y-m-d H:i:s', $orgData['date']) . ')
';
+ }
}
+ $sightingsData[$aid]['html'] .= '
';
}
}
}
@@ -128,11 +148,14 @@
+ Yes + | ++ | ++ No + | +
Date | +Organisation | +Type | +Source | +Event ID | +Attribute ID | +Actions | +
---|---|---|---|---|---|---|
+ | + Html->image('orgs/' . h($item['Organisation']['name']) . '.png', array('alt' => h($item['Organisation']['name']), 'title' => h($item['Organisation']['name']), 'style' => 'width:24px; height:24px')); + else: + echo h($item['Organisation']['name']); + endif; + + ?> + | ++ + | ++ | + | + | + + + + | +
Remove sighting ()?
++ Yes + | ++ | ++ No + | +
Regex entries (in the standard php regex /{regex}/{modifier} format) entered below will restrict matching attributes from being included in the IDS flag sensitive exports (such as NIDS exports).
+Regex entries (in the standard php regex /{regex}/{modifier} format) entered below will restrict matching attributes from being included in the IDS flag sensitive exports (such as NIDS exports).