Merge remote-tracking branch 'upstream/2.4' into guides

pull/4621/head
Steve Clement 2019-05-12 23:14:44 +09:00
commit 2ecb633a5c
7 changed files with 202 additions and 19 deletions

View File

@ -2005,7 +2005,7 @@ class AttributesController extends AppController
'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
'includeProposals', 'returnFormat', 'published', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
'includeWarninglistHits'
'includeWarninglistHits', 'attackGalaxy'
);
$filterData = array(
'request' => $this->request,
@ -2034,9 +2034,19 @@ class AttributesController extends AppController
$returnFormat = 'json';
}
$elementCounter = 0;
$final = $this->Attribute->restSearch($user, $returnFormat, $filters, false, false, $elementCounter);
$responseType = $validFormats[$returnFormat][0];
return $this->RestResponse->viewData($final, $responseType, false, true, false, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
$renderView = '';
$final = $this->Attribute->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView);
if (!empty($renderView) && !empty($final)) {
$this->layout = false;
$final = json_decode($final, true);
foreach ($final as $key => $data) {
$this->set($key, $data);
}
$this->render('/Events/module_views/' . $renderView);
} else {
$responseType = $this->Attribute->validFormats[$returnFormat][0];
return $this->RestResponse->viewData($final, $responseType, false, true, false, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
}
}
// returns an XML with attributes that belong to an event. The type of attributes to be returned can be restricted by type using the 3rd parameter.

View File

@ -3286,7 +3286,7 @@ class EventsController extends AppController
$paramArray = array(
'value', 'type', 'category', 'org', 'tag', 'tags', 'searchall', 'from', 'to', 'last', 'eventid', 'withAttachments',
'metadata', 'uuid', 'published', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'sgReferenceOnly', 'returnFormat',
'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits'
'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits', 'attackGalaxy'
);
$filterData = array(
'request' => $this->request,
@ -3314,9 +3314,20 @@ class EventsController extends AppController
$returnFormat = 'json';
}
$elementCounter = 0;
$final = $this->Event->restSearch($user, $returnFormat, $filters, false, false, $elementCounter);
$responseType = $this->Event->validFormats[$returnFormat][0];
return $this->RestResponse->viewData($final, $responseType, false, true, false, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
$renderView = false;
$final = $this->Event->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView);
if (!empty($renderView) && !empty($final)) {
$this->layout = false;
$final = json_decode($final, true);
foreach ($final as $key => $data) {
$this->set($key, $data);
}
$this->render('/Events/module_views/' . $renderView);
} else {
$responseType = $this->Event->validFormats[$returnFormat][0];
return $this->RestResponse->viewData($final, $responseType, false, true, false, array('X-Result-Count' => $elementCounter, 'X-Export-Module-Used' => $returnFormat, 'X-Response-Format' => $responseType));
}
}
public function downloadOpenIOCEvent($key, $eventid, $enforceWarninglist = false)

View File

