Merge branch '2.4' into issues_1643

pull/1809/head
devnull- 2017-02-20 09:23:07 +01:00 committed by GitHub
commit f35d247930
48 changed files with 1240 additions and 313 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
@ -475,12 +491,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,
@ -496,6 +512,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`),
@ -503,7 +520,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;
-- --------------------------------------------------------
@ -601,15 +620,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;
-- --------------------------------------------------------
@ -624,6 +649,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`)
@ -924,7 +950,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),

View File

@ -1,9 +0,0 @@
/var/www/MISP/app/tmp/logs/resque-*-error.log {
rotate 30
dateext
missingok
notifempty
compress
weekly
copytruncate
}

View File

@ -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 };

View File

@ -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

View File

@ -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

2
PyMISP

@ -1 +1 @@
Subproject commit 26a8f4c66230c0df10b2f9637e53ee1542a26f40
Subproject commit a81f6b5c15e2effbc0b6118f1e1524b0950a576c

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":65}
{"major":2, "minor":4, "hotfix":66}

View File

@ -69,19 +69,25 @@ $config = array(
// Uncomment the following to enable client SSL certificate authentication
/*
'CertAuth' =>
array(
'ca' => array('FIRST.Org'), // allowed CAs
'caId' => 'O', // which attribute will be used to verify the CA
'userModel' => 'User', // name of the User class to check if user exists
'userModelKey' => 'nids_sid', // User field that will be used for querying
'map' => array( // maps client certificate attributes to User properties
array(
// CA
'ca' => array('FIRST.Org'), // List of CAs authorized
'caId' => 'O', // Certificate field used to verify the CA. In this example, the field O (organization) of the client certificate has to equal to 'FIRST.Org' in order to validate the CA
// User/client configuration
'userModel' => 'User', // name of the User class (MISP class) to check if the user exists
'userModelKey' => 'email', // User field that will be used for querying. In this example, the field email of the MISP accounts will be used to search if the user exists.
'map' => array( // maps client certificate attributes to User properties. This map will be used as conditions to find if the user exists. In this example, the client certificate fields 'O' (organization) and 'emailAddress' have to match with the MISP fields 'org' and 'email' to validate the user.
'O' => 'org',
'emailAddress' => 'email',
),
),
// Synchronization/RestAPI
'syncUser' => true, // should the User be synchronized with an external REST API
'userDefaults' => array( // default user attributes, only used when creating new users
'role_id' => 4,
),
'userDefaults' => array( // default user attributes, only used when creating new users. By default, new users are "Read only" users (role_id: 6).
'role_id' => 6,
),
'restApi' => array( // API parameters
'url' => 'https://example.com/data/users', // URL to query
'headers' => array(), // additional headers, used for authentication
@ -93,7 +99,7 @@ $config = array(
'pgp_public' => 'gpgkey',
),
),
'userDefaults' => array('role_id' => 3), // default attributes for new users
'userDefaults' => array('role_id' => 6), // default attributes for new users. By default, new users are "Read only" users (role_id: 6).
),
*/
/*

View File

@ -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

View File

@ -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'),

View File

@ -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');

View File

@ -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');
}
}

View File

@ -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'])) {

View File

@ -1049,7 +1049,7 @@ class UsersController extends AppController {
// shows some statistics about the instance
public function statistics($page = 'data') {
$this->set('page', $page);
$this->set('pages', array('data' => 'Usage data', 'orgs' => 'Organisations', 'tags' => 'Tags', 'attributehistogram' => 'Attribute histogram'));
$this->set('pages', array('data' => 'Usage data', 'orgs' => 'Organisations', 'tags' => 'Tags', 'attributehistogram' => 'Attribute histogram', 'sightings' => 'Sightings toplists'));
$result = array();
if ($page == 'data') {
$result = $this->__statisticsData($this->params['named']);
@ -1063,6 +1063,8 @@ class UsersController extends AppController {
} else {
$this->render('statistics_histogram');
}
} else if ($page == 'sightings') {
$result = $this->__statisticsSightings($this->params['named']);
}
if ($this->_isRest()) {
return $result;
@ -1123,6 +1125,56 @@ class UsersController extends AppController {
}
}
private function __statisticsSightings($params = array()) {
$this->loadModel('Sighting');
$conditions = array('Sighting.org_id' => $this->Auth->user('org_id'));
if (isset($params['timestamp'])) {
$conditions['Sighting.date_sighting >'] = $params['timestamp'];
}
$sightings = $this->Sighting->find('all', array(
'conditions' => $conditions,
'fields' => array('Sighting.date_sighting', 'Sighting.type', 'Sighting.source', 'Sighting.event_id')
));
$data = array();
$toplist = array();
$eventids = array();
foreach ($sightings as $k => $v) {
if ($v['Sighting']['source'] == '') {
$v['Sighting']['source'] = 'Undefined';
}
$v['Sighting']['type'] = array('sighting', 'false-positive', 'expiration')[$v['Sighting']['type']];
if (isset($data[$v['Sighting']['source']][$v['Sighting']['type']])) {
$data[$v['Sighting']['source']][$v['Sighting']['type']]++;
} else {
$data[$v['Sighting']['source']][$v['Sighting']['type']] = 1;
}
if (!isset($toplist[$v['Sighting']['source']])) {
$toplist[$v['Sighting']['source']] = 1;
} else {
$toplist[$v['Sighting']['source']]++;
}
if (!isset($eventids[$v['Sighting']['source']][$v['Sighting']['type']])) {
$eventids[$v['Sighting']['source']][$v['Sighting']['type']] = array();
}
if (!in_array($v['Sighting']['event_id'], $eventids[$v['Sighting']['source']][$v['Sighting']['type']])) {
$eventids[$v['Sighting']['source']][$v['Sighting']['type']][] = $v['Sighting']['event_id'];
}
}
arsort($toplist);
if ($this->_isRest()) {
$data = array(
'toplist' => $toplist,
'eventids' => $eventids
);
return $this->RestResponse->viewData($data, $this->response->type());
} else {
$this->set('eventids', $eventids);
$this->set('toplist', $toplist);
$this->set('data', $data);
$this->render('statistics_sightings');
}
}
private function __statisticsOrgs($params = array()) {
$this->loadModel('Organisation');
$conditions = array();

View File

@ -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) {

View File

@ -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,12 @@ class AppModel extends Model {
case '2.4.55':
$this->updateDatabase('addSightings');
break;
case '2.4.66':
$this->updateDatabase('2.4.66');
$this->cleanCacheFiles();
$this->Sighting = Classregistry::init('Sighting');
$this->Sighting->addUuids();
break;
default:
$this->updateDatabase($command);
break;
@ -557,8 +563,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";';
@ -601,6 +605,18 @@ class AppModel extends Model {
$sqlArray[] = 'ALTER TABLE feeds CHANGE `sharing_group_id` `sharing_group_id` int(11) NOT NULL DEFAULT 0;';
$sqlArray[] = 'ALTER TABLE attributes CHANGE `comment` `comment` text COLLATE utf8_bin;';
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;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
@ -649,11 +665,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();

View File

@ -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) {

View File

@ -924,14 +924,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'])) {
@ -973,16 +967,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']);
@ -1480,10 +1464,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;
}
@ -2461,8 +2441,7 @@ class Event extends AppModel {
'fields' => array('id', 'url', 'name')
)
),
),
'AttributeTag' => array('Tag')
)
),
'EventTag' => array('Tag'),
'Org' => array('fields' => array('id', 'uuid', 'name', 'local')),

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

View File

@ -187,7 +187,10 @@ class CertificateAuthenticate extends BaseAuthenticate
CakeLog::write('alert', 'Could not insert model at database from RestAPI data.');
}
unset($org);
}
} else {
// No match -- User doesn't exist !!!
self::$user = false;
}
unset($U, $User, $q, $k);
}
unset($cn);

View File

@ -4,7 +4,9 @@ This plugin enables CakePHP applications to use client SSL certificates to state
Basically it loads the `SSL_CLIENT_*` variables, parses and maps the certificate information to the user. So you first need a server that checks client certificates and forwards that information to the PHP `$_SERVER` environment.
## Usage
## Configuration
1. Enable the plugin
Enable the plugin at bootstrap.php:
@ -12,34 +14,59 @@ Enable the plugin at bootstrap.php:
CakePlugin::load('CertAuth');
```
And configure it:
2. Configure
* Uncomment the line "'auth'=>array('CertAuth.Certificate')," in Config.php, section "Security"
```php
Configure::write('CertAuth',
array(
'ca' => array( 'FIRST.Org' ), // allowed CAs
'caId' => 'O', // which attribute will be used to verify the CA
'userModel' => 'User', // name of the User class to check if user exists
'userModelKey' => 'nids_sid', // User field that will be used for querying
'map' => array( // maps client certificate attributes to User properties
'O' => 'org',
'emailAddress'=>'email',
),
'syncUser' => true, // should the User be synchronized with an external REST API
'restApi' => array( // API parameters
'url' => 'https://example.com/data/users', // URL to query
'headers' => array(), // additional headers, used for authentication
'param' => array( 'email' => 'email'), // query parameters to add to the URL, mapped to USer properties
'map' => array( // maps REST result to the User properties
'uid' => 'id',
'name' => 'name',
'company' => 'org',
'email' => 'email',
),
),
'userDefaults' => array ( 'role_id' => 3 ), // default attributes for new users
)
);
....
'Security' =>
array(
'level' => 'medium',
'salt' => '',
'cipherSeed' => '',
'auth'=>array('CertAuth.Certificate'), // additional authentication methods
//'auth'=>array('ShibbAuth.ApacheShibb'dd),
),
.....
```
* Uncomment the following lines in Config.php, section "CertAuth" and configure them.
```php
'CertAuth' =>
array(
// CA
'ca' => array('FIRST.Org'), // List of CAs authorized
'caId' => 'O', // Certificate field used to verify the CA. In this example, the field O (organization) of the client certificate has to equal to 'FIRST.Org' in order to validate the CA
// User/client configuration
'userModel' => 'User', // name of the User class (MISP class) to check if the user exists
'userModelKey' => 'email', // User field that will be used for querying. In this example, the field email of the MISP accounts will be used to search if the user exists.
'map' => array( // maps client certificate attributes to User properties. This map will be used as conditions to find if the user exists. In this example, the client certificate fields 'O' (organization) and 'emailAddress' have to match with the MISP fields 'org' and 'email' to validate the user.
'O' => 'org',
'emailAddress' => 'email',
),
// Synchronization/RestAPI
'syncUser' => true, // should the User be synchronized with an external REST API
'userDefaults' => array( // default user attributes, only used when creating new users. By default, new users are "Read only" users (role_id: 6).
'role_id' => 6,
),
'restApi' => array( // API parameters
'url' => 'https://example.com/data/users', // URL to query
'headers' => array(), // additional headers, used for authentication
'param' => array('email' => 'email'), // query parameters to add to the URL, mapped to User properties
'map' => array( // maps REST result to the User properties
'uid' => 'nids_sid',
'team' => 'org',
'email' => 'email',
'pgp_public' => 'gpgkey',
),
),
'userDefaults' => array('role_id' => 6), // default attributes for new users. By default, new users are "Read only" users (role_id: 6).
),
```
If you set *syncUser* to *true* and *restApi.url* to *null*, new users will be created with the defaults defined by *userDefaults* without the need for a REST server.

View File

@ -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] .= '<span class=\'bold\'>' . h($org) . '</span>: <span class=\'green bold\'>' . h($data['count']) . ' (' . date('Y-m-d H:i:s', $data['date']) . ')</span><br />';
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'] .= '<span class=\'blue bold\'>' . ucfirst(h($name)) . '</span><br />';
foreach ($typeData['orgs'] as $org => $orgData) {
$extra = (($org == $me['Organisation']['name']) ? " class= 'bold'" : "");
if ($type == 'expiration') {
$sightingsData[$aid]['html'] .= '<span ' . $extra . '>' . h($org) . '</span>: <span class=\'orange bold\'>' . date('Y-m-d H:i:s', $orgData['date']) . '</span><br />';
} else {
$sightingsData[$aid]['html'] .= '<span ' . $extra . '>' . h($org) . '</span>: <span class=\'' . (($type == 'sighting') ? 'green' : 'red') . ' bold\'>' . h($orgData['count']) . ' (' . date('Y-m-d H:i:s', $orgData['date']) . ')</span><br />';
}
}
$sightingsData[$aid]['html'] .= '<br />';
}
}
}
@ -128,11 +148,14 @@
<div id="attributeList" class="attributeListContainer">
<div class="tabMenu tabMenuEditBlock noPrint">
<span id="create-button" title="Add attribute" class="icon-plus useCursorPointer" onClick="clickCreateButton(<?php echo $event['Event']['id']; ?>, '<?php echo $possibleAction; ?>');"></span>
<span id="multi-edit-button" title="Edit selected Attributes" class="icon-edit mass-select useCursorPointer" onClick="editSelectedAttributes(<?php echo $event['Event']['id']; ?>);"></span>
<span id="multi-tag-button" title="Tag selected Attributes" class="icon-tag mass-select useCursorPointer" onClick="getPopup('selected/true', 'tags', 'selectTaxonomy');"></span>
<span id="multi-delete-button" title="Delete selected Attributes" class = "icon-trash mass-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'deleteAttributes');"></span>
<span id="multi-accept-button" title="Accept selected Proposals" class="icon-ok mass-proposal-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'acceptProposals');"></span>
<span id="multi-discard-button" title="Discard selected Proposals" class = "icon-remove mass-proposal-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'discardProposals');"></span>
<span id="multi-edit-button" title="Edit selected Attributes" class="hidden icon-edit mass-select useCursorPointer" onClick="editSelectedAttributes(<?php echo $event['Event']['id']; ?>);"></span>
<span id="multi-tag-button" title="Tag selected Attributes" class="hidden icon-tag mass-select useCursorPointer" onClick="getPopup('selected/true', 'tags', 'selectTaxonomy');"></span>
<span id="multi-delete-button" title="Delete selected Attributes" class="hidden icon-trash mass-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'deleteAttributes');"></span>
<span id="multi-accept-button" title="Accept selected Proposals" class="hidden icon-ok mass-proposal-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'acceptProposals');"></span>
<span id="multi-discard-button" title="Discard selected Proposals" class="hidden icon-remove mass-proposal-select useCursorPointer" onClick="multiSelectAction(<?php echo $event['Event']['id']; ?>, 'discardProposals');"></span>
<?php if (Configure::read('Plugin.Sightings_enable')): ?>
<span id="multi-sighting-button" title="Sightings display for selected attributes" class="hidden icon-wrench mass-select useCursorPointer sightings_advanced_add" data-object-id="selected"></span>
<?php endif; ?>
</div>
<div class="tabMenu tabMenuToolsBlock noPrint">
<?php if ($mayModify): ?>
@ -184,6 +207,7 @@
<th title="<?php echo $attrDescriptions['distribution']['desc'];?>"><?php echo $this->Paginator->sort('distribution');?></th>
<?php if (Configure::read('Plugin.Sightings_enable') !== false): ?>
<th>Sightings</th>
<th>Activity</th>
<?php endif; ?>
<th class="actions">Actions</th>
</tr>
@ -279,7 +303,7 @@
<?php echo h($object['type']); ?>
</div>
</td>
<td id="<?php echo h($currentType) . '_' . h($object['id']) . '_container'; ?>" class="showspaces <?php echo $extra; ?> limitedWidth">
<td id="<?php echo h($currentType) . '_' . h($object['id']) . '_container'; ?>" class="showspaces <?php echo $extra; ?> limitedWidth shortish">
<div id = "<?php echo $currentType . '_' . $object['id'] . '_value_placeholder'; ?>" class = "inline-field-placeholder"></div>
<?php
if ('attachment' !== $object['type'] && 'malware-sample' !== $object['type']) $editable = ' ondblclick="activateField(\'' . $currentType . '\', \'' . $object['id'] . '\', \'value\', \'' . $event['Event']['id'] . '\');"';
@ -311,7 +335,13 @@
} else if (strpos($object['type'], '|') !== false) {
$filenameHash = explode('|', $object['value']);
echo h($filenameHash[0]);
if (isset($filenameHash[1])) echo '<br />' . $filenameHash[1];
if (isset($filenameHash[1])) {
$separator = '<br />';
if (in_array($object['type'], array('ip-dst|port', 'ip-src|port'))) {
$separator = ':';
}
echo $separator . h($filenameHash[1]);
}
} else if ('vulnerability' == $object['type']) {
if (! is_null(Configure::read('MISP.cveurl'))) {
$cveUrl = Configure::read('MISP.cveurl');
@ -452,25 +482,48 @@
endif;
if (Configure::read('Plugin.Sightings_enable') !== false):
?>
<td class="short <?php echo $extra;?>">
<td class="shortish <?php echo $extra;?>">
<span id="sightingForm_<?php echo h($object['id']);?>">
<?php
if ($object['objectType'] == 0):
echo $this->Form->create('Sighting', array('id' => 'Sighting_' . $object['id'], 'url' => '/sightings/add/' . $object['id'], 'style' => 'display:none;'));
echo $this->Form->input('type', array('label' => false, 'id' => 'Sighting_' . $object['id'] . '_type'));
echo $this->Form->end();
?>
</span>
<span class="icon-thumbs-up useCursorPointer" onClick="addSighting('<?php echo h($object['id']); ?>', '<?php echo h($event['Event']['id']);?>', '<?php echo h($page); ?>');">&nbsp;</span>
<span id="sightingCount_<?php echo h($object['id']); ?>" class="bold sightingsCounter_<?php echo h($object['id']); ?>" data-placement="top" data-toggle="popover" data-trigger="hover" data-content="<?php echo isset($attributeSightingsPopoverText[$object['id']]) ? $attributeSightingsPopoverText[$object['id']] : ''; ?>">
<?php echo (!empty($attributeSightings[$object['id']]) ? count($attributeSightings[$object['id']]) : 0); ?>
<?php
$temp = array();
if (isset($csv[$object['id']]['sighting'])) {
$temp[0] = $csv[$object['id']]['sighting'];
}
if (isset($csv[$object['id']]['false-positive'])) {
$temp[1] = $csv[$object['id']]['false-positive'];
}
?>
<span class="icon-thumbs-up useCursorPointer" onClick="addSighting('0', '<?php echo h($object['id']); ?>', '<?php echo h($event['Event']['id']);?>', '<?php echo h($page); ?>');">&nbsp;</span>
<span class="icon-thumbs-down useCursorPointer" onClick="addSighting('1', '<?php echo h($object['id']); ?>', '<?php echo h($event['Event']['id']);?>', '<?php echo h($page); ?>');">&nbsp;</span>
<span class="icon-wrench useCursorPointer sightings_advanced_add" data-object-id="<?php echo h($object['id']); ?>">&nbsp;</span>
<span id="sightingCount_<?php echo h($object['id']); ?>" class="bold sightingsCounter_<?php echo h($object['id']); ?>" data-placement="top" data-toggle="popover" data-trigger="hover" data-content="<?php echo isset($sightingsData[$object['id']]['html']) ? $sightingsData[$object['id']]['html'] : ''; ?>">
<?php
$s = (!empty($sightingsData[$object['id']]['sighting']['count']) ? $sightingsData[$object['id']]['sighting']['count'] : 0);
$f = (!empty($sightingsData[$object['id']]['false-positive']['count']) ? $sightingsData[$object['id']]['false-positive']['count'] : 0);
$e = (!empty($sightingsData[$object['id']]['expiration']['count']) ? $sightingsData[$object['id']]['expiration']['count'] : 0);
?>
</span>
<span id="ownSightingCount_<?php echo h($object['id']); ?>" class="bold green sightingsCounter_<?php echo h($object['id']); ?>" data-placement="top" data-toggle="popover" data-trigger="hover" data-content="<?php echo isset($attributeSightingsPopoverText[$object['id']]) ? $attributeSightingsPopoverText[$object['id']] : ''; ?>">
<?php echo '(' . (isset($attributeOwnSightings[$object['id']]['count']) ? $attributeOwnSightings[$object['id']]['count'] : 0) . ')'; ?>
<span id="ownSightingCount_<?php echo h($object['id']); ?>" class="bold sightingsCounter_<?php echo h($object['id']); ?>" data-placement="top" data-toggle="popover" data-trigger="hover" data-content="<?php echo isset($sightingsData[$object['id']]['html']) ? $sightingsData[$object['id']]['html'] : ''; ?>">
<?php echo '(<span class="green">' . h($s) . '</span>/<span class="red">' . h($f) . '</span>/<span class="orange">' . h($e) . '</span>)'; ?>
</span>
<?php
endif;
?>
</td>
<td class="short <?php echo $extra; ?>">
<?php
if ($object['objectType'] == 0) {
echo $this->element('sparkline', array('id' => $object['id'], 'csv' => $temp));
}
?>
</td>
<?php
endif;
?>
@ -588,8 +641,6 @@ attributes or the appropriate distribution level. If you think there is a mistak
popoverStartup();
$('.select_attribute').removeAttr('checked');
$('.select_proposal').removeAttr('checked');
$('.mass-select').hide();
$('.mass-proposal-select').hide();
$('.select_attribute').click(function(e) {
if ($(this).is(':checked')) {
if (e.shiftKey) {
@ -620,6 +671,20 @@ attributes or the appropriate distribution level. If you think there is a mistak
$('.screenshot').click(function() {
screenshotPopup($(this).attr('src'), $(this).attr('title'));
});
$('.sightings_advanced_add').click(function() {
var selected = [];
var object_id = $(this).data('object-id');
if (object_id == 'selected') {
$(".select_attribute").each(function() {
if ($(this).is(":checked")) {
selected.push($(this).data("id"));
}
});
object_id = selected.join('|');
}
url = "<?php echo $baseurl; ?>" + "/sightings/advanced/" + object_id;
genericPopup(url, '#screenshot_box');
});
});
</script>
<?php

View File

@ -0,0 +1,48 @@
<!--
Modified version of http://www.tnoda.com/blog/2013-12-19
-->
<?php
echo $this->Html->script('d3');
//echo $this->Html->css('sightingstyle');
?>
<div id="spark_<?php echo h($id); ?>"></div>
<script>
var width = 100;
var height = 25;
var x = d3.scale.linear().range([0, width - 2]);
var y = d3.scale.linear().range([height - 4, 0]);
var parseDate = d3.time.format("%Y%m%d").parse;
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
function sparkline(elemId, data) {
data.forEach(function(d) {
d.date = parseDate(d.Date);
d.close = +d.Close;
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
var svg = d3.select(elemId)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(0, 2)');
svg.append('path')
.datum(data)
.attr('class', 'sparkline')
.attr('d', line);
svg.append('circle')
.attr('class', 'sparkcircle')
.attr('cx', x(data[data.length - 1].date))
.attr('cy', y(data[data.length - 1].close))
.attr('r', 2);
}
var myData = "<?php echo isset($csv[0]) ? $csv[0] : ''; ?>";
if (myData != '') {
var data = d3.csv.parse(myData);
sparkline('#spark_<?php echo h($id); ?>', data);
}
</script>

View File

@ -67,21 +67,21 @@ echo $this->Form->end();
foreach ($formInfoTypes as $formInfoType => $humanisedName) {
echo 'var ' . $formInfoType . 'FormInfoValues = {' . PHP_EOL;
foreach ($info[$formInfoType] as $key => $formInfoData) {
echo '"' . $key . '": "<span class=\"blue bold\">' . h($formInfoData['key']) . '</span>: ' . h($formInfoData['desc']) . '<br />",' . PHP_EOL;
echo '"' . $key . '": "<span class=\"blue bold\">' . h($formInfoData['key']) . '</span>: ' . h($formInfoData['desc']) . '<br />",' . PHP_EOL;
}
echo '}' . PHP_EOL;
}
?>
$('#EventDistribution').change(function() {
if ($('#EventDistribution').val() == 4) $('#SGContainer').show();
else $('#SGContainer').hide();
});
$("#EventDistribution, #EventAnalysis, #EventThreatLevelId").change(function() {
initPopoverContent('Event');
});
$(document).ready(function() {
if ($('#EventDistribution').val() == 4) $('#SGContainer').show();
else $('#SGContainer').hide();

View File

@ -0,0 +1,30 @@
<div class="confirmation">
<?php
echo $this->Form->create($model, array('style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => $url));
echo $this->Form->input($varName, array(
'type' => 'text',
'value' => 'test',
'style' => 'display:none;',
'label' => false,
));
?>
<legend><?php echo h(Inflector::humanize($action)); ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<p><?php echo h($message); ?></p>
<table>
<tr>
<td style="vertical-align:top">
<span id="PromptYesButton" class="btn btn-primary" onClick="multiSelectAction('<?php echo h($id); ?>', '<?php echo h($action); ?>');">Yes</span>
</td>
<td style="width:540px;">
</td>
<td style="vertical-align:top;">
<span class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt();">No</span>
</td>
</tr>
</table>
</div>
<?php
echo $this->Form->end();
?>
</div>

View File

@ -16,7 +16,7 @@
'class' => 'form-control span6'
));
echo $this->Form->input('input_source', array(
'label' => 'Input Source',
'label' => 'Source Format',
'div' => 'input clear',
'options' => array('network' => 'Network', 'local' => 'Local'),
'class' => 'form-control span6'

View File

@ -16,7 +16,7 @@
'class' => 'form-control span6'
));
echo $this->Form->input('input_source', array(
'label' => 'Input Source',
'label' => 'Source Format',
'div' => 'input clear',
'options' => array('network' => 'Network', 'local' => 'Local'),
'class' => 'form-control span6'
@ -189,9 +189,12 @@ var rules = {"pull": {"tags": {"OR":[], "NOT":[]}, "orgs": {"OR":[], "NOT":[]}}}
var validOptions = ['pull'];
var validFields = ['tags', 'orgs'];
var modelContext = 'Feed';
var tags = [];
var orgs = [];
$(document).ready(function() {
rules = convertServerFilterRules(rules);
serverRulePopulateTagPicklist();
feedDistributionChange();
$("#pull_modify").click(function() {
serverRuleFormActivate('pull');

View File

@ -34,8 +34,8 @@
</head>
<body>
<div id="popover_form" class="ajax_popover_form"></div>
<div id="confirmation_box" class="confirmation_box"></div>
<div id="screenshot_box" class="screenshot_box"></div>
<div id="confirmation_box" class="confirmation_box"></div>
<div id="gray_out" class="gray_out"></div>
<div id="container">
<?php echo $this->element('global_menu');

View File

@ -24,9 +24,6 @@ echo $this->Form->end();
echo $this->element('side_menu', array('menuList' => 'admin', 'menuItem' => 'addRole'));
$this->Js->get('#RolePermission')->event('change', 'deactivateActions()');
foreach ($permFlags as $k => $flag) {
if ($k == 'perm_audit' || $k == 'perm_auth') {
continue;
}
if ($k !== 'perm_site_admin') $this->Js->get('#' . $flag['id'])->event('change', 'checkPerms("' . $flag['id'] . '")');
else $this->Js->get('#RolePermSiteAdmin')->event('change', 'checkPerms("RolePermSiteAdmin");activateAll();');
}

View File

@ -24,9 +24,6 @@
echo $this->element('side_menu', array('menuList' => 'admin', 'menuItem' => 'editRole'));
$this->Js->get('#RolePermission')->event('change', 'deactivateActions()');
foreach ($permFlags as $k => $flag) {
if ($k == 'perm_audit' || $k == 'perm_auth') {
continue;
}
if ($k !== 'perm_site_admin') $this->Js->get('#' . $flag['id'])->event('change', 'checkPerms("' . $flag['id'] . '")');
else $this->Js->get('#RolePermSiteAdmin')->event('change', 'checkPerms("RolePermSiteAdmin");activateAll();');
}

View File

@ -0,0 +1,37 @@
<h3>Add Sighting</h3>
<div id="sightingsEventId" class="hidden" data-event-id="<?php echo h($event_id); ?>"></div>
<?php
echo $this->Form->create('Sighting', array('id', 'url' => '/sightings/add/' . urlencode(h($id)), 'style' => 'margin-bottom:0px;'));
echo $this->Form->input('type', array(
'options' => array('Sighting', 'Fase-positive', 'Expiration'),
'default' => 0,
'style' => 'width:230px;margin-right:0px;'
));
echo $this->Form->input('source', array(
'placeholder' => 'honeypot, IDS sensor id, SIEM,...',
'style' => 'width:447px;',
'div' => array('style' => 'width:457px !important;')
));
echo $this->Form->label('Sighting date');
echo $this->Form->input('date', array(
'type' => 'text',
'id' => 'datepicker',
'default' => date('Y-m-d'),
'style' => 'width:110px;',
'div' => array('style' => 'width:120px !important;'),
'label' => false
));
echo $this->Form->input('time', array(
'class' => 'input-mini',
'default' => date('H:i:s'),
'id' => 'timepicker',
'style' => 'width:120px;',
'div' => array('style' => 'width:120px !important;'),
'label' => false
));
?>
<span id="submitButton" class="btn btn-primary" onClick="submitPopoverForm('<?php echo h($id);?>', 'addSighting', '<?php echo h($event_id); ?>')">Add</span>
<div class="input clear"></div>
<?php
echo $this->Form->end();
?>

View File

@ -0,0 +1,59 @@
<div class="sightings_advanced">
<div class="popover-legend"><p><?php echo __('Sighting details'); ?></p></div>
<div style="margin:10px;">
<span id="sightingsGraphToggle" class="btn btn-primary qet toggle-left sightingsToggle" data-type="graph">Graph</span>
<span id="sightingsListAllToggle" class="btn btn-inverse qet toggle sightingsToggle" data-type="all">All</span>
<span id="sightingsListMyToggle" class="btn btn-inverse qet toggle sightingsToggle" data-type="org">My org</span>
<span id="sightingsAddToggle" class="btn btn-inverse qet toggle-right sightingsToggle" data-type="add">Add sighting</span>
</div>
<div id="mainContents" style="margin-top:40px;padding:10px;">
<div id="sightingsData" class="sightingTab"></div>
<span style="float:right;margin-bottom:10px;" class="btn btn-inverse" id="cancel">Cancel</span>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
id = "<?php echo h($id); ?>";
$('#cancel').click(function() {
cancelPopoverForm();
});
$('#datepicker').datepicker({
startDate: '-180d',
endDate: '+1d',
orientation: 'bottom',
autoclose: true,
format: 'yyyy-mm-dd'
});
$('#timepicker').timepicker({
minuteStep: 1,
showMeridian: false,
showSeconds: true,
maxHours: 24
});
loadSightingGraph(id, "attribute");
});
$('.sightingsToggle').click(function() {
$('.sightingsToggle').removeClass('btn-primary');
$('.sightingsToggle').addClass('btn-inverse');
$(this).removeClass('btn-inverse');
$(this).addClass('btn-primary');
var type = $(this).data('type');
$('.sightingTab').empty();
if (type == 'graph') {
loadSightingGraph(id, "attribute");
} else if (type == 'add') {
$.get( "/sightings/add/" + id, function(data) {
$("#sightingsData").html(data);
});
} else {
var org = "";
if (type == 'org') org = "/<?php echo h($me['org_id']);?>"
$.get( "/sightings/listSightings/" + id + "/attribute" + org, function(data) {
$("#sightingsData").html(data);
});
}
});
</script>
<?php echo $this->Js->writeBuffer(); // Write cached scripts

View File

@ -0,0 +1,51 @@
<div>
<div id="org_id" class="hidden"><?php echo h($org_id); ?></div>
<table class="table table-striped table-hover table-condensed" style="display:block; overflow-y:auto;max-height:500px;">
<tr>
<th>Date</th>
<th>Organisation</th>
<th>Type</th>
<th>Source</th>
<th>Event ID</th>
<th>Attribute ID</th>
<th class="actions">Actions</th>
</tr>
<?php
foreach ($sightings as $item):
?>
<tr>
<td class="short"><?php echo date('Y-m-d H:i:s', $item['Sighting']['date_sighting']);?></td>
<td class="short">
<?php
$imgAbsolutePath = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS . h($item['Organisation']['name']) . '.png';
if (file_exists($imgAbsolutePath)):
echo $this->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;
?>
</td>
<td class="short">
<?php
echo $types[$item['Sighting']['type']];
?>
</td>
<td class="short"><?php echo h($item['Sighting']['source']);?></td>
<td class="short"><?php echo h($item['Sighting']['event_id']);?></td>
<td class="short"><?php echo h($item['Sighting']['attribute_id']);?></td>
<td class="short action-links">
<?php
if ($isSiteAdmin || ($item['Sighting']['org_id'] == $me['org_id'] && $isAclAdd)):
?>
<span class="icon-trash useCursorPointer" onClick="quickDeleteSighting('<?php echo h($item['Sighting']['id']); ?>', '<?php echo h($rawId); ?>', '<?php echo h($context); ?>');"></span>
<?php
endif;
?>
</td>
</tr>
<?php
endforeach;
?>
</table>
</div>

View File

@ -0,0 +1,24 @@
<div class="confirmation">
<?php
echo $this->Form->create('Sighting', array('style' => 'margin:0px;', 'id' => 'PromptForm', 'url' => '/sightings/quickDelete/' . $id . '/' . urlencode($rawId) . '/' . $context));
?>
<legend>Remove Sighting</legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<p>Remove sighting (<?php echo h($id); ?>)?</p>
<table>
<tr>
<td style="vertical-align:top">
<span id="PromptYesButton" class="btn btn-primary" onClick="removeSighting('<?php echo h($id); ?>', '<?php echo h($rawId); ?>', '<?php echo h($context); ?>');">Yes</span>
</td>
<td style="width:540px;">
</td>
<td style="vertical-align:top;">
<span class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt(1);">No</span>
</td>
</tr>
</table>
</div>
<?php
echo $this->Form->end();
?>
</div>

View File

@ -0,0 +1,150 @@
<?php
echo $this->Html->script('d3');
echo $this->Html->css('sightingstyle');
?>
<div id="graphContent" class="graphContent"></div>
<script>
var myData = "<?php echo $tsv; ?>";
var colours = {
'Sighting': 'blue',
'False-positive': 'red'
}
var margin = {
top: 20,
right: 60,
bottom: 30,
left: 25
},
width = 980 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.count);
});
var svg = d3.select("#graphContent").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = d3.tsv.parse(myData);
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var sightings = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
count: +d[name]
};
})
};
});
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([
d3.min(sightings, function(c) {
return d3.min(c.values, function(v) {
return v.count;
});
}),
d3.max(sightings, function(c) {
return d3.max(c.values, function(v) {
return v.count;
});
})
]);
var legend = svg.selectAll('g')
.data(sightings)
.enter()
.append('g')
.attr('class', 'sightingsLegend');
legend.append('rect')
.attr('x', width - 20)
.attr('y', function(d, i) {
return i * 20;
})
.attr('width', 10)
.attr('height', 10)
.style('fill', function(d) {
return colours[d.name];
});
legend.append('text')
.attr('x', width - 8)
.attr('y', function(d, i) {
return (i * 20) + 9;
})
.text(function(d) {
return d.name;
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Count");
var sightings = svg.selectAll(".sightings")
.data(sightings)
.enter().append("g")
.attr("class", "sightings");
sightings.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return colours[d.name];
});
</script>

View File

@ -0,0 +1,71 @@
<div class = "index">
<h2>Statistics</h2>
<?php
echo $this->element('Users/statisticsMenu');
?>
<p>A toplist of the top sources for the sightings of your organisation.</p>
<table class="table table-striped table-hover table-condensed" style="display:block; overflow-y:auto;width:700px;">
<tr>
<th>Source</th>
<th>#Entries</th>
<th>#Sighting</th>
<th>#False-positive</th>
<th>Expiration</th>
</tr>
<?php
$count = 0;
foreach ($toplist as $source => $total):
if ($count > 9) break;
?>
<tr>
<td style="width:20%;"><?php echo empty($source) ? 'Undefined' : h($source);?></td>
<td style="width:20%;"><?php echo h($total);?></td>
<td style="width:20%;">
<?php
if (isset($data[$source]['sighting'])):
?>
<a href="<?php echo $baseurl; ?>/events/index/searcheventid:<?php echo h(implode('|', $eventids[$source]['sighting'])); ?>"><?php echo h($data[$source]['sighting']); ?></a>
<?php
else:
echo '0';
endif;
?>
</td>
<td style="width:20%;">
<?php
if (isset($data[$source]['false-positive'])):
?>
<a href="<?php echo $baseurl; ?>/events/index/searcheventid:<?php echo h(implode('|', $eventids[$source]['false-positive'])); ?>"><?php echo h($data[$source]['false-positive']); ?></a>
<?php
else:
echo '0';
endif;
?>
</td>
<td style="width:20%;">
<?php
if (isset($data[$source]['expiration'])):
?>
<a href="<?php echo $baseurl; ?>/events/index/searcheventid:<?php echo h(implode('|', $eventids[$source]['expiration'])); ?>"><?php echo h($data[$source]['expiration']); ?></a>
<?php
else:
echo '0';
endif;
?>
</td>
</tr>
<?php
$count++;
endforeach;
?>
</table>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));
?>
<script type="text/javascript">
$(document).ready(function () {
loadSightingsData();
});
</script>

View File

@ -1,6 +1,5 @@
<div class="whitelist index">
<h2>Signature Whitelist</h2>
<p>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).</p>
<h2>Import Whitelist</h2>
<div class="pagination">
<ul>
<?php

View File

@ -1,6 +1,5 @@
<div class="whitelist index">
<h2>Signature Whitelist</h2>
<p>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).</p>
<div class="pagination">
<ul>
<?php

@ -1 +1 @@
Subproject commit f3f5b3b3ac4a7e453a2a25adcb0342a0454c1155
Subproject commit 7db66e05dd22faee44d2c2e3d0b72903c4336e44

View File

@ -762,6 +762,20 @@ a.proposal_link_red:hover {
color:white;
}
.popover-legend {
border-radius: 5px 5px 0px 0px;
margin-bottom:5px;
padding-left:0px;
background-color:black;
color:white;
font-size: 21px;
line-height: 40px;
}
.popover-legend p {
padding-left:10px;
}
.legend {
display: block;
width: 100%;
@ -1672,6 +1686,21 @@ table.table.table-striped tr.deleted_row td {
transition: all 0.3s ease;
}
.sparkline {
fill: none;
stroke: #000;
stroke-width: 0.5px;
}
.sparkcircle {
fill: #f00;
stroke: none;
}
.sightings_advanced
{
width:1000px;
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(359deg);}

View File

@ -0,0 +1,20 @@
.graphContent {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}

View File

@ -14,6 +14,14 @@ function deleteObject(type, action, id, event) {
});
}
function quickDeleteSighting(id, rawId, context) {
url = "/sightings/quickDelete/" + id + "/" + rawId + "/" + context;
$.get(url, function(data) {
$("#confirmation_box").fadeIn();
$("#confirmation_box").html(data);
});
}
function publishPopup(id, type) {
var action = "alert";
if (type == "publish") action = "publish";
@ -37,6 +45,8 @@ function genericPopup(url, popupTarget) {
$.get(url, function(data) {
$(popupTarget).html(data);
$(popupTarget).fadeIn();
left = ($(window).width() / 2) - ($(popupTarget).width() / 2);
$(popupTarget).css({'left': left + 'px'});
$("#gray_out").fadeIn();
});
}
@ -64,9 +74,11 @@ function editTemplateElement(type, id) {
});
}
function cancelPrompt() {
$("#confirmation_box").fadeIn();
$("#gray_out").fadeOut();
function cancelPrompt(isolated) {
if (isolated == undefined) {
$("#gray_out").fadeOut();
}
$("#confirmation_box").fadeOut();
$("#confirmation_box").empty();
}
@ -94,6 +106,34 @@ function submitDeletion(context_id, action, type, id) {
});
}
function removeSighting(id, rawid, context) {
if (context != 'attribute') {
context = 'event';
}
var formData = $('#PromptForm').serialize();
$.ajax({
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
},
data: formData,
success:function (data, textStatus) {
handleGenericAjaxResponse(data);
},
complete:function() {
$(".loading").hide();
$("#confirmation_box").fadeOut();
var org = "/" + $('#org_id').text();
updateIndex(id, 'event');
$.get( "/sightings/listSightings/" + rawid + "/" + context + org, function(data) {
$("#sightingsData").html(data);
});
},
type:"post",
cache: false,
url:"/sightings/quickDelete/" + id + "/" + rawid + "/" + context,
});
}
function toggleSetting(e, setting, id) {
e.preventDefault();
e.stopPropagation();
@ -340,7 +380,8 @@ function postActivationScripts(name, type, id, field, event) {
$(name + '_solid').hide();
}
function addSighting(attribute_id, event_id, $page) {
function addSighting(type, attribute_id, event_id, page) {
$('#Sighting_' + attribute_id + '_type').val(type);
$.ajax({
data: $('#Sighting_' + attribute_id).closest("form").serialize(),
cache: false,
@ -527,13 +568,13 @@ function toggleAllTaxonomyCheckboxes() {
}
function attributeListAnyAttributeCheckBoxesChecked() {
if ($('.select_attribute:checked').length > 0) $('.mass-select').show();
else $('.mass-select').hide();
if ($('.select_attribute:checked').length > 0) $('.mass-select').removeClass('hidden');
else $('.mass-select').addClass('hidden');
}
function attributeListAnyProposalCheckBoxesChecked() {
if ($('.select_proposal:checked').length > 0) $('.mass-proposal-select').show();
else $('.mass-proposal-select').hide();
if ($('.select_proposal:checked').length > 0) $('.mass-proposal-select').removeClass('hidden');
else $('.mass-proposal-select').addClass('hidden');
}
function taxonomyListAnyCheckBoxesChecked() {
@ -731,6 +772,7 @@ function submitPopoverForm(context_id, referer, update_context_id) {
var url = null;
var context = 'event';
var contextNamingConvention = 'Attribute';
var closePopover = true;
switch (referer) {
case 'add':
url = "/attributes/add/" + context_id;
@ -777,20 +819,38 @@ function submitPopoverForm(context_id, referer, update_context_id) {
case 'replaceAttributes':
url = "/attributes/attributeReplace/" + context_id;
break;
case 'addSighting':
url = "/sightings/add/" + context_id;
closePopover = false;
break;
}
if (url !== null) {
$.ajax({
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
$("#gray_out").fadeOut();
$("#popover_form").fadeOut();
if (closePopover) {
$("#gray_out").fadeOut();
$("#popover_form").fadeOut();
}
},
data: $("#submitButton").closest("form").serialize(),
success:function (data, textStatus) {
var result = handleAjaxPopoverResponse(data, context_id, url, referer, context, contextNamingConvention);
if (closePopover) {
var result = handleAjaxPopoverResponse(data, context_id, url, referer, context, contextNamingConvention);
}
if (referer == 'addSighting') {
updateIndex(update_context_id, 'event');
$.get( "/sightings/listSightings/" + id + "/attribute", function(data) {
$("#sightingsData").html(data);
});
$('.sightingsToggle').removeClass('btn-primary');
$('.sightingsToggle').addClass('btn-inverse');
$('#sightingsListAllToggle').removeClass('btn-inverse');
$('#sightingsListAllToggle').addClass('btn-primary');
}
if (context == 'event' && (referer == 'add' || referer == 'massEdit' || referer == 'replaceAttributes')) eventUnpublish();
$(".loading").show();
$(".loading").hide();
},
type:"post",
url:url
@ -904,7 +964,10 @@ function showMessage(success, message, context) {
}
function cancelPopoverForm() {
$("#popover_form").empty();
$("#gray_out").fadeOut();
$("#popover_form").fadeOut();
$("#screenshot_box").fadeOut();
$("#confirmation_box").fadeOut();
$('#gray_out').fadeOut();
$('#popover_form').fadeOut();
}
@ -1107,6 +1170,7 @@ function getPopup(id, context, target, admin, popupType) {
$(".loading").show();
},
dataType:"html",
async: true,
cache: false,
success:function (data, textStatus) {
$(".loading").hide();
@ -1125,6 +1189,7 @@ function simplePopup(url) {
$(".loading").show();
},
dataType:"html",
async: true,
cache: false,
success:function (data, textStatus) {
$(".loading").hide();
@ -1925,7 +1990,6 @@ function simpleTabPageLast() {
if ($('#SharingGroupRoaming').is(":checked")) {
summaryservers = "any interconnected instances linked by an eligible organisation.";
} else {
console.log(servercounter);
if (servercounter == 0) {
summaryservers = "data marked with this sharing group will not be pushed.";
}
@ -2302,6 +2366,7 @@ function serverRuleUpdate() {
});
}
statusOptions.forEach(function(status) {
console.log();
if (rules[type][field][status].length > 0) {
$('#' + type + '_' + field + '_' + status).show();
var t = '';
@ -2761,6 +2826,28 @@ function loadTagTreemap() {
});
}
function loadSightingsData(timestamp) {
url = "/sightings/toplist";
if (timestamp != undefined) {
url = url + '/' + timestamp;
}
$.ajax({
async:true,
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
},
success:function (data, textStatus) {
$(".sightingsdiv").html(data);
},
complete:function() {
$(".loading").hide();
},
type:"get",
cache: false,
url: url,
});
}
function quickEditEvent(id, field) {
$.ajax({
async:true,
@ -2831,7 +2918,7 @@ function checkAndSetPublishedInfo() {
$(document).keyup(function(e){
if (e.keyCode === 27) {
$("#gray_out").fadeOut();
$("#gray_out").fadeOut();
$("#popover_form").fadeOut();
$("#screenshot_box").fadeOut();
$("#confirmation_box").fadeOut();
@ -2844,3 +2931,9 @@ function closeScreenshot() {
$("#screenshot_box").fadeOut();
$("#gray_out").fadeOut();
}
function loadSightingGraph(id, scope) {
$.get( "/sightings/viewSightings/" + id + "/" + scope, function(data) {
$("#sightingsData").html(data);
});
}