Merge branch '2.4' of https://github.com/MISP/MISP into rework_modules

pull/4584/head
chrisr3d 2019-06-13 14:16:39 +02:00
commit b7b4002fc7
41 changed files with 1301 additions and 336 deletions

View File

@ -1172,6 +1172,9 @@ installCore () {
# install lief
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install https://github.com/lief-project/packages/raw/lief-master-latest/pylief-0.9.0.dev.zip
# install zmq needed by mispzmq
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install zmq
# install python-magic
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic
@ -1269,7 +1272,8 @@ coreCAKE () {
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.python_bin" "${PATH_TO_MISP}/venv/bin/python"
# Set default role
$SUDO_WWW $RUN_PHP -- $CAKE setDefaultRole 3
# TESTME: The following seem defunct, please test.
# $SUDO_WWW $RUN_PHP -- $CAKE setDefaultRole 3
# Tune global time outs
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Session.autoRegenerate" 0
@ -2113,6 +2117,9 @@ installMISPonKali () {
# install python-magic
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic 2> /dev/null > /dev/null
# install zmq needed by mispzmq
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install zmq 2> /dev/null > /dev/null
# Install Crypt_GPG and Console_CommandLine
debug "Installing pear Console_CommandLine"
pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml

View File

@ -1,5 +1,5 @@
; Generated by RHash v1.3.8 on 2019-05-30 at 14:36.25
; Generated by RHash v1.3.8 on 2019-06-13 at 09:09.32
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
;
; 93871 14:36.25 2019-05-30 INSTALL.sh
INSTALL.sh DE23B5D224757A8AB2941D8E15D73F10872D5106 ABEE81992478478406197EEC1891FA7CBDC5B32575447DD6865511B1DE48EC6F D7B9CA78779343C0CD47C9184DCA17DEFA24FA1B6BB35441F574AC40ED5A5AD68738BA91676E528AFC2488B44EC935C6 1FFDD293EF9FD53F80813B33839187ECF00B68FDDECA11327508ACABB99B45F45017CFF4AF2B70CF82D27B4AEEB75C34434B3AA00AD52D3AFC8405E77B8CF348
; 94127 09:09.32 2019-06-13 INSTALL.sh
INSTALL.sh BE7DE7EC5AA24D277F5F752E59356BAD26DC6EF7 E3ACE0081A95A94771EFE51AB8EDEE10CE5687F07A4C36DFD791B9B5F7E128DA 715AE909E454D2A74EEAF8A83D38F82534CB17B9C2BBDF5927251F08BD34651CF46D3260506ACC2BD3F26370E3A89696 24C56E075B7D4415517614C35A255A1D8217495A6A658F6B344A5D98AC24BD9A903E85EC3DC0C25CA89070EF0C0FB82D114ED7FA96D8120179DF4AEF0B1914ED

View File

@ -1 +1 @@
de23b5d224757a8ab2941d8e15d73f10872d5106 INSTALL.sh
be7de7ec5aa24d277f5f752e59356bad26dc6ef7 INSTALL.sh

View File

@ -1 +1 @@
abee81992478478406197eec1891fa7cbdc5b32575447dd6865511b1de48ec6f INSTALL.sh
e3ace0081a95a94771efe51ab8edee10ce5687f07a4c36dfd791b9b5f7e128da INSTALL.sh

View File

@ -1 +1 @@
d7b9ca78779343c0cd47c9184dca17defa24fa1b6bb35441f574ac40ed5a5ad68738ba91676e528afc2488b44ec935c6 INSTALL.sh
715ae909e454d2a74eeaf8a83d38f82534cb17b9c2bbdf5927251f08bd34651cf46d3260506acc2bd3f26370e3a89696 INSTALL.sh

View File

@ -1 +1 @@
1ffdd293ef9fd53f80813b33839187ecf00b68fddeca11327508acabb99b45f45017cff4af2b70cf82d27b4aeeb75c34434b3aa00ad52d3afc8405e77b8cf348 INSTALL.sh
24c56e075b7d4415517614c35a255a1d8217495a6a658f6b344a5d98ac24bd9a903e85ec3dc0c25ca89070ef0c0fb82d114ed7fa96d8120179df4aef0b1914ed INSTALL.sh

View File

@ -465,6 +465,9 @@ installMISPonKali () {
# install python-magic
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic 2> /dev/null > /dev/null
# install zmq needed by mispzmq
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install zmq 2> /dev/null > /dev/null
# Install Crypt_GPG and Console_CommandLine
debug "Installing pear Console_CommandLine"
pear install ${PATH_TO_MISP}/INSTALL/dependencies/Console_CommandLine/package.xml

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":108}
{"major":2, "minor":4, "hotfix":109}

View File

@ -46,7 +46,7 @@ class AppController extends Controller
public $helpers = array('Utility', 'OrgImg', 'FontAwesome');
private $__queryVersion = '73';
private $__queryVersion = '76';
public $pyMispVersion = '2.4.106';
public $phpmin = '7.0';
public $phprec = '7.2';

View File

@ -1277,7 +1277,7 @@ class AttributesController extends AppController
}
if ($this->request->is('ajax')) {
if ($this->request->is('post')) {
if ($this->__delete($id, $hard)) {
if ($this->Attribute->deleteAttribute($id, $this->Auth->user(), $hard)) {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Attribute deleted.')), 'status'=>200, 'type' => 'json'));
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'Attribute was not deleted.')), 'status'=>200, 'type' => 'json'));
@ -1291,7 +1291,7 @@ class AttributesController extends AppController
if (!$this->request->is('post') && !$this->_isRest()) {
throw new MethodNotAllowedException();
}
if ($this->__delete($id, $hard)) {
if ($this->Attribute->deleteAttribute($id, $this->Auth->user(), $hard)) {
if ($this->_isRest() || $this->response->type() === 'application/json') {
$this->set('message', 'Attribute deleted.');
$this->set('_serialize', array('message'));
@ -1360,77 +1360,6 @@ class AttributesController extends AppController
}
}
// unification of the actual delete for the multi-select
private function __delete($id, $hard = false)
{
$this->Attribute->id = $id;
if (!$this->Attribute->exists()) {
return false;
}
$result = $this->Attribute->find('first', array(
'conditions' => array('Attribute.id' => $id),
'fields' => array('Attribute.*'),
'contain' => array('Event' => array(
'fields' => array('Event.*')
)),
));
if (empty($result)) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
// check for permissions
if (!$this->_isSiteAdmin()) {
if ($result['Event']['locked']) {
if ($this->Auth->user('org_id') != $result['Event']['org_id'] || !$this->userRole['perm_sync']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
} else {
if ($this->Auth->user('org_id') != $result['Event']['orgc_id']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
}
}
$date = new DateTime();
if ($hard) {
$save = $this->Attribute->delete($id);
} else {
if (Configure::read('Security.sanitise_attribute_on_delete')) {
$result['Attribute']['category'] = 'Other';
$result['Attribute']['type'] = 'comment';
$result['Attribute']['value'] = 'deleted';
$result['Attribute']['comment'] = '';
$result['Attribute']['to_ids'] = 0;
}
$result['Attribute']['deleted'] = 1;
$result['Attribute']['timestamp'] = $date->getTimestamp();
$save = $this->Attribute->save($result);
$object_refs = $this->Attribute->Object->ObjectReference->find('all', array(
'conditions' => array(
'ObjectReference.referenced_type' => 0,
'ObjectReference.referenced_id' => $id,
),
'recursive' => -1
));
foreach ($object_refs as $ref) {
$ref['ObjectReference']['deleted'] = 1;
$this->Attribute->Object->ObjectReference->save($ref);
}
}
// attachment will be deleted with the beforeDelete() function in the Model
if ($save) {
// 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);
// remove the published flag from the event
$this->Attribute->Event->unpublishEvent($result['Event']['id']);
return true;
} else {
return false;
}
}
public function deleteSelected($id = false, $hard = false)
{
if (!$this->request->is('post')) {
@ -1497,11 +1426,11 @@ class AttributesController extends AppController
$successes = array();
foreach ($attributes as $a) {
if ($hard) {
if ($this->__delete($a['Attribute']['id'], true)) {
if ($this->Attribute->deleteAttribute($a['Attribute']['id'], $this->Auth->user(), true)) {
$successes[] = $a['Attribute']['id'];
}
} else {
if ($this->__delete($a['Attribute']['id'], $a['Attribute']['deleted'] == 1 ? true : false)) {
if ($this->Attribute->deleteAttribute($a['Attribute']['id'], $this->Auth->user(), $a['Attribute']['deleted'] == 1 ? true : false)) {
$successes[] = $a['Attribute']['id'];
}
}

View File

@ -75,6 +75,7 @@ class ACLComponent extends Component
'delete' => array(),
'edit' => array(),
'index' => array(),
'massDelete' => array()
),
'eventDelegations' => array(
'acceptDelegation' => array('perm_add'),
@ -250,6 +251,8 @@ class ACLComponent extends Component
'edit' => array('perm_add'),
'get_row' => array('perm_add'),
'orphanedObjectDiagnostics' => array(),
'proposeObjectsFromAttributes' => array('*'),
'groupAttributesIntoObject' => array('perm_add'),
'revise_object' => array('perm_add'),
'view' => array('*'),
),

View File

@ -27,13 +27,25 @@ class EventBlacklistsController extends AppController
public function index()
{
$passedArgsArray = array();
$passedArgs = $this->passedArgs;
$params = array();
$validParams = array('event_uuid', 'comment');
$validParams = array('event_uuid', 'comment', 'event_info', 'event_orgc');
foreach ($validParams as $validParam) {
if (!empty($this->params['named'][$validParam])) {
$params[$validParam] = $this->params['named'][$validParam];
}
}
if (!empty($this->params['named']['searchall'])) {
$params['AND']['OR'] = array(
'event_uuid' => $this->params['named']['searchall'],
'comment' => $this->params['named']['searchall'],
'event_info' => $this->params['named']['searchall'],
'event_orgc' => $this->params['named']['searchall']
);
}
$this->set('passedArgs', json_encode($passedArgs));
$this->set('passedArgsArray', $passedArgsArray);
$this->BlackList->index($this->_isRest(), $params);
}
@ -51,4 +63,39 @@ class EventBlacklistsController extends AppController
{
$this->BlackList->delete($this->_isRest(), $id);
}
public function massDelete()
{
if ($this->request->is('post') || $this->request->is('put')) {
$ids = $this->request->data['EventBlacklist']['ids'];
$event_ids = json_decode($ids, true);
if (empty($event_ids)) {
throw new NotFoundException(__('Invalid event IDs.'));
}
$result = $this->EventBlacklist->deleteAll(array('EventBlacklist.id' => $event_ids));
if ($result) {
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('EventBlacklist', 'Deleted', $ids, $this->response->type());
} else {
$this->Flash->success('Event deleted.');
$this->redirect(array('controller' => 'eventBlacklists', 'action' => 'index'));
}
} else {
$error = __('Failed to delete Event from EventBlacklist. Error: ') . PHP_EOL . h($result);
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('EventBlacklist', 'Deleted', false, $error, $this->response->type());
} else {
$this->Flash->error($error);
$this->redirect(array('controller' => 'eventBlacklists', 'action' => 'index'));
}
}
} else {
$ids = json_decode($this->request->query('ids'), true);
if (empty($ids)) {
throw new NotFoundException(__('Invalid event IDs.'));
}
$this->set('event_ids', $ids);
}
}
}

View File

@ -4913,7 +4913,12 @@ class EventsController extends AppController
throw new Exception("Invalid options.");
}
$scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores($eventId, $matrixTags);
$event = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $eventId, 'metadata' => true));
if (empty($event)) {
throw new NotFoundException(__('Event not found or you are not authorised to view it.'));
}
$scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores($this->Auth->user(), $eventId, $matrixTags);
$scoresDataEvent = $this->Event->EventTag->getTagScores($eventId, $matrixTags);
$maxScore = 0;
$scoresData = array();
@ -4967,6 +4972,7 @@ class EventsController extends AppController
}
// end FIXME
$this->Galaxy->sortMatrixByScore($tabs, $scores);
if ($this->_isRest()) {
$json = array('matrix' => $tabs, 'scores' => $scores, 'instance-uuid' => $instanceUUID);
$this->response->type('json');

View File

@ -450,7 +450,7 @@ class GalaxyClustersController extends AppController
}
$maxScore = count($scores) > 0 ? max(array_values($scores)) : 0;
$matrixData = $this->GalaxyCluster->Galaxy->getMatrix($mitreAttackGalaxyId);
$matrixData = $this->GalaxyCluster->Galaxy->getMatrix($mitreAttackGalaxyId, $scores);
$tabs = $matrixData['tabs'];
$matrixTags = $matrixData['matrixTags'];
$killChainOrders = $matrixData['killChain'];

View File

@ -989,4 +989,154 @@ class ObjectsController extends AppController
$this->set('captured', $capturedObjects);
$this->set('unmapped', $unmappedAttributes);
}
function proposeObjectsFromAttributes($event_id, $selected_attributes='[]')
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action can only be reached via AJAX.'));
}
$selected_attributes = json_decode($selected_attributes, true);
$res = $this->MispObject->validObjectsFromAttributeTypes($this->Auth->user(), $event_id, $selected_attributes);
$potential_templates = $res['templates'];
$attribute_types = $res['types'];
usort($potential_templates, function($a, $b) {
if ($a['ObjectTemplate']['id'] == $b['ObjectTemplate']['id']) {
return 0;
} else if (is_array($a['ObjectTemplate']['compatibility']) && is_array($b['ObjectTemplate']['compatibility'])) {
return count($a['ObjectTemplate']['compatibility']) > count($b['ObjectTemplate']['compatibility']) ? 1 : -1;
} else if (is_array($a['ObjectTemplate']['compatibility']) && !is_array($b['ObjectTemplate']['compatibility'])) {
return 1;
} else if (!is_array($a['ObjectTemplate']['compatibility']) && is_array($b['ObjectTemplate']['compatibility'])) {
return -1;
} else { // sort based on invalidTypes count
return count($a['ObjectTemplate']['invalidTypes']) > count($b['ObjectTemplate']['invalidTypes']) ? 1 : -1;
}
});
$this->set('potential_templates', $potential_templates);
$this->set('selected_types', $attribute_types);
$this->set('event_id', $event_id);
}
function groupAttributesIntoObject($event_id, $selected_template, $selected_attribute_ids='[]')
{
$event = $this->MispObject->Event->find('first', array(
'recursive' => -1,
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.publish_timestamp'),
'conditions' => array('Event.id' => $event_id)
));
if (empty($event) || (!$this->_isSiteAdmin() && $event['Event']['orgc_id'] != $this->Auth->user('org_id'))) {
throw new NotFoundException(__('Invalid event.'));
}
$hard_delete_attribute = $event['Event']['publish_timestamp'] == 0;
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This action can only be reached via AJAX.'));
}
if ($this->request->is('post')) {
$template = $this->MispObject->ObjectTemplate->find('first', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.id' => $selected_template, 'ObjectTemplate.active' => true)
));
if (empty($template)) {
throw new NotFoundException(__('Invalid template.'));
}
$distribution = $this->request->data['Object']['distribution'];
$sharing_group_id = $this->request->data['Object']['sharing_group_id'];
$comment = $this->request->data['Object']['comment'];
$selected_attribute_ids = json_decode($this->request->data['Object']['selectedAttributeIds'], true);
$selected_object_relation_mapping = json_decode($this->request->data['Object']['selectedObjectRelationMapping'], true);
if ($distribution == 4) {
$sg = $this->MispObject->SharingGroup->find('first', array(
'conditions' => array('SharingGroup.id' => $sharing_group_id),
'recursive' => -1,
'fields' => array('SharingGroup.id', 'SharingGroup.name'),
'order' => false
));
if (empty($sg)) {
throw new NotFoundException(__('Invalid sharing group.'));
}
} else {
$sharing_group_id = 0;
}
$object = array(
'Object' => array(
'distribution' => $distribution,
'sharing_group_id' => $sharing_group_id,
'comment' => $comment,
),
'Attribute' => array()
);
$result = $this->MispObject->groupAttributesIntoObject($this->Auth->user(), $event_id, $object, $template, $selected_attribute_ids, $selected_object_relation_mapping, $hard_delete_attribute);
if (is_numeric($result)) {
$this->MispObject->Event->unpublishEvent($event_id);
return $this->RestResponse->saveSuccessResponse('Objects', 'Created from Attributes', $result, $this->response->type());
} else {
$error = __('Failed to create an Object from Attributes. Error: ') . PHP_EOL . h($result);
return $this->RestResponse->saveFailResponse('Objects', 'Created from Attributes', false, $error, $this->response->type());
}
} else {
$selected_attribute_ids = json_decode($selected_attribute_ids, true);
$selected_attributes = $this->MispObject->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
'Attribute.object_id' => 0
)));
if (empty($selected_attributes)) {
throw new MethodNotAllowedException(__('No Attribute selected.'));
}
$template = $this->MispObject->ObjectTemplate->find('first', array(
'recursive' => -1,
'conditions' => array('ObjectTemplate.id' => $selected_template, 'ObjectTemplate.active' => true),
'contain' => 'ObjectTemplateElement'
));
if (empty($template)) {
throw new NotFoundException(__('Invalid template.'));
}
$conformity_result = $this->MispObject->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $selected_attributes);
$skipped_attributes = 0;
foreach ($selected_attributes as $i => $attribute) {
if (in_array($attribute['Attribute']['type'], $conformity_result['invalidTypes'])) {
unset($selected_attributes[$i]);
$array_position = array_search($attribute['Attribute']['id'], $selected_attribute_ids);
unset($selected_attribute_ids[$array_position]);
$skipped_attributes++;
}
}
$object_relations = array();
foreach ($template['ObjectTemplateElement'] as $template_element) {
$object_relations[$template_element['type']][] = $template_element;
}
$object_references = $this->MispObject->ObjectReference->find('all', array(
'conditions' => array(
'ObjectReference.referenced_id' => $selected_attribute_ids,
),
'recursive' => -1
));
foreach ($object_references as $i => $object_reference) {
$temp_object = $this->MispObject->find('first', array('id' => $object_reference['ObjectReference']['object_id'], 'recursive' => -1));
$temp_attribute = $this->MispObject->Attribute->find('first', array('id' => $object_reference['ObjectReference']['referenced_id'], 'recursive' => -1));
if (!empty($temp_object) && !empty($temp_attribute)) {
$temp_object = $temp_object['Object'];
$temp_attribute = $temp_attribute['Attribute'];
$object_references[$i]['ObjectReference']['object_name'] = $temp_object['name'];
$object_references[$i]['ObjectReference']['attribute_name'] = sprintf('%s/%s: "%s"', $temp_attribute['category'], $temp_attribute['type'], $temp_attribute['value']);
}
}
$distributionData = $this->MispObject->Event->Attribute->fetchDistributionData($this->Auth->user());
$this->set('event_id', $event_id);
$this->set('hard_delete_attribute', $hard_delete_attribute);
$this->set('distributionData', $distributionData);
$this->set('distributionLevels', $this->MispObject->Attribute->distributionLevels);
$this->set('selectedTemplateTd', $selected_template);
$this->set('selectedAttributeIds', $selected_attribute_ids);
$this->set('template', $template);
$this->set('object_relations', $object_relations);
$this->set('attributes', $selected_attributes);
$this->set('skipped_attributes', $skipped_attributes);
$this->set('object_references', $object_references);
}
}
}