@ -0,0 +1,128 @@
<?php
class AttackExport
{
public $additional_params = array(
'flatten' => 1,
'includeEventTags' => 1,
'includeGalaxy' => 1
);
public $non_restrictive_export = true;
public $renderView = 'attack_view';
private $__clusterCounts = array();
private $__attackGalaxy = 'mitre-attack-pattern';
private $__galaxy_id = 0;
private $__galaxy_name = '';
private $__GalaxyModel = null;
private $__tabs = false;
private $__matrixTags = false;
private $__killChainOrders = false;
private $__instanceUUID = false;
private $__scope = 'Event';
public function handler($data, $options = array())
{
if (empty($this->__GalaxyModel)) {
$this->__GalaxyModel = ClassRegistry::init('Galaxy');
}
$this->__attackGalaxy = empty($options['filters']['attackGalaxy']) ? $this->__attackGalaxy : $options['filters']['attackGalaxy'];
$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);
}
}
} else {
$clusterData = $this->__aggregate($data, $clusterData);
}
foreach ($clusterData as $key => $value) {
if (empty($this->__clusterCounts[$key])) {
$this->__clusterCounts[$key] = 1;
} else {
$this->__clusterCounts[$key] += 1;
}
}
return '';
}
private function __aggregate($data, $clusterData)
{
if (!empty($data['Galaxy'])) {
foreach ($data['Galaxy'] as $galaxy) {
if ($galaxy['type'] == $this->__attackGalaxy) {
foreach ($galaxy['GalaxyCluster'] as $galaxyCluster) {
$clusterData[$galaxyCluster['tag_name']] = 1;
}
}
}
}
return $clusterData;
}
public function header($options = array())
{
return '';
}
public function footer()
{
if (empty($this->__GalaxyModel)) {
return '';
}
$maxScore = 0;
foreach ($this->__clusterCounts as $clusterCount) {
if ($clusterCount > $maxScore) {
$maxScore = $clusterCount;
}
}
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($this->__clusterCounts);
$result = array(
'target_type' => strtolower($this->__scope),
'columnOrders' => $this->__killChainOrders,
'tabs' => $this->__tabs,
'scores' => $this->__clusterCounts,
'maxScore' => $maxScore,
'pickingMode' => false
);
if (!empty($colours)) {
$result['colours'] = $colours['mapping'];
$result['interpolation'] = $colours['interpolation'];
}
$result['galaxyName'] = $this->__galaxy_name;
$result['galaxyId'] = $this->__galaxy_id;
$matrixGalaxies = $this->__GalaxyModel->getAllowedMatrixGalaxies();
$result['matrixGalaxies'] = $matrixGalaxies;
return json_encode($result);
}
public function separator()
{
return '';
}
}

View File

