From 221e35827f3c6790e4c136a4aac05bdafae3e05e Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Mon, 11 Nov 2013 23:18:13 +0100 Subject: [PATCH 1/8] First kick at Travis --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..76154f4b2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: php +php: + - 5.5 + - 5.4 + - 5.3 From 7f6c8425ae63477b48bd60724630c7aa1ce8628c Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 12 Nov 2013 15:11:59 +0100 Subject: [PATCH 2/8] Fix to users with auth key access not being able to reset their authkey --- app/Controller/UsersController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index 387ca8c78..abfd1a380 100755 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -407,7 +407,7 @@ class UsersController extends AppController { } $this->User->read(); if ('me' == $id ) $id = $this->Auth->user('id'); - else if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org') == $this->User->data['User']['org'])) throw new MethodNotAllowedException(); + else if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org') == $this->User->data['User']['org']) && ($this->Auth->user('id') != $id)) throw new MethodNotAllowedException(); $newkey = $this->User->generateAuthKey(); $this->User->saveField('authkey', $newkey); $this->Session->setFlash(__('New authkey generated.', true)); From fca87bf491a463f6f214218a0343a07f28646d1d Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Tue, 12 Nov 2013 16:23:37 +0100 Subject: [PATCH 3/8] Initial JSON REST Some small travins changes too. FYI there's an automated travis build available at https://travis-ci.org/MISP/MISP We don't have unit testing and travis setup is subpar so everything will fail for now. --- .travis.yml | 11 +++++ app/Config/routes.php | 2 +- app/Controller/AppController.php | 14 ++++-- app/Controller/AttributesController.php | 62 ++++++++++++------------- app/Controller/EventsController.php | 28 +++++------ app/View/Events/json/index.ctp | 17 +++++++ app/View/Events/json/view.ctp | 36 ++++++++++++++ 7 files changed, 119 insertions(+), 51 deletions(-) create mode 100644 app/View/Events/json/index.ctp create mode 100644 app/View/Events/json/view.ctp diff --git a/.travis.yml b/.travis.yml index 76154f4b2..41af9bde9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,14 @@ php: - 5.5 - 5.4 - 5.3 + +env: + - DB=mysql + +matrix: + allow_failures: + - php: 5.5 + +before_script: + - pecl install Crypt_GPG + - pecl install Net_GeoIP diff --git a/app/Config/routes.php b/app/Config/routes.php index 0b52e108f..3d7779295 100755 --- a/app/Config/routes.php +++ b/app/Config/routes.php @@ -53,7 +53,7 @@ // Activate REST Router::mapResources(array('events', 'attributes')); - Router::parseExtensions('xml'); + Router::parseExtensions('xml', 'json'); /** * Load all plugin routes. See the CakePlugin documentation on diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 16e9b5abc..39d5f06f1 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -70,7 +70,7 @@ class AppController extends Controller { public function beforeFilter() { // REST authentication - if ($this->_isRest()) { + if ($this->_isRest() || $this->isJson()) { // disable CSRF for REST access if (array_key_exists('Security', $this->components)) $this->Security->csrfCheck = false; @@ -142,6 +142,10 @@ class AppController extends Controller { public $userRole = null; + public function isJson(){ + return $this->request->header('Accept') === 'application/json'; + } + //public function blackhole($type) { // // handle errors. // throw new Exception(__d('cake_dev', 'The request has been black-holed')); @@ -211,7 +215,7 @@ class AppController extends Controller { $this->Session->setFlash(__('All done.')); $this->redirect(array('controller' => 'events', 'action' => 'index', 'admin' => false)); } - + public function generateLocked() { if (!self::_isSiteAdmin()) throw new NotFoundException(); $this->loadModel('User'); @@ -222,7 +226,7 @@ class AppController extends Controller { foreach ($orgs as $k => $org) { $orgs[$k]['User']['count'] = $this->User->find('count', array( 'conditions' => array( - 'org =' => $orgs[$k]['User']['org'], + 'org =' => $orgs[$k]['User']['org'], ))); if ($orgs[$k]['User']['count'] > 1) { $localOrgs[] = $orgs[$k]['User']['org']; @@ -231,7 +235,7 @@ class AppController extends Controller { // If we only have a single user for an org, check if that user is a sync user. If not, then it is a valid local org and the events created by him/her should be unlocked. $this->User->recursive = 1; $user = ($this->User->find('first', array( - 'fields' => array('id', 'role_id'), + 'fields' => array('id', 'role_id'), 'conditions' => array('org' => $org['User']['org']), 'contain' => array('Role' => array( 'fields' => array('id', 'perm_sync'), @@ -250,7 +254,7 @@ class AppController extends Controller { 'conditions' => $conditions )); $this->Event->updateAll( - array('Event.locked' => 1), + array('Event.locked' => 1), $conditions ); $this->Session->setFlash('Events updated, '. $toBeUpdated . ' record(s) altered.'); diff --git a/app/Controller/AttributesController.php b/app/Controller/AttributesController.php index e2ab2fb39..cd170da9d 100755 --- a/app/Controller/AttributesController.php +++ b/app/Controller/AttributesController.php @@ -21,7 +21,7 @@ class AttributesController extends AppController { public function beforeFilter() { parent::beforeFilter(); - + $this->Auth->allow('restSearch'); $this->Auth->allow('returnAttributes'); $this->Auth->allow('downloadAttachment'); @@ -251,10 +251,10 @@ class AttributesController extends AppController { throw new NotFoundException(__('Invalid attribute')); } $this->Attribute->read(); - if (!$this->_isSiteAdmin() && - $this->Auth->user('org') != - $this->Attribute->data['Event']['org'] && - ($this->Attribute->data['Event']['distribution'] == 0 || + if (!$this->_isSiteAdmin() && + $this->Auth->user('org') != + $this->Attribute->data['Event']['org'] && + ($this->Attribute->data['Event']['distribution'] == 0 || $this->Attribute->data['Attribute']['distribution'] == 0 )) { throw new UnauthorizedException('You do not have the permission to view this event.'); @@ -532,7 +532,7 @@ class AttributesController extends AppController { $attribute['distribution'] = $this->Event->data['Event']['distribution']; } else { $attribute['distribution'] = Configure::read('MISP.default_attribute_distribution'); - } + } } switch($entry['Type']) { case 'Address': @@ -644,9 +644,9 @@ class AttributesController extends AppController { $uuid = $this->Attribute->data['Attribute']['uuid']; } if (!$this->_isSiteAdmin()) { - // + // if ($this->Attribute->data['Event']['orgc'] == $this->Auth->user('org') - && (($this->userRole['perm_modify'] && $this->Attribute->data['Event']['user_id'] != $this->Auth->user('id')) + && (($this->userRole['perm_modify'] && $this->Attribute->data['Event']['user_id'] != $this->Auth->user('id')) || $this->userRole['perm_modify_org'])) { // Allow the edit } else { @@ -785,10 +785,10 @@ class AttributesController extends AppController { } else { if ($this->_checkOrg() != $this->Attribute->data['Event']['orgc']) { throw new MethodNotAllowedException(); - } + } } } - + // attachment will be deleted with the beforeDelete() function in the Model if ($this->Attribute->delete()) { // delete the attribute from remote servers @@ -796,7 +796,7 @@ class AttributesController extends AppController { // find the uuid $this->__deleteAttributeFromServers($uuid); } - + // We have just deleted the attribute, let's also check if there are any shadow attributes that were attached to it and delete them $this->loadModel('ShadowAttribute'); $this->ShadowAttribute->deleteAll(array('ShadowAttribute.old_id' => $id), false); @@ -1074,7 +1074,7 @@ class AttributesController extends AppController { $this->set('fails', $this->Attribute->checkComposites()); } - + // Use the rest interface to search for attributes. Usage: // MISP-base-url/attributes/restSearch/[api-key]/[value]/[type]/[category]/[orgc] // value, type, category, orgc are optional @@ -1095,7 +1095,7 @@ class AttributesController extends AppController { // 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]}); @@ -1110,9 +1110,9 @@ class AttributesController extends AppController { $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); @@ -1120,23 +1120,23 @@ class AttributesController extends AppController { $subcondition['OR'][] = array('Event.org' => $user['User']['org']); array_push($conditions['AND'], $subcondition); } - + // change the fields here for the attribute export!!!! Don't forget to check for the permissions, since you are not going through fetchevent. Maybe create fetchattribute? - + $params = array( 'conditions' => $conditions, 'fields' => array('Attribute.*', 'Event.org', 'Event.distribution'), 'contain' => 'Event' ); - + $results = $this->Attribute->find('all', $params); $this->loadModel('Whitelist'); $results = $this->Whitelist->removeWhitelistedFromArray($results, false); if (empty($results)) throw new NotFoundException('No matches.'); $this->set('results', $results); } - - // returns an XML with attributes that belong to an event. The type of attributes to be returned can be restricted by type using the 3rd parameter. + + // returns an XML with attributes that belong to an event. The type of attributes to be returned can be restricted by type using the 3rd parameter. // Similar to the restSearch, this parameter can be chained with '&&' and negations are accepted too. For example filename&&!filename|md5 would return all filenames that don't have an md5 // The usage of returnAttributes is the following: [MISP-url]/attributes/returnAttributes/// // The signature flag is off by default, enabling it will only return attribugtes that have the to_ids flag set to true. @@ -1153,11 +1153,11 @@ class AttributesController extends AppController { if ($user['User']['siteAdmin'] || $this->Event->data['Event']['org'] == $user['User']['org']) { $myEventOrAdmin = true; } - + if (!$myEventOrAdmin) { if ($this->Event->data['Event']['distribution'] == 0) { throw new UnauthorizedException('You don\'t have access to that event.'); - } + } } $this->response->type('xml'); // set the content type $this->layout = 'xml/default'; @@ -1178,7 +1178,7 @@ class AttributesController extends AppController { } } } - + // check each attribute foreach($this->Event->data['Attribute'] as $k => $attribute) { $contained = false; @@ -1199,8 +1199,8 @@ class AttributesController extends AppController { foreach ($exclude as $exc) { if (strpos($attribute['type'], $exc) !== false) { $contained = false; - continue 2; - } + continue 2; + } } } // If we still didn't throw the attribute away, let's check if the user requesting the attributes is of the owning organisation of the event @@ -1208,19 +1208,19 @@ class AttributesController extends AppController { if ($contained && !$myEventOrAdmin && $attribute['distribution'] == 0) { $contained = false; } - + // If we have set the sigOnly parameter and the attribute has to_ids set to false, discard it! if ($contained && $sigOnly === 'true' && !$attribute['to_ids']) { $contained = false; } - + // If after all of this $contained is still true, let's add the attribute to the array if ($contained) $attributes[] = $attribute; } if (empty($attributes)) throw new NotFoundException('No matches.'); $this->set('results', $attributes); } - + public function downloadAttachment($key, $id) { $user = $this->checkAuthUser($key); // if the user is authorised to use the api key then user will be populated with the user's account @@ -1233,9 +1233,9 @@ class AttributesController extends AppController { throw new NotFoundException('Invalid attribute or no authorisation to view it.'); } $this->Attribute->read(null, $id); - if (!$user['User']['siteAdmin'] && - $user['User']['org'] != $this->Attribute->data['Event']['org'] && - ($this->Attribute->data['Event']['distribution'] == 0 || + if (!$user['User']['siteAdmin'] && + $user['User']['org'] != $this->Attribute->data['Event']['org'] && + ($this->Attribute->data['Event']['distribution'] == 0 || $this->Attribute->data['Attribute']['distribution'] == 0 )) { throw new NotFoundException('Invalid attribute or no authorisation to view it.'); diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 6e6fcab92..3246aa1c9 100755 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -531,7 +531,7 @@ class EventsController extends AppController { $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')); @@ -542,8 +542,8 @@ class EventsController extends AppController { } } } - - + + /** * Low level function to add an Event based on an Event $data array * @@ -816,7 +816,7 @@ class EventsController extends AppController { $this->set('analysisLevels', $this->Event->analysisLevels); $this->set('eventDescriptions', $this->Event->fieldDescriptions); - + $this->set('event', $this->Event->data); } @@ -1969,17 +1969,17 @@ class EventsController extends AppController { $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), + + // 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; @@ -1991,7 +1991,7 @@ class EventsController extends AppController { } } } - + public function _readGfiXML($data, $id) { $this->loadModel('Attribute'); @@ -2281,7 +2281,7 @@ class EventsController extends AppController { $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(); @@ -2289,7 +2289,7 @@ class EventsController extends AppController { $data['Event']['date'] = '2013-10-09'; $data['Event']['risk'] = 'Undefined'; $data['Event']['analysis'] = '0'; - $data['Event']['distribution'] = '0'; + $data['Event']['distribution'] = '0'; $defaultValues = array( 'md5' => '098f6bcd4621d373cade4e832627b4f6', @@ -2336,11 +2336,11 @@ class EventsController extends AppController { 'type' => $type, 'value' => $defaultValues[$type], 'to_ids' => '0', - 'distribution' => '0', + 'distribution' => '0', ); } } - $this->_add($data, false); + $this->_add($data, false); } - + } diff --git a/app/View/Events/json/index.ctp b/app/View/Events/json/index.ctp new file mode 100644 index 000000000..7ff58911a --- /dev/null +++ b/app/View/Events/json/index.ctp @@ -0,0 +1,17 @@ + &$event) { + // rearrange things to be compatible with the Xml::fromArray() + $events[$key] = $events[$key]['Event']; + unset($events[$key]['Event']); + + // cleanup the array from things we do not want to expose + unset($events[$key]['user_id']); + // hide the org field is we are not in showorg mode + if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) { + unset($events[$key]['org']); + unset($events[$key]['orgc']); + unset($events[$key]['from']); + } + +} +echo json_encode($events); \ No newline at end of file diff --git a/app/View/Events/json/view.ctp b/app/View/Events/json/view.ctp new file mode 100644 index 000000000..e593295d9 --- /dev/null +++ b/app/View/Events/json/view.ctp @@ -0,0 +1,36 @@ + $value) { + unset($event['Event']['Attribute'][$key]['value1']); + unset($event['Event']['Attribute'][$key]['value2']); + unset($event['Event']['Attribute'][$key]['category_order']); +} +if (isset($event['Event']['RelatedEvent'])) { + foreach ($event['Event']['RelatedEvent'] as $key => $value) { + unset($event['Event']['RelatedEvent'][$key]['user_id']); + if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) { + unset($event['Event']['RelatedEvent'][$key]['org']); + unset($event['Event']['RelatedEvent'][$key]['orgc']); + } + } +} + +if (isset($relatedEvents)) { + foreach ($relatedEvents as $relatedEvent) { + $event['Event']['RelatedEvent'][] = $relatedEvent['Event']; + } +} +echo json_encode($event); \ No newline at end of file From 6e3363825f178365aaf4a0410a391095aaa12db7 Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Tue, 12 Nov 2013 16:46:17 +0100 Subject: [PATCH 4/8] PHP 5.4 E_STRICT fix --- plugins/Assets/models/behaviors/LogableBehavior.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Assets/models/behaviors/LogableBehavior.php b/plugins/Assets/models/behaviors/LogableBehavior.php index 0d5afa94a..0c7171ff5 100644 --- a/plugins/Assets/models/behaviors/LogableBehavior.php +++ b/plugins/Assets/models/behaviors/LogableBehavior.php @@ -364,7 +364,7 @@ class LogableBehavior extends ModelBehavior { $this->_saveLog($Model, $logData); } - function beforeSave(Model $Model) { + function beforeSave(Model $Model, $options = array()) { if (isset($this->schema['change']) && $Model->id) { $this->old = $Model->find('first', array( @@ -375,7 +375,7 @@ class LogableBehavior extends ModelBehavior { return true; } - function afterSave(Model $Model, $created) { + function afterSave(Model $Model, $created, $options = array()) { if (!$this->settings[$Model->alias]['enabled']) { return true; From 67eb13880076cf0de0cd9c249a734822969e6f47 Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Wed, 13 Nov 2013 17:22:24 +0100 Subject: [PATCH 5/8] Display footer notice of missing PGP/GPG key --- app/View/Elements/footer.ctp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/View/Elements/footer.ctp b/app/View/Elements/footer.ctp index 26827dfe9..855e14844 100644 --- a/app/View/Elements/footer.ctp +++ b/app/View/Elements/footer.ctp @@ -2,7 +2,13 @@ -element('side_menu', array('menuList' => 'event-collection', 'menuItem' => 'index')); ?> diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index cfdcb9fc5..dfc59c98e 100755 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -2,7 +2,7 @@ $mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id']) || ($isAclModifyOrg && $event['Event']['orgc'] == $me['org'])); $mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']); ?> -element('side_menu', array('menuList' => 'event', 'menuItem' => 'viewEvent')); ?> @@ -54,9 +54,9 @@ $mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']);   -
Risk
+
Risk
- +  
Analysis
@@ -203,7 +203,7 @@ if (!empty($event['Attribute'])):?> $first = 0; ?>
    - '; @@ -394,7 +394,7 @@ if (!empty($event['Attribute'])):?> endif; ?>
    - element('eventdiscussion'); ?>
    diff --git a/app/View/Users/admin_view.ctp b/app/View/Users/admin_view.ctp index f32eec03f..2eb97ccdb 100755 --- a/app/View/Users/admin_view.ctp +++ b/app/View/Users/admin_view.ctp @@ -112,7 +112,7 @@ if (h($user['User']['change_pw']) == 1) { - + @@ -131,6 +131,6 @@ if (h($user['User']['change_pw']) == 1) { endif; ?> -element('side_menu', array('menuList' => 'admin', 'menuItem' => 'viewUser')); ?> \ No newline at end of file From 5f155f47ccf9a074df7e60eadef22555c69d9b48 Mon Sep 17 00:00:00 2001 From: Alexandru Ciobanu Date: Mon, 18 Nov 2013 16:36:18 +0100 Subject: [PATCH 8/8] Fixed validation on Event::_add() Try atomic save for events Add threat level to JSON sample --- app/Controller/EventsController.php | 2 +- tools/curl/input/event.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 0d10fbb79..21efd62fc 100755 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -597,7 +597,7 @@ class EventsController extends AppController { 'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'timestamp', 'distribution') ); - $saveResult = $this->Event->saveAssociated($data, array('validate' => false, 'fieldList' => $fieldList, + $saveResult = $this->Event->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList, 'atomic' => true)); // FIXME chri: check if output of $saveResult is what we expect when data not valid, see issue #104 if ($saveResult) { diff --git a/tools/curl/input/event.json b/tools/curl/input/event.json index f0c6619db..5dbe07233 100644 --- a/tools/curl/input/event.json +++ b/tools/curl/input/event.json @@ -3,7 +3,7 @@ "id": "15", "org": "ORG", "date": "2012-04-12", - "risk": "Medium", + "threat_level_id": "4", "info": "info", "user_id": "1", "uuid": "4f8c2c4e-00dc-42c9-83ad-76e9ff32448e",