View File

@ -1921,94 +1921,149 @@ class UsersController extends AppController
} else {
$galaxy_id = $mitre_galaxy_id;
}
$matrixData = $this->Galaxy->getMatrix($galaxy_id);
$tabs = $matrixData['tabs'];
$matrixTags = $matrixData['matrixTags'];
$killChainOrders = $matrixData['killChain'];
$instanceUUID = $matrixData['instance-uuid'];
$scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores(0, $matrixTags);
$scoresDataEvent = $this->Event->EventTag->getTagScores(0, $matrixTags);
$scoresData = array();
foreach (array_keys($scoresDataAttr['scores'] + $scoresDataEvent['scores']) as $key) {
$scoresData[$key] = (isset($scoresDataAttr['scores'][$key]) ? $scoresDataAttr['scores'][$key] : 0) + (isset($scoresDataEvent['scores'][$key]) ? $scoresDataEvent['scores'][$key] : 0);
$organisations = $this->User->Organisation->find('all', array(
'recursive' => -1,
));
array_unshift($organisations, array('Organisation' => array('id' => 0, 'name' => 'All')));
$this->set('organisations', $organisations);
$picked_organisation = 0;
if (isset($params['organisation']) && $params['organisation'] != 0) {
$org = $this->User->Organisation->find('first', array(
'recursive' => -1,
'conditions' => array('id' => $params['organisation']),
));
if (!empty($org)) {
$picked_organisation = $org;
$this->set('picked_organisation', $picked_organisation);
} else {
$this->set('picked_organisation', array('Organisation' => array('id' => '')));
}
} else {
$this->set('picked_organisation', array('Organisation' => array('id' => '')));
}
$maxScore = max($scoresDataAttr['maxScore'], $scoresDataEvent['maxScore']);
$scores = $scoresData;
// FIXME: temporary fix: add the score of deprecated mitre galaxies to the new one (for the stats)
if ($matrixData['galaxy']['id'] == $mitre_galaxy_id) {
$mergedScore = array();
foreach ($scoresData as $tag => $v) {
$predicateValue = explode(':', $tag, 2)[1];
$predicateValue = explode('=', $predicateValue, 2);
$predicate = $predicateValue[0];
$clusterValue = $predicateValue[1];
$mappedTag = '';
$mappingWithoutExternalId = array();
if ($predicate == 'mitre-attack-pattern') {
$mappedTag = $tag;
$name = explode(" ", $tag);
$name = join(" ", array_slice($name, 0, -2)); // remove " - external_id"
$mappingWithoutExternalId[$name] = $tag;
} else {
$name = explode(" ", $clusterValue);
$name = join(" ", array_slice($name, 0, -2)); // remove " - external_id"
if (isset($mappingWithoutExternalId[$name])) {
$mappedTag = $mappingWithoutExternalId[$name];
$rest_response_empty = true;
$ignore_score = false;
if (
isset($params['dateFrom'])
|| isset($params['dateTo'])
|| isset($params['organisation']) && $params['organisation'] != 0
) { // use restSearch
$ignore_score = true;
$filters = array();
if (isset($params['dateFrom'])) {
$filters['from'] = $params['dateFrom'];
$this->set('dateFrom', $params['dateFrom']);
}
if (isset($params['dateTo'])) {
$filters['to'] = $params['dateTo'];
$this->set('dateTo', $params['dateTo']);
}
if (isset($params['organisation'])) {
$filters['org'] = $params['organisation'];
}
$elementCounter = 0;
$renderView = '';
$final = $this->Event->restSearch($this->Auth->user(), 'attack', $filters, false, false, $elementCounter, $renderView);
$final = json_decode($final, true);
if (!empty($final)) {
$rest_response_empty = false;
foreach ($final as $key => $data) {
$this->set($key, $data);
}
}
}
// No need for restSearch or result is empty
if ($rest_response_empty) {
$matrixData = $this->Galaxy->getMatrix($galaxy_id);
$tabs = $matrixData['tabs'];
$matrixTags = $matrixData['matrixTags'];
$killChainOrders = $matrixData['killChain'];
$instanceUUID = $matrixData['instance-uuid'];
if ($ignore_score) {
$scores_uniform = array('scores' => array(), 'maxScore' => 0);
} else {
$scores_uniform = $this->Event->EventTag->getTagScoresUniform(0, $matrixTags);
}
$scores = $scores_uniform['scores'];
$maxScore = $scores_uniform['maxScore'];
// FIXME: temporary fix: add the score of deprecated mitre galaxies to the new one (for the stats)
if ($matrixData['galaxy']['id'] == $mitre_galaxy_id) {
$mergedScore = array();
foreach ($scores as $tag => $v) {
$predicateValue = explode(':', $tag, 2)[1];
$predicateValue = explode('=', $predicateValue, 2);
$predicate = $predicateValue[0];
$clusterValue = $predicateValue[1];
$mappedTag = '';
$mappingWithoutExternalId = array();
if ($predicate == 'mitre-attack-pattern') {
$mappedTag = $tag;
$name = explode(" ", $tag);
$name = join(" ", array_slice($name, 0, -2)); // remove " - external_id"
$mappingWithoutExternalId[$name] = $tag;
} else {
$adjustedTagName = $this->Galaxy->GalaxyCluster->find('list', array(
'group' => array('GalaxyCluster.id', 'GalaxyCluster.tag_name'),
'conditions' => array('GalaxyCluster.tag_name LIKE' => 'misp-galaxy:mitre-attack-pattern=' . $name . '% T%'),
'fields' => array('GalaxyCluster.tag_name')
));
$adjustedTagName = array_values($adjustedTagName)[0];
$mappingWithoutExternalId[$name] = $adjustedTagName;
$mappedTag = $mappingWithoutExternalId[$name];
$name = explode(" ", $clusterValue);
$name = join(" ", array_slice($name, 0, -2)); // remove " - external_id"
if (isset($mappingWithoutExternalId[$name])) {
$mappedTag = $mappingWithoutExternalId[$name];
} else {
$adjustedTagName = $this->Galaxy->GalaxyCluster->find('list', array(
'group' => array('GalaxyCluster.id', 'GalaxyCluster.tag_name'),
'conditions' => array('GalaxyCluster.tag_name LIKE' => 'misp-galaxy:mitre-attack-pattern=' . $name . '% T%'),
'fields' => array('GalaxyCluster.tag_name')
));
if (!empty($adjustedTagName)) {
$adjustedTagName = array_values($adjustedTagName)[0];
$mappingWithoutExternalId[$name] = $adjustedTagName;
$mappedTag = $mappingWithoutExternalId[$name];
}
}
}
if (isset($mergedScore[$mappedTag])) {
$mergedScore[$mappedTag] += $v;
} else {
$mergedScore[$mappedTag] = $v;
}
}
$scores = $mergedScore;
$maxScore = !empty($mergedScore) ? max(array_values($mergedScore)) : 0;
}
// end FIXME
if (isset($mergedScore[$mappedTag])) {
$mergedScore[$mappedTag] += $v;
} else {
$mergedScore[$mappedTag] = $v;
$this->Galaxy->sortMatrixByScore($tabs, $scores);
if ($this->_isRest()) {
$json = array('matrix' => $tabs, 'scores' => $scores, 'instance-uuid' => $instanceUUID);
return $this->RestResponse->viewData($json, $this->response->type());
} else {
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($scores);
$this->set('target_type', 'attribute');
$this->set('columnOrders', $killChainOrders);
$this->set('tabs', $tabs);
$this->set('scores', $scores);
$this->set('maxScore', $maxScore);
if (!empty($colours)) {
$this->set('colours', $colours['mapping']);
$this->set('interpolation', $colours['interpolation']);
}
$this->set('pickingMode', false);
if ($matrixData['galaxy']['id'] == $mitre_galaxy_id) {
$this->set('defaultTabName', "mitre-attack");
$this->set('removeTrailling', 2);
}
$this->set('galaxyName', $matrixData['galaxy']['name']);
$this->set('galaxyId', $matrixData['galaxy']['id']);
$matrixGalaxies = $this->Galaxy->getAllowedMatrixGalaxies();
$this->set('matrixGalaxies', $matrixGalaxies);
}
$scores = $mergedScore;
$maxScore = max(array_values($mergedScore));
}
// end FIXME
if ($this->_isRest()) {
$json = array('matrix' => $tabs, 'scores' => $scores, 'instance-uuid' => $instanceUUID);
return $this->RestResponse->viewData($json, $this->response->type());
} else {
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($scores);
$this->set('target_type', 'attribute');
$this->set('columnOrders', $killChainOrders);
$this->set('tabs', $tabs);
$this->set('scores', $scores);
$this->set('maxScore', $maxScore);
if (!empty($colours)) {
$this->set('colours', $colours['mapping']);
$this->set('interpolation', $colours['interpolation']);
}
$this->set('pickingMode', false);
if ($matrixData['galaxy']['id'] == $mitre_galaxy_id) {
$this->set('defaultTabName', "mitre-attack");
$this->set('removeTrailling', 2);
}
$this->set('galaxyName', $matrixData['galaxy']['name']);
$this->set('galaxyId', $matrixData['galaxy']['id']);
$matrixGalaxies = $this->Galaxy->getAllowedMatrixGalaxies();
$this->set('matrixGalaxies', $matrixGalaxies);
$this->render('statistics_galaxymatrix');
}
$this->render('statistics_galaxymatrix');
}
public function verifyGPG($full = false)

View File

@ -11,6 +11,7 @@ class AttackExport
public $non_restrictive_export = true;
public $renderView = 'attack_view';
private $__matrixData = null;
private $__clusterCounts = array();
private $__attackGalaxy = 'mitre-attack-pattern';
private $__galaxy_id = 0;
@ -20,7 +21,6 @@ class AttackExport
private $__matrixTags = false;
private $__killChainOrders = false;
private $__instanceUUID = false;
private $__scope = 'Event';
public function handler($data, $options = array())
{
@ -28,35 +28,34 @@ class AttackExport
$this->__GalaxyModel = ClassRegistry::init('Galaxy');
}
$this->__attackGalaxy = empty($options['filters']['attackGalaxy']) ? $this->__attackGalaxy : $options['filters']['attackGalaxy'];
$temp = $this->__GalaxyModel->find('first', array(
if (empty($this->__galaxy_id)) {
$temp = $this->__GalaxyModel->find('first', array(
'recursive' => -1,
'fields' => array('id', 'name'),
'conditions' => array('Galaxy.type' => $this->__attackGalaxy, 'Galaxy.namespace !=' => 'deprecated'),
));
if (empty($temp)) {
return '';
} else {
$this->__galaxy_id = $temp['Galaxy']['id'];
$this->__galaxy_name = $temp['Galaxy']['name'];
}
$matrixData = $this->__GalaxyModel->getMatrix($this->__galaxy_id);
if (empty($this->__tabs)) {
$this->__tabs = $matrixData['tabs'];
$this->__matrixTags = $matrixData['matrixTags'];
$this->__killChainOrders = $matrixData['killChain'];
$this->__instanceUUID = $matrixData['instance-uuid'];
}
$this->__scope = empty($options['scope']) ? 'Event' : $options['scope'];
$clusterData = array();
if ($this->__scope === 'Event') {
$clusterData = $this->__aggregate($data, $clusterData);
if (!empty($data['Attribute'])) {
foreach ($data['Attribute'] as $attribute) {
$clusterData = $this->__aggregate($attribute, $clusterData);
}
));
if (empty($temp)) {
return '';
} else {
$this->__galaxy_id = $temp['Galaxy']['id'];
$this->__galaxy_name = $temp['Galaxy']['name'];
}
}
if (empty($this->__matrixData)) {
$this->__matrixData = $this->__GalaxyModel->getMatrix($this->__galaxy_id);
}
if (empty($this->__tabs)) {
$this->__tabs = $this->__matrixData['tabs'];
$this->__matrixTags = $this->__matrixData['matrixTags'];
$this->__killChainOrders = $this->__matrixData['killChain'];
$this->__instanceUUID = $this->__matrixData['instance-uuid'];
}
$clusterData = $this->__aggregate($data, array());
if (!empty($data['Attribute'])) {
foreach ($data['Attribute'] as $attribute) {
$clusterData = $this->__aggregate($attribute, $clusterData);
}
} else {
$clusterData = $this->__aggregate($data, $clusterData);
}
foreach ($clusterData as $key => $value) {
@ -99,11 +98,12 @@ class AttackExport
$maxScore = $clusterCount;
}
}
$this->__GalaxyModel->sortMatrixByScore($this->__tabs, $this->__clusterCounts);
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($this->__clusterCounts);
$result = array(
'target_type' => strtolower($this->__scope),
'target_type' => 'event',
'columnOrders' => $this->__killChainOrders,
'tabs' => $this->__tabs,
'scores' => $this->__clusterCounts,
@ -114,6 +114,10 @@ class AttackExport
$result['colours'] = $colours['mapping'];
$result['interpolation'] = $colours['interpolation'];
}
if ($this->__galaxy_id == $this->__GalaxyModel->getMitreAttackGalaxyId()) {
$result['defaultTabName'] = 'mitre-attack';
$result['removeTrailling'] = 2;
}
$result['galaxyName'] = $this->__galaxy_name;
$result['galaxyId'] = $this->__galaxy_id;
$matrixGalaxies = $this->__GalaxyModel->getAllowedMatrixGalaxies();

View File

@ -392,8 +392,7 @@ class Attribute extends AppModel
'yara-json' => array('json', 'YaraExport', 'json'),
'rpz' => array('txt', 'RPZExport', 'rpz'),
'csv' => array('csv', 'CsvExport', 'csv'),
'cache' => array('txt', 'CacheExport', 'cache'),
'attack' => array('html', 'AttackExport', 'html')
'cache' => array('txt', 'CacheExport', 'cache')
);
// FIXME we need a better way to list the defaultCategories knowing that new attribute types will continue to appear in the future. We should generate this dynamically or use a function using the default_category of the $typeDefinitions
@ -3168,8 +3167,10 @@ class Attribute extends AppModel
}
if (!empty($results[$key])) {
if (!empty($options['includeGalaxy'])) {
$results[$key] = $this->Event->massageTags($results[$key], 'Attribute');
$results[$key] = $this->Event->massageTags($results[$key], 'Event');
$massaged_attribute = $this->Event->massageTags($results[$key], 'Attribute');
$massaged_event = $this->Event->massageTags($results[$key], 'Event');
$massaged_attribute['Galaxy'] = array_merge_recursive($massaged_attribute['Galaxy'], $massaged_event['Galaxy']);
$results[$key] = $massaged_attribute;
}
$attributes[] = $results[$key];
}
@ -3803,7 +3804,6 @@ class Attribute extends AppModel
} else {
$date = new DateTime();
$attribute['timestamp'] = $date->getTimestamp();
;
}
} else {
$this->create();
@ -3909,6 +3909,72 @@ class Attribute extends AppModel
return true;
}
public function deleteAttribute($id, $user, $hard = false)
{
$this->id = $id;
if (!$this->exists()) {
return false;
}
$result = $this->find('first', array(
'conditions' => array('Attribute.id' => $id),
'recursive' => -1,
'contain' => array('Event')
));
if (empty($result)) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
// check for permissions
if (!$user['Role']['perm_site_admin']) {
if ($result['Event']['locked']) {
if ($user['org_id'] != $result['Event']['org_id'] || !$user['Role']['perm_sync']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
} else {
if ($user['org_id'] != $result['Event']['orgc_id']) {
throw new MethodNotAllowedException(__('Attribute not found or not authorised.'));
}
}
}
$date = new DateTime();
if ($hard) {
$save = $this->delete($id);
} else {
if (Configure::read('Security.sanitise_attribute_on_delete')) {
$result['Attribute']['category'] = 'Other';
$result['Attribute']['type'] = 'comment';
$result['Attribute']['value'] = 'deleted';
$result['Attribute']['comment'] = '';
$result['Attribute']['to_ids'] = 0;
}
$result['Attribute']['deleted'] = 1;
$result['Attribute']['timestamp'] = $date->getTimestamp();
$save = $this->save($result);
$object_refs = $this->Object->ObjectReference->find('all', array(
'conditions' => array(
'ObjectReference.referenced_type' => 0,
'ObjectReference.referenced_id' => $id,
),
'recursive' => -1
));
foreach ($object_refs as $ref) {
$ref['ObjectReference']['deleted'] = 1;
$this->Object->ObjectReference->save($ref);
}
}
// attachment will be deleted with the beforeDelete() function in the Model
if ($save) {
// 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->Event->ShadowAttribute->deleteAll(array('ShadowAttribute.old_id' => $id), false);
// remove the published flag from the event
$this->Event->unpublishEvent($result['Event']['id']);
return true;
} else {
return false;
}
}
public function attachValidationWarnings($adata)
{
if (!$this->__fTool) {

View File

@ -111,45 +111,58 @@ class AttributeTag extends AppModel
));
}
public function getTagScores($eventId=0, $allowedTags=array())
// Fetch all tags attached to attribute belonging to supplied event. No ACL if user not provided
public function getTagScores($user=false, $eventId=0, $allowedTags=array())
{
// get score of galaxy
$db = $this->getDataSource();
$statementArray = array(
'fields' => array('attr_tag.tag_id as id', 'count(attr_tag.tag_id) as value'),
'table' => $db->fullTableName($this),
'alias' => 'attr_tag',
'group' => 'tag_id'
);
if ($eventId != 0) {
$statementArray['conditions'] = array('event_id' => $eventId);
}
// tag along with its occurence in the event
$subQuery = $db->buildStatement(
$statementArray,
$this
);
$subQueryExpression = $db->expression($subQuery)->value;
// get related galaxies
$attributeTagScores = $this->query("SELECT name, value FROM (" . $subQueryExpression . ") AS score, tags WHERE tags.id=score.id;");
// arrange data
$scores = array();
$maxScore = 0;
foreach ($attributeTagScores as $item) {
$score = $item['score']['value'];
$name = $item['tags']['name'];
if (in_array($name, $allowedTags)) {
$maxScore = $score > $maxScore ? $score : $maxScore;
$scores[$name] = $score;
if ($user === false) {
$conditions = array('Tag.id !=' => null);
if ($eventId != 0) {
$conditions['event_id'] = $eventId;
}
$attribute_tag_scores = $this->find('all', array(
'recursive' => -1,
'conditions' => $conditions,
'contain' => array(
'Tag' => array(
'conditions' => array('name' => $allowedTags)
)
),
'fields' => array('Tag.name', 'AttributeTag.event_id')
));
$scores = array('scores' => array(), 'maxScore' => 0);
foreach ($attribute_tag_scores as $attribute_tag_score) {
$tag_name = $attribute_tag_score['Tag']['name'];
if (!isset($scores['scores'][$tag_name])) {
$scores['scores'][$tag_name] = 0;
}
$scores['scores'][$tag_name]++;
$scores['maxScore'] = $scores['scores'][$tag_name] > $scores['maxScore'] ? $scores['scores'][$tag_name] : $scores['maxScore'];
}
} else {
$allowed_tag_lookup_table = array_flip($allowedTags);
$attributes = $this->Attribute->fetchAttributes($user, array('conditions' => array(
'Attribute.event_id' => $eventId
)));
$scores = array('scores' => array(), 'maxScore' => 0);
foreach ($attributes as $attribute) {
foreach ($attribute['AttributeTag'] as $tag) {
$tag_name = $tag['Tag']['name'];
if (isset($allowed_tag_lookup_table[$tag_name])) {
if (!isset($scores['scores'][$tag_name])) {
$scores['scores'][$tag_name] = 0;
}
$scores['scores'][$tag_name]++;
$scores['maxScore'] = $scores['scores'][$tag_name] > $scores['maxScore'] ? $scores['scores'][$tag_name] : $scores['maxScore'];
}
}
}
}
return array('scores' => $scores, 'maxScore' => $maxScore);
return $scores;
}
// find all tags that belong to a list of attributes (contained in the same event)
public function getAttributesTags($user, $requestedEventId, $attributeIds=false) {
public function getAttributesTags($user, $requestedEventId, $attributeIds=false, $includeGalaxies=false) {
$conditions = array('Attribute.event_id' => $requestedEventId);
if (is_array($attributeIds) && $attributeIds !== false) {
$conditions['Attribute.id'] = $attributeIds;
@ -169,12 +182,11 @@ class AttributeTag extends AppModel
'recursive' => -1,
'fields' => array('GalaxyCluster.tag_name', 'GalaxyCluster.id'),
));
$allTags = array();
foreach ($attributes as $attribute) {
$attributeTags = $attribute['AttributeTag'];
foreach ($attributeTags as $k => $attributeTag) {
if (!isset($cluster_names[$attributeTag['Tag']['name']])) {
if ($includeGalaxies || !isset($cluster_names[$attributeTag['Tag']['name']])) {
$allTags[$attributeTag['Tag']['id']] = $attributeTag['Tag'];
}
}

View File

@ -154,7 +154,7 @@ class EventTag extends AppModel
}
return $tags;
}
public function countForTag($tag_id, $user)
{
return $this->find('count', array(
@ -163,39 +163,96 @@ class EventTag extends AppModel
));
}
public function getTagScores($eventId=0, $allowedTags=array())
public function getTagScores($eventId=0, $allowedTags=array(), $propagateToAttribute=false)
{
// get score of galaxy
$db = $this->getDataSource();
$statementArray = array(
'fields' => array('event_tag.tag_id as id', 'count(event_tag.tag_id) as value'),
'table' => $db->fullTableName($this),
'alias' => 'event_tag',
'group' => 'tag_id'
);
if ($eventId != 0) {
$statementArray['conditions'] = array('event_id' => $eventId);
if ($propagateToAttribute) {
$eventTagScores = $this->find('all', array(
'recursive' => -1,
'conditions' => array('Tag.id !=' => null),
'contain' => array(
'Event',
'Tag' => array(
'conditions' => array('name' => $allowedTags)
)
),
'fields' => array('Tag.name', 'Event.attribute_count')
));
} else {
$conditions = array('Tag.id !=' => null);
if ($eventId != 0) {
$conditions['event_id'] = $eventId;
}
$eventTagScores = $this->find('all', array(
'recursive' => -1,
'conditions' => $conditions,
'contain' => array(
'Tag' => array(
'conditions' => array('name' => $allowedTags)
)
),
'group' => 'tag_id',
'fields' => array('Tag.name', 'EventTag.tag_id', 'count(EventTag.tag_id) as score')
));
}
// tag along with its occurence in the event
$subQuery = $db->buildStatement(
$statementArray,
$this
);
$subQueryExpression = $db->expression($subQuery)->value;
// get related galaxies
$attributeTagScores = $this->query("SELECT name, value FROM (" . $subQueryExpression . ") AS score, tags WHERE tags.id=score.id;");
// arrange data
$scores = array();
$maxScore = 0;
foreach ($attributeTagScores as $item) {
$score = $item['score']['value'];
$name = $item['tags']['name'];
foreach ($eventTagScores as $item) {
$score = isset($item['Event']) ? $item['Event']['attribute_count'] : $item[0]['score'];
$name = $item['Tag']['name'];
if (in_array($name, $allowedTags)) {
$maxScore = $score > $maxScore ? $score : $maxScore;
$scores[$name] = $score;
if (!isset($scores[$name])) {
$scores[$name] = 0;
}
$scores[$name] += $score;
}
}
return array('scores' => $scores, 'maxScore' => $maxScore);
}
// Fetch all tags contained in an event (both event and attributes) ignoring the occurence. No ACL
public function getTagScoresUniform($eventId=0, $allowedTags=array())
{
$conditions = array('Tag.id !=' => null);
if ($eventId != 0) {
$conditions['event_id'] = $eventId;
}
$event_tag_scores = $this->find('all', array(
'recursive' => -1,
'conditions' => $conditions,
'contain' => array(
'Tag' => array(
'conditions' => array('name' => $allowedTags)
)
),
'fields' => array('Tag.name', 'EventTag.event_id')
));
$attribute_tag_scores = $this->Event->Attribute->AttributeTag->find('all', array(
'recursive' => -1,
'conditions' => $conditions,
'contain' => array(
'Tag' => array(
'conditions' => array('name' => $allowedTags)
)
),
'fields' => array('Tag.name', 'AttributeTag.event_id')
));
$score_aggregation = array();
foreach ($event_tag_scores as $event_tag_score) {
$score_aggregation[$event_tag_score['Tag']['name']][$event_tag_score['EventTag']['event_id']] = 1;
}
foreach ($attribute_tag_scores as $attribute_tag_score) {
$score_aggregation[$attribute_tag_score['Tag']['name']][$attribute_tag_score['AttributeTag']['event_id']] = 1;
}
$scores = array('scores' => array(), 'maxScore' => 0);
foreach ($score_aggregation as $name => $array_ids) {
$event_count = count($array_ids);
$scores['scores'][$name] = $event_count;
$scores['maxScore'] = $event_count > $scores['maxScore'] ? $event_count : $scores['maxScore'];
}
return $scores;
}
}

View File

@ -397,7 +397,7 @@ class Galaxy extends AppModel
return $galaxies;
}
public function getMatrix($galaxy_id)
public function getMatrix($galaxy_id, $scores=array())
{
$conditions = array('Galaxy.id' => $galaxy_id);
$contains = array(
@ -454,18 +454,7 @@ class Galaxy extends AppModel
}
$matrixData['tabs'] = $cols;
foreach ($matrixData['tabs'] as $k => $v) {
foreach ($matrixData['tabs'][$k] as $kc => $v2) {
// sort clusters in the kill chains
usort(
$matrixData['tabs'][$k][$kc],
function($a, $b) {
return strcmp($a['value'], $b['value']);
}
);
}
}
$this->sortMatrixByScore($matrixData['tabs'], $scores);
// #FIXME temporary fix: retreive tag name of deprecated mitre galaxies (for the stats)
if ($galaxy['Galaxy']['id'] == $this->getMitreAttackGalaxyId()) {
$names = array('Enterprise Attack - Attack Pattern', 'Pre Attack - Attack Pattern', 'Mobile Attack - Attack Pattern');
@ -486,4 +475,34 @@ class Galaxy extends AppModel
$matrixData['matrixTags'] = array_keys($matrixData['matrixTags']);
return $matrixData;
}
public function sortMatrixByScore(&$tabs, $scores)
{
foreach (array_keys($tabs) as $i) {
foreach (array_keys($tabs[$i]) as $j) {
// major ordering based on score, minor based on alphabetical
usort($tabs[$i][$j], function ($a, $b) use($scores) {
if ($a['tag_name'] == $b['tag_name']) {
return 0;
}
if (isset($scores[$a['tag_name']]) && isset($scores[$b['tag_name']])) {
if ($scores[$a['tag_name']] < $scores[$b['tag_name']]) {
$ret = 1;
} else if ($scores[$a['tag_name']] == $scores[$b['tag_name']]) {
$ret = strcmp($a['value'], $b['value']);
} else {
$ret = -1;
}
} else if (isset($scores[$a['tag_name']])) {
$ret = -1;
} else if (isset($scores[$b['tag_name']])) {
$ret = 1;
} else { // none are set
$ret = strcmp($a['value'], $b['value']);
}
return $ret;
});
}
}
}
}

View File

@ -725,4 +725,114 @@ class MispObject extends AppModel
}
return count($orphans);
}
public function validObjectsFromAttributeTypes($user, $event_id, $selected_attribute_ids)
{
$attributes = $this->Attribute->fetchAttributes($user,
array(
'conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
'Attribute.object_id' => 0
),
)
);
if (empty($attributes)) {
return array('templates' => array(), 'types' => array());
}
$attribute_types = array();
foreach ($attributes as $i => $attribute) {
$attribute_types[$attribute['Attribute']['type']] = 1;
$attributes[$i]['Attribute']['object_relation'] = $attribute['Attribute']['type'];
}
$attribute_types = array_keys($attribute_types);
$potential_templates = $this->ObjectTemplate->find('all', array(
'recursive' => -1,
'fields' => array(
'ObjectTemplate.id',
'ObjectTemplate.name',
'COUNT(ObjectTemplateElement.type) as type_count'
),
'conditions' => array(
'ObjectTemplate.active' => true,
'ObjectTemplateElement.type' => $attribute_types
),
'joins' => array(
array(
'table' => 'object_template_elements',
'alias' => 'ObjectTemplateElement',
'type' => 'RIGHT',
'fields' => array('ObjectTemplateElement.object_relation', 'ObjectTemplateElement.type'),
'conditions' => array('ObjectTemplate.id = ObjectTemplateElement.object_template_id')
)
),
'group' => 'ObjectTemplate.name',
'order' => 'type_count DESC'
));
$potential_template_ids = Hash::extract($potential_templates, '{n}.ObjectTemplate.id');
$templates = $this->ObjectTemplate->find('all', array(
'recursive' => -1,
'conditions' => array('id' => $potential_template_ids),
'contain' => 'ObjectTemplateElement'
));
foreach ($templates as $i => $template) {
$res = $this->ObjectTemplate->checkTemplateConformityBasedOnTypes($template, $attributes);
$templates[$i]['ObjectTemplate']['compatibility'] = $res['valid'] ? true : $res['missingTypes'];
$templates[$i]['ObjectTemplate']['invalidTypes'] = $res['invalidTypes'];
$templates[$i]['ObjectTemplate']['invalidTypesMultiple'] = $res['invalidTypesMultiple'];
}
return array('templates' => $templates, 'types' => $attribute_types);
}
public function groupAttributesIntoObject($user, $event_id, $object, $template, $selected_attribute_ids, $selected_object_relation_mapping, $hard_delete_attribute)
{
$saved_object_id = $this->saveObject($object, $event_id, $template, $user);
if (!is_numeric($saved_object_id)) {
return $saved_object_id;
}
$saved_object = $this->find('first', array(
'recursive' => -1,
'conditions' => array('Object.id' => $saved_object_id)
));
$existing_attributes = $this->Attribute->fetchAttributes($user, array('conditions' => array(
'Attribute.id' => $selected_attribute_ids,
'Attribute.event_id' => $event_id,
'Attribute.object_id' => 0
)));
if (empty($existing_attributes)) {
return __('Selected Attributes do not exist.');
}
$event = array('Event' => $existing_attributes[0]['Event']);
// Duplicate the attribute and its context, otherwise connected instances will drop the duplicated UUID
foreach ($existing_attributes as $i => $existing_attribute) {
if (isset($selected_object_relation_mapping[$existing_attribute['Attribute']['id']])) {
$sightings = $this->Event->Sighting->attachToEvent($event, $user, $existing_attribute['Attribute']['id']);
$object_relation = $selected_object_relation_mapping[$existing_attribute['Attribute']['id']];
$created_attribute = $existing_attribute['Attribute'];
unset($created_attribute['timestamp']);
unset($created_attribute['id']);
unset($created_attribute['uuid']);
$created_attribute['object_relation'] = $object_relation;
$created_attribute['object_id'] = $saved_object['Object']['id'];
if (isset($existing_attribute['AttributeTag'])) {
$created_attribute['AttributeTag'] = $existing_attribute['AttributeTag'];
}
if (!empty($sightings)) {
$created_attribute['Sighting'] = $sightings;
}
$saved_object['Attribute'][$i] = $created_attribute;
$this->Attribute->captureAttribute($created_attribute, $event_id, $user, $saved_object['Object']['id']);
$this->Attribute->deleteAttribute($existing_attribute['Attribute']['id'], $user, $hard_delete_attribute);
}
}
return $saved_object['Object']['id'];
}
}

View File

@ -204,6 +204,74 @@ class ObjectTemplate extends AppModel
return true;
}
public function checkTemplateConformityBasedOnTypes($template, $attributes)
{
$to_return = array('valid' => true, 'missingTypes' => array());
if (!empty($template['ObjectTemplate']['requirements'])) {
// check for all required attributes
if (!empty($template['ObjectTemplate']['requirements']['required'])) {
foreach ($template['ObjectTemplate']['requirements']['required'] as $requiredField) {
$requiredType = Hash::extract($template['ObjectTemplateElement'], sprintf('{n}[object_relation=%s].type', $requiredField))[0];
$found = false;
foreach ($attributes as $attribute) {
if ($attribute['Attribute']['type'] == $requiredType) {
$found = true;
}
}
if (!$found) {
$to_return = array('valid' => false, 'missingTypes' => array($requiredType));
}
}
}
// check for all required one of attributes
if (!empty($template['ObjectTemplate']['requirements']['requiredOneOf'])) {
$found = false;
$all_required_type = array();
foreach ($template['ObjectTemplate']['requirements']['requiredOneOf'] as $requiredField) {
$requiredType = Hash::extract($template['ObjectTemplateElement'], sprintf('{n}[object_relation=%s].type', $requiredField));
$requiredType = empty($requiredType) ? NULL : $requiredType[0];
$all_required_type[] = $requiredType;
foreach ($attributes as $attribute) {
if ($attribute['Attribute']['type'] == $requiredType) {
$found = true;
}
}
}
if (!$found) {
$to_return = array('valid' => false, 'missingTypes' => $all_required_type);
}
}
}
// at this point, an object could created; checking if all attribute are valids
$valid_types = array();
$to_return['invalidTypes'] = array();
$to_return['invalidTypesMultiple'] = array();
foreach ($template['ObjectTemplateElement'] as $templateElement) {
$valid_types[$templateElement['type']] = $templateElement['multiple'];
}
$check_for_multiple_type = array();
foreach ($attributes as $attribute) {
if (isset($valid_types[$attribute['Attribute']['type']])) {
if (!$valid_types[$attribute['Attribute']['type']]) { // is not multiple
if (isset($check_for_multiple_type[$attribute['Attribute']['type']])) {
$to_return['invalidTypesMultiple'][] = $attribute['Attribute']['type'];
} else {
$check_for_multiple_type[$attribute['Attribute']['type']] = 1;
}
}
} else {
$to_return['invalidTypes'][] = $attribute['Attribute']['type'];
}
}
$to_return['invalidTypes'] = array_unique($to_return['invalidTypes']);
$to_return['invalidTypesMultiple'] = array_unique($to_return['invalidTypesMultiple']);
if (!empty($to_return['invalidTypesMultiple'])) {
$to_return['valid'] = false;
}
return $to_return;
}
// simple test to see if there are any object templates - if not trigger update
public function populateIfEmpty($user)
{

View File

@ -24,11 +24,21 @@
$full = $isAclTagger && $tagAccess;
$tagData = "";
foreach ($tags as $tag) {
if (empty($tag['Tag'])) {
$tag['Tag'] = $tag;
}
if (empty($tag['Tag']['colour'])) {
$tag['Tag']['colour'] = '#0088cc';
}
$aStyle = 'display:inline-block; background-color:' . h($tag['Tag']['colour']) . ';color:' . $this->TextColour->getTextColour($tag['Tag']['colour']) . ';';
$aClass = $full ? 'tagFirstHalf' : 'tag';
$aText = h($tag['Tag']['name']);
$aSearchTagUrl = $baseurl . '/events/index/searchtag: ' . h($tag['Tag']['id']);
$span1 = sprintf('<a href="%s" style="%s" class="%s">%s</a>', $aSearchTagUrl, $aStyle, $aClass, $aText);
if (!empty($tag['Tag']['id'])) {
$aSearchTagUrl = $baseurl . '/events/index/searchtag: ' . h($tag['Tag']['id']);
$span1 = sprintf('<a href="%s" style="%s" class="%s">%s</a>', $aSearchTagUrl, $aStyle, $aClass, $aText);
} else {
$aSpan1 = sprintf('<span style="%s" class="%s">%s</span>', $aStyle, $aClass, $aText);
}
$span2 = '';
if ($full) {
$spanClass = "tagSecondHalf useCursorPointer noPrint";

View File

@ -79,6 +79,15 @@
'onClick' => 'popoverPopup',
'onClickParams' => array('this', 'selected/attribute', 'galaxies', 'selectGalaxyNamespace')
),
array(
'id' => 'group-into-object-button',
'title' => __('Group selected Attributes into an Object'),
'class' => 'mass-select hidden',
'fa-icon' => 'object-group',
'fa-source' => 'fa',
'onClick' => 'proposeObjectsFromSelectedAttributes',
'onClickParams' => array('this', $event['Event']['id'])
),
array(
'id' => 'multi-delete-button',
'title' => __('Delete selected Attributes'),

View File

@ -101,12 +101,12 @@ foreach($tabs as $tabName => $column):
</div>
<?php endif; ?>
<div id="matrix_container" class="fixed-table-container-inner" style="max-height: 670px;" data-picking-mode="<?php echo $pickingMode ? 'true' : 'false'; ?>">
<div id="matrix_container" class="fixed-table-container-inner" style="" data-picking-mode="<?php echo $pickingMode ? 'true' : 'false'; ?>">
<div class="tab-content">
<?php foreach($tabs as $tabName => $column): ?>
<div class="tab-pane <?php echo $tabName==$defaultTabName ? "active" : ""; ?>" id="tabMatrix-<?php echo h($tabName); ?>">
<div class="header-background"></div>
<div class="fixed-table-container-inner" style="max-height: 670px;">
<div class="fixed-table-container-inner" style="">
<table class="table table-condensed matrix-table">
<thead>
<tr>
@ -116,7 +116,10 @@ foreach($tabs as $tabName => $column):
?>
<th>
<?php echo h(ucfirst($name)); ?>
<div class="th-inner"><?php echo h(ucfirst($name)); ?></div>
<div class="th-inner" style="flex-direction: column; align-items: flex-start; padding-top: 3px;">
<span><?php echo h(ucfirst($name)); ?></span>
<i style="font-size: smaller;"><?php echo sprintf(__('(%s items)'), isset($column[$co]) ? count($column[$co]) : 0); ?></i>
</div>
</th>
<?php endforeach; ?>
@ -197,5 +200,5 @@ foreach($tabs as $tabName => $column):
</select>
</div>
<div class="templateChoiceButton btn-matrix-submit submit-container hide"><?php echo __('Submit'); ?></div>
<div role="button" tabindex="0" aria-label="<?php echo __('Cancel');?>" title="<?php echo __('Cancel');?>" class="templateChoiceButton templateChoiceButtonLast" onClick="cancelPopoverForm('#popover_form_large');"><?php echo __('Cancel'); ?></div>
<div role="button" tabindex="0" aria-label="<?php echo __('Cancel');?>" title="<?php echo __('Cancel');?>" class="templateChoiceButton templateChoiceButtonLast" onClick="cancelPopoverForm('#popover_matrix');"><?php echo __('Cancel'); ?></div>
<?php endif; ?>

View File

@ -16,8 +16,36 @@
?>
</ul>
</div>
<div>
<?php
$data = array(
'children' => array(
array(
'children' => array(
array(
'class' => 'hidden mass-select',
'fa-icon' => 'trash',
'onClick' => "multiSelectDeleteEventBlacklist",
'onClickParams' => array('1', '0')
)
)
),
array(
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
)
)
);
echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data));
?>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th>
<input class="select_all select" type="checkbox" title="<?php echo __('Select all');?>" role="button" tabindex="0" aria-label="<?php echo __('Select all events on current page');?>" onClick="toggleAllCheckboxes();" />&nbsp;
</th>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('org');?></th>
<th><?php echo $this->Paginator->sort('event_uuid', __('Event UUID'));?></th>
@ -28,6 +56,9 @@
</tr><?php
foreach ($response as $item): ?>
<tr>
<td style="width:10px;">
<input class="select" type="checkbox" data-id="<?php echo h($item['EventBlacklist']['id']); ?>" aria-label="select <?php echo h($item['EventBlacklist']['id'])?>" />
</td>
<td class="short"><?php echo h($item['EventBlacklist']['id']); ?>&nbsp;</td>
<td class="short"><?php echo (isset($item['EventBlacklist']['event_orgc']) ? h($item['EventBlacklist']['event_orgc']) : '&nbsp;'); ?></td>
<td class="short"><?php echo h($item['EventBlacklist']['event_uuid']); ?>&nbsp;</td>
@ -35,7 +66,7 @@ foreach ($response as $item): ?>
<td class="short"><?php echo (isset($item['EventBlacklist']['event_info']) ? h($item['EventBlacklist']['event_info']) : '&nbsp;'); ?></td>
<td class="short"><?php echo (isset($item['EventBlacklist']['comment']) ? h($item['EventBlacklist']['comment']) : '&nbsp;'); ?></td>
<td class="short action-links">
<a href="<?php echo $baseurl;?>/eventBlacklists/edit/<?php echo h($item['EventBlacklist']['id']); ?>"><span class="fa fa-edit" title=<?php echo __('Edit')?> role="button" tabindex="0" aria-label="Edit blacklist entry">&nbsp;</span></a>
<a href="<?php echo $baseurl;?>/eventBlacklists/edit/<?php echo h($item['EventBlacklist']['id']); ?>"><span class="fa fa-edit black" title=<?php echo __('Edit')?> role="button" tabindex="0" aria-label="Edit blacklist entry">&nbsp;</span></a>
<?php echo $this->Form->postLink('', array('action' => 'delete', h($item['EventBlacklist']['id'])), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete the blacklist entry for the event UUID %s?', h($item['EventBlacklist']['event_uuid']))); ?>
</td>
</tr><?php
@ -59,6 +90,25 @@ endforeach; ?>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
popoverStartup();
$('.select').on('change', function() {
listCheckboxesChecked();
});
$('.select').on('change', function() {
listCheckboxesChecked();
});
$('#quickFilterButton').click(function() {
runIndexQuickFilter();
});
$('#quickFilterField').on('keypress', function (e) {
if(e.which === 13) {
runIndexQuickFilter();
}
});
});
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'eventBlacklists'));
?>