@ -392,7 +392,8 @@ class Attribute extends AppModel
'yara-json' => array('json', 'YaraExport', 'json'),
'rpz' => array('rpz', 'RPZExport', 'rpz'),
'csv' => array('csv', 'CsvExport', 'csv'),
'cache' => array('txt', 'CacheExport', 'cache')
'cache' => array('txt', 'CacheExport', 'cache'),
'attack' => array('html', 'AttackExport', 'html')
);
// 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
@ -2972,6 +2973,9 @@ class Attribute extends AppModel
if (isset($options['limit'])) {
$params['limit'] = $options['limit'];
}
if (!empty($options['includeGalaxy'])) {
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
}
if (Configure::read('MISP.proposals_block_attributes') && isset($options['conditions']['AND']['Attribute.to_ids']) && $options['conditions']['AND']['Attribute.to_ids'] == 1) {
$this->bindModel(array('hasMany' => array('ShadowAttribute' => array('foreignKey' => 'old_id'))));
$proposalRestriction = array(
@ -3114,6 +3118,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');
}
$attributes[] = $results[$key];
}
}
@ -3142,8 +3150,10 @@ class Attribute extends AppModel
$eventTags[$results[$key]['Event']['id']][] = $tag;
}
}
foreach ($eventTags[$results[$key]['Event']['id']] as $eventTag) {
$results[$key]['EventTag'][] = $eventTag['EventTag'];
if (!empty($eventTags)) {
foreach ($eventTags[$results[$key]['Event']['id']] as $eventTag) {
$results[$key]['EventTag'][] = $eventTag['EventTag'];
}
}
return $results;
}
@ -3926,7 +3936,7 @@ class Attribute extends AppModel
return $conditions;
}
public function restSearch($user, $returnFormat, $filters, $paramsOnly = false, $jobId = false, &$elementCounter = 0)
public function restSearch($user, $returnFormat, $filters, $paramsOnly = false, $jobId = false, &$elementCounter = 0, &$renderView = false)
{
if (!isset($this->validFormats[$returnFormat][1])) {
throw new NotFoundException('Invalid output format.');
@ -3947,6 +3957,9 @@ class Attribute extends AppModel
unset($filters['value']);
}
}
if (!empty($exportTool->renderView)) {
$renderView = $exportTool->renderView;
}
if (isset($filters['searchall'])) {
if (!empty($filters['value'])) {
$filters['wildcard'] = $filters['value'];
@ -3968,6 +3981,9 @@ class Attribute extends AppModel
'includeProposals' => !empty($filters['includeProposals']) ? $filters['includeProposals'] : 0,
'includeWarninglistHits' => !empty($filters['includeWarninglistHits']) ? $filters['includeWarninglistHits'] : 0
);
if (!empty($filters['attackGalaxy'])) {
$params['attackGalaxy'] = $filters['attackGalaxy'];
}
if (isset($filters['include_event_uuid'])) {
$params['includeEventUuid'] = $filters['include_event_uuid'];
}

View File

@ -178,7 +178,8 @@ class Event extends AppModel
'stix2' => array('json', 'Stix2Export', 'json'),
'yara' => array('txt', 'YaraExport', 'yara'),
'yara-json' => array('json', 'YaraExport', 'json'),
'cache' => array('txt', 'CacheExport', 'cache')
'cache' => array('txt', 'CacheExport', 'cache'),
'attack' => array('html', 'AttackExport', 'html')
);
public $csv_event_context_fields_to_fetch = array(
@ -2735,17 +2736,17 @@ class Event extends AppModel
public function sendAlertEmailRouter($id, $user, $oldpublish = null)
{
if (Configure::read('MISP.block_old_event_alert') && Configure::read('MISP.block_old_event_alert_age') && is_numeric(Configure::read('MISP.block_old_event_alert_age'))) {
if (Configure::read('MISP.block_old_event_alert') && !empty(Configure::read('MISP.block_old_event_alert_age') && is_numeric(Configure::read('MISP.block_old_event_alert_age')))) {
$oldest = time() - (Configure::read('MISP.block_old_event_alert_age') * 86400);
$event = $this->find('first', array(
'conditions' => array('Event.id' => $id),
'recursive' => -1,
'fields' => array('Event.date')
'fields' => array('Event.timestamp')
));
if (empty($event)) {
return false;
}
if (strtotime($event['Event']['date']) < $oldest) {
if (intval($event['Event']['timestamp']) < $oldest) {
return true;
}
}
@ -6040,7 +6041,7 @@ class Event extends AppModel
}
}
public function restSearch($user, $returnFormat, $filters, $paramsOnly = false, $jobId = false, &$elementCounter = 0)
public function restSearch($user, $returnFormat, $filters, $paramsOnly = false, $jobId = false, &$elementCounter = 0, &$renderView = false)
{
if (!isset($this->validFormats[$returnFormat][1])) {
throw new NotFoundException('Invalid output format.');
@ -6061,6 +6062,11 @@ class Event extends AppModel
$filters['published'] = 1;
}
}
if (!empty($exportTool->renderView)) {
$renderView = $exportTool->renderView;
}
if (!empty($filters['ignore'])) {
$filters['to_ids'] = array(0, 1);
$filters['published'] = array(0, 1);
@ -6140,7 +6146,11 @@ class Event extends AppModel
unset($temp);
fwrite($tmpfile, $exportTool->footer($exportToolParams));
fseek($tmpfile, 0);
$final = fread($tmpfile, fstat($tmpfile)['size']);
if (fstat($tmpfile)['size'] > 0) {
$final = fread($tmpfile, fstat($tmpfile)['size']);
} else {
$final = 0;
}
fclose($tmpfile);
return $final;
}

View File

@ -44,7 +44,8 @@
"event_timestamp" => __('Only return attributes from events that have received a modification after the given timestamp. The input can be a timetamp or a short-hand time description (7d or 24h for example). You can also pass a list with two values to set a time range (for example ["14d", "7d"]).'),
"sgReferenceOnly" => __('If this flag is set, sharing group objects will not be included, instead only the sharing group ID is set.'),
"eventinfo" => __("Filter on the event's info field."),
"searchall" => __("Search for a full or a substring (delimited by % for substrings) in the event info, event tags, attribute tags, attribute values or attribute comment fields.")
"searchall" => __("Search for a full or a substring (delimited by % for substrings) in the event info, event tags, attribute tags, attribute values or attribute comment fields."),
"attackGalaxy" => __("Select the ATT&CK matrix like galaxy to use when using returnFormat = attack. Defaults to the Mitre ATT&CK library via mitre-attack-pattern.")
),
'url' => array(
$baseurl . '/attributes/restSearch',

View File

@ -0,0 +1,7 @@
<div>
<div id="attackmatrix_div" style="position: relative; border: solid 1px;" class="statistics_attack_matrix">
<?php
echo $this->element('view_galaxy_matrix');
?>
</div>
</div>