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';
+ }
+?>
+
+
+
+
+
+
+
+
+
+
+ Form->create('Galaxy', array('url' => '/galaxies/attachMultipleClusters/' . (empty($target_id) ? $eventId : $target_id ) . '/' . (empty($target_type) ? 'event' : $target_type), 'style' => 'margin:0px;'));
+ echo $this->Form->input('target_ids', array('type' => 'text'));
+ echo $this->Form->end();
+ ?>
+
+
+
+
+
+ $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 = ' | ';
+ }
+ $td .= ' | ';
+ $tr .= $td;
+ }
+ $tr .= '';
+ $body .= $tr;
+ $i++;
+ } while($added);
+ echo $body;
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
-
-
-
-
-
-
-
-
-
-
- Form->create('Galaxy', array('url' => '/galaxies/attachMultipleClusters/' . (empty($target_id) ? $eventId : $target_id ) . '/' . (empty($target_type) ? 'event' : $target_type), 'style' => 'margin:0px;'));
- echo $this->Form->input('target_ids', array('type' => 'text'));
- echo $this->Form->end();
- ?>
-
-
-
-
-
-
" 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 .= '
' . json_encode($galaxy['Galaxy']['kill_chain_order']) . '
';
+ }
+ 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);
}