View File

@ -0,0 +1,29 @@
<div class="confirmation">
<?php
echo $this->Form->create('EventBlacklist', array('style' => 'margin:0px;', 'id' => 'PromptForm'));
echo $this->Form->input('ids', array(
'type' => 'hidden',
'div' => 'hidden',
'value' => json_encode($event_ids)
));
?>
<legend><?php echo __('Delete blacklisted events'); ?></legend>
<div style="padding-left:5px;padding-right:5px;padding-bottom:5px;">
<p><?php echo __('Are you sure you want to delete from the blacklist the selected events?'); ?></p>
<table>
<tr>
<td style="vertical-align:top">
<span role="button" tabindex="0" aria-label="Publish" title="Publish" id="PromptYesButton" class="btn btn-primary" onClick="submitPublish()">Yes</span>
</td>
<td style="width:540px;">
</td>
<td style="vertical-align:top;">
<span role="button" tabindex="0" aria-label="Cancel" title="Cancel" class="btn btn-inverse" id="PromptNoButton" onClick="cancelPrompt();">No</span>
</td>
</tr>
</table>
</div>
<?php
echo $this->Form->end();
?>
</div>

View File

@ -10,7 +10,7 @@ App::uses('AppHelper', 'View/Helper');
$g = hexdec(substr($RGB, 3, 2));
$b = hexdec(substr($RGB, 5, 2));
$average = ((2 * $r) + $b + (3 * $g))/6;
if ($average < 128) {
if ($average < 127) {
return 'white';
} else {
return 'black';

View File

@ -40,6 +40,7 @@
<body>
<div id="popover_form" class="ajax_popover_form"></div>
<div id="popover_form_large" class="ajax_popover_form ajax_popover_form_large"></div>
<div id="popover_matrix" class="ajax_popover_form ajax_popover_matrix"></div>
<div id="popover_box" class="popover_box"></div>
<div id="screenshot_box" class="screenshot_box"></div>
<div id="confirmation_box" class="confirmation_box"></div>

View File

@ -0,0 +1,179 @@
<button class="btn btn-inverse" onclick="showObjectProposition()"><i class="fas fa-chevron-left"></i></button>
<?php
echo $this->Form->create('Object', array('url' => $baseurl . '/objects/groupAttributesIntoObject/' . $event_id . '/' . $selectedTemplateTd));
?>
<dl style="margin-top: 10px;">
<dt><?php echo __('Object Template');?></dt>
<dd><a href="<?php echo $baseurl . '/ObjectTemplates/view/' . h($template['ObjectTemplate']['id']) ?>"><?php echo Inflector::humanize(h($template['ObjectTemplate']['name'])) . ' v' . h($template['ObjectTemplate']['version']); ?></a></dd>
<dt><?php echo __('Description');?></dt>
<dd><?php echo h($template['ObjectTemplate']['description']); ?></dd>
<dt><?php echo __('Meta category');?></dt>
<dd><?php echo h($template['ObjectTemplate']['meta-category']); ?></dd>
<dt><?php echo __('Distribution');?></dt>
<dd>
<?php echo $this->Form->input('Object.distribution', array(
'class' => 'Object_distribution_select',
'options' => $distributionData['levels'],
'default' => $distributionData['initial'],
'label' => false,
'style' => 'margin-bottom:5px;',
'div' => false
)); ?>
<?php echo $this->Form->input('Object.sharing_group_id', array(
'class' => 'Object_sharing_group_id_select',
'options' => $distributionData['sgs'],
'label' => false,
'div' => false,
'style' => 'display:none;margin-bottom:5px;',
'value' => 0
)); ?>
<dt><?php echo __('Comment');?></dt>
<dd>
<?php echo $this->Form->input('Object.comment', array(
'type' => 'textarea',
'style' => 'height:20px;width:400px;',
'required' => false,
'allowEmpty' => true,
'label' => false,
'div' => false
)); ?>
<div class="hidden">
<?php
echo $this->Form->input('selectedTemplateId', array('type' => 'hidden', 'value' => $selectedTemplateTd));
echo $this->Form->input('selectedAttributeIds', array('type' => 'hidden', 'value' => json_encode($selectedAttributeIds)));
echo $this->Form->input('selectedObjectRelationMapping', array('value' => ''));
echo $this->Form->end();
?>
</div>
</dl>
<div style="border: 2px solid #3465a4 ; border-radius: 3px; overflow: hidden;">
<table class="table table-striped table-condensed" style="margin-bottom: 0px;">
<thead>
<tr>
<th><?php echo __('ID'); ?></th>
<th><?php echo __('Name :: Type'); ?></th>
<th><?php echo __('Date'); ?></th>
<th><?php echo __('Category'); ?></th>
<th><?php echo __('Value'); ?></th>
<th><?php echo __('Distribution'); ?></th>
</tr>
</thead>
<tbody id='attributeMappingBody'>
<?php foreach ($attributes as $attribute): ?>
<tr>
<td id="isAttributeId"><?php echo h($attribute['Attribute']['id']); ?></td>
<td>
<span style="display: block;">
<select id="isAttributeMapping" style="margin-bottom: 5px;" onchange="updateObjectRelationDescription(this);">
<?php foreach ($object_relations[$attribute['Attribute']['type']] as $object_relation): ?>
<option value="<?php echo h($object_relation['object_relation']); ?>" title="<?php echo h($object_relation['description']); ?>"><?php echo h($object_relation['object_relation']); ?></option>
<?php endforeach; ?>
</select>
:: <?php echo h($attribute['Attribute']['type']); ?>
</span>
<i id="objectRelationDescription" class="apply_css_arrow"><?php echo h($object_relations[$attribute['Attribute']['type']][0]['description']); ?></i>
</td>
<td style="min-width: 75px;"><?php echo h(date('Y-m-d', $attribute['Attribute']['timestamp'])); ?></td>
<td><?php echo h($attribute['Attribute']['category']); ?></td>
<td style="white-space: nowrap;"><?php echo h($attribute['Attribute']['value']); ?></td>
<td><?php echo h($distributionLevels[$attribute['Attribute']['distribution']]); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php if ($skipped_attributes > 0): ?>
<div class="alert" style="margin-top: 15px;">
<strong><?php echo __('Skipped'); ?></strong> <?php echo h($skipped_attributes) . __(' Attribute(s)') ?>
</div>
<?php endif; ?>
<?php if (!empty($object_references)): ?>
<div class="alert alert-danger" style="margin-top: 15px;">
<strong><?php echo __('Dropped Object references'); ?></strong>
<div><?php echo __('As these Attributes are converted into an Objects, the meaning of the Refences might change. If you whish to preserve the References, you will have to created them after the merge. Take note of them!') ?></div>
<div><?php echo __('The following References will be dropped after the merge:') ?></div>
<div style="max-height: 170px; overflow-y: auto; border: 1px solid #e6cace; border-radius: 4px; padding: 5px;">
<table style="margin: 2px;">
<?php foreach ($object_references as $object_reference): ?>
<?php $object_reference = $object_reference['ObjectReference']; ?>
<tr>
<td><span style="margin-right: 5px;">&#8226;</span></td>
<td><?php echo sprintf('<strong>%s</strong> (%s)', h($object_reference['object_name']), h($object_reference['object_id'])); ?></td>
<td style="text-align: center;">
<div style="display: inline-block; position: relative; margin: 10px 10px 0px 10px; top: -8px;">
<span style=""><?php echo h($object_reference['relationship_type']); ?></span>
<i class="fas fa-long-arrow-alt-right" style="font-size: x-large; position: absolute; left: calc(50% - 10px); top: 10px;"></i>
</div>
</td>
<td><?php echo sprintf('<strong>%s</strong> (%s)', h($object_reference['attribute_name']), h($object_reference['referenced_id'])); ?></td>
<?php if ($object_reference['comment'] !== ''): ?>
<td><span style="margin: 0px 10px;">-</span></td>
<td style="margin-left: 10px"><?php echo h($object_reference['comment']); ?></td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</table>
</div>
</div>
<?php endif; ?>
<div style="margin-top: 15px; text-align: center;">
<div>
<button class="btn btn-primary" onclick="submitMergeAttributeIntoObjectForm(this);"><?php echo __('Merge above Attributes into an Object'); ?></button>
</div>
<span class="red bold" data-original-title="" title="">
<?php echo sprintf(__('Selected Attributes will be %s deleted'), '<strong style="font-size: medium">' . ($hard_delete_attribute ? __('hard') : __('soft')) . '</strong>'); ?>
</span>
</div>
<script>
$(".Object_distribution_select").change(function() {
checkAndEnable($(this).parent().find('.Object_sharing_group_id_select'), $(this).val() == 4);
});
function submitMergeAttributeIntoObjectForm(btn) {
var $btn = $(btn);
var $form = $('#ObjectGroupAttributesIntoObjectForm');
var attribute_mapping = {};
$('#attributeMappingBody').find('tr').each(function() {
var $tr = $(this);
var attr_id = $tr.find('#isAttributeId').text();
var attr_mapping = $tr.find('#isAttributeMapping').val();
attribute_mapping[attr_id] = attr_mapping;
});
$('#ObjectSelectedObjectRelationMapping').val(JSON.stringify(attribute_mapping));
var btn_text_backup = '';
$.ajax({
data: $form.serialize(),
beforeSend: function (XMLHttpRequest) {
btn_text_backup = $btn.text();
$btn.html('<it class="fa fa-spinner fa-spin"></it>');
},
success:function (data, textStatus) {
if (data.errors !== undefined) {
showMessage('fail', responseArray.errors);
$btn.text(btn_text_backup);
return false;
} else {
location.reload();
}
},
error:function() {
showMessage('fail', 'Could not merge Attributes into an Object.');
showObjectProposition();
},
type:"post",
url: $form.attr('action')
});
}
function updateObjectRelationDescription(changed) {
var $select = $(changed);
var text = $select.find(":selected").attr('title');
$select.parent().parent().find('#objectRelationDescription').text(text);
}
</script>

View File

@ -0,0 +1,85 @@
<div style="max-width: 1000px; max-height: 800px; overflow-y: auto; min-height: 400px; min-width: 700px;">
<div>
<?php echo !empty($selected_types) ? '<strong>' . __('Selected types: ') . '</strong>' : ''; ?>
<?php foreach ($selected_types as $type): ?>
<span class="label label-info"><?php echo h($type) ?></span>
<?php endforeach; ?>
</div>
<?php if (empty($potential_templates)): ?>
<?php echo __('No matching Object.'); ?>
<?php else: ?>
<table id="tableGroupAttributeIntoObject" class="table table-condensed table-hover">
<thead>
<tr>
<th><?php echo __('Template'); ?></th>
<th><?php echo __('Object name'); ?></th>
<th><?php echo __('Category'); ?></th>
<th><?php echo __('Description'); ?></th>
<th title="<?php echo __('Compatiblity or Attribute type missing from the selection'); ?>"><?php echo __('Compatiblity'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($potential_templates as $i => $potential_template): ?>
<tr class="useCursorPointer" style="<?php echo $potential_template['ObjectTemplate']['compatibility'] === true ? '' : 'cursor: not-allowed;' ?>" data-objecttemplateid="<?php echo h($potential_template['ObjectTemplate']['id']); ?>" data-enabled="<?php echo $potential_template['ObjectTemplate']['compatibility'] === true ? 'true' : 'false'; ?>">
<td class="ignoreSelection">
<a href="<?php echo $baseurl . '/ObjectTemplates/view/' . h($potential_template['ObjectTemplate']['id']) ?>"><?php echo h($potential_template['ObjectTemplate']['id']); ?></a>
</td>
<td><?php echo h($potential_template['ObjectTemplate']['name']); ?></td>
<td><?php echo h($potential_template['ObjectTemplate']['meta-category']); ?></td>
<?php
$v = h($potential_template['ObjectTemplate']['description']);
$v = strlen($v) > 100 ? substr($v, 0, 100) . '...' : $v;
?>
<td style="max-width: 500px;" title="<?php echo h($potential_template['ObjectTemplate']['description']); ?>">
<?php echo $v; ?>
</td>
<?php if ($potential_template['ObjectTemplate']['compatibility'] === true): ?>
<td>
<i class="fa fa-check" style="font-size: medium;" title="<?php echo __('This Object is compatible for the merge'); ?>"></i>
<?php if (!empty($potential_template['ObjectTemplate']['invalidTypes'])): ?>
<?php foreach ($potential_template['ObjectTemplate']['invalidTypes'] as $type): ?>
<span class="label label-warning" title="<?php echo __('This Attribute type cannot be part of this Object template. If you merge the selected Attributes into this object, all Attribute having this type will be ignored.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
<?php endif; ?>
</td>
<?php else: ?>
<td style="max-width: 500px;">
<?php foreach ($potential_template['ObjectTemplate']['compatibility'] as $type): ?>
<span class="label label-important" title="<?php echo __('This Attribute type is missing from the selection. Add it to the selection to be able to merge the selected Attributes into this Object.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
<?php foreach ($potential_template['ObjectTemplate']['invalidTypesMultiple'] as $type): ?>
<span class="label" title="<?php echo __('This Attribute type is not allowed to be present multiple time in this Object. Consider only picking one.'); ?>"><?php echo h($type); ?></span>
<?php endforeach; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div id="resultPreview" class="hidden" style="height: calc(100% - 20px);"></div>
<?php endif; ?>
</div>
<script>
$('#tableGroupAttributeIntoObject > tbody > tr[data-enabled="true"] > td:not(.ignoreSelection)').click(function() {
var object_template_id = $(this).parent().data('objecttemplateid');
if (object_template_id !== undefined) {
var $parentDIV = $('#tableGroupAttributeIntoObject').parent();
var bb = $parentDIV[0].getBoundingClientRect();
$parentDIV.css({height: bb.height, width: bb.width});
$('#tableGroupAttributeIntoObject').toggle('slide');
$('#resultPreview').show().html('<div style="align-items: center; justify-content: center; display: flex; height: 100%; width: 100%"><i class="fas fa-spinner fa-spin" style="font-size: xx-large;"></i></div>');
$.get('<?php echo $baseurl . '/objects/groupAttributesIntoObject/' . h($event_id) . '/' ?>' + object_template_id + '/' + getSelected(), function(data) {
$('#resultPreview').html(data);
});
}
});
function showObjectProposition() {
$('#resultPreview').html('');
$('#tableGroupAttributeIntoObject').toggle('slide', {
direction: 'left',
});
$('#resultPreview').hide();
}
</script>

View File

@ -5,19 +5,65 @@
?>
<p style="margin-bottom: 40px;"><?php echo sprintf(__('A heatmap showing the usage of %s.'), $galaxyName);?></p>
<select id="galaxyMatrixPicker" onchange="this.options[this.selectedIndex].value && (window.location = window.location.pathname.replace(/\/*galaxy_id:\d+/, '') + '/' + 'galaxy_id:' + this.options[this.selectedIndex].value);" >
<?php foreach ($matrixGalaxies as $k => $galaxy): ?>
<option value="<?php echo h($galaxy['Galaxy']['id']); ?>" <?php echo $galaxy['Galaxy']['id'] == $galaxyId ? 'selected' : ''; ?> ><?php echo h($galaxy['Galaxy']['name']); ?></option>
<?php endforeach; ?>
</select>
<div style="height: 80px;">
<div class="input select">
<label>Galaxy</label>
<select id="galaxyMatrixPicker" data-toggle="chosen">
<?php foreach ($matrixGalaxies as $k => $galaxy): ?>
<option value="<?php echo h($galaxy['Galaxy']['id']); ?>" <?php echo $galaxy['Galaxy']['id'] == $galaxyId ? 'selected' : ''; ?> ><?php echo h($galaxy['Galaxy']['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="input select">
<label>Organisation</label>
<select id="organisationPicker" data-toggle="chosen">
<?php foreach ($organisations as $k => $org): ?>
<option value="<?php echo isset($org['Organisation']['id']) ? h($org['Organisation']['id']) : ''; ?>" <?php echo $org['Organisation']['id'] == $picked_organisation['Organisation']['id'] ? 'selected' : ''; ?> ><?php echo h($org['Organisation']['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="display: inline-block;">
<label>Dates</label>
<input id="dateFrom" class="datepicker" placeholder="from" value="<?php echo isset($dateFrom) ? h($dateFrom) : ''; ?>"></input>
<i class="fas fa-arrow-right"></i>
<input id="dateTo" class="datepicker" placeholder="to" value="<?php echo isset($dateTo) ? h($dateTo) : ''; ?>"></input>
</div>
<button id="btnSubmit" class="btn btn-primary"><?php echo __('Submit') ?></button>
</div>
<div id="attackmatrix_div" style="position: relative; border: solid 1px;" class="statistics_attack_matrix">
<?php
echo $this->element('view_galaxy_matrix');
?>
<div id="attackmatrix_div" style="position: relative; border: solid 1px;" class="statistics_attack_matrix">
<?php
echo $this->element('view_galaxy_matrix');
?>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('#btnSubmit').click(function() {
var value = $('#galaxyMatrixPicker').val();
var organisation = $('#organisationPicker').val();
var dateFrom = $('#dateFrom').val();
var dateTo = $('#dateTo').val();
var eventTagsOnAttributes = $('#eventTagsOnAttributes').is(':checked');
var url = '<?php echo $baseurl; ?>/users/statistics/galaxyMatrix/galaxy_id:' + value
if (organisation) {
url += '/' + 'organisation:' + organisation;
}
if (dateFrom) {
url += '/' + 'dateFrom:' + dateFrom;
}
if (dateTo) {
url += '/' + 'dateTo:' + dateTo;
}
$(this).text('').append('<i class="fas fa-spinner fa-spin"></i>')
window.location = url;
});
$('[data-toggle="chosen"]').chosen();
})
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'statistics'));

@ -1 +1 @@
Subproject commit 8c69da1fd9c0be2ec742370f4918f0f88554644f
Subproject commit 0ebe2c50a7a04ae2108e0c32da058826cf15387c

View File

@ -22,6 +22,7 @@ import time
import uuid
import base64
import stix2misp_mapping
import stix_edh
import stix.extensions.marking.ais
from mixbox.namespaces import NamespaceNotFoundError
from operator import attrgetter
@ -1207,7 +1208,9 @@ def _update_namespaces():
# can add additional ones whenever it is needed
ADDITIONAL_NAMESPACES = [
Namespace('http://us-cert.gov/ciscp', 'CISCP',
'http://www.us-cert.gov/sites/default/files/STIX_Namespace/ciscp_vocab_v1.1.1.xsd')
'http://www.us-cert.gov/sites/default/files/STIX_Namespace/ciscp_vocab_v1.1.1.xsd'),
Namespace('http://taxii.mitre.org/messages/taxii_xml_binding-1.1', 'TAXII',
'http://docs.oasis-open.org/cti/taxii/v1.1.1/cs01/schemas/TAXII-XMLMessageBinding-Schema.xsd')
]
for namespace in ADDITIONAL_NAMESPACES:
register_namespace(namespace)

View File

@ -49,6 +49,9 @@ td.matrix-interaction.cell-picked {
overflow-y: auto;
max-height: 670px;
}
#popover_matrix div.fixed-table-container-inner {
max-height: calc(80vh - 100px);
}
.extra-wrap {
width: 100%;

View File

@ -853,6 +853,14 @@ a.proposal_link_red:hover {
left: calc(50% - 700px);
}
.ajax_popover_matrix {
width: 96vw;
min-width: 1400px;
left: 2vw;
max-height: 80vh;
position: absolute;
}
.popover_box {
display:none;
position: fixed;

View File

@ -1,6 +1,4 @@
(function () {
var minWidth = 1400;
var savedTopOffset;
var clusterNameToIdMapping = new Map();
var typeaheadDataMatrixSearch;
var pickedGalaxies = [];
@ -13,13 +11,6 @@
$('#attack-matrix-tabscontroller span').off('click.tab').on('click.tab', function (e) {
$(this).tab('show');
var jfilter = '.info_container_eventgraph_network';
var colNum = $(jfilter+' .matrix-table > thead > tr > th :visible').length;
$('#attackmatrix_div').css('min-width', 100*colNum);
jfilter = '.ajax_popover_form';
var colNum = $(jfilter+' .matrix-table > thead > tr > th :visible').length;
$('#popover_form_large').css('min-width', 100*colNum);
adapt_position_from_viewport(100*colNum);
});
// form
@ -31,7 +22,7 @@
$('.ajax_popover_form .btn-matrix-submit').click(function() {
makeTagging(pickedGalaxies);
cancelPopoverForm('#popover_form_large');
cancelPopoverForm('#popover_matrix');
});
var scoredCells = $('.ajax_popover_form .heatCell').filter(function() {
return $(this).attr('data-score') > 0;
@ -177,24 +168,8 @@
$(jfilter + ' #matrix-heatmap-legend-caret-value').text(score);
}
function adapt_position_from_viewport(minOverwrite) {
minOverwrite = minOverwrite !== undefined ? minOverwrite : minWidth;
minOverwrite = minWidth > minOverwrite ? minWidth : minOverwrite;
if($(window).width()*0.5+700 <= minOverwrite) {
var topOff = $('#popover_form_large').offset().top;
savedTopOffset = topOff >= $(document).scrollTop() ? topOff - $(document).scrollTop() : topOff;
$('#popover_form_large').css({
position: 'absolute',
left: '10px',
top: savedTopOffset+$(document).scrollTop()+'px'
});
} else {
$('#popover_form_large').css({
position: 'absolute',
left: '',
top: savedTopOffset
});
}
function adapt_position_from_viewport() {
$('#popover_matrix').css('top', document.documentElement.scrollTop + 120 + 'px');
}
function matrixContextualMenu(cell, x, y, tagName, tagId, func_name) {

View File

@ -845,6 +845,22 @@ function multiSelectToggleFeeds(on, cache) {
});
}
function multiSelectDeleteEventBlacklist(on, cache) {
var selected = [];
$(".select").each(function() {
if ($(this).is(":checked")) {
var temp = $(this).data("id");
if (temp != null) {
selected.push(temp);
}
}
});
$.get("/eventBlacklists/massDelete?ids=" + JSON.stringify(selected), function(data) {
$("#confirmation_box").html(data);
openPopup("#confirmation_box");
});
}
function multiSelectAction(event, context) {
var settings = {
deleteAttributes: {
@ -913,6 +929,11 @@ function addSelectedTaxonomies(taxonomy) {
});
}
function proposeObjectsFromSelectedAttributes(clicked, event_id) {
var selectedAttributeIds = getSelected();
popoverPopup(clicked, event_id + '/' + selectedAttributeIds, 'objects', 'proposeObjectsFromAttributes');
}
function hideSelectedTags(taxonomy) {
$.get("/taxonomies/taxonomyMassHide/"+taxonomy, function(data) {
$("#confirmation_box").html(data);
@ -1456,20 +1477,23 @@ function templateElementFileCategoryChange(category) {
}
}
function openPopup(id) {
var window_height = $(window).height();
var popup_height = $(id).height();
if (window_height < popup_height) {
$(id).css("top", 50);
$(id).css("height", window_height);
$(id).addClass('vertical-scroll');
} else {
if (window_height > (300 + popup_height)) {
var top_offset = ((window_height - popup_height) / 2) - 150;
function openPopup(id, adjust_layout) {
adjust_layout = adjust_layout === undefined ? true : adjust_layout;
if (adjust_layout) {
var window_height = $(window).height();
var popup_height = $(id).height();
if (window_height < popup_height) {
$(id).css("top", 50);
$(id).css("height", window_height);
$(id).addClass('vertical-scroll');
} else {
var top_offset = (window_height - popup_height) / 2;
if (window_height > (300 + popup_height)) {
var top_offset = ((window_height - popup_height) / 2) - 150;
} else {
var top_offset = (window_height - popup_height) / 2;
}
$(id).css("top", top_offset + 'px');
}
$(id).css("top", top_offset + 'px');
}
$("#gray_out").fadeIn();
$(id).fadeIn();
@ -1555,7 +1579,7 @@ function openPopover(clicked, data, hover, placement) {
function getMatrixPopup(scope, scope_id, galaxy_id) {
cancelPopoverForm();
getPopup(scope_id + '/' + galaxy_id + '/' + scope, 'events', 'viewGalaxyMatrix', '', '#popover_form_large');
getPopup(scope_id + '/' + galaxy_id + '/' + scope, 'events', 'viewGalaxyMatrix', '', '#popover_matrix');
}
function getPopup(id, context, target, admin, popupType) {
@ -1578,7 +1602,7 @@ function getPopup(id, context, target, admin, popupType) {
success:function (data, textStatus) {
$(".loading").hide();
$(popupType).html(data);
openPopup(popupType);
openPopup(popupType, false);
},
error:function() {
$(".loading").hide();
@ -3673,7 +3697,7 @@ function quickSubmitGalaxyForm(cluster_ids, additionalData) {
var scope = additionalData['target_type'];
fetchFormDataAjax("/galaxies/attachMultipleClusters/" + target_id + "/" + scope, function(formData) {
$('body').append($('<div id="temp"/>').html(formData));
$('#temp #GalaxyTargetIds').val(JSON.stringify(selected_tag_ids));
$('#temp #GalaxyTargetIds').val(JSON.stringify(cluster_ids));
if (target_id == 'selected') {
$('#AttributeAttributeIds, #GalaxyAttributeIds').val(getSelected());
}
@ -3733,6 +3757,7 @@ $(document).keyup(function(e){
$("#gray_out").fadeOut();
$("#popover_form").fadeOut();
$("#popover_form_large").fadeOut();
$("#popover_matrix").fadeOut();
$("#screenshot_box").fadeOut();
$("#popover_box").fadeOut();
$("#confirmation_box").fadeOut();

View File

@ -173,6 +173,9 @@ installCore () {
# install lief
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install https://github.com/lief-project/packages/raw/lief-master-latest/pylief-0.9.0.dev.zip
# install zmq needed by mispzmq
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install zmq
# install python-magic
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install python-magic

View File

@ -72,13 +72,13 @@ echo " - Change DELETE FROM to > 0 in misp-wipe.sql to also remove default ones"
echo " - Defaults are created on first login"
MySQLRUser=${MySQLRUser:-$MySQLUUser}
MySQLRPass=${MySQLRPass:-$MySQLUPass}
mysql -u $MySQLRUser -p$MySQLRPass $MISPDB < $SQL
mysql --host $MISPDBHost -u $MySQLRUser -p$MySQLRPass $MISPDB < $SQL
echo "Inserting default values to MySQL tables"
TMP=/tmp/misp-wipe-$$.sql
cd $MISPPath
sed -n '/Default values for initial installation/ { s///; :a; n; p; ba; }' INSTALL/MYSQL.sql | egrep -v '(admin_settings|db_version)' > $TMP
mysql -u $MySQLRUser -p$MySQLRPass $MISPDB < $TMP
mysql --host $MISPDBHost -u $MySQLRUser -p$MySQLRPass $MISPDB < $TMP
rm -f $TMP
echo "Wiping files"