Merge branch 'develop' of github.com:MISP/MISP into develop

pull/9679/merge
Sami Mokaddem 2024-05-13 09:28:05 +02:00
commit 5596283b4f
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
9 changed files with 245 additions and 76 deletions

View File

@ -331,7 +331,7 @@ class ACLComponent extends Component
'feeds' => array(
'add' => array(),
'cacheFeeds' => array(),
'compareFeeds' => ['host_org_user'],
'compareFeeds' => ['*'],
'delete' => array(),
'disable' => array(),
'edit' => array(),
@ -342,11 +342,11 @@ class ACLComponent extends Component
'fetchSelectedFromFreetextIndex' => array(),
'getEvent' => array(),
'importFeeds' => array(),
'index' => ['host_org_user'],
'index' => ['*'],
'loadDefaultFeeds' => array(),
'previewEvent' => ['host_org_user'],
'previewIndex' => ['host_org_user'],
'searchCaches' => ['host_org_user'],
'previewEvent' => ['*'],
'previewIndex' => ['*'],
'searchCaches' => ['*'],
'toggleSelected' => array(),
'view' => ['host_org_user'],
),
@ -919,7 +919,7 @@ class ACLComponent extends Component
}
return true;
};
$this->dynamicChecks['password_change_enabled'] = function (array $user) {
$this->dynamicChecks['password_change_enabled'] = function ($user) {
if (Configure::read('MISP.disable_user_password_change')) {
throw new ForbiddenException('User password change has been disabled on this instance.');
}
@ -931,7 +931,7 @@ class ACLComponent extends Component
}
return true;
};
$this->dynamicChecks['password_forgotten_enabled'] = function (array $user) {
$this->dynamicChecks['password_forgotten_enabled'] = function ($user) {
if (empty(Configure::read('Security.allow_password_forgotten'))) {
throw new ForbiddenException('Password reset has been disabled on this instance.');
}

View File

@ -74,9 +74,12 @@ class FeedsController extends AppController
);
}
}
$host_org_id = (int)Configure::read('MISP.host_org_id');
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $host_org_id) {
$conditions[] = ['Feed.lookup_visible' => 1];
}
$loggedUser = $this->Auth->user();
$this->loadModel('TagCollection');
$this->CRUD->index([
'filters' => [
'Feed.name',
@ -703,9 +706,11 @@ class FeedsController extends AppController
'conditions' => ['id' => $feedId],
'recursive' => -1,
]);
if (empty($feed)) {
if (empty($feed) || !$this->__canViewFeed($feed)) {
throw new NotFoundException(__('Invalid feed.'));
}
if (!empty($feed['Feed']['settings'])) {
$feed['Feed']['settings'] = json_decode($feed['Feed']['settings'], true);
}
@ -858,13 +863,22 @@ class FeedsController extends AppController
$this->render('freetext_index');
}
private function __canViewFeed($feed)
{
$host_org_id = (int)Configure::read('MISP.host_org_id');
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $host_org_id && !$feed['Feed']['lookup_visible']) {
return false;
}
return true;
}
public function previewEvent($feedId, $eventUuid, $all = false)
{
$feed = $this->Feed->find('first', [
'conditions' => ['id' => $feedId],
'recursive' => -1,
]);
if (empty($feed)) {
if (empty($feed) || !$this->__canViewFeed($feed)) {
throw new NotFoundException(__('Invalid feed.'));
}
try {
@ -1033,7 +1047,8 @@ class FeedsController extends AppController
public function compareFeeds($id = false)
{
$feeds = $this->Feed->compareFeeds($id);
$limited = !$this->_isSiteAdmin() && $this->Auth->user('org_id') !== (int)Configure::read('MISP.host_org_id');
$feeds = $this->Feed->compareFeeds($limited);
if ($this->_isRest()) {
return $this->RestResponse->viewData($feeds, $this->response->type());
} else {
@ -1106,7 +1121,9 @@ class FeedsController extends AppController
if (!empty($this->params['named']['value'])) {
$value = $this->params['named']['value'];
}
$hits = $this->Feed->searchCaches($value);
$host_org_id = (int)Configure::read('MISP.host_org_id');
$limited = !$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $host_org_id;
$hits = $this->Feed->searchCaches($value, $limited);
if ($this->_isRest()) {
return $this->RestResponse->viewData($hits, $this->response->type());
} else {

View File

@ -1083,7 +1083,11 @@ class Event extends AppModel
// prepare Object for sync
if (!empty($data['Object'])) {
foreach ($data['Object'] as $key => $object) {
if (!empty(Configure::read('MISP.enable_synchronisation_filtering_on_type')) && in_array($object['template_uuid'], $pushRules['type_objects']['NOT'])) {
if (
!empty(Configure::read('MISP.enable_synchronisation_filtering_on_type')) &&
!empty($pushRules['type_objects']['NOT']) &&
in_array($object['template_uuid'], $pushRules['type_objects']['NOT'])
) {
unset($data['Object'][$key]);
continue;
}

View File

@ -158,6 +158,51 @@ class Feed extends AppModel
return $result;
}
private function checkEventAgainstRules(array $event, array $rules): bool
{
$tags = [];
if (!empty($event['Tag'])) {
$tags = Hash::extract($event, 'Tag.{n}.name');
}
// Check the tag rules
if (!empty($rules['tags']['OR'])) {
if (empty(array_intersect($rules['tags']['OR'], $tags))) {
return false;
}
}
if (!empty($rules['tags']['NOT'])) {
if (!empty(array_intersect($rules['tags']['NOT'], $tags))) {
return false;
}
}
// check the org rules
if (!empty($rules['orgs']['OR'])) {
if (!in_array($event['Orgc']['uuid'], $rules['orgs']['OR']) && !in_array($event['Orgc']['name'], $rules['orgs']['OR'])) {
return false;
}
}
if (!empty($rules['orgs']['NOT'])) {
if (in_array($event['Orgc']['uuid'], $rules['orgs']['NOT']) || in_array($event['Orgc']['name'], $rules['orgs']['NOT'])) {
return false;
}
}
//check misc rules
$url_params = empty($rules['url_params']) ? null : json_decode($rules['url_params'], true);
if ($url_params) {
if (isset($url_params['timestamp'])) {
$timestamp = $this->resolveTimeDelta($url_params['timestamp']);
if ($event['timestamp'] < $timestamp) {
return false;
}
}
}
return true;
}
/**
* Gets the event UUIDs from the feed by ID
* Returns an array with the UUIDs of events that are new or that need updating.
@ -171,6 +216,12 @@ class Feed extends AppModel
{
$manifest = $this->isFeedLocal($feed) ? $this->downloadManifest($feed) : $this->getRemoteManifest($feed, $HttpSocket);
$this->Event = ClassRegistry::init('Event');
$rules = json_decode($feed['Feed']['rules'], true);
foreach ($manifest as $k => $event) {
if (!$this->checkEventAgainstRules($event, $rules)) {
unset($manifest[$k]);
}
}
$events = $this->Event->find('all', array(
'conditions' => array(
'Event.uuid' => array_keys($manifest),
@ -650,7 +701,7 @@ class Feed extends AppModel
if ($scope === 'Feed') {
$params = array(
'recursive' => -1,
'fields' => array('id', 'name', 'url', 'provider', 'source_format')
'fields' => array('id', 'name', 'url', 'provider', 'source_format', 'lookup_visible')
);
if (!$user['Role']['perm_site_admin']) {
$params['conditions'] = array('Feed.lookup_visible' => 1);
@ -1651,17 +1702,21 @@ class Feed extends AppModel
return true;
}
public function compareFeeds($id = false)
public function compareFeeds($limited = false)
{
$redis = $this->setupRedis();
if ($redis === false) {
return array();
}
$fields = array('id', 'input_source', 'source_format', 'url', 'provider', 'name', 'default');
$conditions = ['Feed.caching_enabled' => 1];
if ($limited) {
$conditions['Feed.lookup_visible'] = 1;
}
$feeds = $this->find('all', array(
'recursive' => -1,
'fields' => $fields,
'conditions' => array('Feed.caching_enabled' => 1)
'conditions' => $conditions
));
// we'll use this later for the intersect
$fields[] = 'values';
@ -1675,24 +1730,27 @@ class Feed extends AppModel
$feeds[$k]['Feed']['values'] = $redis->sCard('misp:feed_cache:' . $feed['Feed']['id']);
}
$feeds = array_values($feeds);
$this->Server = ClassRegistry::init('Server');
$servers = $this->Server->find('all', array(
'recursive' => -1,
'fields' => array('id', 'url', 'name'),
'contain' => array('RemoteOrg' => array('fields' => array('RemoteOrg.id', 'RemoteOrg.name'))),
'conditions' => array('Server.caching_enabled' => 1)
));
foreach ($servers as $k => $server) {
if (!$redis->exists('misp:server_cache:' . $server['Server']['id'])) {
unset($servers[$k]);
continue;
$servers = [];
if (!$limited) {
$this->Server = ClassRegistry::init('Server');
$servers = $this->Server->find('all', array(
'recursive' => -1,
'fields' => array('id', 'url', 'name'),
'contain' => array('RemoteOrg' => array('fields' => array('RemoteOrg.id', 'RemoteOrg.name'))),
'conditions' => array('Server.caching_enabled' => 1)
));
foreach ($servers as $k => $server) {
if (!$redis->exists('misp:server_cache:' . $server['Server']['id'])) {
unset($servers[$k]);
continue;
}
$servers[$k]['Server']['input_source'] = 'network';
$servers[$k]['Server']['source_format'] = 'misp';
$servers[$k]['Server']['provider'] = $servers[$k]['RemoteOrg']['name'];
$servers[$k]['Server']['default'] = false;
$servers[$k]['Server']['is_misp_server'] = true;
$servers[$k]['Server']['values'] = $redis->sCard('misp:server_cache:' . $server['Server']['id']);
}
$servers[$k]['Server']['input_source'] = 'network';
$servers[$k]['Server']['source_format'] = 'misp';
$servers[$k]['Server']['provider'] = $servers[$k]['RemoteOrg']['name'];
$servers[$k]['Server']['default'] = false;
$servers[$k]['Server']['is_misp_server'] = true;
$servers[$k]['Server']['values'] = $redis->sCard('misp:server_cache:' . $server['Server']['id']);
}
foreach ($feeds as $k => $feed) {
foreach ($feeds as $k2 => $feed2) {
@ -1922,7 +1980,7 @@ class Feed extends AppModel
return $result;
}
public function searchCaches($value)
public function searchCaches($value, bool $limited = false)
{
$hits = array();
$this->Server = ClassRegistry::init('Server');
@ -1942,10 +2000,12 @@ class Feed extends AppModel
$v = strtolower(trim($v));
}
if ($v === false || $redis->sismember('misp:feed_cache:combined', md5($v))) {
$conditions = ['caching_enabled' => 1];
if ($limited) {
$conditions['lookup_visible'] = 1;
}
$feeds = $this->find('all', array(
'conditions' => array(
'caching_enabled' => 1
),
'conditions' => $conditions,
'recursive' => -1,
'fields' => array('Feed.id', 'Feed.name', 'Feed.url', 'Feed.source_format')
));
@ -1992,7 +2052,7 @@ class Feed extends AppModel
}
}
}
if ($v === false || $redis->sismember('misp:server_cache:combined', md5($v))) {
if (!$limited && ($v === false || $redis->sismember('misp:server_cache:combined', md5($v)))) {
$servers = $this->Server->find('all', array(
'conditions' => array(
'caching_enabled' => 1

View File

@ -231,8 +231,13 @@
$relatedData[__('Event UUIDs')] = implode('<br>', array_map('h', $feed['event_uuids']));
}
$popover = '';
foreach ($relatedData as $k => $v) {
$popover .= '<span class="bold black">' . $k . '</span>: <span class="blue">' . $v . '</span><br>';
$event_count = count($relatedData);
if ($event_count > 20) {
$popover = '<span class="bold black">' . __('Events') . '</span>: <span class="blue">' . __('Zounds... of events (%d)', $event_count) . '</span><br>';
} else {
foreach ($relatedData as $k => $v) {
$popover .= '<span class="bold black">' . h($k) . '</span>: <span class="blue">' . $v . '</span><br>';
}
}
if ($isSiteAdmin || $hostOrgUser) {
if ($feed['source_format'] === 'misp') {
@ -271,7 +276,6 @@
}
if (isset($object['Server'])) {
foreach ($object['Server'] as $server) {
$popover = '';
foreach ($server as $k => $v) {
if ($k == 'id') continue;
if (is_array($v)) {
@ -282,21 +286,37 @@
} else {
$v = h($v);
}
$popover .= '<span class=\'bold black\'>' . Inflector::humanize(h($k)) . '</span>: <span class="blue">' . $v . '</span><br />';
}
if (empty($server['event_uuids'])) {
$server['event_uuids'] = [0 => 1]; // Make sure to print the content once
}
foreach ($server['event_uuids'] as $k => $event_uuid) {
$event_count = count($server['event_uuids']);
$popover = '';
if ($event_count > 20) {
$liContents = '';
$url = $isSiteAdmin ? sprintf('%s/servers/previewEvent/%s/%s', $baseurl, h($server['id']), h($event_uuid)) : '#';
$message = __('Zounds... of events (%d)', $event_count);
$url = $isSiteAdmin ? sprintf('%s/servers/previewIndex/%s', $baseurl, h($server['id'])) : '#';
$popover = '<span class=\'bold black\'>' . __('Event uuid') . '</span>: <span class="blue">' . $message . '</span><br />';
$liContents .= sprintf(
'<a href="%s" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a>&nbsp;',
$url,
h($popover),
'S' . h($server['id']) . ':' . ($k + 1)
'S' . h($server['id']) . ':' . $message
);
echo "<li>$liContents</li>";
} else {
foreach ($server['event_uuids'] as $k => $event_uuid) {
$popover = '<span class=\'bold black\'>' . __('Event uuid') . '</span>: <span class="blue">' . h($event_uuid) . '</span><br />';
$liContents = '';
$url = $isSiteAdmin ? sprintf('%s/servers/previewEvent/%s/%s', $baseurl, h($server['id']), h($event_uuid)) : '#';
$liContents .= sprintf(
'<a href="%s" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a>&nbsp;',
$url,
h($popover),
'S' . h($server['id']) . ':' . ($k + 1)
);
echo "<li>$liContents</li>";
}
}
}
}

View File

@ -8,6 +8,7 @@
__('Provider') => $relatedFeed['provider'],
];
$popover = '';
$canPivot = $relatedFeed['lookup_visible'] || $isSiteAdmin || $me['org_id'] == Configure::read('MISP.Host_org_id');
foreach ($relatedData as $k => $v) {
$popover .= sprintf(
'<span class="bold">%s</span>: <span class="blue">%s</span><br>',
@ -15,28 +16,49 @@
h($v)
);
}
if ($relatedFeed ['source_format'] === 'misp') {
$htmlElements[] = sprintf(
'<form action="%s/feeds/previewIndex/%s" method="post" style="margin:0px;">%s</form>',
h($baseurl),
h($relatedFeed['id']),
sprintf(
'<input type="hidden" name="data[Feed][eventid]" value="%s">
<input type="submit" class="linkButton useCursorPointer" value="%s" data-toggle="popover" data-content="%s" data-trigger="hover">',
h(json_encode($relatedFeed['event_uuids'] ?? [])),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')',
h($popover)
)
if (!$canPivot) {
$popover .= sprintf(
'<span class="bold">%s</span>: <span class="blue">%s</span><br />',
__('Note'),
__('You don\'t have the required permissions to pivot to the details.')
);
if ($relatedFeed ['source_format'] === 'misp') {
$htmlElements[] = sprintf(
'<span data-toggle="popover" data-content="%s" data-trigger="hover">%s</span>',
h($popover),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')'
);
} else {
$htmlElements[] = sprintf(
'<span data-toggle="popover" data-content="%s" data-trigger="hover">%s</span><br>',
h($popover),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')'
);
}
} else {
$htmlElements[] = sprintf(
'<a href="%s/feeds/previewIndex/%s" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a><br>',
h($baseurl),
h($relatedFeed['id']),
h($popover),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')'
);
if ($relatedFeed ['source_format'] === 'misp') {
$htmlElements[] = sprintf(
'<form action="%s/feeds/previewIndex/%s" method="post" style="margin:0px;">%s</form>',
h($baseurl),
h($relatedFeed['id']),
sprintf(
'<input type="hidden" name="data[Feed][eventid]" value="%s">
<input type="submit" class="linkButton useCursorPointer" value="%s" data-toggle="popover" data-content="%s" data-trigger="hover">',
h(json_encode($relatedFeed['event_uuids'] ?? [])),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')',
h($popover)
)
);
} else {
$htmlElements[] = sprintf(
'<a href="%s/feeds/previewIndex/%s" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a><br>',
h($baseurl),
h($relatedFeed['id']),
h($popover),
h($relatedFeed['name']) . ' (' . $relatedFeed['id'] . ')'
);
}
}
}
} else {

View File

@ -12,6 +12,7 @@
$relatedData['url'] = $relatedServer['url'];
}
$popover = '';
$canPivot = $isSiteAdmin || $me['org_id'] == Configure::read('MISP.Host_org_id');
foreach ($relatedData as $k => $v) {
$popover .= sprintf(
'<span class="bold">%s</span>: <span class="blue">%s</span><br />',
@ -19,16 +20,32 @@
h($v)
);
}
$serverHtml[] = sprintf(
'<span style="white-space: nowrap; display: inline-block">%s</span>',
sprintf(
'<a href="%s/servers/previewIndex/%s" class="linkButton useCursorPointer" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a>&nbsp;',
$baseurl,
h($relatedServer['id']),
h($popover),
h($relatedServer['name']) . ' (' . $relatedServer['id'] . ')'
)
);
if (!$canPivot) {
$popover .= sprintf(
'<span class="bold">%s</span>: <span class="blue">%s</span><br />',
__('Note'),
__('You don\'t have the required permissions to pivot to the details.')
);
$serverHtml[] = sprintf(
'<span style="white-space: nowrap; display: inline-block">%s</span>',
sprintf(
'<span data-toggle="popover" data-content="%s" data-trigger="hover">%s</span>',
h($popover),
h($relatedServer['name']) . ' (' . $relatedServer['id'] . ')'
)
);
} else {
$serverHtml[] = sprintf(
'<span style="white-space: nowrap; display: inline-block">%s</span>',
sprintf(
'<a href="%s/servers/previewIndex/%s" class="linkButton useCursorPointer" data-toggle="popover" data-content="%s" data-trigger="hover">%s</a>&nbsp;',
$baseurl,
h($relatedServer['id']),
h($popover),
h($relatedServer['name']) . ' (' . $relatedServer['id'] . ')'
)
);
}
}
} else {
$relatedData[] = __(
@ -44,7 +61,15 @@
)
);
}
echo sprintf(
'<div class="correlation-container" style="margin-bottom: 15px;">%s</div>',
'<h3>%s %s</h3><div class="inline correlation-container" style="margin-bottom: 15px;">%s</div>',
__('Related Servers'),
sprintf(
'<a href="#attributeList" title="%s" onclick="%s">%s</a>',
__('Show just attributes that have server hits'),
"toggleBoolFilter('server')",
__('(show)')
),
implode(PHP_EOL, $serverHtml)
);

View File

@ -76,6 +76,7 @@ $tableData[] = [
?>
<li class="<?php echo $i > $display_threshold ? 'correlation-expanded-area' : ''; ?>" style="<?php echo $i > $display_threshold ? 'display: none;' : ''; ?>">
<?php echo $this->element('/Events/View/related_event', array(
'ownOrg' => $relatedEvent['Event']['orgc_id'] == $me['org_id'],
'related' => $relatedEvent['Event'],
'relatedEventCorrelationCount' => array(),
'href_url' => $baseurl . '/servers/previewEvent/' . $server['Server']['id']

View File

@ -1819,5 +1819,25 @@
"exportable": true,
"hide_tag": false
}
},
{
"Feed": {
"name": "Infoblox-Threat-Intelligence",
"provider": "infoblox.com",
"url": "https:\/\/github.com\/infobloxopen\/threat-intelligence\/tree\/main\/indicators\/misp",
"rules": "{\"tags\":{\"OR\":[],\"NOT\":[]},\"orgs\":{\"OR\":[],\"NOT\":[]}}",
"enabled": true,
"distribution": "0",
"default": false,
"source_format": "misp",
"fixed_event": false,
"delta_merge": false,
"publish": false,
"override_ids": false,
"settings": "{\"csv\":{\"value\":\"\",\"delimiter\":\"\"},\"common\":{\"excluderegex\":\"\"}}",
"input_source": "network",
"delete_local_file": false,
"lookup_visible": false
}
}
]