MISP/app/Controller/EventsController.php

1929 lines
76 KiB
PHP
Raw Normal View History

2013-01-25 08:51:20 +01:00
<?php
App::uses('AppController', 'Controller');
App::uses('Xml', 'Utility');
/**
* Events Controller
*
* @property Event $Event
*/
2013-01-25 08:51:20 +01:00
class EventsController extends AppController {
/**
* Components
*
* @var array
*/
2013-01-25 08:51:20 +01:00
public $components = array(
'Security',
'Email',
'RequestHandler',
'IOCExport',
'IOCImport',
'Cidr'
2013-01-25 08:51:20 +01:00
);
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
2013-01-25 08:51:20 +01:00
'order' => array(
'Event.timestamp' => 'DESC'
),
2013-01-25 08:51:20 +01:00
);
public $helpers = array('Js' => array('Jquery'));
2013-01-25 08:51:20 +01:00
public function beforeFilter() {
parent::beforeFilter();
// what pages are allowed for non-logged-in users
$this->Auth->allow('xml');
$this->Auth->allow('nids');
$this->Auth->allow('hids_md5');
$this->Auth->allow('hids_sha1');
$this->Auth->allow('text');
$this->Auth->allow('dot');
$this->Auth->allow('restSearch');
2013-01-25 08:51:20 +01:00
// TODO Audit, activate logable in a Controller
if (count($this->uses) && $this->{$this->modelClass}->Behaviors->attached('SysLogLogable')) {
$this->{$this->modelClass}->setUserData($this->activeUser);
}
// convert uuid to id if present in the url, and overwrite id field
if (isset($this->params->query['uuid'])) {
$params = array(
'conditions' => array('Event.uuid' => $this->params->query['uuid']),
'recursive' => 0,
'fields' => 'Event.id'
);
2013-01-25 08:51:20 +01:00
$result = $this->Event->find('first', $params);
if (isset($result['Event']) && isset($result['Event']['id'])) {
$id = $result['Event']['id'];
$this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now
}
}
// if not admin or own org, check private as well..
if (!$this->_isSiteAdmin()) {
$this->paginate = Set::merge($this->paginate,array(
'conditions' =>
array("OR" => array(
array('Event.org =' => $this->Auth->user('org')),
array('Event.distribution >' => 0),
))));
2013-01-25 08:51:20 +01:00
}
}
/**
* index method
*
* @return void
*/
2013-01-25 08:51:20 +01:00
public function index() {
// list the events
2013-06-10 17:24:41 +02:00
// TODO information exposure vulnerability - as we don't limit the filter depending on the CyDefSIG.showorg parameter
// this filter will work if showorg=false and users will be able to perform the filtering and see what events were posted by what org.
// same goes for orgc in all cases
//transform POST into GET
if($this->request->is("post")) {
$url = array('action'=>'index');
$filters = array();
if (isset($this->data['Event'])) {
$filters = $this->data['Event'];
}
//redirect user to the index page including the selected filters
$this->redirect(array_merge($url,$filters));
}
2013-06-17 17:15:04 +02:00
$this->Event->recursive = -1;
$this->Event->contain('User.email');
// check each of the passed arguments whether they're a filter (could also be a sort for example) and if yes, add it to the pagination conditions
foreach ($this->passedArgs as $k => $v) {
if (substr($k, 0, 6) === 'search') {
$searchTerm = substr($k, 6);
switch ($searchTerm) {
case 'published' :
if ($v == 2) continue 2;
else $this->paginate['conditions'][] = array('Event.' . substr($k, 6) . ' =' => $v);
break;
case 'Datefrom' :
if (!$v) continue 2;
$this->paginate['conditions'][] = array('Event.date >=' => $v);
break;
case 'Dateuntil' :
if (!$v) continue 2;
$this->paginate['conditions'][] = array('Event.date <=' => $v);
break;
case 'org' :
if (!$v) continue 2;
// if the first character is '!', search for NOT LIKE the rest of the string (excluding the '!' itself of course)
$pieces = explode('|', $v);
foreach ($pieces as $piece) {
if ($piece[0] == '!') {
$this->paginate['conditions']['AND'][] = array('Event.orgc' . ' NOT LIKE' => '%' . substr($piece, 1) . '%');
} else {
$this->paginate['conditions']['AND']['OR'][] = array('Event.orgc' . ' LIKE' => '%' . $piece . '%');
}
}
break;
case 'info' :
if (!$v) continue 2;
// if the first character is '!', search for NOT LIKE the rest of the string (excluding the '!' itself of course)
$pieces = explode('|', $v);
foreach ($pieces as $piece) {
if ($piece[0] == '!') {
$this->paginate['conditions']['AND'][] = array('Event.info' . ' NOT LIKE' => '%' . substr($piece, 1) . '%');
} else {
$this->paginate['conditions']['AND']['OR'][] = array('Event.info' . ' LIKE' => '%' . $piece . '%');
}
}
break;
break;
default:
if (!$v) continue 2;
$this->paginate['conditions'][] = array('Event.' . substr($k, 6) . ' LIKE' => '%' . $v . '%');
break;
}
}
}
$this->paginate = Set::merge($this->paginate, array('contain' => array(
'ThreatLevel' => array(
'fields' => array(
'ThreatLevel.name'))
),
));
$this->set('events', $this->paginate());
2013-01-25 08:51:20 +01:00
if (!$this->Auth->user('gpgkey')) {
$this->Session->setFlash(__('No GPG key set in your profile. To receive emails, submit your public key in your profile.'));
}
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
$this->set('distributionLevels', $this->Event->distributionLevels);
2013-01-25 08:51:20 +01:00
}
/**
* view method
*
* @param int $id
* @return void
* @throws NotFoundException
*/
2013-09-05 10:40:53 +02:00
public function view($id = null, $continue=false, $fromEvent=null) {
// If the length of the id provided is 36 then it is most likely a Uuid - find the id of the event, change $id to it and proceed to read the event as if the ID was entered.
$perm_publish = $this->userRole['perm_publish'];
if (strlen($id) == 36) {
$this->Event->recursive = -1;
$temp = $this->Event->findByUuid($id);
if ($temp == null) throw new NotFoundException(__('Invalid event'));
$id = $temp['Event']['id'];
}
$isSiteAdmin = $this->_isSiteAdmin();
$this->Event->id = $id;
if(!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event.'));
2013-01-25 08:51:20 +01:00
}
$results = $this->__fetchEvent($id);
if ($this->_isRest()) {
$this->loadModel('Attribute');
foreach ($results[0]['Attribute'] as &$attribute) {
if ($this->Attribute->typeIsAttachment($attribute['type'])) {
$encodedFile = $this->Attribute->base64EncodeAttachment($attribute);
$attribute['data'] = $encodedFile;
}
}
}
2013-09-02 11:36:27 +02:00
// This happens if the user doesn't have permission to view the event.
// TODO change this to NotFoundException to keep it in line with the other invalid event messages, but will have to check if it impacts the sync before doing that
if (!isset($results[0])) {
$this->Session->setFlash(__('Invalid event.'));
$this->redirect(array('controller' => 'events', 'action' => 'index'));
}
// We'll only have one event in the array since we specified an id. The array returned only has several elements in the xml exports
$result = $results[0];
$this->loadModel('Attribute');
$this->set('authkey', $this->Auth->user('authkey'));
$this->set('baseurl', Configure::read('CyDefSIG.baseurl'));
$this->set('relatedAttributes', $result['RelatedAttribute']);
// passing decriptions for model fields
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
$this->set('attrDescriptions', $this->Attribute->fieldDescriptions);
$this->set('event', $result);
if(isset($result['ShadowAttribute'])) {
$this->set('remaining', $result['ShadowAttribute']);
}
$this->set('relatedEvents', $result['RelatedEvent']);
$this->set('categories', $this->Attribute->validate['category']['rule'][1]);
// passing type and category definitions (explanations)
$this->set('typeDefinitions', $this->Attribute->typeDefinitions);
$this->set('categoryDefinitions', $this->Attribute->categoryDefinitions);
// combobox for analysis
$this->set('distributionDescriptions', $this->Event->distributionDescriptions);
$this->set('distributionLevels', $this->Event->distributionLevels);
// combobox for analysis
$analysiss = $this->Event->validate['analysis']['rule'][1];
$analysiss = $this->_arrayToValuesIndexArray($analysiss);
$this->set('analysiss', $analysiss);
// tooltip for analysis
$this->set('analysisDescriptions', $this->Event->analysisDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
if (!$this->_isRest()) {
$this->helpers[] = 'Pivot';
if ($continue) {
$data = $this->__continuePivoting($result['Event']['id'], $result['Event']['info'], $result['Event']['date'], $fromEvent);
} else {
$data = $this->__startPivoting($result['Event']['id'], $result['Event']['info'], $result['Event']['date']);
}
$this->set('allPivots', $this->Session->read('pivot_thread'));
$pivot = $this->Session->read('pivot_thread');
$this->__arrangePivotVertical($pivot);
$this->__setDeletable($pivot, $id, true);
$this->set('pivot', $pivot);
$this->set('currentEvent', $id);
$this->set('allPivots', $this->Session->read('pivot_thread'));
// Show the discussion
$this->loadModel('Thread');
$params = array('conditions' => array('event_id' => $id),
'recursive' => -1,
'fields' => array('id', 'event_id', 'distribution', 'title')
);
$thread = $this->Thread->find('first', $params);
if (empty($thread)) {
$newThread = array(
'date_created' => date('Y/m/d H:i:s'),
'date_modified' => date('Y/m/d H:i:s'),
'user_id' => $this->Auth->user('id'),
'event_id' => $id,
'title' => 'Discussion about Event #' . $result['Event']['id'] . ' (' . $result['Event']['info'] . ')',
'distribution' => $result['Event']['distribution'],
'post_count' => 0,
'org' => $result['Event']['orgc']
);
$this->Thread->save($newThread);
$thread = ($this->Thread->read());
} else {
if ($thread['Thread']['distribution'] != $result['Event']['distribution']) {
$this->Thread->saveField('distribution', $result['Event']['distribution']);
}
}
$this->loadModel('Post');
$this->paginate['Post'] = array(
'limit' => 5,
'conditions' => array('Post.thread_id' => $thread['Thread']['id']),
'contain' => 'User'
);
$posts = $this->paginate('Post');
// Show the discussion
$this->set('posts', $posts);
$this->set('thread_id', $thread['Thread']['id']);
$this->set('myuserid', $this->Auth->user('id'));
$this->set('thread_title', $thread['Thread']['title']);
if ($this->request->is('ajax')) {
$this->disableCache();
$this->layout = 'ajax';
$this->render('/Elements/eventdiscussion');
}
$pivot = $this->Session->read('pivot_thread');
$this->__arrangePivotVertical($pivot);
$this->__setDeletable($pivot, $id, true);
$this->set('pivot', $pivot);
}
$this->set('currentEvent', $id);
}
private function __startPivoting($id, $info, $date){
$this->Session->write('pivot_thread', null);
$initial_pivot = array('id' => $id, 'info' => $info, 'date' => $date, 'depth' => 0, 'height' => 0, 'children' => array(), 'deletable' => true);
$this->Session->write('pivot_thread', $initial_pivot);
2013-01-25 08:51:20 +01:00
}
2013-09-05 10:40:53 +02:00
private function __continuePivoting($id, $info, $date, $fromEvent){
$pivot = $this->Session->read('pivot_thread');
$newPivot = array('id' => $id, 'info' => $info, 'date' => $date, 'depth' => null, 'children' => array(), 'deletable' => true);
2013-09-05 10:40:53 +02:00
if (!$this->__checkForPivot($pivot, $id)) {
$pivot = $this->__insertPivot($pivot, $fromEvent, $newPivot, 0);
}
$this->Session->write('pivot_thread', $pivot);
}
2013-09-05 10:40:53 +02:00
private function __insertPivot($pivot, $oldId, $newPivot, $depth) {
$depth++;
if ($pivot['id'] == $oldId) {
$newPivot['depth'] = $depth;
$pivot['children'][] = $newPivot;
return $pivot;
}
foreach($pivot['children'] as $k => $v) {
$pivot['children'][$k] = $this->__insertPivot($v, $oldId, $newPivot, $depth);
}
return $pivot;
}
2013-09-05 10:40:53 +02:00
private function __checkForPivot($pivot, $id) {
if ($id == $pivot['id']) return true;
foreach ($pivot['children'] as $k => $v) {
if ($this->__checkForPivot($v, $id)) {
return true;
}
}
return false;
2013-09-05 10:40:53 +02:00
}
private function __arrangePivotVertical(&$pivot) {
if (empty($pivot)) return null;
$max = count($pivot['children']) - 1;
if ($max < 0) $max = 0;
$temp = 0;
$pivot['children'] = array_values($pivot['children']);
foreach ($pivot['children'] as $k => $v) {
$pivot['children'][$k]['height'] = ($temp+$k)*50;
$temp += $this->__arrangePivotVertical($pivot['children'][$k]);
2013-09-10 10:48:29 +02:00
if ($k == $max) $temp = $pivot['children'][$k]['height'] / 50;
}
2013-09-10 10:48:29 +02:00
return $temp;
}
public function removePivot($id, $eventId, $self = false) {
$pivot = $this->Session->read('pivot_thread');
if ($pivot['id'] == $id) {
$pivot = null;
$this->Session->write('pivot_thread', null);
$this->redirect(array('controller' => 'events', 'action' => 'view', $eventId));
} else {
$pivot = $this->__doRemove($pivot, $id);
2013-09-05 10:40:53 +02:00
}
$this->Session->write('pivot_thread', $pivot);
$pivot = $this->__arrangePivotVertical($pivot);
$this->redirect(array('controller' => 'events', 'action' => 'view', $eventId, true, $eventId));
}
private function __removeChildren(&$pivot, $id) {
if ($pivot['id'] == $id) {
$pivot['children'] = array();
} else {
foreach ($pivot['children'] as $k => $v) {
$this->__removeChildren($v, $id);
}
}
}
private function __doRemove(&$pivot, $id) {
foreach ($pivot['children'] as $k => $v) {
if ($v['id'] == $id) {
unset ($pivot['children'][$k]);
return $pivot;
} else {
$pivot['children'][$k] = $this->__doRemove($pivot['children'][$k], $id);
}
}
return $pivot;
}
private function __setDeletable(&$pivot, $id, $root=false) {
if ($pivot['id'] == $id && !$root) {
$pivot['deletable'] = false;
return true;
}
$containsCurrent = false;
foreach ($pivot['children'] as $k => $v) {
$containsCurrent = $this->__setDeletable($pivot['children'][$k], $id);
if ($containsCurrent && !$root) $pivot['deletable'] = false;
}
return !$pivot['deletable'];
}
2013-01-25 08:51:20 +01:00
/**
* add method
*
* @return void
*/
2013-01-25 08:51:20 +01:00
public function add() {
if ($this->request->is('post')) {
if ($this->_isRest()) {
// Distribution, reporter for the events pushed will be the owner of the authentication key
$this->request->data['Event']['user_id'] = $this->Auth->user('id');
2013-01-25 08:51:20 +01:00
}
if (!empty($this->data)) {
$ext = '';
2013-05-22 12:49:40 +02:00
if (isset($this->data['Event']['submittedgfi'])) {
2013-01-25 08:51:20 +01:00
App::uses('File', 'Utility');
2013-05-22 12:49:40 +02:00
$file = new File($this->data['Event']['submittedgfi']['name']);
2013-01-25 08:51:20 +01:00
$ext = $file->ext();
}
2013-05-22 12:49:40 +02:00
if (isset($this->data['Event']['submittedgfi']) && ($ext != 'zip') && $this->data['Event']['submittedgfi']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedgfi']['tmp_name'])) {
2013-01-25 08:51:20 +01:00
$this->Session->setFlash(__('You may only upload GFI Sandbox zip files.'));
} else {
$add = $this->Event->_add($this->request->data, $this->_isRest(), $this->Auth->user(), '');
if ($add && !is_numeric($add)) {
2013-01-25 08:51:20 +01:00
if ($this->_isRest()) {
// REST users want to see the newly created event
$this->view($this->Event->getId());
$this->render('view');
} else {
// TODO now save uploaded attributes using $this->Event->getId() ..
if (isset($this->data['Event']['submittedgfi'])) $this->_addGfiZip($this->Event->getId());
2013-01-25 08:51:20 +01:00
// redirect to the view of the newly created event
if (!CakeSession::read('Message.flash')) {
$this->Session->setFlash(__('The event has been saved'));
} else {
$existingFlash = CakeSession::read('Message.flash');
$this->Session->setFlash(__('The event has been saved. ' . $existingFlash['message']));
}
$this->redirect(array('action' => 'view', $this->Event->getId()));
}
} else {
if ($this->_isRest()) { // TODO return error if REST
if(is_numeric($add)) {
$this->response->header('Location', Configure::read('CyDefSIG.baseurl') . '/events/' . $add);
$this->response->send();
}
2013-01-25 08:51:20 +01:00
// REST users want to see the failed event
$this->view($this->Event->getId());
2013-01-25 08:51:20 +01:00
$this->render('view');
} else {
$this->Session->setFlash(__('The event could not be saved. Please, try again.'), 'default', array(), 'error');
// TODO return error if REST
}
}
}
}
}
// combobox for distribution
$distributions = array_keys($this->Event->distributionDescriptions);
$distributions = $this->_arrayToValuesIndexArray($distributions);
$this->set('distributions', $distributions);
// tooltip for distribution
$this->set('distributionDescriptions', $this->Event->distributionDescriptions);
$this->set('distributionLevels', $this->Event->distributionLevels);
2013-01-25 08:51:20 +01:00
// combobox for risks
$threat_levels = $this->Event->ThreatLevel->find('all');
$this->set('threatLevels', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.name'));
$this->set('riskDescriptions', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.form_description'));
2013-01-25 08:51:20 +01:00
// combobox for analysis
$analysiss = $this->Event->validate['analysis']['rule'][1];
$analysiss = $this->_arrayToValuesIndexArray($analysiss);
$this->set('analysiss',$analysiss);
// tooltip for analysis
$this->set('analysisDescriptions', $this->Event->analysisDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
}
public function addIOC($id) {
2013-08-12 17:23:32 +02:00
$this->Event->recursive = -1;
$this->Event->read(null, $id);
if (!$this->_isSiteAdmin() && ($this->Event->data['Event']['orgc'] != $this->_checkOrg() || !$this->userRole['perm_modify'])) {
throw new UnauthorizedException('You do not have permission to do that.');
}
if ($this->request->is('post')) {
if (!empty($this->data)) {
$ext = '';
if (isset($this->data['Event']['submittedioc'])) {
App::uses('File', 'Utility');
$file = new File($this->data['Event']['submittedioc']['name']);
$ext = $file->ext();
}
if (isset($this->data['Event']['submittedioc']) && ($ext != 'ioc') && $this->data['Event']['submittedioc']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedioc']['tmp_name'])) {
$this->Session->setFlash(__('You may only upload OpenIOC ioc files.'));
}
if (isset($this->data['Event']['submittedioc'])) $this->_addIOCFile($id);
// redirect to the view of the newly created event
if (!CakeSession::read('Message.flash')) {
$this->Session->setFlash(__('The event has been saved'));
} else {
$existingFlash = CakeSession::read('Message.flash');
$this->Session->setFlash(__('The event has been saved. ' . $existingFlash['message']));
}
}
}
// set the id
$this->set('id', $id);
// set whether it is published or not
$this->set('published', $this->Event->data['Event']['published']);
}
public function add_xml() {
if (!$this->userRole['perm_modify']) {
throw new UnauthorizedException('You do not have permission to do that.');
}
if ($this->request->is('post')) {
if (!empty($this->data)) {
$ext = '';
if (isset($this->data['Event']['submittedxml'])) {
App::uses('File', 'Utility');
$file = new File($this->data['Event']['submittedxml']['name']);
$ext = $file->ext();
}
if (isset($this->data['Event']['submittedxml']) && ($ext != 'xml') && $this->data['Event']['submittedxml']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedxml']['tmp_name'])) {
$this->Session->setFlash(__('You may only upload OpenIOC ioc files.'));
}
if (isset($this->data['Event']['submittedxml'])) $this->_addXMLFile();
// redirect to the view of the newly created event
if (!CakeSession::read('Message.flash')) {
$this->Session->setFlash(__('The event has been saved'));
} else {
$existingFlash = CakeSession::read('Message.flash');
$this->Session->setFlash(__('The event has been saved. ' . $existingFlash['message']));
}
}
}
}
/**
* Low level function to add an Event based on an Event $data array
*
* @return bool true if success
*/
public function _add(&$data, $fromXml, $or='', $passAlong = null, $fromPull = false) {
2013-05-28 11:15:21 +02:00
$this->Event->create();
2013-01-25 08:51:20 +01:00
// force check userid and orgname to be from yourself
$auth = $this->Auth;
2013-01-25 08:51:20 +01:00
$data['Event']['user_id'] = $auth->user('id');
$date = new DateTime();
//if ($this->checkAction('perm_sync')) $data['Event']['org'] = Configure::read('CyDefSIG.org');
//else $data['Event']['org'] = $auth->user('org');
$data['Event']['org'] = $auth->user('org');
// set these fields if the event is freshly created and not pushed from another instance.
// Moved out of if (!$fromXML), since we might get a restful event without the orgc/timestamp set
if (!isset ($data['Event']['orgc'])) $data['Event']['orgc'] = $data['Event']['org'];
2013-01-25 08:51:20 +01:00
if ($fromXml) {
// Workaround for different structure in XML/array than what CakePHP expects
$this->Event->cleanupEventArrayFromXML($data);
// the event_id field is not set (normal) so make sure no validation errors are thrown
// LATER do this with $this->validator()->remove('event_id');
unset($this->Event->Attribute->validate['event_id']);
unset($this->Event->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
}
2013-05-28 11:15:21 +02:00
unset ($data['Event']['id']);
if (isset($data['Event']['uuid'])) {
2013-01-25 08:51:20 +01:00
// check if the uuid already exists
$existingEventCount = $this->Event->find('count', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
if ($existingEventCount > 0) {
2013-05-28 11:15:21 +02:00
// RESTfull, set responce location header..so client can find right URL to edit
if ($fromPull) return false;
2013-01-25 08:51:20 +01:00
$existingEvent = $this->Event->find('first', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
$this->response->header('Location', Configure::read('CyDefSIG.baseurl') . '/events/' . $existingEvent['Event']['id']);
$this->response->send();
2013-01-25 08:51:20 +01:00
return false;
}
}
if (isset($data['Attribute'])) {
foreach ($data['Attribute'] as &$attribute) {
unset ($attribute['id']);
}
2013-01-25 08:51:20 +01:00
}
// FIXME chri: validatebut the necessity for all these fields...impact on security !
2013-05-28 11:15:21 +02:00
$fieldList = array(
'Event' => array('org', 'orgc', 'date', 'threat_level_id', 'analysis', 'info', 'user_id', 'published', 'uuid', 'timestamp', 'distribution', 'locked'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'timestamp', 'distribution')
2013-05-28 11:15:21 +02:00
);
$saveResult = $this->Event->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList,
'atomic' => true));
2013-05-28 11:15:21 +02:00
// FIXME chri: check if output of $saveResult is what we expect when data not valid, see issue #104
2013-01-25 08:51:20 +01:00
if ($saveResult) {
2013-05-28 11:15:21 +02:00
if (!empty($data['Event']['published']) && 1 == $data['Event']['published']) {
2013-01-25 08:51:20 +01:00
// do the necessary actions to publish the event (email, upload,...)
if ('true' != Configure::read('MISP.disablerestalert')) {
$this->Event->sendAlertEmailRouter($this->Event->getId(), $this->Auth->user(), $this->_isSiteAdmin());
}
$this->Event->publish($this->Event->getId(), $passAlong);
2013-01-25 08:51:20 +01:00
}
return true;
} else {
//throw new MethodNotAllowedException("Validation ERROR: \n".var_export($this->Event->validationErrors, true));
return false;
}
}
public function _edit(&$data, $id) {
$this->Event->read(null, $id);
if (!isset ($data['Event']['orgc'])) $data['Event']['orgc'] = $data['Event']['org'];
if ($this->Event->data['Event']['timestamp'] < $data['Event']['timestamp']) {
} else {
return 'Event exists and is the same or newer.';
}
2013-08-12 17:23:32 +02:00
if (!$this->Event->data['Event']['locked']) {
return 'Event originated on this instance, any changes to it have to be done locally.';
}
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'from', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp')
);
$data['Event']['id'] = $this->Event->data['Event']['id'];
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$existingAttribute = $this->__searchUuidInAttributeArray($attribute['uuid'], $this->Event->data);
if (count($existingAttribute)) {
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if ($data['Event']['Attribute'][$k]['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
} else {
unset($data['Event']['Attribute'][$k]);
}
} else {
unset($data['Event']['Attribute'][$k]['id']);
}
}
}
$this->Event->cleanupEventArrayFromXML($data);
$saveResult = $this->Event->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList));
if ($saveResult) return 'success';
else return 'Saving the event has failed.';
}
private function __searchUuidInAttributeArray($uuid, &$attr_array) {
foreach ($attr_array['Attribute'] as &$attr) {
if ($attr['uuid'] == $uuid) return array('Attribute' => $attr);
}
return false;
}
/**
* edit method
*
* @param int $id
* @return void
* @throws NotFoundException
*/
2013-01-25 08:51:20 +01:00
public function edit($id = null) {
$this->Event->id = $id;
$date = new DateTime();
2013-01-25 08:51:20 +01:00
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
2013-01-28 09:32:01 +01:00
$this->Event->read(null, $id);
// check for if private and user not authorised to edit, go away
if (!$this->_isSiteAdmin() && !($this->userRole['perm_sync'] && $this->_isRest())) {
if (($this->Event->data['Event']['org'] != $this->_checkOrg()) || !($this->userRole['perm_modify'])) {
2013-08-12 17:23:32 +02:00
$this->Session->setFlash(__('You are not authorised to do that. Please considering using the propose attribute feature.'));
$this->redirect(array('controller' => 'events', 'action' => 'index'));
}
}
2013-01-25 08:51:20 +01:00
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->_isRest()) {
2013-08-12 17:23:32 +02:00
$saveEvent = false;
2013-01-25 08:51:20 +01:00
// Workaround for different structure in XML/array than what CakePHP expects
$this->Event->cleanupEventArrayFromXML($this->request->data);
// the event_id field is not set (normal) so make sure no validation errors are thrown
// LATER do this with $this->validator()->remove('event_id');
unset($this->Event->Attribute->validate['event_id']);
unset($this->Event->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
// http://book.cakephp.org/2.0/en/models/saving-your-data.html
// Creating or updating is controlled by the models id field.
// If $Model->id is set, the record with this primary key is updated.
// Otherwise a new record is created
// reposition to get the event.id with given uuid
$existingEvent = $this->Event->findByUuid($this->request->data['Event']['uuid']);
2013-08-12 17:23:32 +02:00
// If the event exists...
2013-01-25 08:51:20 +01:00
if (count($existingEvent)) {
$this->request->data['Event']['id'] = $existingEvent['Event']['id'];
// Conditions affecting all:
2013-08-12 17:23:32 +02:00
// user.org == event.org
// edit timestamp newer than existing event timestamp
if (isset($this->request->data['Event']['timestamp']) && $this->request->data['Event']['timestamp'] > $existingEvent['Event']['timestamp']) {
// If the above is true, we have two more options:
2013-08-12 17:23:32 +02:00
// For users that are of the creating org of the event, always allow the edit
// For users that are sync users, only allow the edit if the event is locked
if ($existingEvent['Event']['orgc'] === $this->_checkOrg()
|| ($this->userRole['perm_sync'] && $existingEvent['Event']['locked'])) {
2013-08-12 17:23:32 +02:00
// Only allow an edit if this is true!
$saveEvent = true;
}
}
}
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'from', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp')
);
2013-05-28 11:15:21 +02:00
$c = 0;
if (isset($this->request->data['Attribute'])) {
foreach ($this->request->data['Attribute'] as $attribute) {
$existingAttribute = $this->Event->Attribute->findByUuid($attribute['uuid']);
if (count($existingAttribute)) {
$this->request->data['Attribute'][$c]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if ($this->request->data['Attribute'][$c]['timestamp'] > $existingAttribute['Attribute']['id']) {
} else {
unset($this->request->data['Attribute'][$c]);
//$this->request->data['Attribute'][$c] = $existingAttribute['Attribute'];
2013-01-25 08:51:20 +01:00
}
}
2013-05-28 11:15:21 +02:00
$c++;
2013-01-25 08:51:20 +01:00
}
}
2013-05-28 11:15:21 +02:00
// this saveAssociated() function will save not only the event, but also the attributes
// from the attributes attachments are also saved to the disk thanks to the afterSave() fonction of Attribute
if ($saveEvent) {
2013-01-25 08:51:20 +01:00
$saveResult = $this->Event->saveAssociated($this->request->data, array('validate' => true, 'fieldList' => $fieldList));
} else {
2013-08-12 17:23:32 +02:00
throw new MethodNotAllowedException();
2013-01-25 08:51:20 +01:00
}
if ($saveResult) {
// TODO RESTfull: we now need to compare attributes, to see if we need to do a RESTfull attribute delete
$message = 'Saved';
$this->set('event', $this->Event->data);
//if published -> do the actual publishing
if ((!empty($this->request->data['Event']['published']) && 1 == $this->request->data['Event']['published'])) {
// do the necessary actions to publish the event (email, upload,...)
$this->Event->publish($existingEvent['Event']['id']);
}
2013-01-25 08:51:20 +01:00
// REST users want to see the newly created event
$this->view($this->Event->getId());
$this->render('view');
return true;
} else {
$message = 'Error';
$this->set(array('message' => $message,'_serialize' => array('message'))); // $this->Event->validationErrors
$this->render('edit');
//throw new MethodNotAllowedException("Validation ERROR: \n".var_export($this->Event->validationErrors, true));
return false;
}
}
// say what fields are to be updated
$fieldList = array('date', 'threat_level_id', 'analysis', 'info', 'published', 'distribution', 'timestamp');
$this->Event->read();
2013-01-25 08:51:20 +01:00
// always force the org, but do not force it for admins
if (!$this->_isSiteAdmin()) {
2013-01-25 08:51:20 +01:00
// set the same org as existed before
$this->request->data['Event']['org'] = $this->Event->data['Event']['org'];
2013-01-25 08:51:20 +01:00
}
// we probably also want to remove the published flag
$this->request->data['Event']['published'] = 0;
$date = new DateTime();
$this->request->data['Event']['timestamp'] = $date->getTimestamp();
2013-01-25 08:51:20 +01:00
if ($this->Event->save($this->request->data, true, $fieldList)) {
$this->Session->setFlash(__('The event has been saved'));
$this->redirect(array('action' => 'view', $id));
} else {
$this->Session->setFlash(__('The event could not be saved. Please, try again.'));
}
} else {
if(!$this->userRole['perm_modify']) $this->redirect(array('controller' => 'events', 'action' => 'index', 'admin' => false));
2013-01-25 08:51:20 +01:00
$this->request->data = $this->Event->read(null, $id);
}
// combobox for distribution
$distributions = array_keys($this->Event->distributionDescriptions);
$distributions = $this->_arrayToValuesIndexArray($distributions);
$this->set('distributions', $distributions);
2013-01-25 08:51:20 +01:00
// tooltip for distribution
$this->set('distributionDescriptions', $this->Event->distributionDescriptions);
$this->set('distributionLevels', $this->Event->distributionLevels);
2013-01-25 08:51:20 +01:00
// combobox for types
$threat_levels = $this->Event->ThreatLevel->find('all');
$this->set('threatLevels', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.name'));
$this->set('riskDescriptions', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.form_description'));
2013-01-25 08:51:20 +01:00
// combobox for analysis
$analysiss = $this->Event->validate['analysis']['rule'][1];
$analysiss = $this->_arrayToValuesIndexArray($analysiss);
$this->set('analysiss',$analysiss);
2013-01-25 08:51:20 +01:00
// tooltip for analysis
$this->set('analysisDescriptions', $this->Event->analysisDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
$this->set('event', $this->Event->data);
2013-01-25 08:51:20 +01:00
}
/**
* delete method
*
* @param int $id
* @return void
* @throws MethodNotAllowedException
* @throws NotFoundException
*/
2013-01-25 08:51:20 +01:00
public function delete($id = null) {
if (!$this->request->is('post') && !$this->_isRest()) {
throw new MethodNotAllowedException();
}
$this->Event->id = $id;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
if ('true' == Configure::read('CyDefSIG.sync')) {
// find the uuid
$result = $this->Event->findById($id);
$uuid = $result['Event']['uuid'];
}
if (!$this->_isSiteAdmin()) {
$this->Event->read();
2013-08-12 17:23:32 +02:00
if (!$this->Event->data['Event']['orgc'] == $this->_checkOrg()) {
throw new MethodNotAllowedException();
}
}
2013-01-25 08:51:20 +01:00
if ($this->Event->delete()) {
// delete the event from remote servers
//if ('true' == Configure::read('CyDefSIG.sync')) { // TODO test..(!$this->_isRest()) &&
// $this->__deleteEventFromServers($uuid);
//}
$this->Session->setFlash(__('Event deleted'));
// if coming from index, redirect to referer (to have the filter working)
// else redirect to index
if (strpos($this->referer(), '/view') !== FALSE)
$this->redirect(array('action' => 'index'));
else
$this->redirect($this->referer(array('action' => 'index')));
2013-01-25 08:51:20 +01:00
}
$this->Session->setFlash(__('Event was not deleted'));
$this->redirect(array('action' => 'index'));
}
/**
* Delets this specific event to all remote servers
* TODO move this to a component(?)
*/
2013-01-25 08:51:20 +01:00
private function __deleteEventFromServers($uuid) {
// get a list of the servers
$this->loadModel('Server');
$servers = $this->Server->find('all', array(
'conditions' => array('Server.push' => true)
));
// iterate over the servers and upload the event
if(empty($servers))
return;
App::uses('SyncTool', 'Tools');
2013-01-25 08:51:20 +01:00
foreach ($servers as &$server) {
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket($server);
2013-01-25 08:51:20 +01:00
$this->Event->deleteEventFromServer($uuid, $server, $HttpSocket);
}
}
2013-11-06 10:52:18 +01:00
/**
* Publishes the event without sending an alert email
*
* @throws NotFoundException
*/
2013-01-25 08:51:20 +01:00
public function publish($id = null) {
$this->Event->id = $id;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
// update the event and set the from field to the current instance's organisation from the bootstrap. We also need to save id and info for the logs.
$this->Event->recursive = -1;
$event = $this->Event->read(null, $id);
if (!$this->_isSiteAdmin()) {
if (!$this->userRole['perm_publish'] && !$this->Auth->user('org') === $this->Event->data['Event']['orgc']) {
throw new MethodNotAllowedException('You don\'t have the permission to do that.');
}
}
2013-01-25 08:51:20 +01:00
// only allow form submit CSRF protection.
if ($this->request->is('post') || $this->request->is('put')) {
// Performs all the actions required to publish an event
$result = $this->Event->publish($id);
if (!Configure::read('MISP.background_jobs')) {
if (!is_array($result)) {
// redirect to the view event page
$this->Session->setFlash(__('Event published, but NO mail sent to any participants.', true));
} else {
$lastResult = array_pop($result);
$resultString = (count($result) > 0) ? implode(', ', $result) . ' and ' . $lastResult : $lastResult;
$this->Session->setFlash(__(sprintf('Event not published to %s, re-try later. If the issue persists, make sure that the correct sync user credentials are used for the server link and that the sync user on the remote server has authentication privileges.', $resultString), true));
}
2013-01-25 08:51:20 +01:00
} else {
$this->Session->setFlash(__('Job queued.'));
2013-01-25 08:51:20 +01:00
}
$this->redirect(array('action' => 'view', $id));
}
}
/**
* Send out an alert email to all the users that wanted to be notified.
* Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted
*
* @throws NotFoundException
*/
2013-01-25 08:51:20 +01:00
public function alert($id = null) {
$this->Event->id = $id;
$this->Event->recursive = 0;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$this->Event->recursive = -1;
$this->Event->read(null, $id);
if (!$this->_isSiteAdmin()) {
if (!$this->userRole['perm_publish'] && !$this->Auth->user('org') === $this->Event->data['Event']['orgc']) {
throw new MethodNotAllowedException('You don\'t have the permission to do that.');
}
}
2013-01-25 08:51:20 +01:00
// only allow form submit CSRF protection.
if ($this->request->is('post') || $this->request->is('put')) {
// send out the email
$emailResult = $this->Event->sendAlertEmailRouter($id, $this->Auth->user(), $this->_isSiteAdmin());
2013-01-25 08:51:20 +01:00
if (is_bool($emailResult) && $emailResult = true) {
// Performs all the actions required to publish an event
$result = $this->Event->publish($id);
2013-01-25 08:51:20 +01:00
if (!is_array($result)) {
// redirect to the view event page
$this->Session->setFlash(__('Email sent to all participants.', true));
} else {
$lastResult = array_pop($result);
$resultString = (count($result) > 0) ? implode(', ', $result) . ' and ' . $lastResult : $lastResult;
$this->Session->setFlash(__(sprintf('Not published given no connection to %s but email sent to all participants.', $resultString), true));
}
} elseif (!is_bool($emailResult)) {
// Performs all the actions required to publish an event
$result = $this->Event->publish($id);
2013-01-25 08:51:20 +01:00
if (!is_array($result)) {
// redirect to the view event page
$this->Session->setFlash(__('Published but no email sent given GnuPG is not configured.', true));
} else {
$lastResult = array_pop($result);
$resultString = (count($result) > 0) ? implode(', ', $result) . ' and ' . $lastResult : $lastResult;
$this->Session->setFlash(__(sprintf('Not published given no connection to %s but no email sent given GnuPG is not configured.', $resultString), true));
}
} else {
$this->Session->setFlash(__('Sending of email failed', true), 'default', array(), 'error');
}
$this->redirect(array('action' => 'view', $id));
}
}
/**
* Send out an contact email to the person who posted the event.
* Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted
*
* @throws NotFoundException
*/
2013-01-25 08:51:20 +01:00
public function contact($id = null) {
$this->Event->id = $id;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
// User has filled in his contact form, send out the email.
if ($this->request->is('post') || $this->request->is('put')) {
$message = $this->request->data['Event']['message'];
$all = $this->request->data['Event']['person'];
if ($this->Event->sendContactEmailRouter($id, $message, $all, $this->Auth->user(), $this->_isSiteAdmin())) {
2013-01-25 08:51:20 +01:00
// redirect to the view event page
$this->Session->setFlash(__('Email sent to the reporter.', true));
} else {
$this->Session->setFlash(__('Sending of email failed', true), 'default', array(), 'error');
}
$this->redirect(array('action' => 'view', $id));
}
// User didn't see the contact form yet. Present it to him.
if (empty($this->data)) {
$this->data = $this->Event->read(null, $id);
2013-01-25 08:51:20 +01:00
}
}
public function automation() {
2013-01-25 08:51:20 +01:00
// Simply display a static view
if (!$this->userRole['perm_auth']) {
$this->redirect(array('controller' => 'events', 'action' => 'index'));
}
2013-01-25 08:51:20 +01:00
// generate the list of Attribute types
$this->loadModel('Attribute');
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
}
public function export() {
// Check if the background jobs are enabled - if not, fall back to old export page.
if (Configure::read('MISP.background_jobs')) {
$now = time();
// as a site admin we'll use the ADMIN identifier, not to overwrite the cached files of our own org with a file that includes too much data.
if ($this->_isSiteAdmin()) {
$useOrg = 'ADMIN';
$conditions = null;
2013-11-18 11:35:02 +01:00
} else {
$useOrg = $this->Auth->User('org');
$conditions = array('orgc' => $this->Auth-user('org'));
2013-11-18 11:35:02 +01:00
}
$this->Event->recursive = -1;
$newestEvent = $this->Event->find('first', array(
'conditions' => $conditions,
'fields' => 'timestamp',
'order' => 'Event.timestamp DESC',
));
$this->loadModel('Job');
foreach ($this->Event->export_types as $k => $type) {
$job = $this->Job->find('first', array(
'fields' => array('id', 'progress'),
'conditions' => array(
'job_type' => 'cache_' . $k,
'org' => $useOrg
),
'order' => array('Job.id' => 'desc')
));
$dir = new Folder(APP . 'tmp/cached_exports/' . $k);
if ($k === 'text') {
// Since all of the text export files are generated together, we might as well just check for a single one md5.
$file = new File($dir->pwd() . DS . 'misp.text_md5.' . $useOrg . $type['extension']);
} else {
$file = new File($dir->pwd() . DS . 'misp.' . $k . '.' . $useOrg . $type['extension']);
}
if (!$file->exists()) {
$lastModified = 'N/A';
$this->Event->export_types[$k]['recommendation'] = 1;
} else {
$fileChange = $file->lastChange();
$lastModified = $this->__timeDifference($now, $fileChange);
if ($fileChange > $newestEvent['Event']['timestamp']) {
$this->Event->export_types[$k]['recommendation'] = 0;
} else {
$this->Event->export_types[$k]['recommendation'] = 1;
}
}
$this->Event->export_types[$k]['lastModified'] = $lastModified;
if (!empty($job)) {
$this->Event->export_types[$k]['job_id'] = $job['Job']['id'];
$this->Event->export_types[$k]['progress'] = $job['Job']['progress'];
} else {
$this->Event->export_types[$k]['job_id'] = -1;
$this->Event->export_types[$k]['progress'] = 0;
}
//$this->Event->export_types[$k]['recommendation']
}
$this->set('useOrg', $useOrg);
$this->set('export_types', $this->Event->export_types);
// generate the list of Attribute types
$this->loadModel('Attribute');
//$lastModified = strftime("%d, %m, %Y, %T", $lastModified);
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
} else {
// generate the list of Attribute types
$this->loadModel('Attribute');
//$lastModified = strftime("%d, %m, %Y, %T", $lastModified);
$this->set('sigTypes', array_keys($this->Attribute->typeDefinitions));
$this->render('/Events/export_alternate');
}
}
public function downloadExport($type, $extra = null) {
if ($this->_isSiteAdmin()) $org = 'ADMIN';
else $org = $this->Auth->user('org');
$this->autoRender = false;
if ($extra != null) $extra = '_' . $extra;
$this->response->type($this->Event->export_types[$type]['extension']);
$path = 'tmp/cached_exports/' . $type . DS . 'misp.' . strtolower($this->Event->export_types[$type]['type']) . $extra . '.' . $org . $this->Event->export_types[$type]['extension'];
$newFileName = 'misp.' . $this->Event->export_types[$type]['type'] . '.' . $org . $this->Event->export_types[$type]['extension'];
$this->response->file($path, array('download' => true));
}
private function __timeDifference($now, $then) {
$periods = array("second", "minute", "hour", "day", "week", "month", "year");
$lengths = array("60","60","24","7","4.35","12");
$difference = $now - $then;
for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {
$difference /= $lengths[$j];
}
$difference = round($difference);
if($difference != 1) {
$periods[$j].= "s";
}
return $difference . " " . $periods[$j] . " ago";
}
public function xml($key, $eventid=null, $withAttachment = false) {
if ($key != 'download') {
// check if the key is valid -> search for users based on key
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
// display the full xml
$this->response->type('xml'); // set the content type
$this->layout = 'xml/default';
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.xml"');
} else {
if (!$this->Auth->user('id')) {
throw new UnauthorizedException('You have to be logged in to do that.');
}
// display the full xml
$this->response->type('xml'); // set the content type
$this->layout = 'xml/default';
if ($eventid == null) {
$this->header('Content-Disposition: download; filename="misp.export.all.xml"');
} else {
$this->header('Content-Disposition: download; filename="misp.export.event' . $eventid . '.xml"');
}
}
$results = $this->__fetchEvent($eventid);
if ($withAttachment) {
$this->loadModel('Attribute');
foreach ($results[0]['Attribute'] as &$attribute) {
if ($this->Attribute->typeIsAttachment($attribute['type'])) {
$encodedFile = $this->Attribute->base64EncodeAttachment($attribute);
$attribute['data'] = $encodedFile;
}
}
}
// Whitelist check
$this->loadModel('Whitelist');
$results = $this->Whitelist->removeWhitelistedFromArray($results, false);
$this->set('results', $results);
}
2013-01-25 08:51:20 +01:00
// Grab an event or a list of events for the event view or any of the XML exports. The returned object includes an array of events (or an array that only includes a single event if an ID was given)
// Included with the event are the attached attributes, shadow attributes, related events, related attribute information for the event view and the creating user's email address where appropriate
private function __fetchEvent($eventid = null, $idList = null, $orgFromFetch = null, $isSiteAdmin = false) {
// if we come from automation, we may not be logged in - instead we used an auth key in the URL.
if (!empty($orgFromFetch)) {
$org = $orgFromFetch;
} else {
$org = $this->_checkOrg();
$isSiteAdmin = $this->_isSiteAdmin();
}
if (!empty($orgFromFetch)) $org = $orgFromFetch;
else $org = $this->_checkOrg();
$results = $this->Event->fetchEvent($eventid, $idList, $org, $isSiteAdmin);
return $results;
2013-01-25 08:51:20 +01:00
}
2013-11-19 11:03:30 +01:00
public function nids($format = 'suricata', $key = '', $id = null, $continue = false) {
// backwards compatibility, swap key and format
if ($format != 'snort' && $format != 'suricata') {
$key = $format;
$format = 'suricata'; // default format
}
if ($key != 'download') {
$this->response->type('txt'); // set the content type
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.rules"');
$this->layout = 'text/default';
// check if the key is valid -> search for users based on key
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
} else {
//$this->autoRender = false;
$this->response->type('txt'); // set the content type
$this->header('Content-Disposition: download; filename="misp.nids.rules"');
$this->layout = 'text/default';
// check if there's a user logged in or not
if (!$this->Auth->user('id')) {
throw new UnauthorizedException('You have to be logged in to do that.');
}
$user = $this->Auth->user;
}
2013-01-25 08:51:20 +01:00
// display the full snort rulebase
2013-01-25 08:51:20 +01:00
$this->loadModel('Attribute');
2013-11-19 11:03:30 +01:00
$rules = $this->Attribute->nids($user['User']['siteAdmin'], $user['User']['org'], $format, $user['User']['nids_sid'], $id, $continue);
$this->set('rules', $rules);
2013-01-25 08:51:20 +01:00
}
public function hids($type, $key) {
if ($key != 'download') {
// check if the key is valid -> search for users based on key
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
$this->response->type('txt'); // set the content type
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.' . $type . '.rules"');
$this->layout = 'text/default';
2013-01-25 08:51:20 +01:00
} else {
// check if there's a user logged in or not
if (!$this->Auth->user('id')) {
throw new UnauthorizedException('You have to be logged in to do that.');
}
2013-11-18 11:35:02 +01:00
$user = $this->Auth->user;
$this->response->type(array('txt' => 'text/html')); // set the content type
$this->header('Content-Disposition: download; filename="misp.' . $type . '.rules"');
$this->layout = 'text/default';
2013-01-25 08:51:20 +01:00
}
$this->loadModel('Attribute');
2013-11-18 11:35:02 +01:00
$rules = $this->Attribute->hids($user['User']['siteAdmin'], $user['User']['org'], $type);
$this->set('rules', $rules);
2013-01-25 08:51:20 +01:00
}
// csv function
// Usage: csv($key, $eventid) - key can be a valid auth key or the string 'download'. Download requires the user to be logged in interactively and will generate a .csv file
// $eventid can be one of 3 options: left empty it will get all the visible to_ids attributes,
public function csv($key, $eventid=0, $ignore=0) {
$list = array();
if ($key != 'download') {
// check if the key is valid -> search for users based on key
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
$this->response->type('csv'); // set the content type
if ($eventid == 0) {
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.all_attributes.csv"');
} else if ($eventid === 'search') {
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.search_result.csv"');
} else {
2014-01-10 13:56:35 +01:00
$this->header('Content-Disposition: download; filename="misp.event_' . $eventid . '.csv"');
}
$this->layout = 'text/default';
$isSiteAdmin = $user['User']['siteAdmin'];
$org = $user['User']['org'];
2013-01-25 08:51:20 +01:00
} else {
if (!$this->Auth->user('id')) {
throw new UnauthorizedException('You have to be logged in to do that.');
}
$this->response->type('csv'); // set the content type
if ($eventid == 0) {
$this->header('Content-Disposition: download; filename="misp.all_attributes.csv"');
} else {
$this->header('Content-Disposition: download; filename="misp.event_' . $eventid . '.csv"');
}
$this->layout = 'text/default';
$isSiteAdmin = $this->_isSiteAdmin();
$org = $this->Auth->user('org');
}
// if it's a search, grab the attributeIDList from the session and get the IDs from it. Use those as the condition
// We don't need to look out for permissions since that's filtered by the search itself
// We just want all the attributes found by the search
if ($eventid === 'search') {
$list = $this->Session->read('search_find_attributeidlist');
}
$attributes = $this->Event->csv($org, $isSiteAdmin, $eventid, $ignore, $list);
$this->loadModel('Whitelist');
$final = array();
$attributes = $this->Whitelist->removeWhitelistedFromArray($attributes, true);
foreach ($attributes as $attribute) {
$final[] = $attribute['Attribute']['uuid'] . ',' . $attribute['Attribute']['event_id'] . ',' . $attribute['Attribute']['category'] . ',' . $attribute['Attribute']['type'] . ',' . $attribute['Attribute']['value'];
2013-01-25 08:51:20 +01:00
}
$this->set('final', $final);
2013-01-25 08:51:20 +01:00
}
//public function dot($key) {
// // check if the key is valid -> search for users based on key
// $this->loadModel('User');
// // no input sanitization necessary, it's done by model
// $this->User->recursive=0;
// $user = $this->User->findByAuthkey($key);
// if (empty($user)) {
// throw new UnauthorizedException('Incorrect authentication key');
// }
// // display the full snort rulebase
// $this->response->type('txt'); // set the content type
// $this->header('Content-Disposition: inline; filename="cydefsig.rules"');
// $this->layout = 'text/default';
// $rules= array();
// $this->loadModel('Attribute');
// $params = array(
// 'recursive' => 0,
// 'fields' => array('Attribute.*')
// );
// $items = $this->Attribute->find('all', $params);
// $composite_types = $this->Attribute->getCompositeTypes();
// // rebuild the array with the correct data
// foreach ($items as &$item) {
// if (in_array($item['Attribute']['type'], $composite_types)) {
// // create a new item that will contain value2
// $new_item = $item;
// // set the correct type for the first item
// $pieces = explode('|', $item['Attribute']['type']);
// $item['Attribute']['type'] = $pieces[0];
// // set the correct data for the new item
// $new_item['Attribute']['type'] = (isset($pieces[1]))? $pieces[1] : 'md5';
// $new_item['Attribute']['value'] = $item['Attribute']['value2'];
// unset($new_item['Attribute']['value1']);
// unset($new_item['Attribute']['value2']);
// // store the new item
// $items[] = $new_item;
// }
// // set the correct fields for the attribute
// if (isset($item['Attribute']['value1'])) {
// $item['Attribute']['value'] = $item['Attribute']['value1'];
// }
// unset($item['Attribute']['value1']);
// unset($item['Attribute']['value2']);
// }
// debug($items);
// // iterate over the array to build the GV links
// require_once 'Image/GraphViz.php';
// $gv = new Image_GraphViz();
// $gv->addEdge(array('wake up' => 'visit bathroom'));
// $gv->addEdge(array('visit bathroom' => 'make coffee'));
// foreach ($items as &$item) {
// $gv->addNode('Node 1',
// array(''));
// }
// debug($gv);
// $gv->image();
//}
public function _addGfiZip($id) {
2013-05-22 12:49:40 +02:00
if (!empty($this->data) && $this->data['Event']['submittedgfi']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedgfi']['tmp_name'])) {
$zipData = fread(fopen($this->data['Event']['submittedgfi']['tmp_name'], "r"),
$this->data['Event']['submittedgfi']['size']);
2013-01-25 08:51:20 +01:00
// write
$rootDir = APP . "files" . DS . $id . DS;
App::uses('Folder', 'Utility');
$dir = new Folder($rootDir, true);
$destpath = $rootDir;
$file = new File ($destpath);
if (!preg_match('@^[\w-,\s,\.]+\.[A-Za-z0-9_]{2,4}$@', $this->data['Event']['submittedgfi']['name'])) throw new Exception ('Filename not allowed');
if (PHP_OS == 'WINNT') {
$zipfile = new File ($destpath . DS . $this->data['Event']['submittedgfi']['name']);
} else {
$zipfile = new File ($destpath . $this->data['Event']['submittedgfi']['name']);
}
2013-01-25 08:51:20 +01:00
$result = $zipfile->write($zipData);
if (!$result) $this->Session->setFlash(__('Problem with writing the zip file. Please report to administrator.'));
// extract zip..
$execRetval = '';
$execOutput = array();
exec("unzip " . $zipfile->path . ' -d ' . $rootDir, $execOutput, $execRetval);
2013-01-25 08:51:20 +01:00
if ($execRetval != 0) { // not EXIT_SUCCESS
// do some?
throw new Exception('An error has occured while attempting to unzip the GFI sandbox .zip file. We apologise for the inconvenience.');
2013-01-25 08:51:20 +01:00
}
// now open the xml..
if (PHP_OS == 'WINNT') {
$xml = $rootDir . 'Analysis' . DS . 'analysis.xml';
} else {
$xml = $rootDir . DS . 'Analysis' . DS . 'analysis.xml';
}
2013-05-22 12:49:40 +02:00
$fileData = fread(fopen($xml, "r"), $this->data['Event']['submittedgfi']['size']);
2013-01-25 08:51:20 +01:00
// read XML
$this->_readGfiXML($fileData, $id);
2013-01-25 08:51:20 +01:00
}
}
public function _addIOCFile($id) {
if (!empty($this->data) && $this->data['Event']['submittedioc']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedioc']['tmp_name'])) {
$iocData = fread(fopen($this->data['Event']['submittedioc']['tmp_name'], "r"),
$this->data['Event']['submittedioc']['size']);
// write
$rootDir = APP . "files" . DS . $id . DS;
App::uses('Folder', 'Utility');
$dir = new Folder($rootDir . 'ioc', true);
$destpath = $rootDir . 'ioc';
$file = new File ($destpath);
if (!preg_match('@^[\w-,\s,\.]+\.[A-Za-z0-9_]{2,4}$@', $this->data['Event']['submittedioc']['name'])) throw new Exception ('Filename not allowed');
$iocfile = new File ($destpath . DS . $this->data['Event']['submittedioc']['name']);
$result = $iocfile->write($iocData);
if (!$result) $this->Session->setFlash(__('Problem with writing the ioc file. Please report to administrator.'));
2013-01-25 08:51:20 +01:00
// now open the xml..
$xml = $rootDir . DS . 'Analysis' . DS . 'analysis.xml';
$fileData = fread(fopen($destpath . DS . $this->data['Event']['submittedioc']['name'], "r"), $this->data['Event']['submittedioc']['size']);
// Load event and populate the event data
$this->Event->id = $id;
$this->Event->recursive = -1;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$this->Event->read(null, $id);
$saveEvent['Event'] = $this->Event->data['Event'];
$saveEvent['Event']['published'] = false;
$dist = '3';
if (Configure::read('MISP.default_attribute_distribution') != null) {
if (Configure::read('MISP.default_attribute_distribution') === 'event') {
$dist = $this->Event->data['Event']['distribution'];
} else {
$dist = '';
$dist .= Configure::read('MISP.default_attribute_distribution');
}
}
2013-01-25 08:51:20 +01:00
// read XML
$event = $this->IOCImport->readXML($fileData, $id, $dist);
// make some changes to have $saveEvent in the format that is needed to save the event together with its attributes
$fails = $event['Fails'];
$saveEvent['Attribute'] = $event['Attribute'];
// we've already stored these elsewhere, unset them so we can extract the event related data
unset($event['Attribute']);
unset($event['Fails']);
// Keep this for later if we want to let an ioc create the event data automatically in a later version
// save the event related data into $saveEvent['Event']
//$saveEvent['Event'] = $event;
//$saveEvent['Event']['id'] = $id;
$fieldList = array(
'Event' => array('published', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'distribution', 'timestamp', 'comment')
);
// Save it all
$saveResult = $this->Event->saveAssociated($saveEvent, array('validate' => true, 'fieldList' => $fieldList));
// set stuff for the view and render the showIOCResults view.
$this->set('attributes', $saveEvent['Attribute']);
if (isset($fails)) {
$this->set('fails', $fails);
}
$this->set('eventId', $id);
$this->set('graph', $event['Graph']);
$this->set('saveEvent', $saveEvent);
$this->render('showIOCResults');
2013-01-25 08:51:20 +01:00
}
}
public function _addXMLFile() {
if (!empty($this->data) && $this->data['Event']['submittedxml']['size'] > 0 &&
is_uploaded_file($this->data['Event']['submittedxml']['tmp_name'])) {
$xmlData = fread(fopen($this->data['Event']['submittedxml']['tmp_name'], "r"),
$this->data['Event']['submittedxml']['size']);
App::uses('Xml', 'Utility');
$xmlArray = Xml::toArray(Xml::build($xmlData));
// In case we receive an event that is not encapsulated in a response. This should never happen (unless it's a copy+paste fail),
// but just in case, let's clean it up anyway.
if (isset($xmlArray['Event'])) {
$xmlArray['response']['Event'] = $xmlArray['Event'];
unset($xmlArray['Event']);
}
if (!isset($xmlArray['response']) || !isset($xmlArray['response']['Event'])) {
throw new Exception('This is not a valid MISP XML file.');
}
if (isset($xmlArray['response']['Event'][0])) {
foreach ($xmlArray['response']['Event'] as $event) {
$temp['Event'] = $event;
$this->Event->_add($temp, true, $this->Auth->user());
}
} else {
$temp['Event'] = $xmlArray['response']['Event'];
$this->Event->_add($temp, true, $this->Auth->user());
}
}
}
public function _readGfiXML($data, $id) {
2013-01-25 08:51:20 +01:00
$this->loadModel('Attribute');
$this->Event->recursive = -1;
$this->Event->read(array('id', 'uuid', 'distribution'), $id);
2013-01-25 08:51:20 +01:00
// import XML class
App::uses('Xml', 'Utility');
// now parse it
$parsedXml = Xml::build($data, array('return' => 'simplexml'));
2013-01-25 08:51:20 +01:00
// xpath..
if (Configure::read('MISP.default_attribute_distribution') != null) {
if (Configure::read('MISP.default_attribute_distribution') === 'event') {
$dist = $this->Event->data['Event']['distribution'];
} else {
$dist = '';
$dist .= Configure::read('MISP.default_attribute_distribution');
}
}
2013-01-25 08:51:20 +01:00
//Payload delivery -- malware-sample
$results = $parsedXml->xpath('/analysis');
foreach ($results as $result) {
foreach ($result[0]->attributes() as $key => $val) {
if ((string)$key == 'filename') $realFileName = (string)$val;
}
}
$realMalware = $realFileName;
$rootDir = APP . "files" . DS . $id . DS;
$malware = $rootDir . DS . 'sample';
$this->Event->Attribute->uploadAttachment($malware, $realFileName, true, $id, null, '', $this->Event->data['Event']['uuid'] . '-sample', $dist, true);
2013-01-25 08:51:20 +01:00
//Network activity -- .pcap
$realFileName = 'analysis.pcap';
$rootDir = APP . "files" . DS . $id . DS;
$malware = $rootDir . DS . 'Analysis' . DS . 'analysis.pcap';
$this->Event->Attribute->uploadAttachment($malware, $realFileName, false, $id, 'Network activity', '', $this->Event->data['Event']['uuid'] . '-analysis.pcap', $dist, true);
2013-01-25 08:51:20 +01:00
//Artifacts dropped -- filename|md5
$files = array();
// TODO what about stored_modified_file ??
$results = $parsedXml->xpath('/analysis/processes/process/stored_files/stored_created_file');
foreach ($results as $result) {
$arrayItemKey = '';
$arrayItemValue = '';
foreach ($result[0]->attributes() as $key => $val) {
if ($key == 'filename') $arrayItemKey = (string)$val;
if ($key == 'md5') $arrayItemValue = (string)$val;
if ($key == 'filesize') $arrayItemSize = $val;
}
//$files[$arrayItemKey] = $arrayItemValue;
if ($arrayItemSize > 0) {
$files[] = array('key' => $arrayItemKey, 'val' => $arrayItemValue);
2013-01-25 08:51:20 +01:00
}
}
//$files = array_unique($files);
// write content..
foreach ($files as $file) {
$keyName = $file['key'];
if (!strpos($file['key'], $realMalware)) {
2013-01-25 08:51:20 +01:00
$itsType = 'malware-sample';
} else {
$itsType = 'filename|md5';
}
// the actual files..
// seek $val in dirs and add..
$ext = substr($file['key'], strrpos($file['key'], '.'));
$actualFileName = $file['val'] . $ext;
$actualFileNameBase = str_replace('\\', '/', $file['key']);
2013-01-25 08:51:20 +01:00
$actualFileNameArray[] = basename($actualFileNameBase);
$tempExplode = explode('\\', $file['key']);
$realFileName = end($tempExplode);
2013-01-25 08:51:20 +01:00
// have the filename, now look at parents parent for the process number
$express = "/analysis/processes/process/stored_files/stored_created_file[@md5='" . $file['val'] . "']/../..";
2013-01-25 08:51:20 +01:00
$results = $parsedXml->xpath($express);
foreach ($results as $result) {
foreach ($result[0]->attributes() as $key => $val) {
if ((string)$key == 'index') $index = (string)$val;
}
}
$actualFile = $rootDir . DS . 'Analysis' . DS . 'proc_' . $index . DS . 'modified_files' . DS . $actualFileName;
$extraPath = 'Analysis' . DS . 'proc_' . $index . DS . 'modified_files' . DS;
$file = new File($actualFile);
if ($file->exists()) { // TODO put in array for test later
$this->Event->Attribute->uploadAttachment($actualFile, $realFileName, true, $id, null, $extraPath, $keyName, $dist, true); // TODO was false
} else {
2013-01-25 08:51:20 +01:00
}
}
2013-01-25 08:51:20 +01:00
//Network activity -- ip-dst
$ips = array();
$hostnames = array();
2013-01-25 08:51:20 +01:00
$results = $parsedXml->xpath('/analysis/processes/process/networkpacket_section/connect_to_computer');
foreach ($results as $result) {
foreach ($result[0]->attributes() as $key => $val) {
if ($key == 'remote_ip') $ips[] = (string)$val;
if ($key == 'remote_hostname') $hostnames[] = (string)$val;
2013-01-25 08:51:20 +01:00
}
}
// write content..
// ip-s
2013-01-25 08:51:20 +01:00
foreach ($ips as $ip) {
// add attribute..
$this->Attribute->create();
2013-01-25 08:51:20 +01:00
$this->Attribute->save(array(
'event_id' => $id,
'category' => 'Network activity',
'type' => 'ip-dst',
'value' => $ip,
'to_ids' => false,
'distribution' => $dist,
'comment' => 'GFI import',
));
2013-01-25 08:51:20 +01:00
}
foreach ($hostnames as $hostname) {
// add attribute..
$this->Attribute->create();
$this->Attribute->save(array(
'event_id' => $id,
'category' => 'Network activity',
'type' => 'hostname',
'value' => $hostname,
'to_ids' => false,
'distribution' => $dist,
'comment' => 'GFI import',
));
2013-01-25 08:51:20 +01:00
}
// Persistence mechanism -- regkey|value
$regs = array();
$results = $parsedXml->xpath('/analysis/processes/process/registry_section/set_value');
foreach ($results as $result) {
$arrayItemKey = '';
$arrayItemValue = '';
foreach ($result[0]->attributes() as $key => $val) {
if ($key == 'key_name') $arrayItemKey = (string)$val;
if ($key == 'data') $arrayItemValue = (string)$val;
}
$regs[$arrayItemKey] = str_replace('(UNICODE_0x00000000)', '', $arrayItemValue);
}
//$regs = array_unique($regs);
// write content..
foreach ($regs as $key => $val) {
// add attribute..
$this->Attribute->create();
if ($this->strposarray($val,$actualFileNameArray)) {
$this->Attribute->save(array(
'event_id' => $id,
'comment' => 'GFI import',
'category' => 'Persistence mechanism', // 'Persistence mechanism'
'type' => 'regkey|value',
'value' => $key . '|' . $val,
'distribution' => $dist,
'to_ids' => false
));
}
2013-01-25 08:51:20 +01:00
}
}
public function strposarray($string, $array) {
$toReturn = false;
foreach ($array as $item) {
if (strpos($string,$item)) {
$toReturn = true;
}
}
return $toReturn;
}
public function downloadSearchResult() {
$idList = $this->Session->read('search_find_idlist');
$this->Session->write('search_find_idlist', '');
// display the full xml
$this->response->type('xml'); // set the content type
$this->layout = 'xml/default';
$this->header('Content-Disposition: download; filename="misp.search.results.xml"');
$results = $this->__fetchEvent(null, $idList);
// Whitelist check
$this->loadModel('Whitelist');
$results = $this->Whitelist->removeWhitelistedFromArray($results, false);
$this->set('results', $results);
$this->render('xml');
}
// Use the rest interface to search for attributes or events. Usage:
// MISP-base-url/events/restSearch/[api-key]/[value]/[type]/[category]/[orgc]
// value, type, category, orgc are optional
// target can be either "event" or "attribute"
// the last 4 fields accept the following operators:
// && - you can use && between two search values to put a logical OR between them. for value, 1.1.1.1&&2.2.2.2 would find attributes with the value being either of the two.
// ! - you can negate a search term. For example: google.com&&!mail would search for all attributes with value google.com but not ones that include mail. www.google.com would get returned, mail.google.com wouldn't.
public function restSearch($key, $value=null, $type=null, $category=null, $org=null) {
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
$value = str_replace('|', '/', $value);
$this->response->type('xml'); // set the content type
$this->layout = 'xml/default';
$this->header('Content-Disposition: download; filename="misp.search.events.results.xml"');
$conditions['AND'] = array();
$subcondition = array();
$this->loadModel('Attribute');
// add the values as specified in the 2nd parameter to the conditions
$values = explode('&&', $value);
$parameters = array('value', 'type', 'category', 'org');
foreach ($parameters as $k => $param) {
if (isset(${$parameters[$k]})) {
$elements = explode('&&', ${$parameters[$k]});
foreach($elements as $v) {
if (substr($v, 0, 1) == '!') {
if ($parameters[$k] === 'value' && preg_match('@^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$@', substr($v, 1))) {
$cidrresults = $this->Cidr->CIDR(substr($v, 1));
foreach ($cidrresults as $result) {
$subcondition['AND'][] = array('Attribute.value NOT LIKE' => $result);
}
} else {
if ($parameters[$k] === 'org') {
$subcondition['AND'][] = array('Event.' . $parameters[$k] . ' NOT LIKE' => '%'.substr($v, 1).'%');
} else {
$subcondition['AND'][] = array('Attribute.' . $parameters[$k] . ' NOT LIKE' => '%'.substr($v, 1).'%');
}
}
} else {
if ($parameters[$k] === 'value' && preg_match('@^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$@', substr($v, 1))) {
$cidrresults = $this->Cidr->CIDR($v);
foreach ($cidrresults as $result) {
$subcondition['OR'][] = array('Attribute.value LIKE' => $result);
}
} else {
if ($parameters[$k] === 'org') {
$subcondition['OR'][] = array('Event.' . $parameters[$k] . ' LIKE' => '%'.$v.'%');
} else {
$subcondition['OR'][] = array('Attribute.' . $parameters[$k] . ' LIKE' => '%'.$v.'%');
}
}
}
}
array_push ($conditions['AND'], $subcondition);
$subcondition = array();
}
}
// If we are looking for an attribute, we want to retrieve some extra data about the event to be able to check for the permissions.
if (!$user['User']['siteAdmin']) {
$temp = array();
$temp['AND'] = array('Event.distribution >' => 0, 'Attribute.distribution >' => 0);
$subcondition['OR'][] = $temp;
$subcondition['OR'][] = array('Event.org' => $user['User']['org']);
array_push($conditions['AND'], $subcondition);
}
$params = array(
'conditions' => $conditions,
'fields' => array('Attribute.event_id'),
);
$attributes = $this->Attribute->find('all', $params);
$eventIds = array();
foreach ($attributes as $attribute) {
if (!in_array($attribute['Attribute']['event_id'], $eventIds)) $eventIds[] = $attribute['Attribute']['event_id'];
}
if (!empty($eventIds)) {
$results = $this->__fetchEvent(null, $eventIds, $user['User']['org'], true);
} else {
throw new NotFoundException('No matches.');
}
$this->loadModel('Whitelist');
$results = $this->Whitelist->removeWhitelistedFromArray($results, true);
$this->set('results', $results);
}
public function downloadOpenIOCEvent($eventid) {
// return a downloadable text file called misp.openIOC.<eventId>.ioc for individual events
// TODO implement mass download of all events - maybe in a zip file?
$this->response->type('text'); // set the content type
if ($eventid == null) {
throw new Exception('Not yet implemented');
// $this->header('Content-Disposition: download; filename="misp.openIOC.ioc"');
} else {
$this->header('Content-Disposition: download; filename="misp.openIOC' . $eventid . '.ioc"');
}
$this->layout = 'text/default';
// get the event if it exists and load it together with its attributes
$this->Event->id = $eventid;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$this->Event->contain('Attribute');
$event = $this->Event->read(null, $eventid);
foreach ($event['Attribute'] as $k => $attribute) {
if (!$attribute['to_ids']) unset($event['Attribute'][$k]);
}
$this->loadModel('Whitelist');
$temp = $this->Whitelist->removeWhitelistedFromArray(array($event), false);
$event = $temp[0];
//$event['Attribute'] = $this->Whitelist->removeWhitelistedFromArray($event['Attribute'], false);
// set up helper variables for the authorisation check in the component
$isMyEvent = false;
if ($this->Auth->User('org') == $event['Event']['org']) $isMyEvent = true;
$isSiteAdmin = $this->_isSiteAdmin();
// send the event and the vars needed to check authorisation to the Component
$final = $this->IOCExport->buildAll($event, $isMyEvent, $isSiteAdmin);
$this->set('final', $final);
}
public function create_dummy_event() {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException('You don\'t have the privileges to access this.');
$date = new DateTime();
$data['Event']['info'] = 'Test event showing every category-type combination';
$data['Event']['date'] = '2013-10-09';
$data['Event']['threat_level_id'] = 4; //'Undefined'
$data['Event']['analysis'] = '0';
$data['Event']['distribution'] = '0';
$defaultValues = array(
'md5' => '098f6bcd4621d373cade4e832627b4f6',
'sha1' => 'a7645200866fd00bde529733ceac8506ab1f5518',
'sha256' => '0f58957831a9cf0b768451ee6b236555f519c04f0da5a5ea87538fd0990b29d1',
'filename' => 'test.exe',
'filename|md5' => 'test.exe|8886be8e4e862189a68d27e8fc7a6144',
'filename|sha1' => 'test.exe|a7645200866fd00bde529733ceac8506ab1f5518',
'filename|sha256' => 'test.exe|0f58957831a9cf0b768451ee6b236555f519c04f0da5a5ea87538fd0990b29d1',
'ip-src' => '1.1.1.1',
'ip-dst' => '2.2.2.2',
'hostname' => 'www.futuremark.com',
'domain' => 'evildomain.org',
'email-src' => 'bla@bla.com',
'email-dst' => 'hmm@hmm.com',
'email-subject' => 'Some made-up email subject',
'email-attachment' => 'filename.exe',
'url' => 'http://www.evilsite.com/test',
'http-method' => 'POST',
'user-agent' => 'Microsoft Internet Explorer',
'regkey' => 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run\fishy',
'regkey|value' => 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run\fishy|%ProgramFiles%\Malicios\malware.exe',
'AS' => '45566',
'snort' => 'alert ip 1.1.1.1 any -> $HOME_NET any (msg: "MISP e1 Incoming From IP: 1.1.1.1"; classtype:trojan-activity; sid:21; rev:1; priority:1; reference:url,http://localhost:8888/events/view/1;)',
'pattern-in-file' => 'Somestringinfile',
'pattern-in-traffic' => 'Somestringintraffic',
'pattern-in-memory' => 'Somestringinmemory',
'yara' => 'rule silent_banker : banker{meta:description = "This is just an example" thread_level = 3 in_the_wild = true strings: $a = {6A 40 68 00 30 00 00 6A 14 8D 91} $b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9} $c = "UVODFRYSIHLNWPEJXQZAKCBGMT" condition:}',
'vulnerability' => 'CVE-2011-0001',
'attachment' => 'file.txt',
'malware-sample' => 'test.exe|8886be8e4e862189a68d27e8fc7a6144',
'link' => 'http://www.somesite.com/',
'comment' => 'Comment',
'text' => 'Any text',
'other' => 'Could be anything',
'named pipe' => '\\.\pipe\PipeName',
'mutex' => 'mutexstring',
);
$this->loadModel('Attribute');
foreach ($this->Attribute->categoryDefinitions as $category => $v) {
foreach ($v['types'] as $k => $type) {
$data['Attribute'][] = array(
'category' => $category,
'type' => $type,
'value' => $defaultValues[$type],
'to_ids' => '0',
'distribution' => '0',
);
}
}
$this->Event->_add($data, false, $this->Auth->user());
}
public function proposalEventIndex() {
$this->loadModel('ShadowAttribute');
$this->ShadowAttribute->recursive = -1;
$result = $this->ShadowAttribute->find('all', array(
'fields' => array('event_id'),
'group' => 'event_id',
'conditions' => array(
'ShadowAttribute.event_org =' => $this->Auth->user('org'),
)));
$this->Event->recursive = -1;
$conditions = array();
foreach ($result as $eventId) {
$conditions['OR'][] = array('Event.id =' => $eventId['ShadowAttribute']['event_id']);
}
if (empty($result)) {
$conditions['OR'][] = array('Event.id =' => -1);
}
$this->paginate = array(
'conditions' => $conditions,
'contain' => array(
'ThreatLevel' => array(
'fields' => array(
'ThreatLevel.name')),
'User' => array(
'fields' => array(
'User.email'
)),
'ShadowAttribute'=> array(
'fields' => array(
'ShadowAttribute.id'
)),
));
$this->set('events', $this->paginate());
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
$this->set('analysisLevels', $this->Event->analysisLevels);
$this->set('distributionLevels', $this->Event->distributionLevels);
}
private function __setHeaderForAdd($eventId) {
$this->response->header('Location', Configure::read('CyDefSIG.baseurl') . '/events/' . $eventId);
$this->response->send();
}
2013-01-25 08:51:20 +01:00
}