diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index c5e425b17..47515a79a 100755 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -103,6 +103,21 @@ class AppController extends Controller public function beforeFilter() { + if (Configure::read('Security.allow_cors')) { + // Add CORS headers + $this->response->cors($this->request, + explode(',', Configure::read('Security.cors_origins')), + ['*'], + ['Origin', 'Content-Type', 'Authorization', 'Accept']); + + if ($this->request->is('options')) { + // Stop here! + // CORS only needs the headers + $this->response->send(); + $this->_stop(); + } + } + if (!empty($this->params['named']['sql'])) { $this->sql_dump = 1; } diff --git a/app/Controller/Component/ACLComponent.php b/app/Controller/Component/ACLComponent.php index b02b2c3cb..46d18dc6d 100644 --- a/app/Controller/Component/ACLComponent.php +++ b/app/Controller/Component/ACLComponent.php @@ -148,7 +148,7 @@ class ACLComponent extends Component 'viewEventAttributes' => array('*'), 'viewEventGraph' => array('*'), 'viewGraph' => array('*'), - 'viewMitreAttackMatrix' => array('*'), + 'viewGalaxyMatrix' => array('*'), 'xml' => array('*') ), 'favouriteTags' => array( diff --git a/app/Controller/Component/RestResponseComponent.php b/app/Controller/Component/RestResponseComponent.php index e37399bd2..04addb0ee 100644 --- a/app/Controller/Component/RestResponseComponent.php +++ b/app/Controller/Component/RestResponseComponent.php @@ -423,14 +423,23 @@ class RestResponseComponent extends Component $type = 'json'; } $cakeResponse = new CakeResponse(array('body'=> $response, 'status' => $code, 'type' => $type)); + + if (Configure::read('Security.allow_cors')) { + $headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Authorization, Accept"; + $headers["Access-Control-Allow-Methods"] = "*"; + $headers["Access-Control-Allow-Origin"] = explode(',', Configure::read('Security.cors_origins')); + } + if (!empty($headers)) { foreach ($headers as $key => $value) { $cakeResponse->header($key, $value); } } + if ($download) { $cakeResponse->download($download); } + return $cakeResponse; } diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index ea2ea21d9..4e03e5276 100644 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -4663,15 +4663,16 @@ class EventsController extends AppController return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json')); } - public function viewMitreAttackMatrix($scope_id, $scope='event', $disable_picking=false) + public function viewGalaxyMatrix($scope_id, $galaxy_id, $scope='event', $disable_picking=false) { $this->loadModel('Galaxy'); + $mitreAttackGalaxyId = $this->Galaxy->getMitreAttackGalaxyId(); + $matrixData = $this->Galaxy->getMatrix($galaxy_id); - $attackTacticData = $this->Galaxy->getMitreAttackMatrix(); - $attackTactic = $attackTacticData['attackTactic']; - $attackTags = $attackTacticData['attackTags']; - $killChainOrders = $attackTacticData['killChain']; - $instanceUUID = $attackTacticData['instance-uuid']; + $tabs = $matrixData['tabs']; + $matrixTags = $matrixData['matrixTags']; + $killChainOrders = $matrixData['killChain']; + $instanceUUID = $matrixData['instance-uuid']; if ($scope == 'event') { $eventId = $scope_id; @@ -4692,17 +4693,20 @@ class EventsController extends AppController throw new Exception("Invalid options."); } - $scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores($eventId, $attackTags); - $scoresDataEvent = $this->Event->EventTag->getTagScores($eventId, $attackTags); + $scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores($eventId, $matrixTags); + $scoresDataEvent = $this->Event->EventTag->getTagScores($eventId, $matrixTags); + $maxScore = 0; $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); + $sum = (isset($scoresDataAttr['scores'][$key]) ? $scoresDataAttr['scores'][$key] : 0) + (isset($scoresDataEvent['scores'][$key]) ? $scoresDataEvent['scores'][$key] : 0); + $scoresData[$key] = $sum; + $maxScore = max($maxScore, $sum); } - $maxScore = max($scoresDataAttr['maxScore'], $scoresDataEvent['maxScore']); + $scores = $scoresData; if ($this->_isRest()) { - $json = array('matrix' => $attackTactic, 'scores' => $scores, 'instance-uuid' => $instanceUUID); + $json = array('matrix' => $tabs, 'scores' => $scores, 'instance-uuid' => $instanceUUID); $this->response->type('json'); return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json')); } else { @@ -4716,14 +4720,22 @@ class EventsController extends AppController $this->set('eventId', $eventId); $this->set('target_type', $scope); - $this->set('killChainOrders', $killChainOrders); - $this->set('attackTactic', $attackTactic); + $this->set('columnOrders', $killChainOrders); + $this->set('tabs', $tabs); $this->set('scores', $scores); $this->set('maxScore', $maxScore); - $this->set('colours', $colours); + if (!empty($colours)) { + $this->set('colours', $colours['mapping']); + $this->set('interpolation', $colours['interpolation']); + } $this->set('pickingMode', !$disable_picking); $this->set('target_id', $scope_id); - $this->render('/Elements/view_mitre_attack_matrix'); + if ($matrixData['galaxy']['id'] == $mitreAttackGalaxyId) { + $this->set('defaultTabName', 'mitre-attack'); + $this->set('removeTrailling', 2); + } + + $this->render('/Elements/view_galaxy_matrix'); } } diff --git a/app/Controller/GalaxiesController.php b/app/Controller/GalaxiesController.php index 20543ed88..eaee5fa97 100644 --- a/app/Controller/GalaxiesController.php +++ b/app/Controller/GalaxiesController.php @@ -79,33 +79,13 @@ class GalaxiesController extends AppController public function selectGalaxy($target_id, $target_type='event', $namespace='misp') { - $expectedDescription = 'ATT&CK Tactic'; + $mitreAttackGalaxyId = $this->Galaxy->getMitreAttackGalaxyId(); $conditions = $namespace == '0' ? array() : array('namespace' => $namespace); - if ($namespace == 'mitre-attack' || $namespace == '0') { - $conditions[] = array('description !=' => $expectedDescription); - $conditions2 = array('namespace' => 'mitre-attack'); - $conditions2[] = array('description' => $expectedDescription); - - $tacticGalaxies = $this->Galaxy->find('all', array( - 'recursive' => -1, - 'conditions' => $conditions2, - )); - } $galaxies = $this->Galaxy->find('all', array( 'recursive' => -1, 'conditions' => $conditions, 'order' => array('name asc') )); - if (!empty($tacticGalaxies)) { - array_unshift($galaxies, array('Galaxy' => array( - 'id' => '-1', - 'uuid' => '-1', - 'name' => $expectedDescription, - 'type' => '-1', - 'icon' => '/img/mitre-attack-icon.ico', - 'namespace' => 'mitre-attack' - ))); - } $items = array(); $items[] = array( @@ -113,7 +93,7 @@ class GalaxiesController extends AppController 'value' => "/galaxies/selectCluster/" . h($target_id) . '/' . h($target_type) . '/0' ); foreach ($galaxies as $galaxy) { - if ($galaxy['Galaxy']['id'] != -1) { + if (!isset($galaxy['Galaxy']['kill_chain_order'])) { $items[] = array( 'name' => h($galaxy['Galaxy']['name']), 'value' => "/galaxies/selectCluster/" . $target_id . '/' . $target_type . '/' . $galaxy['Galaxy']['id'], @@ -123,13 +103,17 @@ class GalaxiesController extends AppController 'infoExtra' => $galaxy['Galaxy']['description'], ) ); - } else { // attackMatrix - $items[] = array( + } else { // should use matrix instead + $param = array( 'name' => $galaxy['Galaxy']['name'], - 'functionName' => "getMitreMatrixPopup('" . $target_type . "', '" . $target_id . "')", + 'functionName' => "getMatrixPopup('" . $target_type . "', '" . $target_id . "', " . $galaxy['Galaxy']['id'] . ")", 'isPill' => true, - 'img' => "/img/mitre-attack-icon.ico", + 'isMatrix' => true ); + if ($galaxy['Galaxy']['id'] == $mitreAttackGalaxyId) { + $param['img'] = "/img/mitre-attack-icon.ico"; + } + $items[] = $param; } } diff --git a/app/Controller/GalaxyClustersController.php b/app/Controller/GalaxyClustersController.php index 9b6d87256..149711948 100644 --- a/app/Controller/GalaxyClustersController.php +++ b/app/Controller/GalaxyClustersController.php @@ -143,6 +143,8 @@ class GalaxyClustersController extends AppController $cluster['GalaxyCluster']['tag_count'] = count($tag['EventTag']); $cluster['GalaxyCluster']['tag_id'] = $tag['Tag']['id']; } + } else { + throw new NotFoundException('Cluster not found.'); } if ($this->_isRest()) { $cluster['GalaxyCluster']['Galaxy'] = $cluster['Galaxy']; diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php index a93c1b40b..e6aaf35af 100644 --- a/app/Controller/UsersController.php +++ b/app/Controller/UsersController.php @@ -1859,14 +1859,17 @@ class UsersController extends AppController { $this->loadModel('Event'); $this->loadModel('Galaxy'); - $attackTacticData = $this->Galaxy->getMitreAttackMatrix(); - $attackTactic = $attackTacticData['attackTactic']; - $attackTags = $attackTacticData['attackTags']; - $killChainOrders = $attackTacticData['killChain']; - $instanceUUID = $attackTacticData['instance-uuid']; - $scoresDataAttr = $this->Event->Attribute->AttributeTag->getTagScores(0, $attackTags); - $scoresDataEvent = $this->Event->EventTag->getTagScores(0, $attackTags); + $galaxy_id = $this->Galaxy->getMitreAttackGalaxyId(); + $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); @@ -1875,7 +1878,7 @@ class UsersController extends AppController $scores = $scoresData; if ($this->_isRest()) { - $json = array('matrix' => $attackTactic, 'scores' => $scores, 'instance-uuid' => $instanceUUID); + $json = array('matrix' => $tabs, 'scores' => $scores, 'instance-uuid' => $instanceUUID); return $this->RestResponse->viewData($json, $this->response->type()); } else { App::uses('ColourGradientTool', 'Tools'); @@ -1883,12 +1886,17 @@ class UsersController extends AppController $colours = $gradientTool->createGradientFromValues($scores); $this->set('target_type', 'attribute'); - $this->set('killChainOrders', $killChainOrders); - $this->set('attackTactic', $attackTactic); + $this->set('columnOrders', $killChainOrders); + $this->set('tabs', $tabs); $this->set('scores', $scores); $this->set('maxScore', $maxScore); - $this->set('colours', $colours); + if (!empty($colours)) { + $this->set('colours', $colours['mapping']); + $this->set('interpolation', $colours['interpolation']); + } $this->set('pickingMode', false); + $this->set('defaultTabName', "mitre-attack"); + $this->set('removeTrailling', 2); $this->render('statistics_attackmatrix'); } diff --git a/app/Lib/Tools/ColourGradientTool.php b/app/Lib/Tools/ColourGradientTool.php index a68c4b4ed..bb0798742 100644 --- a/app/Lib/Tools/ColourGradientTool.php +++ b/app/Lib/Tools/ColourGradientTool.php @@ -1,45 +1,113 @@ interpolateColors($starColor, $endColor, $maxDec+1, true); $coloursMapping = array(); foreach ($items as $name => $val) { - $ratio = ($val-$minDec)*($intervalHex); - $colour = $maxDec == $minDec ? $maxColorHex : $ratio + $minColorHex; - $coloursMapping[$name] = '#' . str_pad(dechex($colour), 6, '0', STR_PAD_LEFT); + $color = $interpolation[$val]; + $coloursMapping[$name] = '#' . str_pad(dechex($color[0]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($color[1]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($color[2]), 2, '0', STR_PAD_LEFT); + } + return array('mapping' => $coloursMapping, 'interpolation' => $interpolation); + } + + private function hue2rgb($p, $q, $t) { + if ($t < 0) $t += 1; + if ($t > 1) $t -= 1; + if ($t < 1/6) return $p + ($q - $p) * 6 * $t; + if ($t < 1/2) return $q; + if ($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6; + return $p; + } + + private function hsl2rgb($color) { + $l = $color[2]; + if ($color[1] == 0) { + $l = round($l*255); + return array($l, $l, $l); + } else { + $s = $color[1]; + $q = ($l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s); + $p = 2 * $l - $q; + $r = $this->hue2rgb($p, $q, $color[0] + 1/3); + $g = $this->hue2rgb($p, $q, $color[0]); + $b = $this->hue2rgb($p, $q, $color[0] - 1/3); + return array(round($r*255), round($g*255), round($b*255)); } - return $coloursMapping; + } + + private function rgb2hsl($color) { + $r = $color[0]/255; + $g = $color[1]/255; + $b = $color[2]/255; + $arrRGB = array($r, $g, $b); + + $max = max($arrRGB); + $min = min($arrRGB); + $h = ($max - $min) / 2; + $s = $h; + $l = $h; + + if ($max == $min) { + $s = 0; // achromatic + $h = 0; + } else { + $d = $max - $min; + $s = ($l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min) ); + if ($max == $r) { + $h = ($g - $b) / $d + ($g < $b ? 6 : 0); + } elseif ($max == $g) { + $h = ($b - $r) / $d + 2; + } elseif ($max == $b) { + $h = ($r - $g) / $d + 4; + } + $h = $h / 6; + return array($h, $s, $l); + } + } + + private function interpolateColor($color1, $color2, $factor, $useHSL=false) { + if ($useHSL) { + $hsl1 = $this->rgb2hsl($color1); + $hsl2 = $this->rgb2hsl($color2); + for ($i=0; $i<3; $i++) { + $hsl1[$i] += $factor*($hsl2[$i] - $hsl1[$i]); + } + $result = $this->hsl2rgb($hsl1); + } else { + $result = $color1; + for ($i = 0; $i < 3; $i++) { + $result[$i] = round($result[$i] + $factor * ($color2[$i] - $color1[$i])); + } + } + return $result; + } + + public function interpolateColors($hexColor1, $hexColor2, $steps, $useHSL=false) { + $stepFactor = 1 / ($steps - 1); + $interpolatedColorArray = array(); + $color1 = sscanf($hexColor1, "#%02x%02x%02x"); + $color2 = sscanf($hexColor2, "#%02x%02x%02x"); + + for($i = 0; $i < $steps; $i++) { + $interpolatedColorArray[$i] = $this->interpolateColor($color1, $color2, $stepFactor * $i, $useHSL); + } + + return $interpolatedColorArray; } } diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php index 1e9bfacf9..742b3a7d3 100644 --- a/app/Model/AppModel.php +++ b/app/Model/AppModel.php @@ -72,7 +72,7 @@ class AppModel extends Model 7 => false, 8 => false, 9 => false, 10 => false, 11 => false, 12 => false, 13 => false, 14 => false, 15 => false, 18 => false, 19 => false, 20 => false, 21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false, - 27 => false, 28 => false + 27 => false, 28 => false, 29 => false ); public function afterSave($created, $options = array()) @@ -1089,6 +1089,9 @@ class AppModel extends Model case 28: $sqlArray[] = "ALTER TABLE `servers` ADD `caching_enabled` tinyint(1) NOT NULL DEFAULT 0;"; break; + case 29: + $sqlArray[] = "ALTER TABLE `galaxies` ADD `kill_chain_order` text NOT NULL;"; + break; case 'fixNonEmptySharingGroupID': $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; diff --git a/app/Model/Galaxy.php b/app/Model/Galaxy.php index 6e9b413cf..1a2900b64 100644 --- a/app/Model/Galaxy.php +++ b/app/Model/Galaxy.php @@ -20,6 +20,14 @@ class Galaxy extends AppModel public function beforeValidate($options = array()) { parent::beforeValidate(); + if (isset($this->data['Galaxy']['kill_chain_order'])) { + $json = json_encode($this->data['Galaxy']['kill_chain_order']); + if ($json !== null) { + $this->data['Galaxy']['kill_chain_order'] = $json; + } else { + unset($this->data['Galaxy']['kill_chain_order']); + } + } return true; } @@ -28,6 +36,18 @@ class Galaxy extends AppModel $this->GalaxyCluster->deleteAll(array('GalaxyCluster.galaxy_id' => $this->id)); } + public function afterFind($results, $primary = false) + { + foreach ($results as $k => $v) { + if (isset($v['Galaxy']['kill_chain_order']) && $v['Galaxy']['kill_chain_order'] !== '') { + $results[$k]['Galaxy']['kill_chain_order'] = json_decode($v['Galaxy']['kill_chain_order'], true); + } else { + unset($results[$k]['Galaxy']['kill_chain_order']); + } + } + return $results; + } + private function __load_galaxies($force = false) { $dir = new Folder(APP . 'files' . DS . 'misp-galaxy' . DS . 'galaxies'); @@ -39,7 +59,7 @@ class Galaxy extends AppModel $file->close(); } $galaxyTypes = array(); - foreach ($galaxies as $galaxy) { + foreach ($galaxies as $i => $galaxy) { $galaxyTypes[$galaxy['type']] = $galaxy['type']; } $temp = $this->find('all', array( @@ -353,81 +373,24 @@ class Galaxy extends AppModel } } - public function getMitreAttackGalaxyId($type="mitre-enterprise-attack-attack-pattern") + public function getMitreAttackGalaxyId($type="mitre-attack-pattern", $namespace="mitre-attack") { $galaxy = $this->find('first', array( 'recursive' => -1, 'fields' => 'id', - 'conditions' => array('Galaxy.type' => $type), + 'conditions' => array('Galaxy.type' => $type, 'Galaxy.namespace' => $namespace), )); return empty($galaxy) ? 0 : $galaxy['Galaxy']['id']; } - public function getMitreAttackMatrix() + public function getMatrix($galaxy_id) { - $killChainOrderEnterprise = array( - 'initial-access', - 'execution', - 'persistence', - 'privilege-escalation', - 'defense-evasion', - 'credential-access', - 'discovery', - 'lateral-movement', - 'collection', - 'exfiltration', - 'command-and-control' - ); - $killChainOrderMobile = array( - 'persistence', - 'privilege-escalation', - 'defense-evasion', - 'credential-access', - 'discovery', - 'lateral-movement', - 'effects', 'collection', - 'exfiltration', - 'command-and-control', - 'general-network-based', - 'cellular-network-based', - 'could-based' - ); - $killChainOrderPre = array( - 'priority-definition-planning', - 'priority-definition-direction', - 'target-selection', - 'technical-information-gathering', - 'people-information-gathering', - 'organizational-information-gathering', - 'technical-weakness-identification', - 'people-weakness-identification', - 'organizational-weakness-identification', - 'adversary-opsec', - 'establish-&-maintain-infrastructure', - 'persona-development', - 'build-capabilities', - 'test-capabilities', - 'stage-capabilities', - 'app-delivery-via-authorized-app-store', - 'app-delivery-via-other-means', - 'exploit-via-cellular-network', - 'exploit-via-internet', - ); - - $killChainOrders = array( - 'mitre-enterprise-attack-attack-pattern' => $killChainOrderEnterprise, - 'mitre-mobile-attack-attack-pattern' => $killChainOrderMobile, - 'mitre-pre-attack-attack-pattern' => $killChainOrderPre, - ); - - $expectedDescription = 'ATT&CK Tactic'; - $expectedNamespace = 'mitre-attack'; - $conditions = array('Galaxy.description' => $expectedDescription, 'Galaxy.namespace' => $expectedNamespace); + $conditions = array('Galaxy.id' => $galaxy_id); $contains = array( 'GalaxyCluster' => array('GalaxyElement'), ); - $galaxies = $this->find('all', array( + $galaxy = $this->find('first', array( 'recursive' => -1, 'contain' => $contains, 'conditions' => $conditions, @@ -435,45 +398,60 @@ class Galaxy extends AppModel $mispUUID = Configure::read('MISP')['uuid']; - $attackTactic = array( - 'killChain' => $killChainOrders, - 'attackTactic' => array(), - 'attackTags' => array(), - 'instance-uuid' => $mispUUID + if (!isset($galaxy['Galaxy']['kill_chain_order'])) { + throw new Exception(__("Galaxy cannot be represented as a matrix")); + + } + $matrixData = array( + 'killChain' => $galaxy['Galaxy']['kill_chain_order'], + 'tabs' => array(), + 'matrixTags' => array(), + 'instance-uuid' => $mispUUID, + 'galaxy' => $galaxy['Galaxy'] ); - foreach ($galaxies as $galaxy) { - $galaxyType = $galaxy['Galaxy']['type']; - $clusters = $galaxy['GalaxyCluster']; - $attackClusters = array(); - // add cluster if kill_chain is present - foreach ($clusters as $cluster) { - if (empty($cluster['GalaxyElement'])) { - continue; + $clusters = $galaxy['GalaxyCluster']; + $cols = array(); + + foreach ($clusters as $cluster) { + if (empty($cluster['GalaxyElement'])) { + continue; + } + + $toBeAdded = false; + $clusterType = $cluster['type']; + $galaxyElements = $cluster['GalaxyElement']; + foreach ($galaxyElements as $element) { + // add cluster if kill_chain is present + if ($element['key'] == 'kill_chain') { + $kc = explode(":", $element['value']); + $galaxyType = $kc[0]; + $kc = $kc[1]; + $cols[$galaxyType][$kc][] = $cluster; + $toBeAdded = true; } - $toBeAdded = false; - $clusterType = $cluster['type']; - $galaxyElements = $cluster['GalaxyElement']; - foreach ($galaxyElements as $element) { - if ($element['key'] == 'kill_chain') { - $kc = explode(":", $element['value'])[2]; - $attackClusters[$kc][] = $cluster; - $toBeAdded = true; - } - if ($element['key'] == 'external_id') { - $cluster['external_id'] = $element['value']; - } + if ($element['key'] == 'external_id') { + $cluster['external_id'] = $element['value']; } if ($toBeAdded) { - array_push($attackTactic['attackTags'], $cluster['tag_name']); + array_push($matrixData['matrixTags'], $cluster['tag_name']); } } - $attackTactic['attackTactic'][$galaxyType] = array( - 'clusters' => $attackClusters, - 'galaxy' => $galaxy['Galaxy'], - ); + } + $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']); + } + ); + } } - return $attackTactic; + return $matrixData; } } diff --git a/app/Model/Server.php b/app/Model/Server.php index 57cfd3b89..2ed315ad1 100644 --- a/app/Model/Server.php +++ b/app/Model/Server.php @@ -1105,6 +1105,24 @@ class Server extends AppModel 'test' => 'testBoolFalse', 'type' => 'boolean', 'null' => true + ), + 'allow_cors' => array( + 'level' => 1, + 'description' => __('Allow cross-origin requests to this instance, matching origins given in Security.cors_origins. Set to false to totally disable'), + 'value' => false, + 'errorMessage' => '', + 'test' => 'testBool', + 'type' => 'boolean', + 'null' => true + ), + 'cors_origins' => array( + 'level' => 1, + 'description' => __('Set the origins from which MISP will allow cross-origin requests. Useful for external integration. Comma seperate if you need more than one.'), + 'value' => '', + 'errorMessage' => '', + 'test' => 'testForEmpty', + 'type' => 'string', + 'null' => true ) ), 'SecureAuth' => array( diff --git a/app/View/Elements/view_galaxy_matrix.ctp b/app/View/Elements/view_galaxy_matrix.ctp new file mode 100644 index 000000000..b16566c66 --- /dev/null +++ b/app/View/Elements/view_galaxy_matrix.ctp @@ -0,0 +1,198 @@ + "abc def ghi", will be: "abc" + - $colours: The colour associated with the tag name (if provided) +* +* +* +*/ +?> + +Html->script('attack_matrix'); + echo $this->Html->css('attack_matrix'); +?> + $colArr) { + $col = str_pad(dechex($colArr[0]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($colArr[1]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($colArr[2]), 2, '0', STR_PAD_LEFT); + $interpolation[$k] = '#' . $col; + if ($k == 0) { // force small area on white + $interpolation[$k] .= ' 3%'; + } + } + $colorScale = implode($interpolation, ', '); + } else { + $colorScale = 'black'; + } +?> +
+ +
+ +
+ +
+ +
+ + + 0 + + +
+ 0 +
+ +
+ + +
+ + + + + +
+
+ $column): ?> +
" id="tabMatrix-"> +
+
+ + + + + + + + + + + '; + $added = false; + foreach($columnOrders[$tabName] as $co) { + if (isset($column[$co][$i])) { + $added = true; + $td = ' $cell); + } + $value = isset($cell['value']) ? $cell['value'] : 0; + if (isset($removeTrailling) && $removeTrailling > 0) { + $name = explode(" ", $value); + $name = join(" ", array_slice($name, 0, -$removeTrailling)); // remove " - external_id" + } else { + $name = $value; + } + $tagName = isset($cell['tag_name']) ? $cell['tag_name'] : $name; + $score = empty($scores[$tagName]) ? 0 : $scores[$tagName]; + $clusterId = isset($cell['id']) ? $cell['id'] : $name; + $externalId = isset($cell['external_id']) ? $cell['external_id'] : ''; + $clusetersNamesMapping[$clusterId] = $name . ($externalId !== '' ? ' (' . $externalId. ')' : ''); + + $td .= ' class="heatCell matrix-interaction ' . ($pickingMode ? 'cell-picking"' : '"'); + $td .= isset($colours[$tagName]) ? ' style="background: ' . h($colours[$tagName]) . '; color: ' . h($this->TextColour->getTextColour($colours[$tagName])) . '"' : '' ; + $td .= ' data-score="'.h($score).'"'; + $td .= ' data-tag_name="'.h($tagName).'"'; + $td .= ' data-cluster-id="'.h($clusterId).'"'; + if ($pickingMode) { + $td .= ' data-target-type="attribute"'; + $td .= ' data-target-id="'.h($target_id).'"'; + } + $td .= ' title="'.h($externalId).'"'; + $td .= '>' . h($name); + + } else { // empty cell + $td = ''; + $tr .= $td; + } + $tr .= ''; + $body .= $tr; + $i++; + } while($added); + echo $body; + ?> + +
+ +
+
'; + } + $td .= '
+
+
+ +
+
+ + + +
+ +
+
+ diff --git a/app/View/Elements/view_mitre_attack_matrix.ctp b/app/View/Elements/view_mitre_attack_matrix.ctp deleted file mode 100644 index 05aa248b5..000000000 --- a/app/View/Elements/view_mitre_attack_matrix.ctp +++ /dev/null @@ -1,134 +0,0 @@ - -
- -
- -
- -
- -
- - 0 - - -
- 0 -
- -
- -
- - - -
-
- -
" id="tabMatrix-"> -
-
- - - - - - - - - - - '; - $killChainOrder = $killChainOrders[$galaxyType]; - $attackClusters = $galaxy['clusters']; - foreach($killChainOrder as $kc) { - if(!isset($attackClusters[$kc])) { // undefined index - $td = ' -
- -
-
'; - } else { - $clusters = $attackClusters[$kc]; - $td = ''; - $i++; - } while($added); - ?> -
-
-
- -
-
- - - -
- -
-
- - -Html->script('bootstrap-typeahead'); - echo $this->Html->script('attack_matrix'); - echo $this->Html->css('attack_matrix'); -?> diff --git a/app/View/Events/view.ctp b/app/View/Events/view.ctp index caeb95193..6566effa6 100644 --- a/app/View/Events/view.ctp +++ b/app/View/Events/view.ctp @@ -33,6 +33,7 @@ } } echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event', 'menuItem' => 'viewEvent', 'mayModify' => $mayModify, 'mayPublish' => $mayPublish)); + echo $this->Html->css('attack_matrix'); ?>
/event/1", function(data) { + $.get("/events/viewGalaxyMatrix///event/1", function(data) { $("#attackmatrix_div").html(data); }); } diff --git a/app/View/Galaxies/view.ctp b/app/View/Galaxies/view.ctp index bb5f24c56..622f05ffc 100644 --- a/app/View/Galaxies/view.ctp +++ b/app/View/Galaxies/view.ctp @@ -23,6 +23,14 @@
+ ' . __('Kill chain order') . ' '; + $kco .= ''; + } + echo $kco; + ?>
@@ -36,5 +44,9 @@ $(document).ready(function () { $.get("", function(data) { $("#clusters_div").html(data); }); + + var $kco = $('#killChainOrder'); + var j = syntaxHighlightJson($kco.text(), 8) + $kco.html(j); }); diff --git a/app/View/Helper/GenericPickerHelper.php b/app/View/Helper/GenericPickerHelper.php index 04d744ef1..3d07a3030 100644 --- a/app/View/Helper/GenericPickerHelper.php +++ b/app/View/Helper/GenericPickerHelper.php @@ -86,6 +86,10 @@ class GenericPickerHelper extends AppHelper { if (isset($param['template']['infoExtra'])) { $pill_html .= $this->_View->element('genericPickerElements/info_extra', array('infoExtra' => $param['template']['infoExtra'], 'forceIcon' => true)); } + if (isset($param['isMatrix']) && $param['isMatrix']) { + $span = ''; + $pill_html .= $span; + } $pill_html .= ''; $pill_html .= ''; return $pill_html; diff --git a/app/View/Users/statistics_attackmatrix.ctp b/app/View/Users/statistics_attackmatrix.ctp index c810a1090..5b2cf49c3 100644 --- a/app/View/Users/statistics_attackmatrix.ctp +++ b/app/View/Users/statistics_attackmatrix.ctp @@ -7,10 +7,10 @@
element('view_mitre_attack_matrix'); + echo $this->element('view_galaxy_matrix'); ?>
- + tr { @@ -17,6 +17,9 @@ .matrix-table tbody { /*height: 670px;*/ overflow-y: scroll; + /* display: inline-block; + position: relative; */ + top: -23px; } table.matrix-table { @@ -102,7 +105,6 @@ div.th-inner { #matrix-heatmap-legend { width: 300px; height: 10px; - background: linear-gradient(to right, white, #0000FF); } .attack-matrix-options div { diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index d8fc86cf5..9b2f872cb 100644 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -902,6 +902,7 @@ a.pill-pre-picker { background-color: #fcfcfc; font-weight: bold; border: 1px #65737ec8 solid; + position: relative; } .nav-pills > .active > a.pill-pre-picker { background-color: #65737e32; diff --git a/app/webroot/js/attack_matrix.js b/app/webroot/js/attack_matrix.js index cff008e24..d2b44d1d3 100644 --- a/app/webroot/js/attack_matrix.js +++ b/app/webroot/js/attack_matrix.js @@ -265,7 +265,7 @@ function makeTagging(tagIds) { $('#GalaxyTargetIds').val(JSON.stringify(tagIds)); - $('#GalaxyViewMitreAttackMatrixForm').submit(); + $('#GalaxyViewGalaxyMatrixForm').submit(); } function filterEvent(tagName, tagId) { diff --git a/app/webroot/js/misp.js b/app/webroot/js/misp.js index 1ba09f76c..beb0924bc 100644 --- a/app/webroot/js/misp.js +++ b/app/webroot/js/misp.js @@ -1476,9 +1476,9 @@ function openPopover(clicked, data, hover, placement) { } } -function getMitreMatrixPopup(scope_id, scope) { +function getMatrixPopup(scope, scope_id, galaxy_id) { cancelPopoverForm(); - getPopup(scope + '/' + scope_id, 'events', 'viewMitreAttackMatrix', '', '#popover_form_large'); + getPopup(scope_id + '/' + galaxy_id + '/' + scope, 'events', 'viewGalaxyMatrix', '', '#popover_form_large'); } function getPopup(id, context, target, admin, popupType) { @@ -3880,7 +3880,10 @@ function insertJSONRestResponse() { $('#json-response-container').html(parsedJson); } -function syntaxHighlightJson(json) { +function syntaxHighlightJson(json, indent) { + if (indent === undefined) { + indent = 2; + } if (typeof json == 'string') { json = JSON.parse(json); }