chg: [periodic_notification] Improved report and parametrized tags for trending

pull/8575/head
Sami Mokaddem 2022-09-08 14:54:04 +02:00
parent 5d801ba473
commit bf39655c26
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
5 changed files with 82 additions and 61 deletions

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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) : ?>

View File

@ -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'],

View File

@ -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;">