60, 'recursive' => -1, 'contain' => array( 'User' => array( 'fields' => array('User.id', 'User.org_id', 'User.email'), ), 'Organisation' => array( 'fields' => array('Organisation.name', 'Organisation.id'), ), 'RemoteOrg' => array( 'fields' => array('RemoteOrg.name', 'RemoteOrg.id'), ), ), 'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events 'order' => array( 'Server.priority' => 'ASC' ), ); public $uses = array('Server', 'Event'); public function beforeFilter() { $this->Auth->allow(['cspReport']); // cspReport must work without authentication parent::beforeFilter(); $this->Security->unlockedActions[] = 'cspReport'; // permit reuse of CSRF tokens on some pages. switch ($this->request->params['action']) { case 'push': case 'pull': case 'getVersion': case 'testConnection': $this->Security->csrfUseOnce = false; } } public function index() { // Do not fetch server authkey from DB $fields = $this->Server->schema(); unset($fields['authkey']); $fields = array_keys($fields); if ($this->_isRest()) { $params = array( 'fields' => $fields, 'recursive' => -1, 'contain' => array( 'User' => array( 'fields' => array('User.id', 'User.org_id', 'User.email'), ), 'Organisation' => array( 'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid', 'Organisation.nationality', 'Organisation.sector', 'Organisation.type'), ), 'RemoteOrg' => array( 'fields' => array('RemoteOrg.id', 'RemoteOrg.name', 'RemoteOrg.uuid', 'RemoteOrg.nationality', 'RemoteOrg.sector', 'RemoteOrg.type'), ), ), ); $servers = $this->Server->find('all', $params); $servers = $this->Server->attachServerCacheTimestamps($servers); return $this->RestResponse->viewData($servers, $this->response->type()); } else { $this->paginate['fields'] = $fields; $servers = $this->paginate(); $servers = $this->Server->attachServerCacheTimestamps($servers); $this->set('servers', $servers); $collection = array(); $collection['orgs'] = $this->Server->Organisation->find('list', array( 'fields' => array('id', 'name'), )); $this->loadModel('Tag'); $collection['tags'] = $this->Tag->find('list', array( 'fields' => array('id', 'name'), )); $this->set('collection', $collection); } } public function previewIndex($id) { $urlparams = ''; $passedArgs = array(); $server = $this->Server->find('first', array('conditions' => array('Server.id' => $id), 'recursive' => -1)); if (empty($server)) { throw new NotFoundException('Invalid server ID.'); } $validFilters = $this->Server->validEventIndexFilters; foreach ($validFilters as $k => $filter) { if (isset($this->passedArgs[$filter])) { $passedArgs[$filter] = $this->passedArgs[$filter]; if ($k != 0) { $urlparams .= '/'; } $urlparams .= $filter . ':' . $this->passedArgs[$filter]; } } $combinedArgs = array_merge($this->passedArgs, $passedArgs); if (!isset($combinedArgs['sort'])) { $combinedArgs['sort'] = 'timestamp'; $combinedArgs['direction'] = 'desc'; } if (empty($combinedArgs['page'])) { $combinedArgs['page'] = 1; } if (empty($combinedArgs['limit'])) { $combinedArgs['limit'] = 60; } try { list($events, $total_count) = $this->Server->previewIndex($server, $this->Auth->user(), $combinedArgs); } catch (Exception $e) { if ($this->_isRest()) { return $this->RestResponse->throwException(500, $e->getMessage()); } else { $this->Flash->error(__('Download failed.') . ' ' . $e->getMessage()); $this->redirect(array('action' => 'index')); } } if ($this->_isRest()) { return $this->RestResponse->viewData($events, $this->response->type()); } $this->loadModel('Event'); $this->set('threatLevels', $this->Event->ThreatLevel->listThreatLevels()); App::uses('CustomPaginationTool', 'Tools'); $customPagination = new CustomPaginationTool(); $params = $customPagination->createPaginationRules($events, $this->passedArgs, $this->alias); if (!empty($total_count)) { $params['pageCount'] = ceil($total_count / $params['limit']); } $this->params->params['paging'] = array($this->modelClass => $params); if (count($events) > 60) { $customPagination->truncateByPagination($events, $params); } $this->set('events', $events); $this->set('eventDescriptions', $this->Event->fieldDescriptions); $this->set('analysisLevels', $this->Event->analysisLevels); $this->set('distributionLevels', $this->Event->distributionLevels); $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group'); $this->set('shortDist', $shortDist); $this->set('id', $id); $this->set('urlparams', $urlparams); $this->set('passedArgs', json_encode($passedArgs)); $this->set('passedArgsArray', $passedArgs); $this->set('server', $server); } public function previewEvent($serverId, $eventId, $all = false) { $server = $this->Server->find('first', array( 'conditions' => array('Server.id' => $serverId), 'recursive' => -1, )); if (empty($server)) { throw new NotFoundException('Invalid server ID.'); } try { $event = $this->Server->previewEvent($server, $eventId); } catch (NotFoundException $e) { throw new NotFoundException(__("Event '%s' not found.", $eventId)); } catch (Exception $e) { $this->Flash->error(__('Download failed. %s', $e->getMessage())); $this->redirect(array('action' => 'previewIndex', $serverId)); } if ($this->_isRest()) { return $this->RestResponse->viewData($event, $this->response->type()); } $this->loadModel('Warninglist'); if (isset($event['Event']['Attribute'])) { $this->Warninglist->attachWarninglistToAttributes($event['Event']['Attribute']); } if (isset($event['Event']['ShadowAttribute'])) { $this->Warninglist->attachWarninglistToAttributes($event['Event']['ShadowAttribute']); } $this->loadModel('Event'); $params = $this->Event->rearrangeEventForView($event, $this->passedArgs, $all); $this->__removeGalaxyClusterTags($event); $this->params->params['paging'] = array('Server' => $params); $this->set('event', $event); $this->set('server', $server); $dataForView = array( 'Attribute' => array('attrDescriptions' => 'fieldDescriptions', 'distributionDescriptions' => 'distributionDescriptions', 'distributionLevels' => 'distributionLevels'), 'Event' => array('eventDescriptions' => 'fieldDescriptions', 'analysisLevels' => 'analysisLevels'), 'Object' => array() ); foreach ($dataForView as $m => $variables) { if ($m === 'Event') { $currentModel = $this->Event; } elseif ($m === 'Attribute') { $currentModel = $this->Event->Attribute; } elseif ($m === 'Object') { $currentModel = $this->Event->Object; } foreach ($variables as $alias => $variable) { $this->set($alias, $currentModel->{$variable}); } } $this->set('threatLevels', $this->Event->ThreatLevel->listThreatLevels()); $this->set('title_for_layout', __('Remote event preview')); } private function __removeGalaxyClusterTags(array &$event) { $galaxyTagIds = []; foreach ($event['Galaxy'] as $galaxy) { foreach ($galaxy['GalaxyCluster'] as $galaxyCluster) { $galaxyTagIds[$galaxyCluster['tag_id']] = true; } } if (empty($galaxyTagIds)) { return; } foreach ($event['Tag'] as $k => $eventTag) { if (isset($galaxyTagIds[$eventTag['id']])) { unset($event['Tag'][$k]); } } } public function compareServers() { list($servers, $overlap) = $this->Server->serverEventsOverlap(); $this->set('servers', $servers); $this->set('overlap', $overlap); $this->set('title_for_layout', __('Server overlap analysis matrix')); } public function filterEventIndex($id) { if (!$this->_isSiteAdmin()) { throw new MethodNotAllowedException('You are not authorised to do that.'); } $validFilters = $this->Server->validEventIndexFilters; $validatedFilterString = ''; foreach ($this->passedArgs as $k => $v) { if (in_array('' . $k, $validFilters)) { if ($validatedFilterString != '') { $validatedFilterString .= '/'; } $validatedFilterString .= $k . ':' . $v; } } $this->set('id', $id); $this->set('validFilters', $validFilters); $this->set('filter', $validatedFilterString); } public function add() { if ($this->request->is('post')) { if ($this->_isRest()) { if (!isset($this->request->data['Server'])) { $this->request->data = array('Server' => $this->request->data); } } if (!empty($this->request->data['Server']['json'])) { $json = json_decode($this->request->data['Server']['json'], true); } elseif ($this->_isRest()) { if (empty($this->request->data['Server']['remote_org_id'])) { throw new MethodNotAllowedException('No remote org ID set. Please pass it as remote_org_id'); } } $fail = false; if (empty(Configure::read('MISP.host_org_id'))) { $this->request->data['Server']['internal'] = 0; } // test the filter fields if (!empty($this->request->data['Server']['pull_rules']) && !JsonTool::isValid($this->request->data['Server']['pull_rules'])) { $fail = true; $error_msg = __('The pull filter rules must be in valid JSON format.'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('pull_rules' => $error_msg), $this->response->type()); } else { $this->Flash->error($error_msg); } } if (!$fail && !empty($this->request->data['Server']['push_rules']) && !JsonTool::isValid($this->request->data['Server']['push_rules'])) { $fail = true; $error_msg = __('The push filter rules must be in valid JSON format.'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('push_rules' => $error_msg), $this->response->type()); } else { $this->Flash->error($error_msg); } } if (!$fail) { if ($this->_isRest()) { $defaultPushRules = json_encode(["tags" => ["OR" => [], "NOT" => []], "orgs" => ["OR" => [], "NOT" => []]]); $defaultPullRules = json_encode(["tags" => ["OR" => [], "NOT" => []], "orgs" => ["OR" => [], "NOT" => []], "type_attributes" => ["NOT" => []], "type_objects" => ["NOT" => []], "url_params" => ""]); $defaults = array( 'push' => 0, 'pull' => 0, 'push_sightings' => 0, 'push_galaxy_clusters' => 0, 'pull_galaxy_clusters' => 0, 'caching_enabled' => 0, 'json' => '[]', 'push_rules' => $defaultPushRules, 'pull_rules' => $defaultPullRules, 'self_signed' => 0, 'remove_missing_tags' => 0 ); foreach ($defaults as $default => $dvalue) { if (!isset($this->request->data['Server'][$default])) { $this->request->data['Server'][$default] = $dvalue; } } } // force check userid and orgname to be from yourself $this->request->data['Server']['org_id'] = $this->Auth->user('org_id'); if ($this->_isRest()) { if (empty($this->request->data['Server']['remote_org_id'])) { return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('Organisation' => 'Remote Organisation\'s id/uuid not given (remote_org_id)'), $this->response->type()); } if (Validation::uuid($this->request->data['Server']['remote_org_id'])) { $orgCondition = array('uuid' => $this->request->data['Server']['remote_org_id']); } else { $orgCondition = array('id' => $this->request->data['Server']['remote_org_id']); } $existingOrgs = $this->Server->Organisation->find('first', array( 'conditions' => $orgCondition, 'recursive' => -1, 'fields' => array('id', 'uuid') )); if (empty($existingOrgs)) { return $this->RestResponse->saveFailResponse('Servers', 'add', false, array('Organisation' => 'Invalid Remote Organisation'), $this->response->type()); } } else { if ($this->request->data['Server']['organisation_type'] < 2) { $this->request->data['Server']['remote_org_id'] = $json['id']; } else { $existingOrgs = $this->Server->Organisation->find('first', array( 'conditions' => array('uuid' => $json['uuid']), 'recursive' => -1, 'fields' => array('id', 'uuid') )); if (!empty($existingOrgs)) { $fail = true; $this->Flash->error(__('That organisation could not be created as the uuid is in use already.')); } if (!$fail) { $this->Server->Organisation->create(); $orgSave = $this->Server->Organisation->save(array( 'name' => $json['name'], 'uuid' => $json['uuid'], 'local' => 0, 'created_by' => $this->Auth->user('id') )); if (!$orgSave) { $this->Flash->error(__('Couldn\'t save the new organisation, are you sure that the uuid is in the correct format? Also, make sure the organisation\'s name doesn\'t clash with an existing one.')); $fail = true; $this->request->data['Server']['external_name'] = $json['name']; $this->request->data['Server']['external_uuid'] = $json['uuid']; } else { $this->request->data['Server']['remote_org_id'] = $this->Server->Organisation->id; $this->request->data['Server']['organisation_type'] = 1; } } } } if (!$fail) { if (Configure::read('MISP.host_org_id') == 0 || $this->request->data['Server']['remote_org_id'] != Configure::read('MISP.host_org_id')) { $this->request->data['Server']['internal'] = 0; } $this->request->data['Server']['org_id'] = $this->Auth->user('org_id'); if (empty($this->request->data['Server']['push_rules'])) { $this->request->data['Server']['push_rules'] = $defaultPushRules; } if (empty($this->request->data['Server']['pull_rules'])) { $this->request->data['Server']['pull_rules'] = $defaultPullRules; } if ($this->Server->save($this->request->data)) { if (isset($this->request->data['Server']['submitted_cert'])) { $this->__saveCert($this->request->data, $this->Server->id, false); } if (isset($this->request->data['Server']['submitted_client_cert'])) { $this->__saveCert($this->request->data, $this->Server->id, true); } if ($this->_isRest()) { $server = $this->Server->find('first', array( 'conditions' => array('Server.id' => $this->Server->id), 'recursive' => -1 )); return $this->RestResponse->viewData($server, $this->response->type()); } else { $this->Flash->success(__('The server has been saved')); $this->redirect(array('action' => 'index')); } } else { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'add', false, $this->Server->validationErrors, $this->response->type()); } else { $this->Flash->error(__('The server could not be saved. Please, try again.')); } } } } } if ($this->_isRest()) { return $this->RestResponse->describe('Servers', 'add', false, $this->response->type()); } else { $organisationOptions = array(0 => 'Local organisation', 1 => 'External organisation', 2 => 'New external organisation'); $temp = $this->Server->Organisation->find('all', array( 'fields' => array('id', 'name', 'local'), 'order' => array('lower(Organisation.name) ASC') )); $allOrgs = []; $localOrganisations = array(); $externalOrganisations = array(); foreach ($temp as $o) { if ($o['Organisation']['local']) { $localOrganisations[$o['Organisation']['id']] = $o['Organisation']['name']; } else { $externalOrganisations[$o['Organisation']['id']] = $o['Organisation']['name']; } $allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']); } $allTypes = $this->Server->getAllTypes(); $this->set('host_org_id', Configure::read('MISP.host_org_id')); $this->set('organisationOptions', $organisationOptions); $this->set('localOrganisations', $localOrganisations); $this->set('externalOrganisations', $externalOrganisations); $this->set('allOrganisations', $allOrgs); $this->set('allAttributeTypes', $allTypes['attribute']); $this->set('allObjectTypes', $allTypes['object']); $this->set('allTags', $this->__getTags()); $this->set('host_org_id', Configure::read('MISP.host_org_id')); $this->set('pull_scope', 'server'); $this->render('edit'); } } public function edit($id = null) { $this->Server->id = $id; if (!$this->Server->exists()) { throw new NotFoundException(__('Invalid server')); } $s = $this->Server->read(null, $id); if ($this->request->is('post') || $this->request->is('put')) { if ($this->_isRest()) { if (!isset($this->request->data['Server'])) { $this->request->data = array('Server' => $this->request->data); } } if (empty(Configure::read('MISP.host_org_id'))) { $this->request->data['Server']['internal'] = 0; } if (isset($this->request->data['Server']['json'])) { $json = json_decode($this->request->data['Server']['json'], true); } else { $json = null; } $fail = false; // test the filter fields if (!empty($this->request->data['Server']['pull_rules']) && !JsonTool::isValid($this->request->data['Server']['pull_rules'])) { $fail = true; $error_msg = __('The pull filter rules must be in valid JSON format.'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('pull_rules' => $error_msg), $this->response->type()); } else { $this->Flash->error($error_msg); } } if (!$fail && !empty($this->request->data['Server']['push_rules']) && !JsonTool::isValid($this->request->data['Server']['push_rules'])) { $fail = true; $error_msg = __('The push filter rules must be in valid JSON format.'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('push_rules' => $error_msg), $this->response->type()); } else { $this->Flash->error($error_msg); } } $pushRules = $this->_jsonDecode($this->request->data['Server']['push_rules']); $this->loadModel('Tag'); foreach ($pushRules['tags'] as $operator => $list) { foreach ($list as $i => $tagName) { if (!is_numeric($tagName)) { // tag added from freetext $tag_id = $this->Tag->captureTag(['name' => $tagName], $this->Auth->user()); $list[$i] = $tag_id; } } } if (!$fail) { // say what fields are to be updated $fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy'); $this->request->data['Server']['id'] = $id; if (isset($this->request->data['Server']['authkey']) && "" != $this->request->data['Server']['authkey']) { $fieldList[] = 'authkey'; } if (isset($this->request->data['Server']['organisation_type']) && isset($json)) { // adds 'remote_org_id' in the fields to update $fieldList[] = 'remote_org_id'; if ($this->request->data['Server']['organisation_type'] < 2) { $this->request->data['Server']['remote_org_id'] = $json['id']; } else { $existingOrgs = $this->Server->Organisation->find('first', array( 'conditions' => array('uuid' => $json['uuid']), 'recursive' => -1, 'fields' => array('id', 'uuid') )); if (!empty($existingOrgs)) { $fail = true; if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'edit', false, array('Organisation' => 'Remote Organisation\'s uuid already used'), $this->response->type()); } else { $this->Flash->error(__('That organisation could not be created as the uuid is in use already.')); } } if (!$fail) { $this->Server->Organisation->create(); $orgSave = $this->Server->Organisation->save(array( 'name' => $json['name'], 'uuid' => $json['uuid'], 'local' => 0, 'created_by' => $this->Auth->user('id') )); if (!$orgSave) { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'edit', false, $this->Server->Organisation->validationError, $this->response->type()); } else { $this->Flash->error(__('Couldn\'t save the new organisation, are you sure that the uuid is in the correct format?.')); } $fail = true; $this->request->data['Server']['external_name'] = $json['name']; $this->request->data['Server']['external_uuid'] = $json['uuid']; } else { $this->request->data['Server']['remote_org_id'] = $this->Server->Organisation->id; } } } if (empty(Configure::read('MISP.host_org_id')) || $this->request->data['Server']['remote_org_id'] != Configure::read('MISP.host_org_id')) { $this->request->data['Server']['internal'] = 0; } } } if (!$fail) { // Save the data if ($this->Server->save($this->request->data, true, $fieldList)) { if (isset($this->request->data['Server']['submitted_cert']) && (!isset($this->request->data['Server']['delete_cert']) || !$this->request->data['Server']['delete_cert'])) { $this->__saveCert($this->request->data, $this->Server->id, false); } else { if (isset($this->request->data['Server']['delete_cert']) && $this->request->data['Server']['delete_cert']) { $this->__saveCert($this->request->data, $this->Server->id, false, true); } } if (isset($this->request->data['Server']['submitted_client_cert']) && (!isset($this->request->data['Server']['delete_client_cert']) || !$this->request->data['Server']['delete_client_cert'])) { $this->__saveCert($this->request->data, $this->Server->id, true); } else { if (isset($this->request->data['Server']['delete_client_cert']) && $this->request->data['Server']['delete_client_cert']) { $this->__saveCert($this->request->data, $this->Server->id, true, true); } } if ($this->_isRest()) { $server = $this->Server->find('first', array( 'conditions' => array('Server.id' => $this->Server->id), 'recursive' => -1 )); return $this->RestResponse->viewData($server, $this->response->type()); } else { $this->Flash->success(__('The server has been saved')); $this->redirect(array('action' => 'index')); } } else { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'edit', false, $this->Server->validationError, $this->response->type()); } else { $this->Flash->error(__('The server could not be saved. Please, try again.')); } } } } else { $this->Server->read(null, $id); $this->Server->set('authkey', ''); $this->request->data = $this->Server->data; } if ($this->_isRest()) { return $this->RestResponse->describe('Servers', 'edit', false, $this->response->type()); } else { $organisationOptions = array(0 => 'Local organisation', 1 => 'External organisation', 2 => 'New external organisation'); $temp = $this->Server->Organisation->find('all', array( 'fields' => array('id', 'name', 'local'), 'order' => array('lower(Organisation.name) ASC') )); $allOrgs = []; $localOrganisations = array(); $externalOrganisations = array(); foreach ($temp as $o) { if ($o['Organisation']['local']) { $localOrganisations[$o['Organisation']['id']] = $o['Organisation']['name']; } else { $externalOrganisations[$o['Organisation']['id']] = $o['Organisation']['name']; } $allOrgs[] = array('id' => $o['Organisation']['id'], 'name' => $o['Organisation']['name']); } $allTypes = $this->Server->getAllTypes(); $oldRemoteSetting = 0; if (!$this->Server->data['RemoteOrg']['local']) { $oldRemoteSetting = 1; } $this->set('host_org_id', Configure::read('MISP.host_org_id')); $this->set('oldRemoteSetting', $oldRemoteSetting); $this->set('oldRemoteOrg', $this->Server->data['RemoteOrg']['id']); $this->set('organisationOptions', $organisationOptions); $this->set('localOrganisations', $localOrganisations); $this->set('externalOrganisations', $externalOrganisations); $this->set('allOrganisations', $allOrgs); $this->set('allTags', $this->__getTags()); $this->set('allAttributeTypes', $allTypes['attribute']); $this->set('allObjectTypes', $allTypes['object']); $this->set('server', $s); $this->set('id', $id); $this->set('host_org_id', Configure::read('MISP.host_org_id')); $this->set('pull_scope', 'server'); } } public function delete($id = null) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(__('This endpoint expects POST requests.')); } $this->Server->id = $id; if (!$this->Server->exists()) { throw new NotFoundException(__('Invalid server')); } $s = $this->Server->read(null, $id); if ($this->Server->delete()) { $message = __('Server deleted'); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'delete', $message, $this->response->type()); } else { $this->Flash->success($message); $this->redirect(array('controller' => 'servers', 'action' => 'index')); } } $message = __('Server was not deleted'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'delete', $id, $message, $this->response->type()); } else { $this->Flash->error($message); $this->redirect(array('action' => 'index')); } } public function eventBlockRule() { $this->AdminSetting = ClassRegistry::init('AdminSetting'); $setting = $this->AdminSetting->find('first', [ 'conditions' => ['setting' => 'eventBlockRule'], 'recursive' => -1 ]); if (empty($setting)) { $setting = ['setting' => 'eventBlockRule']; if ($this->request->is('post')) { $this->AdminSetting->create(); } } if ($this->request->is('post')) { if (!empty($this->request->data['Server'])) { $this->request->data = $this->request->data['Server']; } $setting['AdminSetting']['setting'] = 'eventBlockRule'; $setting['AdminSetting']['value'] = $this->request->data['value']; $result = $this->AdminSetting->save($setting); if ($result) { $message = __('Settings saved'); } else { $message = __('Could not save the settings. Invalid input.'); } if ($this->_isRest()) { if ($result) { return $this->RestResponse->saveFailResponse('Servers', 'eventBlockRule', false, $message, $this->response->type()); } else { return $this->RestResponse->saveSuccessResponse('Servers', 'eventBlockRule', $message, $this->response->type()); } } else { if ($result) { $this->Flash->success($message); $this->redirect('/'); } else { $this->Flash->error($message); } } } $this->set('setting', $setting); } /** * Pull one or more events with attributes from a remote instance. * Set $technique to * full - download everything * incremental - only new events * - specific id of the event to pull */ public function pull($id = null, $technique = 'full') { if (empty($id)) { if (!empty($this->request->data['id'])) { $id = $this->request->data['id']; } else { throw new NotFoundException(__('Invalid server')); } } $s = $this->Server->find('first', [ 'conditions' => ['id' => $id], 'recursive' => -1, ]); if (empty($s)) { throw new NotFoundException(__('Invalid server')); } $error = false; if (false == $s['Server']['pull'] && ($technique === 'full' || $technique === 'incremental')) { $error = __('Pull setting not enabled for this server.'); } if (false == $s['Server']['pull_galaxy_clusters'] && ($technique === 'pull_relevant_clusters')) { $error = __('Pull setting not enabled for this server.'); } if (empty($error)) { if (!Configure::read('MISP.background_jobs')) { $result = $this->Server->pull($this->Auth->user(), $technique, $s); if (is_array($result)) { $success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]); } else { $error = $result; } $this->set('successes', $result[0]); $this->set('fails', $result[1]); $this->set('pulledProposals', $result[2]); $this->set('pulledSightings', $result[3]); } else { $this->loadModel('Job'); $jobId = $this->Job->createJob( $this->Auth->user(), Job::WORKER_DEFAULT, 'pull', 'Server: ' . $id, __('Pulling.') ); $this->Server->getBackgroundJobsTool()->enqueue( BackgroundJobsTool::DEFAULT_QUEUE, BackgroundJobsTool::CMD_SERVER, [ 'pull', $this->Auth->user('id'), $id, $technique, $jobId ], false, $jobId ); $success = __('Pull queued for background execution. Job ID: %s', $jobId); } } if ($this->_isRest()) { if (!empty($error)) { return $this->RestResponse->saveFailResponse('Servers', 'pull', $id, $error, $this->response->type()); } else { return $this->RestResponse->saveSuccessResponse('Servers', 'pull', $id, $this->response->type(), $success); } } else { if (!empty($error)) { $this->Flash->error($error); $this->redirect(array('action' => 'index')); } else { $this->Flash->success($success); $this->redirect($this->referer()); } } } public function push($id = null, $technique=false) { if (!empty($id)) { $this->Server->id = $id; } else if (!empty($this->request->data['id'])) { $this->Server->id = $this->request->data['id']; } else { throw new NotFoundException(__('Invalid server')); } if (!empty($this->request->data['technique'])) { $technique = $this->request->data['technique']; } if (!$this->Server->exists()) { throw new NotFoundException(__('Invalid server')); } $s = $this->Server->read(null, $id); if (!Configure::read('MISP.background_jobs')) { App::uses('SyncTool', 'Tools'); $syncTool = new SyncTool(); $HttpSocket = $syncTool->setupHttpSocket($s); $result = $this->Server->push($id, $technique, false, $HttpSocket, $this->Auth->user()); if ($result === false) { $error = __('The remote server is too outdated to initiate a push towards it. Please notify the hosting organisation of the remote instance.'); } elseif (!is_array($result)) { $error = $result; } if (!empty($error)) { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'push', false, $error, $this->response->type()); } else { $this->Flash->info($error); $this->redirect(array('action' => 'index')); } } if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'push', $id, $this->response->type(), __('Push complete. %s events pushed, %s events could not be pushed.', count($result[0]), count($result[1]))); } else { $this->set('successes', $result[0]); $this->set('fails', $result[1]); } } else { $this->loadModel('Job'); $jobId = $this->Job->createJob( $this->Auth->user(), Job::WORKER_DEFAULT, 'push', 'Server: ' . $id, __('Pushing.') ); $this->Server->getBackgroundJobsTool()->enqueue( BackgroundJobsTool::DEFAULT_QUEUE, BackgroundJobsTool::CMD_SERVER, [ 'push', $this->Auth->user('id'), $id, $technique, $jobId ], false, $jobId ); $message = sprintf(__('Push queued for background execution. Job ID: %s'), $jobId); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'push', $message, $this->response->type()); } $this->Flash->success($message); $this->redirect(array('action' => 'index')); } } private function __saveCert($server, $id, $client = false, $delete = false) { if ($client) { $subm = 'submitted_client_cert'; $attr = 'client_cert_file'; $ins = '_client'; } else { $subm = 'submitted_cert'; $attr = 'cert_file'; $ins = ''; } if (!$delete) { $ext = ''; App::uses('File', 'Utility'); App::uses('Folder', 'Utility'); App::uses('FileAccessTool', 'Tools'); if (isset($server['Server'][$subm]['name'])) { if ($this->request->data['Server'][$subm]['size'] != 0) { if (!$this->Server->checkFilename($server['Server'][$subm]['name'])) { throw new Exception(__('Filename not allowed')); } $file = new File($server['Server'][$subm]['name']); $ext = $file->ext(); if (!$server['Server'][$subm]['size'] > 0) { $this->Flash->error(__('Incorrect extension or empty file.')); $this->redirect(array('action' => 'index')); } // read pem file data $pemData = FileAccessTool::readFromFile($server['Server'][$subm]['tmp_name'], $server['Server'][$subm]['size']); } else { return true; } } else { $pemData = base64_decode($server['Server'][$subm]); } $destpath = APP . "files" . DS . "certs" . DS; $dir = new Folder(APP . "files" . DS . "certs", true); $pemfile = new File($destpath . $id . $ins . '.' . $ext); $result = $pemfile->write($pemData); $s = $this->Server->read(null, $id); $s['Server'][$attr] = $s['Server']['id'] . $ins . '.' . $ext; if ($result) { $this->Server->save($s); } } else { $s = $this->Server->read(null, $id); $s['Server'][$attr] = ''; $this->Server->save($s); } return true; } public function serverSettingsReloadSetting($setting, $id) { $pathToSetting = explode('.', $setting); if ( strpos($setting, 'Plugin.Enrichment') !== false || strpos($setting, 'Plugin.Import') !== false || strpos($setting, 'Plugin.Export') !== false || strpos($setting, 'Plugin.Cortex') !== false || strpos($setting, 'Plugin.Action') !== false || strpos($setting, 'Plugin.Workflow') !== false ) { $settingObject = $this->Server->getCurrentServerSettings(); } else { $settingObject = $this->Server->serverSettings; } foreach ($pathToSetting as $key) { if (!isset($settingObject[$key])) { throw new MethodNotAllowedException(); } $settingObject = $settingObject[$key]; } $result = $this->Server->serverSettingReadSingle($settingObject, $setting, $key); $this->set('setting', $result); $priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green'); $this->set('priorityErrorColours', $priorityErrorColours); $priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated'); $this->set('priorities', $priorities); $this->set('k', $id); $this->layout = false; $subGroup = 'general'; if ($pathToSetting[0] === 'Plugin') { $subGroup = explode('_', $pathToSetting[1])[0]; } $this->set('subGroup', $subGroup); $this->render('/Elements/healthElements/settings_row'); } public function serverSettings($tab=false) { if (!$this->request->is('get')) { throw new MethodNotAllowedException('Just GET method is allowed.'); } $tabs = array( 'MISP' => array('count' => 0, 'errors' => 0, 'severity' => 5), 'Encryption' => array('count' => 0, 'errors' => 0, 'severity' => 5), 'Proxy' => array('count' => 0, 'errors' => 0, 'severity' => 5), 'Security' => array('count' => 0, 'errors' => 0, 'severity' => 5), 'Plugin' => array('count' => 0, 'errors' => 0, 'severity' => 5), 'SimpleBackgroundJobs' => array('count' => 0, 'errors' => 0, 'severity' => 5) ); $writeableErrors = array(0 => __('OK'), 1 => __('not found'), 2 => __('is not writeable')); $readableErrors = array(0 => __('OK'), 1 => __('not readable')); $gpgErrors = array(0 => __('OK'), 1 => __('FAIL: settings not set'), 2 => __('FAIL: Failed to load GnuPG'), 3 => __('FAIL: Issues with the key/passphrase'), 4 => __('FAIL: sign failed')); $proxyErrors = array(0 => __('OK'), 1 => __('not configured (so not tested)'), 2 => __('Getting URL via proxy failed')); $zmqErrors = array(0 => __('OK'), 1 => __('not enabled (so not tested)'), 2 => __('Python ZeroMQ library not installed correctly.'), 3 => __('ZeroMQ script not running.')); $sessionErrors = array( 0 => __('OK'), 1 => __('Too many expired sessions in the database, please clear the expired sessions'), 2 => __('PHP session handler is using the default file storage. This is not recommended, please use the redis or database storage'), 8 => __('Alternative setting used'), 9 => __('Test failed') ); $moduleErrors = array(0 => __('OK'), 1 => __('System not enabled'), 2 => __('No modules found')); $backgroundJobsErrors = array( 0 => __('OK'), 1 => __('Not configured (so not tested)'), 2 => __('Error connecting to Redis.'), 3 => __('Error connecting to Supervisor.'), 4 => __('Error connecting to Redis and Supervisor.') ); $finalSettings = $this->Server->serverSettingsRead(); $issues = array( 'errors' => array( 0 => array( 'value' => 0, 'description' => __('MISP will not operate correctly or will be unsecure until these issues are resolved.') ), 1 => array( 'value' => 0, 'description' => __('Some of the features of MISP cannot be utilised until these issues are resolved.') ), 2 => array( 'value' => 0, 'description' => __('There are some optional tweaks that could be done to improve the looks of your MISP instance.') ), ), 'deprecated' => array(), 'overallHealth' => 3, ); $dumpResults = array(); $tempArray = array(); foreach ($finalSettings as $k => $result) { if ($result['level'] == 3) { $issues['deprecated']++; } $tabs[$result['tab']]['count']++; if (isset($result['error']) && $result['level'] < 3) { $issues['errors'][$result['level']]['value']++; if ($result['level'] < $issues['overallHealth']) { $issues['overallHealth'] = $result['level']; } $tabs[$result['tab']]['errors']++; if ($result['level'] < $tabs[$result['tab']]['severity']) { $tabs[$result['tab']]['severity'] = $result['level']; } } if (isset($result['optionsSource']) && is_callable($result['optionsSource'])) { $result['options'] = $result['optionsSource'](); } $dumpResults[] = $result; if ($result['tab'] == $tab) { if (isset($result['subGroup'])) { $tempArray[$result['subGroup']][] = $result; } else { $tempArray['general'][] = $result; } } } $finalSettings = $tempArray; // Diagnostics portion $diagnostic_errors = 0; App::uses('File', 'Utility'); App::uses('Folder', 'Utility'); if ($tab === 'correlations') { $this->loadModel('Correlation'); $correlation_metrics = $this->Correlation->collectMetrics(); $this->set('correlation_metrics', $correlation_metrics); } if ($tab === 'files') { if (!empty(Configure::read('Security.disable_instance_file_uploads'))) { throw new MethodNotAllowedException(__('This functionality is disabled.')); } $files = $this->Server->grabFiles(); $this->set('files', $files); } // Only run this check on the diagnostics tab if ($tab === 'diagnostics' || $tab === 'download' || $this->_isRest()) { $php_ini = php_ini_loaded_file(); $this->set('php_ini', $php_ini); $attachmentTool = new AttachmentTool(); try { $advanced_attachments = $attachmentTool->checkAdvancedExtractionStatus(); } catch (Exception $e) { $this->log($e->getMessage(), LOG_NOTICE); $advanced_attachments = false; } $this->set('advanced_attachments', $advanced_attachments); $gitStatus = $this->Server->getCurrentGitStatus(true); $this->set('branch', $gitStatus['branch']); $this->set('commit', $gitStatus['commit']); $this->set('latestCommit', $gitStatus['latestCommit']); $this->set('version', $gitStatus['version']); $phpSettings = array( 'max_execution_time' => array( 'explanation' => 'The maximum duration that a script can run (does not affect the background workers). A too low number will break long running scripts like comprehensive API exports', 'recommended' => 300, 'unit' => 'seconds', ), 'memory_limit' => array( 'explanation' => 'The maximum memory that PHP can consume. It is recommended to raise this number since certain exports can generate a fair bit of memory usage', 'recommended' => 2048, 'unit' => 'MB' ), 'upload_max_filesize' => array( 'explanation' => 'The maximum size that an uploaded file can be. It is recommended to raise this number to allow for the upload of larger samples', 'recommended' => 50, 'unit' => 'MB' ), 'post_max_size' => array( 'explanation' => 'The maximum size of a POSTed message, this has to be at least the same size as the upload_max_filesize setting', 'recommended' => 50, 'unit' => 'MB' ) ); foreach ($phpSettings as $setting => $settingArray) { $phpSettings[$setting]['value'] = $this->Server->getIniSetting($setting); if ($phpSettings[$setting]['value'] && $settingArray['unit'] && $settingArray['unit'] === 'MB') { // convert basic unit to M $phpSettings[$setting]['value'] = (int) floor($phpSettings[$setting]['value'] / 1024 / 1024); } } $this->set('phpSettings', $phpSettings); if ($gitStatus['version'] && $gitStatus['version']['upToDate'] === 'older') { $diagnostic_errors++; } // check if the STIX and Cybox libraries are working and the correct version using the test script stixtest.py $stix = $this->Server->stixDiagnostics($diagnostic_errors); $yaraStatus = $this->Server->yaraDiagnostics($diagnostic_errors); // if GnuPG is set up in the settings, try to encrypt a test message $gpgStatus = $this->Server->gpgDiagnostics($diagnostic_errors); // if the message queue pub/sub is enabled, check whether the extension works $zmqStatus = $this->Server->zmqDiagnostics($diagnostic_errors); // if Proxy is set up in the settings, try to connect to a test URL $proxyStatus = $this->Server->proxyDiagnostics($diagnostic_errors); // if SimpleBackgroundJobs is set up in the settings, try to connect to Redis $backgroundJobsStatus = $this->Server->backgroundJobsDiagnostics($diagnostic_errors); // get the DB diagnostics $dbDiagnostics = $this->Server->dbSpaceUsage(); $dbSchemaDiagnostics = $this->Server->dbSchemaDiagnostic(); $dbConfiguration = $this->Server->dbConfiguration(); $redisInfo = $this->Server->redisInfo(); $moduleTypes = array('Enrichment', 'Import', 'Export', 'Cortex'); foreach ($moduleTypes as $type) { $moduleStatus[$type] = $this->Server->moduleDiagnostics($diagnostic_errors, $type); } // get php session diagnostics $sessionStatus = $this->Server->sessionDiagnostics($diagnostic_errors); $this->loadModel('AttachmentScan'); try { $attachmentScan = ['status' => true, 'software' => $this->AttachmentScan->diagnostic()]; } catch (Exception $e) { $attachmentScan = ['status' => false, 'error' => $e->getMessage()]; } $securityAudit = (new SecurityAudit())->run($this->Server); $view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'dbConfiguration', 'redisInfo', 'attachmentScan', 'securityAudit'); } else { $view = []; } // check whether the files are writeable $writeableDirs = $this->Server->writeableDirsDiagnostics($diagnostic_errors); $writeableFiles = $this->Server->writeableFilesDiagnostics($diagnostic_errors); $readableFiles = $this->Server->readableFilesDiagnostics($diagnostic_errors); $extensions = $this->Server->extensionDiagnostics(); // check if the encoding is not set to utf8 $dbEncodingStatus = $this->Server->databaseEncodingDiagnostics($diagnostic_errors); $view = array_merge($view, compact('diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors', 'readableErrors', 'writeableDirs', 'writeableFiles', 'readableFiles', 'extensions', 'dbEncodingStatus')); $this->set($view); $workerIssueCount = 4; $worker_array = array(); if (Configure::read('MISP.background_jobs')) { $workerIssueCount = 0; $worker_array = $this->Server->workerDiagnostics($workerIssueCount); } $this->set('worker_array', $worker_array); if ($tab === 'download' || $this->_isRest()) { foreach ($dumpResults as $key => $dr) { unset($dumpResults[$key]['description']); } $dump = array( 'version' => $gitStatus['version'], 'phpSettings' => $phpSettings, 'gpgStatus' => $gpgErrors[$gpgStatus['status']], 'proxyStatus' => $proxyErrors[$proxyStatus], 'zmqStatus' => $zmqStatus, 'stix' => $stix, 'moduleStatus' => $moduleStatus, 'writeableDirs' => $writeableDirs, 'writeableFiles' => $writeableFiles, 'readableFiles' => $readableFiles, 'dbDiagnostics' => $dbDiagnostics, 'dbSchemaDiagnostics' => $dbSchemaDiagnostics, 'dbConfiguration' => $dbConfiguration, 'redisInfo' => $redisInfo, 'finalSettings' => $dumpResults, 'extensions' => $extensions, 'workers' => $worker_array, 'backgroundJobsStatus' => $backgroundJobsErrors[$backgroundJobsStatus] ); foreach ($dump['finalSettings'] as $k => $v) { if (!empty($v['redacted'])) { $dump['finalSettings'][$k]['value'] = '*****'; } } $this->response->body(json_encode($dump, JSON_PRETTY_PRINT)); $this->response->type('json'); $this->response->download('MISP.report.json'); return $this->response; } $priorities = array(0 => 'Critical', 1 => 'Recommended', 2 => 'Optional', 3 => 'Deprecated'); $this->set('priorities', $priorities); $this->set('workerIssueCount', $workerIssueCount); $priorityErrorColours = array(0 => 'red', 1 => 'yellow', 2 => 'green'); $this->set('priorityErrorColours', $priorityErrorColours); $this->set('phpversion', phpversion()); $this->set('phpmin', $this->phpmin); $this->set('phprec', $this->phprec); $this->set('phptoonew', $this->phptoonew); $this->set('pythonmin', $this->pythonmin); $this->set('pythonrec', $this->pythonrec); $this->set('title_for_layout', __('Diagnostics')); } public function startWorker($type) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (Configure::read('SimpleBackgroundJobs.enabled')) { $message = __('Worker start signal sent'); $this->Server->getBackgroundJobsTool()->startWorkerByQueue($type); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'startWorker', $type, $this->response->type(), $message); } else { $this->Flash->info($message); $this->redirect('/servers/serverSettings/workers'); } } // CakeResque $validTypes = array('default', 'email', 'scheduler', 'cache', 'prio', 'update'); if (!in_array($type, $validTypes)) { throw new MethodNotAllowedException('Invalid worker type.'); } $prepend = ''; if ($type != 'scheduler') { $workerIssueCount = 0; $workerDiagnostic = $this->Server->workerDiagnostics($workerIssueCount); if ($type == 'update' && isset($workerDiagnostic['update']['ok']) && $workerDiagnostic['update']['ok']) { $message = __('Only one `update` worker can run at a time'); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'startWorker', false, $message, $this->response->type()); } else { $this->Flash->error($message); $this->redirect('/servers/serverSettings/workers'); } } shell_exec($prepend . APP . 'Console' . DS . 'cake CakeResque.CakeResque start --interval 5 --queue ' . $type .' > /dev/null 2>&1 &'); } else { shell_exec($prepend . APP . 'Console' . DS . 'cake CakeResque.CakeResque startscheduler -i 5 > /dev/null 2>&1 &'); } $message = __('Worker start signal sent'); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'startWorker', $type, $this->response->type(), $message); } else { $this->Flash->info($message); $this->redirect('/servers/serverSettings/workers'); } } public function stopWorker($pid) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } $message = __('Worker stop signal sent'); if (Configure::read('SimpleBackgroundJobs.enabled')) { $this->Server->getBackgroundJobsTool()->stopWorker($pid); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'stopWorker', $pid, $this->response->type(), $message); } else { $this->Flash->info($message); $this->redirect('/servers/serverSettings/workers'); } } // CakeResque $this->Server->killWorker($pid, $this->Auth->user()); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'stopWorker', $pid, $this->response->type(), $message); } else { $this->Flash->info($message); $this->redirect('/servers/serverSettings/workers'); } } public function getWorkers() { if (Configure::read('MISP.background_jobs')) { $workerIssueCount = 0; $worker_array = $this->Server->workerDiagnostics($workerIssueCount); } else { $worker_array = [__('Background jobs not enabled')]; } return $this->RestResponse->viewData($worker_array); } public function idTranslator($localId = null) { // We retrieve the list of remote servers that we can query $servers = $this->Server->find('all', [ 'conditions' => ['OR' => ['pull' => true, 'push' => true]], 'recursive' => -1, 'order' => ['Server.priority ASC'], ]); // We generate the list of servers for the dropdown $displayServers = array(); foreach ($servers as $s) { $displayServers[] = [ 'name' => $s['Server']['name'], 'value' => $s['Server']['id'], ]; } $this->set('servers', $displayServers); if ($localId || $this->request->is('post')) { if ($localId && $this->request->is('get')) { $this->request->data['Event']['local'] = 'local'; $this->request->data['Event']['uuid'] = $localId; } $remote_events = array(); if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] === "local") { $local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $this->request->data['Event']['uuid']); } else if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] === "remote" && !empty($this->request->data['Server']['id'])) { //We check on the remote server for any event with this id and try to find a match locally $conditions = array('AND' => array('Server.id' => $this->request->data['Server']['id'], 'Server.pull' => true)); $remote_server = $this->Server->find('first', array('conditions' => $conditions)); if (!empty($remote_server)) { try { $remote_event = $this->Event->downloadEventMetadataFromServer($this->request->data['Event']['uuid'], $remote_server); } catch (Exception $e) { $this->Flash->error(__("Issue while contacting the remote server to retrieve event information")); return; } if (empty($remote_event)) { $this->Flash->error(__("This event could not be found or you don't have permissions to see it.")); return; } $local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $remote_event['uuid']); // we record it to avoid re-querying the same server in the 2nd phase if (!empty($local_event)) { $remote_events[] = array( "server_id" => $remote_server['Server']['id'], "server_name" => $remote_server['Server']['name'], "url" => $remote_server['Server']['url']."/events/view/".$remote_event['id'], "remote_id" => $remote_event['id'] ); } } } if (empty($local_event)) { $this->Flash->error(__("This event could not be found or you don't have permissions to see it.")); return; } else { $this->Flash->success(__('The event has been found.')); } // In the second phase, we query all configured sync servers to get their info on the event foreach ($servers as $server) { // We check if the server was not already contacted in phase 1 if (count($remote_events) > 0 && $remote_events[0]['server_id'] == $server['Server']['id']) { continue; } $exception = null; try { $remoteEvent = $this->Event->downloadEventMetadataFromServer($local_event['Event']['uuid'], $server); } catch (Exception $e) { $remoteEvent = null; $exception = $e->getMessage(); } $remoteEventId = isset($remoteEvent['id']) ? $remoteEvent['id'] : null; $remote_events[] = array( "server_id" => $server['Server']['id'], "server_name" => $server['Server']['name'], "url" => isset($remoteEventId) ? $server['Server']['url'] . "/events/view/" . $remoteEventId : $server['Server']['url'], "remote_id" => isset($remoteEventId) ? $remoteEventId : false, "exception" => $exception, ); } $this->set('local_event', $local_event); $this->set('remote_events', $remote_events); } $this->set('title_for_layout', __('Event ID translator')); } public function getSubmodulesStatus() { $this->set('submodules', $this->Server->getSubmodulesGitStatus()); $this->render('ajax/submoduleStatus'); } public function getSetting($settingName) { $setting = $this->Server->getSettingData($settingName); if (!$setting) { throw new NotFoundException(__('Setting %s is invalid.', $settingName)); } if (!empty($setting["redacted"])) { throw new ForbiddenException(__('This setting is redacted.')); } if (Configure::check($settingName)) { $setting['value'] = Configure::read($settingName); } return $this->RestResponse->viewData($setting); } public function serverSettingsEdit($settingName, $id = false, $forceSave = false) { if (!$this->_isRest()) { if (!isset($id)) { throw new MethodNotAllowedException(); } $this->set('id', $id); } $setting = $this->Server->getSettingData($settingName); if ($setting === false) { throw new NotFoundException(__('Setting %s is invalid.', $settingName)); } if (!empty($setting['cli_only'])) { throw new MethodNotAllowedException(__('This setting can only be edited via the CLI.')); } if ($this->request->is('get')) { $value = Configure::read($setting['name']); if (isset($value)) { $setting['value'] = $value; } $setting['setting'] = $setting['name']; if (isset($setting['optionsSource']) && is_callable($setting['optionsSource'])) { $setting['options'] = $setting['optionsSource'](); } $subGroup = explode('.', $setting['name']); if ($subGroup[0] === 'Plugin') { $subGroup = explode('_', $subGroup[1])[0]; } else { $subGroup = 'general'; } if ($this->_isRest()) { if (!empty($setting['redacted'])) { throw new ForbiddenException(__('This setting is redacted.')); } return $this->RestResponse->viewData([$setting['name'] => $setting['value']]); } else { $this->set('subGroup', $subGroup); $this->set('setting', $setting); $this->render('ajax/server_settings_edit'); } } else if ($this->request->is('post')) { if (!isset($this->request->data['Server'])) { $this->request->data = array('Server' => $this->request->data); } if (!isset($this->request->data['Server']['value']) || !is_scalar($this->request->data['Server']['value'])) { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'Invalid input. Expected: {"value": "new_setting"}', $this->response->type()); } } if (!empty($this->request->data['Server']['force'])) { $forceSave = $this->request->data['Server']['force']; } if (trim($this->request->data['Server']['value']) === '*****') { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'No change.', $this->response->type()); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'No change.')), 'status'=>200, 'type' => 'json')); } } $this->autoRender = false; if (!Configure::read('MISP.system_setting_db') && !is_writeable(APP . 'Config/config.php')) { $this->loadModel('Log'); $this->Log->create(); $this->Log->save(array( 'org' => $this->Auth->user('Organisation')['name'], 'model' => 'Server', 'model_id' => 0, 'email' => $this->Auth->user('email'), 'action' => 'serverSettingsEdit', 'user_id' => $this->Auth->user('id'), 'title' => 'Server setting issue', 'change' => 'There was an issue witch changing ' . $setting['name'] . ' to ' . $this->request->data['Server']['value'] . '. The error message returned is: app/Config.config.php is not writeable to the apache user. No changes were made.', )); if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'app/Config.config.php is not writeable to the apache user.', $this->response->type()); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'app/Config.config.php is not writeable to the apache user.')), 'status'=>200, 'type' => 'json')); } } $result = $this->Server->serverSettingsEditValue($this->Auth->user(), $setting, $this->request->data['Server']['value'], $forceSave); if ($result === true) { if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Servers', 'serverSettingsEdit', false, $this->response->type(), 'Field updated'); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.')), 'status'=>200, 'type' => 'json')); } } else { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, $result, $this->response->type()); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json')); } } } } public function killAllWorkers($force = false) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } $this->Server->killAllWorkers($this->Auth->user(), $force); if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Server', 'killAllWorkers', false, $this->response->type(), __('Killing workers.')); } $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers')); } public function restartWorkers() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (Configure::read('SimpleBackgroundJobs.enabled')) { $this->Server->getBackgroundJobsTool()->restartWorkers(); } else { // CakeResque $this->Server->restartWorkers($this->Auth->user()); } if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Server', 'restartWorkers', false, $this->response->type(), __('Restarting workers.')); } $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers')); } public function restartDeadWorkers() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (Configure::read('SimpleBackgroundJobs.enabled')) { $this->Server->getBackgroundJobsTool()->restartDeadWorkers(); } else { // CakeResque $this->Server->restartDeadWorkers($this->Auth->user()); } if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Server', 'restartDeadWorkers', false, $this->response->type(), __('Restarting workers.')); } $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'workers')); } public function deleteFile($type, $filename) { if ($this->request->is('post')) { $validItems = $this->Server->getFileRules(); App::uses('File', 'Utility'); $existingFile = new File($validItems[$type]['path'] . DS . $filename); if (!$existingFile->exists()) { $this->Flash->error(__('File not found.', true), 'default', array(), 'error'); $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } if ($existingFile->delete()) { $this->Flash->success('File deleted.'); } else { $this->Flash->error(__('File could not be deleted.', true), 'default', array(), 'error'); } $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } else { throw new MethodNotAllowedException('This action expects a POST request.'); } } public function uploadFile($type) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (!empty(Configure::read('Security.disable_instance_file_uploads'))) { throw new MethodNotAllowedException(__('Feature disabled.')); } $validItems = $this->Server->getFileRules(); // Check if there were problems with the file upload // only keep the last part of the filename, this should prevent directory attacks $filename = basename($this->request->data['Server']['file']['name']); if (!preg_match("/" . $validItems[$type]['regex'] . "/", $filename)) { $this->Flash->error($validItems[$type]['regex_error'], 'default', array(), 'error'); $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } if (empty($this->request->data['Server']['file']['tmp_name']) || !is_uploaded_file($this->request->data['Server']['file']['tmp_name'])) { $this->Flash->error(__('Upload failed.', true), 'default', array(), 'error'); $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } // check if the file already exists App::uses('File', 'Utility'); $existingFile = new File($validItems[$type]['path'] . DS . $filename); if ($existingFile->exists()) { $this->Flash->info(__('File already exists. If you would like to replace it, remove the old one first.', true), 'default', array(), 'error'); $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } $result = move_uploaded_file($this->request->data['Server']['file']['tmp_name'], $validItems[$type]['path'] . DS . $filename); if ($result) { $this->Flash->success('File uploaded.'); } else { $this->Flash->error(__('Upload failed.', true), 'default', array(), 'error'); } $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'files')); } public function fetchServersForSG($idList = '{}') { $id_exclusion_list = json_decode($idList, true); $temp = $this->Server->find('all', array( 'conditions' => array( 'id !=' => $id_exclusion_list, ), 'recursive' => -1, 'fields' => array('id', 'name', 'url') )); $servers = array(); foreach ($temp as $server) { $servers[] = array('id' => $server['Server']['id'], 'name' => $server['Server']['name'], 'url' => $server['Server']['url']); } $this->layout = false; $this->autoRender = false; $this->set('servers', $servers); $this->render('ajax/fetch_servers_for_sg'); } public function postTest() { if (!$this->request->is('post')) { throw new MethodNotAllowedException('Invalid request, expecting a POST request.'); } // Fix for PHP-FPM / Nginx / etc // Fix via https://www.popmartian.com/tipsntricks/2015/07/14/howto-use-php-getallheaders-under-fastcgi-php-fpm-nginx-etc/ if (!function_exists('getallheaders')) { $headers = []; foreach ($_SERVER as $name => $value) { $name = strtolower($name); if (substr($name, 0, 5) === 'http_') { $headers[str_replace('_', '-', substr($name, 5))] = $value; } } } else { $headers = getallheaders(); $headers = array_change_key_case($headers, CASE_LOWER); } $result = [ 'body' => $this->request->data, 'headers' => [ 'Content-type' => isset($headers['content-type']) ? $headers['content-type'] : 0, 'Accept' => isset($headers['accept']) ? $headers['accept'] : 0, 'Authorization' => isset($headers['authorization']) ? 'OK' : 0, ], ]; return $this->RestResponse->viewData($result, 'json'); } public function getRemoteUser($id) { $user = $this->Server->getRemoteUser($id); if ($user === null) { throw new NotFoundException(__('Invalid server')); } return $this->RestResponse->viewData($user); } public function testConnection($id = false) { $server = $this->Server->find('first', ['conditions' => ['Server.id' => $id]]); if (!$server) { throw new NotFoundException(__('Invalid server')); } @session_write_close(); // close session to allow concurrent requests $result = $this->Server->runConnectionTest($server); if ($result['status'] == 1) { if (isset($result['info']['version']) && preg_match('/^[0-9]+\.+[0-9]+\.[0-9]+$/', $result['info']['version'])) { $perm_sync = isset($result['info']['perm_sync']) ? $result['info']['perm_sync'] : false; $perm_sighting = isset($result['info']['perm_sighting']) ? $result['info']['perm_sighting'] : false; $local_version = $this->Server->checkMISPVersion(); $version = explode('.', $result['info']['version']); $mismatch = false; $newer = false; $parts = array('major', 'minor', 'hotfix'); foreach ($parts as $k => $v) { if (!$mismatch) { if ($version[$k] > $local_version[$v]) { $mismatch = $v; $newer = 'remote'; } elseif ($version[$k] < $local_version[$v]) { $mismatch = $v; $newer = 'local'; } } } if (!$mismatch && $version[2] < 111) { $mismatch = 'proposal'; } if (!$perm_sync && !$perm_sighting) { $result['status'] = 7; return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json')); } if (!$perm_sync && $perm_sighting) { $result['status'] = 8; return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json')); } return $this->RestResponse->viewData([ 'status' => 1, 'local_version' => implode('.', $local_version), 'version' => implode('.', $version), 'mismatch' => $mismatch, 'newer' => $newer, 'post' => isset($result['post']) ? $result['post']['status'] : 'too old', 'response_encoding' => isset($result['post']['content-encoding']) ? $result['post']['content-encoding'] : null, 'request_encoding' => isset($result['info']['request_encoding']) ? $result['info']['request_encoding'] : null, 'client_certificate' => $result['client_certificate'], ], 'json'); } else { $result['status'] = 3; } } return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json')); } public function startZeroMQServer() { $pubSubTool = $this->Server->getPubSubTool(); $result = $pubSubTool->restartServer(); if ($result === true) { return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully started.')), 'status'=>200, 'type' => 'json')); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json')); } } public function stopZeroMQServer() { $pubSubTool = $this->Server->getPubSubTool(); $result = $pubSubTool->killService(); if ($result === true) { return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'ZeroMQ server successfully killed.')), 'status'=>200, 'type' => 'json')); } else { return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Could not kill the previous instance of the ZeroMQ script.')), 'status'=>200, 'type' => 'json')); } } public function statusZeroMQServer() { $pubSubTool = $this->Server->getPubSubTool(); $result = $pubSubTool->statusCheck(); if (!empty($result)) { $this->set('events', $result['publishCount']); $this->set('messages', $result['messageCount']); $this->set('time', $result['timestamp']); $this->set('time2', $result['timestampSettings']); } $this->render('ajax/zeromqstatus'); } public function purgeSessions() { if ($this->Server->updateDatabase('cleanSessionTable') == false) { $this->Flash->error('Could not purge the session table.'); } $this->redirect('/servers/serverSettings/diagnostics'); } public function clearWorkerQueue($worker) { if (!$this->request->is('Post') || $this->request->is('ajax')) { throw new MethodNotAllowedException(); } if (Configure::read('SimpleBackgroundJobs.enabled')) { $this->Server->getBackgroundJobsTool()->purgeQueue($worker); } else { // CakeResque $worker_array = array('cache', 'default', 'email', 'prio'); if (!in_array($worker, $worker_array)) { throw new MethodNotAllowedException('Invalid worker'); } $redis = Resque::redis(); $redis->del('queue:' . $worker); } $this->Flash->success('Queue cleared.'); $this->redirect($this->referer()); } public function getVersion() { $user = $this->_closeSession(); $versionArray = $this->Server->checkMISPVersion(); $response = [ 'version' => $versionArray['major'] . '.' . $versionArray['minor'] . '.' . $versionArray['hotfix'], 'pymisp_recommended_version' => $this->pyMispVersion, 'perm_sync' => (bool) $user['Role']['perm_sync'], 'perm_sighting' => (bool) $user['Role']['perm_sighting'], 'perm_galaxy_editor' => (bool) $user['Role']['perm_galaxy_editor'], 'request_encoding' => $this->CompressedRequestHandler->supportedEncodings(), 'filter_sightings' => true, // check if Sightings::filterSightingUuidsForPush method is supported ]; return $this->RestResponse->viewData($response, 'json'); } /** * @deprecated Use field `pymisp_recommended_version` from getVersion instead */ public function getPyMISPVersion() { $this->set('response', array('version' => $this->pyMispVersion)); $this->set('_serialize', 'response'); } public function checkout() { $result = $this->Server->checkoutMain(); } public function update($branch = false) { if ($this->request->is('post')) { $filterData = array( 'request' => $this->request, 'named_params' => $this->params['named'], 'paramArray' => ['branch'], 'ordered_url_params' => [], 'additional_delimiters' => PHP_EOL ); $exception = false; $settings = $this->_harvestParameters($filterData, $exception); $status = $this->Server->getCurrentGitStatus(); $raw = array(); if (empty($status['branch'])) { // do not try to update if you are not on branch $msg = 'Update failed, you are not on branch'; $raw[] = $msg; $update = $msg; } else { if ($settings === false) { $settings = []; } $update = $this->Server->update($status, $raw, $settings); } if ($this->_isRest()) { return $this->RestResponse->viewData(array('results' => $raw), $this->response->type()); } else { return new CakeResponse(array('body' => $update, 'type' => 'txt')); } } else { $this->set('isUpdatePossible', $this->Server->isUpdatePossible()); $this->set('branch', $this->Server->getCurrentBranch()); $this->render('ajax/update'); } } public function ondemandAction() { $this->AdminSetting = ClassRegistry::init('AdminSetting'); $actions = $this->Server->actions_description; $default_fields = array( 'title' => '', 'description' => '', 'liveOff' => false, 'recommendBackup' => false, 'exitOnError' => false, 'requirements' => '', 'url' => $this->baseurl . '/' ); foreach($actions as $id => $action) { foreach($default_fields as $field => $value) { if (!isset($action[$field])) { $actions[$id][$field] = $value; } } $done = $this->AdminSetting->getSetting($id); $actions[$id]['done'] = ($done == '1'); } $this->set('actions', $actions); $this->set('updateLocked', $this->Server->isUpdateLocked()); } public function updateProgress($ajaxHtml=false) { $this->AdminSetting = ClassRegistry::init('AdminSetting'); $dbVersion = $this->AdminSetting->getSetting('db_version'); $updateProgress = $this->Server->getUpdateProgress(); $updateProgress['db_version'] = $dbVersion; $maxUpdateNumber = max(array_keys(Server::DB_CHANGES)); $updateProgress['complete_update_remaining'] = max($maxUpdateNumber - $dbVersion, 0); $updateProgress['update_locked'] = $this->Server->isUpdateLocked(); $updateProgress['lock_remaining_time'] = $this->Server->getLockRemainingTime(); $updateProgress['update_fail_number_reached'] = $this->Server->UpdateFailNumberReached(); $currentIndex = $updateProgress['current']; $currentCommand = !isset($updateProgress['commands'][$currentIndex]) ? '' : $updateProgress['commands'][$currentIndex]; $lookupString = preg_replace('/\s{2,}/', '', substr($currentCommand, 0, -1)); $sqlInfo = $this->Server->query("SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST;"); if (empty($sqlInfo)) { $updateProgress['process_list'] = array(); } else { // retrieve current update process foreach($sqlInfo as $row) { if (preg_replace('/\s{2,}/', '', $row['PROCESSLIST']['INFO']) == $lookupString) { $sqlInfo = $row['PROCESSLIST']; break; } } $updateProgress['process_list'] = array(); $updateProgress['process_list']['STATE'] = isset($sqlInfo['STATE']) ? $sqlInfo['STATE'] : ''; $updateProgress['process_list']['PROGRESS'] = isset($sqlInfo['PROGRESS']) ? $sqlInfo['PROGRESS'] : 0; $updateProgress['process_list']['STAGE'] = isset($sqlInfo['STAGE']) ? $sqlInfo['STAGE'] : 0; $updateProgress['process_list']['MAX_STAGE'] = isset($sqlInfo['MAX_STAGE']) ? $sqlInfo['MAX_STAGE'] : 0; } $this->set('ajaxHtml', $ajaxHtml); if ($this->request->is('ajax') && $ajaxHtml) { $this->set('updateProgress', $updateProgress); $this->layout = false; } elseif ($this->request->is('ajax') || $this->_isRest()) { return $this->RestResponse->viewData(h($updateProgress), $this->response->type()); } else { $this->set('updateProgress', $updateProgress); } } public function getSubmoduleQuickUpdateForm($submodule_path=false) { $this->set('submodule', base64_decode($submodule_path)); $this->render('ajax/submodule_quick_update_form'); } public function updateSubmodule() { if ($this->request->is('post')) { $request = $this->request->data; $submodule = $request['Server']['submodule']; $res = $this->Server->updateSubmodule($this->Auth->user(), $submodule); return new CakeResponse(array('body'=> json_encode($res), 'type' => 'json')); } else { throw new MethodNotAllowedException(); } } public function getInstanceUUID() { return $this->RestResponse->viewData(array('uuid' => Configure::read('MISP.uuid')), $this->response->type()); } public function cache($id = 'all') { if (Configure::read('MISP.background_jobs')) { $this->loadModel('Job'); $jobId = $this->Job->createJob( $this->Auth->user(), Job::WORKER_DEFAULT, 'cache_servers', intval($id) ? $id : 'all', __('Starting server caching.') ); $this->Server->getBackgroundJobsTool()->enqueue( BackgroundJobsTool::DEFAULT_QUEUE, BackgroundJobsTool::CMD_SERVER, [ 'cacheServer', $this->Auth->user('id'), $id, $jobId ], false, $jobId ); $message = 'Server caching job initiated.'; } else { $result = $this->Server->cacheServerInitiator($this->Auth->user(), $id); if (!$result) { $this->Flash->error(__('Caching the servers has failed.')); $this->redirect(array('action' => 'index')); } $message = __('Caching the servers has successfully completed.'); } if ($this->_isRest()) { return $this->RestResponse->saveSuccessResponse('Server', 'cache', false, $this->response->type(), $message); } else { $this->Flash->info($message); $this->redirect(array('action' => 'index')); } } public function updateJSON() { $results = $this->Server->updateJSON(); return $this->RestResponse->viewData($results, $this->response->type()); } public function createSync() { if ($this->_isSiteAdmin()) { throw new MethodNotAllowedException('Site admin accounts cannot be used to create server sync configurations.'); } $baseurl = Configure::read('MISP.external_baseurl'); if (empty($baseurl)) { $baseurl = Configure::read('MISP.baseurl'); if (empty($baseurl)) { $baseurl = Router::url('/', true); } } $host_org_id = Configure::read('MISP.host_org_id'); if (empty($host_org_id)) { throw new MethodNotAllowedException(__('Cannot create sync config - no host org ID configured for the instance.')); } $this->loadModel('Organisation'); $host_org = $this->Organisation->find('first', array( 'conditions' => array('Organisation.id' => $host_org_id), 'recursive' => -1, 'fields' => array('name', 'uuid') )); if (empty($host_org)) { throw new MethodNotAllowedException(__('Configured host org not found. Please make sure that the setting is current on the instance.')); } if (Configure::read('Security.advanced_authkeys')) { $this->loadModel('AuthKey'); $authkey = $this->AuthKey->createnewkey($this->Auth->user('id'), null, __('Auto generated sync key - %s', date('Y-m-d H:i:s'))); } else { $this->loadModel('User'); $authkey = $this->User->find('column', [ 'conditions' => ['User.id' => $this->Auth->user('id')], 'recursive' => -1, 'fields' => ['User.authkey'] ]); $authkey = $authkey[0]; } $server = array( 'Server' => array( 'url' => $baseurl, 'uuid' => Configure::read('MISP.uuid'), 'authkey' => h($authkey), 'Organisation' => array( 'name' => $host_org['Organisation']['name'], 'uuid' => $host_org['Organisation']['uuid'], ) ) ); if ($this->_isRest()) { return $this->RestResponse->viewData($server, $this->response->type()); } else { $this->set('server', $server); } } public function import() { if ($this->request->is('post')) { $server = $this->request->data; if (isset($server['Server'])) { $server = $server['Server']; } if (isset($server['json'])) { $server = json_decode($server['json'], true)['Server']; } $this->loadModel('Organisation'); $org_id = $this->Organisation->captureOrg($server['Organisation'], $this->Auth->user()); $toSave = array( 'push' => 0, 'pull' => 0, 'caching_enabled' => 0, 'json' => '[]', 'push_rules' => '[]', 'pull_rules' => '[]', 'self_signed' => 0, 'org_id' => $this->Auth->user('org_id'), 'remote_org_id' => $org_id, 'name' => empty($server['name']) ? $server['url'] : $server['name'], 'url' => $server['url'], 'uuid' => $server['uuid'], 'authkey' => $server['authkey'] ); $this->Server->create(); $result = $this->Server->save($toSave); if ($result) { if ($this->_isRest()) { $server = $this->Server->find('first', array( 'conditions' => array('Server.id' => $this->Server->id), 'recursive' => -1 )); return $this->RestResponse->viewData($server, $this->response->type()); } else { $this->Flash->success(__('The server has been saved')); $this->redirect(array('action' => 'index', $this->Server->id)); } } else { if ($this->_isRest()) { return $this->RestResponse->saveFailResponse('Servers', 'addFromJson', false, $this->Server->validationErrors, $this->response->type()); } else { $this->Flash->error(__('Could not save the server. Error: %s', json_encode($this->Server->validationErrors, true))); $this->redirect(array('action' => 'index')); } } } } public function resetRemoteAuthKey($id) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(__('This endpoint expects POST requests.')); } $result = $this->Server->resetRemoteAuthkey($id); if ($result !== true) { if (!$this->_isRest()) { $this->Flash->error($result); $this->redirect(array('action' => 'index')); } else { return $this->RestResponse->saveFailResponse('Servers', 'resetRemoteAuthKey', $id, $message, $this->response->type()); } } else { $message = __('API key updated.'); if (!$this->_isRest()) { $this->Flash->success($message); $this->redirect(array('action' => 'index')); } else { return $this->RestResponse->saveSuccessResponse('Servers', 'resetRemoteAuthKey', $message, $this->response->type()); } } } public function changePriority($id = false, $direction = 'down') { $this->Server->id = $id; if (!$this->Server->exists()) { throw new InvalidArgumentException(__('ID has to be a valid server connection')); } if ($direction !== 'up' && $direction !== 'down') { throw new InvalidArgumentException(__('Invalid direction. Valid options: ', 'up', 'down')); } $success = $this->Server->reprioritise($id, $direction); if ($success) { $message = __('Priority changed.'); return $this->RestResponse->saveSuccessResponse('Servers', 'changePriority', $message, $this->response->type()); } else { $message = __('Priority could not be changed.'); return $this->RestResponse->saveFailResponse('Servers', 'changePriority', $id, $message, $this->response->type()); } } public function releaseUpdateLock() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(__('This endpoint expects POST requests.')); } $this->Server->changeLockState(false); $this->Server->resetUpdateFailNumber(); $this->redirect(array('action' => 'updateProgress')); } public function dbSchemaDiagnostic() { $dbSchemaDiagnostics = $this->Server->dbSchemaDiagnostic(); if ($this->_isRest()) { return $this->RestResponse->viewData($dbSchemaDiagnostics, $this->response->type()); } else { $this->set('checkedTableColumn', $dbSchemaDiagnostics['checked_table_column']); $this->set('dbSchemaDiagnostics', $dbSchemaDiagnostics['diagnostic']); $this->set('dbIndexDiagnostics', $dbSchemaDiagnostics['diagnostic_index']); $this->set('expectedDbVersion', $dbSchemaDiagnostics['expected_db_version']); $this->set('actualDbVersion', $dbSchemaDiagnostics['actual_db_version']); $this->set('error', $dbSchemaDiagnostics['error']); $this->set('remainingLockTime', $dbSchemaDiagnostics['remaining_lock_time']); $this->set('updateFailNumberReached', $dbSchemaDiagnostics['update_fail_number_reached']); $this->set('updateLocked', $dbSchemaDiagnostics['update_locked']); $this->set('dataSource', $dbSchemaDiagnostics['dataSource']); $this->set('columnPerTable', $dbSchemaDiagnostics['columnPerTable']); $this->set('indexes', $dbSchemaDiagnostics['indexes']); $this->render('/Elements/healthElements/db_schema_diagnostic'); } } public function dbConfiguration() { $dbConfiguration = $this->Server->dbConfiguration(); if ($this->_isRest()) { return $this->RestResponse->viewData($dbConfiguration, $this->response->type()); } else { $this->set('dbConfiguration', $dbConfiguration); $this->render('/Elements/healthElements/db_config_diagnostic'); } } public function cspReport() { if (!$this->request->is('post')) { throw new MethodNotAllowedException('This action expects a POST request.'); } $report = JsonTool::decode($this->request->input()); if (!isset($report['csp-report'])) { throw new RuntimeException("Invalid report"); } $message = 'CSP reported violation'; $remoteIp = $this->_remoteIp(); if ($remoteIp) { $message .= ' from IP ' . $remoteIp; } $report = JsonTool::encode($report['csp-report'], true); if (strlen($report) > 1024 * 1024) { // limit report to 1 kB $report = substr($report, 0, 1024 * 1024) . '...'; } $this->log("$message: $report"); return new CakeResponse(['status' => 204]); } /** * List all tags for the rule picker. * * @return array */ private function __getTags() { $this->loadModel('Tag'); $list = $this->Tag->find('list', array( 'recursive' => -1, 'order' => array('LOWER(TRIM(Tag.name))' => 'ASC'), 'fields' => array('name'), )); $allTags = array(); foreach ($list as $id => $name) { $allTags[] = array('id' => $id, 'name' => trim($name)); } return $allTags; } public function removeOrphanedCorrelations() { $count = $this->Server->removeOrphanedCorrelations(); $message = __('%s orphaned correlation removed', $count); if ($this->_isRest()) { return $this->RestResponse->viewData($message, $this->response->type()); } else { $this->Flash->success($message); $this->redirect(array('action' => 'serverSettings', 'diagnostics')); } } public function queryAvailableSyncFilteringRules($serverID) { if (!$this->_isRest()) { throw new MethodNotAllowedException(__('This method can only be access via REST')); } $server = $this->Server->find('first', ['conditions' => ['Server.id' => $serverID]]); if (!$server) { throw new NotFoundException(__('Invalid server')); } $syncFilteringRules = $this->Server->queryAvailableSyncFilteringRules($server); return $this->RestResponse->viewData($syncFilteringRules); } public function getAvailableSyncFilteringRules() { if (!$this->_isRest()) { throw new MethodNotAllowedException(__('This method can only be access via REST')); } $syncFilteringRules = $this->Server->getAvailableSyncFilteringRules($this->Auth->user()); return $this->RestResponse->viewData($syncFilteringRules); } public function pruneDuplicateUUIDs() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } $this->loadModel('Attribute'); $duplicates = $this->Attribute->find('all', array( 'fields' => array('Attribute.uuid', 'count(*) as occurance'), 'recursive' => -1, 'group' => array('Attribute.uuid HAVING COUNT(*) > 1'), )); $counter = 0; foreach ($duplicates as $duplicate) { $attributes = $this->Attribute->find('all', array( 'recursive' => -1, 'conditions' => array('uuid' => $duplicate['Attribute']['uuid']) )); foreach ($attributes as $k => $attribute) { if ($k > 0) { $this->Attribute->delete($attribute['Attribute']['id']); $counter++; } } } $this->Server->updateDatabase('makeAttributeUUIDsUnique'); $this->Flash->success('Done. Deleted ' . $counter . ' duplicate attribute(s).'); $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } public function removeDuplicateEvents() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } $this->loadModel('Event'); $duplicates = $this->Event->find('all', array( 'fields' => array('Event.uuid', 'count(*) as occurance'), 'recursive' => -1, 'group' => array('Event.uuid HAVING COUNT(*) > 1'), )); $counter = 0; // load this so we can remove the blocklist item that will be created, this is the one case when we do not want it. if (Configure::read('MISP.enableEventBlocklisting') !== false) { $this->EventBlocklist = ClassRegistry::init('EventBlocklist'); } foreach ($duplicates as $duplicate) { $events = $this->Event->find('all', array( 'recursive' => -1, 'conditions' => array('uuid' => $duplicate['Event']['uuid']) )); foreach ($events as $k => $event) { if ($k > 0) { $uuid = $event['Event']['uuid']; $this->Event->delete($event['Event']['id']); $counter++; // remove the blocklist entry that we just created with the event deletion, if the feature is enabled // We do not want to block the UUID, since we just deleted a copy if (Configure::read('MISP.enableEventBlocklisting') !== false) { $this->EventBlocklist->deleteAll(array('EventBlocklist.event_uuid' => $uuid)); } } } } $this->Server->updateDatabase('makeEventUUIDsUnique'); $this->Flash->success('Done. Removed ' . $counter . ' duplicate events.'); $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } public function upgrade2324() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (!Configure::read('MISP.background_jobs')) { $this->Server->upgrade2324($this->Auth->user('id')); $this->Flash->success('Done. For more details check the audit logs.'); $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } else { $this->loadModel('Job'); $jobId = $this->Job->createJob( $this->Auth->user(), Job::WORKER_DEFAULT, 'upgrade_24', 'Old database', __('Job created.') ); $this->Server->getBackgroundJobsTool()->enqueue( BackgroundJobsTool::DEFAULT_QUEUE, BackgroundJobsTool::CMD_ADMIN, [ 'jobUpgrade24', $jobId, $this->Auth->user('id'), ], true, $jobId ); $this->Flash->success(__('Job queued. You can view the progress if you navigate to the active jobs view (administration -> jobs).')); $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } } public function cleanModelCaches() { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } $this->Server->cleanCacheFiles(); $this->Flash->success('Caches cleared.'); $this->redirect(array('controller' => 'servers', 'action' => 'serverSettings', 'diagnostics')); } public function updateDatabase($command) { if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } if (is_numeric($command)) { $command = intval($command); } $this->Server->updateDatabase($command); $this->Flash->success('Done.'); if ($liveOff) { $this->redirect(array('controller' => 'servers', 'action' => 'updateProgress')); } else { $this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration')); } } public function ipUser($input = false) { $params = $this->IndexFilter->harvestParameters(['ip']); if (!empty($params['ip'])) { $input = $params['ip']; } $redis = $this->Server->setupRedis(); if (!is_array($input)) { $input = [$input]; } $users = []; foreach ($input as $ip) { if (!filter_var($ip, FILTER_VALIDATE_IP)) { continue; } $user_id = $redis->get('misp:ip_user:' . $ip); if (empty($user_id)) { continue; } $this->loadModel('User'); $user = $this->User->find('first', [ 'recursive' => -1, 'conditions' => ['User.id' => $user_id], 'contain' => ['Organisation.name'] ]); if (empty($user)) { throw new NotFoundException(__('User not found (perhaps it has been removed?).')); } $users[$ip] = [ 'id' => $user['User']['id'], 'email' => $user['User']['email'], ]; } return $this->RestResponse->viewData($users, $this->response->type()); } /** * @deprecated * @return void */ public function rest() { $this->redirect(['controller' => 'api', 'action' => 'rest']); } }