mirror of https://github.com/MISP/MISP
chg: [periodic_notification] Improved report and parametrized tags for trending
parent
5d801ba473
commit
bf39655c26
|
@ -2810,8 +2810,9 @@ class UsersController extends AppController
|
|||
public function viewPeriodicSummary(string $period)
|
||||
{
|
||||
$summary = $this->User->generatePeriodicSummary($this->Auth->user('id'), $period);
|
||||
$notification_settings = $this->User->getUsablePeriodicSettingForUser($this->Auth->user('id'), $period);
|
||||
$this->set('notification_settings', $notification_settings);
|
||||
$periodic_settings = $this->User->extractPeriodicSettingForUser($this->Auth->user('id'));
|
||||
$notification_settings = $this->User->getUsablePeriodicSettingForUser($periodic_settings);
|
||||
$this->set('periodic_settings', $periodic_settings);
|
||||
$this->set('summary', $summary);
|
||||
$this->set('period', $period);
|
||||
}
|
||||
|
|
|
@ -1649,9 +1649,10 @@ class User extends AppModel
|
|||
return substr(hash('sha256', "{$user['id']}|$salt"), 0, 8);
|
||||
}
|
||||
|
||||
public function extractPeriodicSettingForUser($user): array
|
||||
public function extractPeriodicSettingForUser($user, $decode=false): array
|
||||
{
|
||||
$filter_names = ['orgc_id', 'distribution', 'sharing_group_id', 'event_info', 'tags'];
|
||||
$filter_names = ['orgc_id', 'distribution', 'sharing_group_id', 'event_info', 'tags', 'trending_for_tags'];
|
||||
$filter_to_decode = ['tags', 'trending_for_tags', ];
|
||||
if (is_numeric($user)) {
|
||||
$user = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
|
@ -1670,12 +1671,17 @@ class User extends AppModel
|
|||
$periodic_settings_indexed[$filter_name] = $periodic_settings[0]['value'][$filter_name];
|
||||
}
|
||||
}
|
||||
foreach ($filter_to_decode as $filter) {
|
||||
if (!empty($decode) && !empty($periodic_settings_indexed[$filter])) {
|
||||
$periodic_settings_indexed[$filter] = JsonTool::decode($periodic_settings_indexed[$filter]);
|
||||
}
|
||||
}
|
||||
return $periodic_settings_indexed;
|
||||
}
|
||||
|
||||
public function getUsablePeriodicSettingForUser($user, $period): array
|
||||
public function getUsablePeriodicSettingForUser(array $periodicSettings): array
|
||||
{
|
||||
return $this->__getUsableFilters($this->extractPeriodicSettingForUser($user), $period);
|
||||
return $this->__getUsableFilters($periodicSettings);
|
||||
}
|
||||
|
||||
public function saveNotificationSettings(int $user_id, array $data): bool
|
||||
|
@ -1695,12 +1701,15 @@ class User extends AppModel
|
|||
]);
|
||||
if ($success) {
|
||||
$periodic_settings = $data['periodic_settings'];
|
||||
if (empty($periodic_settings['tags'])) {
|
||||
$periodic_settings['tags'] = '[]';
|
||||
} else {
|
||||
$decodedTags = json_decode($periodic_settings['tags'], true);
|
||||
if ($decodedTags === null) {
|
||||
return false;
|
||||
$param_to_decode = ['tags', 'trending_for_tags', ];
|
||||
foreach ($param_to_decode as $param) {
|
||||
if (empty($periodic_settings[$param])) {
|
||||
$periodic_settings[$param] = '[]';
|
||||
} else {
|
||||
$decodedTags = json_decode($periodic_settings[$param], true);
|
||||
if ($decodedTags === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$notification_filters = [
|
||||
|
@ -1709,6 +1718,7 @@ class User extends AppModel
|
|||
'sharing_group_id' => $periodic_settings['distribution'] != 4 ? '' : ($periodic_settings['sharing_group_id'] ?? []),
|
||||
'event_info' => $periodic_settings['event_info'] ?? '',
|
||||
'tags' => $periodic_settings['tags'] ?? '[]',
|
||||
'trending_for_tags' => $periodic_settings['trending_for_tags'] ?? '[]',
|
||||
];
|
||||
$new_user_setting = [
|
||||
'UserSetting' => [
|
||||
|
@ -1731,7 +1741,7 @@ class User extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
* generatePeriodicSummary
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param string $period
|
||||
|
@ -1751,8 +1761,9 @@ class User extends AppModel
|
|||
}
|
||||
App::import('Tools', 'SendEmail');
|
||||
$emailTemplate = $this->prepareEmailTemplate($period);
|
||||
$filters = $this->getUsablePeriodicSettingForUser($existingUser, $period);
|
||||
$filtersForRestSearch = $filters;
|
||||
$periodicSettings = $this->extractPeriodicSettingForUser($user, true);
|
||||
$filters = $this->getUsablePeriodicSettingForUser($periodicSettings);
|
||||
$filtersForRestSearch = $filters; // filters for restSearch are slightly different than fetchEvent
|
||||
$filters['last'] = $this->resolveTimeDelta($filters['last']);
|
||||
$events = $this->__getEventsForFilters($user, $filters);
|
||||
|
||||
|
@ -1771,11 +1782,10 @@ class User extends AppModel
|
|||
$aggregated_context = $this->__renderAggregatedContext($finalContext);
|
||||
|
||||
$rollingWindows = 2;
|
||||
$tagFilterPrefixes = ['misp-galaxy:mitre-attack-pattern', 'admiralty-scale'];
|
||||
$trendAnalysis = $this->Event->getTrendsForTags($user, $filters, $this->__periodToDays($period), $rollingWindows, $tagFilterPrefixes);
|
||||
$trendAnalysis = $this->Event->getTrendsForTags($user, $filters, $this->__periodToDays($period), $rollingWindows, $periodicSettings['trending_for_tags']);
|
||||
$trendData = [
|
||||
'trendAnalysis' => $trendAnalysis,
|
||||
'tagFilterPrefixes' => $tagFilterPrefixes,
|
||||
'tagFilterPrefixes' => $periodicSettings['trending_for_tags'],
|
||||
];
|
||||
$trending_summary = $this->__renderTrendingSummary($trendData);
|
||||
|
||||
|
@ -1837,7 +1847,7 @@ class User extends AppModel
|
|||
$filters['event_info'] = $period_filters['event_info'];
|
||||
}
|
||||
if (!empty($period_filters['tags'])) {
|
||||
$filters['tags'] = JsonTool::decode($period_filters['tags']);
|
||||
$filters['tags'] = $period_filters['tags'];
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
|
|
|
@ -7,19 +7,13 @@ $currentPeriod = $allTimestamps[0];
|
|||
$previousPeriod = $allTimestamps[1];
|
||||
$previousPeriod2 = $allTimestamps[2];
|
||||
|
||||
$clusteredTags[$previousPeriod]['admiralty-scale:source-reliability="d"'] = [
|
||||
'occurence' => (float) 0.33,
|
||||
'raw_change' => (int) 1,
|
||||
'percent_change' => (int) 100,
|
||||
'change_sign' => (int) 1
|
||||
];
|
||||
$allUniqueTagsPerPeriod = array_map(function ($tags) {
|
||||
return array_keys($tags);
|
||||
}, $clusteredTags);
|
||||
$allUniqueTags = array_unique(array_merge($allUniqueTagsPerPeriod[$currentPeriod], $allUniqueTagsPerPeriod[$previousPeriod], $allUniqueTagsPerPeriod[$previousPeriod2]));
|
||||
App::uses('ColourPaletteTool', 'Tools');
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$COLOR_PALETTE = $paletteTool->createColourPalette(count($allUniqueTags));
|
||||
$COLOR_PALETTE = $paletteTool->createColourPalette(max(count($allUniqueTags), 1));
|
||||
|
||||
$trendIconMapping = [
|
||||
1 => '▲',
|
||||
|
@ -119,40 +113,44 @@ if (!function_exists('computeLinePositions')) {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div style="padding: 0 40px; margin: -40px 20px 0 0;">
|
||||
<div class="chart-container">
|
||||
<div class="canvas">
|
||||
<?php foreach ($chartData as $tag => $coords) : ?>
|
||||
<?php for ($i=0; $i < 3; $i++) : ?>
|
||||
<?php
|
||||
$coord = $coords[$i];
|
||||
$previousCoord = isset($coords[$i - 1]) ? $coords[$i - 1] : false;
|
||||
?>
|
||||
<span class="dot" style="<?= sprintf('left: %spx; top: %spx; background-color: %s;', $coord[0], $coord[1], $colorForTags[$tag]) ?>" title="<?= h($tag) ?>"></span>
|
||||
<?php
|
||||
if (!empty($previousCoord)) {
|
||||
$linePosition = computeLinePositions($previousCoord[0], $previousCoord[1], $coord[0], $coord[1]);
|
||||
echo sprintf(
|
||||
'<span class="line" style="left: %spx; top: %spx; width: %spx; transform: rotate(%srad); background-color: %s;" title="%s"></span>',
|
||||
$linePosition['left'],
|
||||
$linePosition['top'],
|
||||
$linePosition['width'],
|
||||
$linePosition['angle'],
|
||||
$colorForTags[$tag],
|
||||
h($tag),
|
||||
);
|
||||
}
|
||||
?>
|
||||
<?php endfor ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', 0, 0) ?>"><?= __('Period 2') ?></span>
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', $canvasWidth/2, 0) ?>"><?= __('Previous period') ?></span>
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', $canvasWidth, 0) ?>"><?= __('Current period') ?></span>
|
||||
<?php if (!empty($allUniqueTags)): ?>
|
||||
<div style="padding: 0 40px; margin: -40px 20px 0 0;">
|
||||
<div class="chart-container">
|
||||
<div class="canvas">
|
||||
<?php foreach ($chartData as $tag => $coords) : ?>
|
||||
<?php for ($i=0; $i < 3; $i++) : ?>
|
||||
<?php
|
||||
$coord = $coords[$i];
|
||||
$previousCoord = isset($coords[$i - 1]) ? $coords[$i - 1] : false;
|
||||
?>
|
||||
<span class="dot" style="<?= sprintf('left: %spx; top: %spx; background-color: %s;', $coord[0], $coord[1], $colorForTags[$tag]) ?>" title="<?= h($tag) ?>"></span>
|
||||
<?php
|
||||
if (!empty($previousCoord)) {
|
||||
$linePosition = computeLinePositions($previousCoord[0], $previousCoord[1], $coord[0], $coord[1]);
|
||||
echo sprintf(
|
||||
'<span class="line" style="left: %spx; top: %spx; width: %spx; transform: rotate(%srad); background-color: %s;" title="%s"></span>',
|
||||
$linePosition['left'],
|
||||
$linePosition['top'],
|
||||
$linePosition['width'],
|
||||
$linePosition['angle'],
|
||||
$colorForTags[$tag],
|
||||
h($tag),
|
||||
);
|
||||
}
|
||||
?>
|
||||
<?php endfor ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', 0, 0) ?>"><?= __('Period 2') ?></span>
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', $canvasWidth/2, 0) ?>"><?= __('Previous period') ?></span>
|
||||
<span style="<?= sprintf('position: absolute; white-space: nowrap; translate: -50%%; left: %spx; top: %spx;', $canvasWidth, 0) ?>"><?= __('Current period') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p><?= __('- No tag for the selected tag namespace -') ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($allTags)) : ?>
|
||||
|
@ -211,10 +209,15 @@ if (!function_exists('computeLinePositions')) {
|
|||
</tr>
|
||||
</thead>
|
||||
<?php foreach ($tagFilterPrefixes as $tagPrefix) : ?>
|
||||
<?php
|
||||
if (empty($allTags[$tagPrefix])) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<h4><?= __('Taxonomy: %s', sprintf('<code>%s</code>', h($tagPrefix))) ?></h4>
|
||||
<h4><?= __('Tag namespace: %s', sprintf('<code>%s</code>', h($tagPrefix))) ?></h4>
|
||||
</td>
|
||||
</tr>
|
||||
<?php foreach ($allTags[$tagPrefix] as $tagName) : ?>
|
||||
|
|
|
@ -57,6 +57,13 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
'type' => 'tagsPicker',
|
||||
'placeholder' => '["tlp:red"]',
|
||||
],
|
||||
sprintf('<h4>%s</h4>', __('Notification filters')),
|
||||
[
|
||||
'field' => 'periodic_settings.trending_for_tags',
|
||||
'label' => __('Generate trends for tag namespaces'),
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '["misp-galaxy:mitre-attack-pattern", "admiralty-scale"]',
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'action' => $this->request->params['action'],
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
<h2><?= __('MISP %s summary', h($period)); ?></h2>
|
||||
|
||||
<button type="button" class="btn btn-inverse" data-toggle="collapse" data-target="#summary-filters">
|
||||
<?= __('Show filters used to generate the summary') ?>
|
||||
<?= __('Show settings used to generate the summary') ?>
|
||||
</button>
|
||||
|
||||
<div id="summary-filters" class="collapse">
|
||||
<pre>
|
||||
<?= JsonTool::encode($notification_settings, true) ?>
|
||||
<?= JsonTool::encode($periodic_settings, true) ?>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="report-container" style="margin-top: 2em;">
|
||||
|
|
Loading…
Reference in New Issue