fix: merge develop branch

pull/7520/head
Luciano Righetti 2021-07-14 08:27:54 +02:00
commit a3dd6a850a
59 changed files with 1334 additions and 715 deletions

View File

@ -75,6 +75,8 @@
# $ MISPvars
MISPvars () {
debug "Setting generic ${LBLUE}MISP${NC} variables shared by all flavours" 2> /dev/null
# Some distros have no openssl installed by default, catch that exception.
$(openssl help 2> /dev/null) || (echo "No openssl, please install to continue"; exit -1)
# Local non-root MISP user
MISP_USER="${MISP_USER:-misp}"
MISP_PASSWORD="${MISP_PASSWORD:-$(openssl rand -hex 32)}"
@ -1077,7 +1079,7 @@ runTests () {
key = \"${AUTH_KEY}\"" |sudo tee ${PATH_TO_MISP}/PyMISP/tests/keys.py
sudo chown -R $WWW_USER:$WWW_USER $PATH_TO_MISP/PyMISP/
${SUDO_WWW} sh -c "cd $PATH_TO_MISP/PyMISP && git submodule foreach git pull origin master"
${SUDO_WWW} sh -c "cd $PATH_TO_MISP/PyMISP && git submodule foreach git pull origin main"
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -e $PATH_TO_MISP/PyMISP/.[fileobjects,neo,openioc,virustotal,pdfexport]
${SUDO_WWW} sh -c "cd $PATH_TO_MISP/PyMISP && ${PATH_TO_MISP}/venv/bin/python tests/testlive_comprehensive.py"
}
@ -2234,12 +2236,12 @@ enableEPEL_REMI_8 () {
sudo dnf install http://rpms.remirepo.net/enterprise/remi-release-8.rpm -y
sudo dnf install dnf-utils -y
sudo dnf module enable php:remi-7.4 -y
[[ ${DISTRI} == "centos8stream" ]] && sudo dnf config-manager --set-enabled powertools
[[ ${DISTRI} == "centos8" ]] && sudo dnf config-manager --set-enabled powertools
([[ ${DISTRI} == "centos8stream" ]] || [[ ${DISTRI} == "centos8" ]] || [[ ${DISTRI} == "rocky8.4" ]]) && sudo dnf config-manager --set-enabled powertools
}
enableREMI_f33 () {
sudo dnf install http://rpms.remirepo.net/fedora/remi-release-33.rpm
enableREMI_fedora () {
[[ "${DISTRI%??}" == "fedora" ]] && sudo dnf install http://rpms.remirepo.net/fedora/remi-release-${DISTRI:6}.rpm -y
dnf list installed mod_lua && sudo dnf remove mod_lua -y
sudo dnf install dnf-utils -y
sudo dnf module enable php:remi-7.4 -y
}
@ -2301,7 +2303,7 @@ yumInstallCoreDeps8 () {
policycoreutils-python-utils \
langpacks-en glibc-all-langpacks \
libxslt-devel zlib-devel ssdeep-devel -y
sudo alternatives --set python /usr/bin/python3
readlink -f /usr/bin/python | grep python3 || sudo alternatives --set python /usr/bin/python3
# Enable and start redis
sudo systemctl enable --now redis.service
@ -2496,9 +2498,8 @@ installCoreRHEL8 () {
cd $PATH_TO_MISP/app/files/scripts/python-cybox
$SUDO_WWW git config core.filemode false
# If you umask is has been changed from the default, it is a good idea to reset it to 0022 before installing python modules
([[ ${DISTRI} == 'fedora33' ]] || [[ ${DISTRI} == 'rhel8.3' ]]) && sudo dnf install cmake3 -y && CMAKE_BIN='cmake3'
[[ ${DISTRI} == 'centos8stream' ]] && sudo dnf install cmake -y && CMAKE_BIN='cmake'
[[ ${DISTRI} == 'centos8' ]] && sudo dnf install cmake -y && CMAKE_BIN='cmake'
([[ ${DISTRI} == 'fedora33' ]] || [[ ${DISTRI} == 'fedora34' ]] || [[ ${DISTRI} == 'rhel8.3' ]]) && sudo dnf install cmake3 -y && CMAKE_BIN='cmake3'
([[ ${DISTRI} == 'centos8stream' ]] || [[ ${DISTRI} == 'centos8' ]] || [[ ${DISTRI} == 'rocky8.4' ]]) && sudo dnf install cmake -y && CMAKE_BIN='cmake'
UMASK=$(umask)
umask 0022
@ -2916,7 +2917,7 @@ mispmodulesRHEL () {
[[ "${DIST_VER}" =~ ^[7].* ]] && sudo dnf install openjpeg-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
# some misp-modules dependencies for RHEL8
([[ "${DISTRI}" == "fedora33" ]] || [[ "${DIST_VER}" =~ ^[8].* ]]) && sudo dnf install openjpeg2-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
([[ "${DISTRI}" == "fedora33" ]] || [[ "${DISTRI}" == "fedora34" ]] || [[ "${DIST_VER}" =~ ^[8].* ]]) && sudo dnf install openjpeg2-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
sudo chmod 2777 /usr/local/src
sudo chown root:users /usr/local/src
@ -3545,8 +3546,8 @@ installMISPRHEL () {
apacheConfig_RHEL7
fi
if [[ "${DISTRI}" == "fedora33" ]]; then
enableREMI_f33
if [[ "${DISTRI%??}" == "fedora" ]]; then
enableREMI_fedora
yumInstallCoreDeps8
installEntropyRHEL
installCoreRHEL8

View File

@ -1,5 +1,5 @@
; Generated by RHash v1.4.0 on 2021-04-27 at 19:40.45
; Generated by RHash v1.3.9 on 2021-07-02 at 13:54.25
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
;
; 162034 19:40.45 2021-04-27 INSTALL.sh
INSTALL.sh D560285EFF4EF709FD094256982F149B8C8E4110 549613A0405D89549885525B58529905B0DE16677C7CBAA1ECFFEB239E268004 221B86BE366A9FAA94080A28B0A7D8A107F4A44C678AF6F2606D973A1C8F4076508AEE501D2ED29075B0FF1642744211 C9D1A61236DA0A0FD468A777DBD529F10371361463F2437861B637084BEA14AD42C8EA61BE40489A9F2F239229AE8930FDF1BB4787DD296EB49E129CF3C64B47
; 162398 13:54.25 2021-07-02 INSTALL.sh
INSTALL.sh 7A77001F5A72A613D79CCB9D8DD52E4940A24624 1A4A66510D15575BF7D369E6CC0551F3421BDA2D7772E5A18DF53B7C87252425 5F495653054CF223A0283D8CB5E903A9422864F473FBEB45A3DFE2C95252E3171C809413318D31AB1CC34B4A5FD4D872 90D438EBF4237155D8647094D905410D7BB8F4B0A70EB444247E9C19D0C749553A28D1F5433A205A37F8CDBEBD7A9F815854CF3CD7B2980D676F37A4DB7776D2

View File

@ -1 +1 @@
d560285eff4ef709fd094256982f149b8c8e4110 INSTALL.sh
7a77001f5a72a613d79ccb9d8dd52e4940a24624 INSTALL.sh

View File

@ -1 +1 @@
549613a0405d89549885525b58529905b0de16677c7cbaa1ecffeb239e268004 INSTALL.sh
1a4a66510d15575bf7d369e6cc0551f3421bda2d7772e5a18df53b7c87252425 INSTALL.sh

View File

@ -1 +1 @@
221b86be366a9faa94080a28b0a7d8a107f4a44c678af6f2606d973a1c8f4076508aee501d2ed29075b0ff1642744211 INSTALL.sh
5f495653054cf223a0283d8cb5e903a9422864f473fbeb45a3dfe2c95252e3171c809413318d31ab1cc34b4a5fd4d872 INSTALL.sh

View File

@ -1 +1 @@
c9d1a61236da0a0fd468a777dbd529f10371361463f2437861b637084bea14ad42c8ea61be40489a9f2f239229ae8930fdf1bb4787dd296eb49e129cf3c64b47 INSTALL.sh
90d438ebf4237155d8647094d905410d7bb8f4b0a70eb444247e9c19d0c749553a28d1f5433a205a37f8cdbebd7a9f815854cf3cd7b2980d676f37a4db7776d2 INSTALL.sh

View File

@ -711,8 +711,8 @@ installMISPRHEL () {
apacheConfig_RHEL7
fi
if [[ "${DISTRI}" == "fedora33" ]]; then
enableREMI_f33
if [[ "${DISTRI%??}" == "fedora" ]]; then
enableREMI_fedora
yumInstallCoreDeps8
installEntropyRHEL
installCoreRHEL8

View File

@ -1,3 +1,5 @@
-- db_version of this file: 61
-- --------------------------------------------------------
--
@ -634,21 +636,6 @@ KEY `org_id` (`org_id`),
KEY `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- -------------------------------------------------------
--
-- Table structure for `org_blacklists`
--
CREATE TABLE IF NOT EXISTS `org_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`org_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
`org_name` varchar(255) COLLATE utf8_bin NOT NULL,
`comment` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- --------------------------------------------------------
--
@ -804,7 +791,7 @@ CREATE TABLE IF NOT EXISTS `organisations` (
INDEX `name` (`name`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `org_blacklists` (
CREATE TABLE IF NOT EXISTS `org_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`org_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
@ -1670,7 +1657,7 @@ INSERT IGNORE INTO `template_element_texts` (`id`, `name`, `template_element_id`
(11, 'Persistence mechanism', 41, 'The following fields allow you to describe the persistence mechanism used by the malware'),
(12, 'Indicators', 45, 'Just paste your list of indicators based on type into the appropriate field. All of the fields are optional, so inputting a list of IP addresses into the Network indicator field for example is sufficient to complete this template.');
INSERT IGNORE INTO `org_blacklists` (`org_uuid`, `created`, `org_name`, `comment`) VALUES
INSERT IGNORE INTO `org_blocklists` (`org_uuid`, `created`, `org_name`, `comment`) VALUES
('58d38339-7b24-4386-b4b4-4c0f950d210f', NOW(), 'Setec Astrononomy', 'default example'),
('58d38326-eda8-443a-9fa8-4e12950d210f', NOW(), 'Acme Finance', 'default example');

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":144}
{"major":2, "minor":4, "hotfix":146}

View File

@ -342,24 +342,36 @@ class EventShell extends AppShell
public function publish_sightings()
{
$this->ConfigLoad->execute();
$id = $this->args[0];
$passAlong = $this->args[1];
$jobId = $this->args[2];
$userId = $this->args[3];
list($id, $passAlong, $jobId, $userId) = $this->args;
$user = $this->getUser($userId);
$job = $this->Job->read(null, $jobId);
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
$result = $this->Event->publish_sightings($id, $passAlong);
$job['Job']['progress'] = 100;
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
if ($result) {
$job['Job']['message'] = 'Sightings published.';
} else {
$job['Job']['message'] = 'Sightings published, but the upload to other instances may have failed.';
$sightingsUuidsToPush = [];
if (isset($this->args[4])) { // push just specific sightings
$path = APP . 'tmp/cache/ingest' . DS . $this->args[4];
$tempFile = new File($path);
$inputData = $tempFile->read();
if ($inputData === false) {
$this->error("File `$path` not found.");
}
$sightingsUuidsToPush = $this->Event->jsonDecode($inputData);
$tempFile->delete();
}
$this->Job->save($job);
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
$result = $this->Event->publish_sightings($id, $passAlong, $sightingsUuidsToPush);
$count = count($sightingsUuidsToPush);
$message = $count === 0 ? "All sightings published" : "$count sightings published";
if ($result) {
$message .= '.';
} else {
$message .= ', but the upload to other instances may have failed.';
}
$this->Job->saveStatus($jobId, true, $message);
$log = ClassRegistry::init('Log');
$log->createLogEntry($user, 'publish_sightings', 'Event', $id, 'Sightings for event (' . $id . '): published.', 'publish_sightings updated');
$title = $count === 0 ? "All sightings for event published." : "$count sightings for event published.";
$log->createLogEntry($user, 'publish_sightings', 'Event', $id, $title, 'publish_sightings updated');
}
public function publish_galaxy_clusters()

View File

@ -18,6 +18,7 @@ App::uses('RequestRearrangeTool', 'Tools');
* @property CRUDComponent $CRUD
* @property IndexFilterComponent $IndexFilter
* @property RateLimitComponent $RateLimit
* @property CompressedRequestHandlerComponent $CompressedRequestHandler
*/
class AppController extends Controller
{
@ -792,16 +793,11 @@ class AppController extends Controller
public function queryACL($debugType='findMissingFunctionNames', $content = false)
{
$this->autoRender = false;
$this->layout = false;
$validCommands = array('printAllFunctionNames', 'findMissingFunctionNames', 'printRoleAccess');
if (!in_array($debugType, $validCommands)) {
throw new MethodNotAllowedException('Invalid function call.');
}
$this->set('data', $this->ACL->$debugType($content));
$this->set('flags', JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
$this->response->type('json');
$this->render('/Servers/json/simple');
return $this->RestResponse->viewData($this->ACL->$debugType($content), 'json');
}
/*
@ -1525,17 +1521,7 @@ class AppController extends Controller
if (isset($sessionUser['authkey_id'])) {
// Reload authkey
$this->loadModel('AuthKey');
$authKey = $this->AuthKey->find('first', [
'conditions' => ['id' => $sessionUser['authkey_id'], 'user_id' => $user['id']],
'fields' => ['id', 'expiration', 'allowed_ips'],
'recursive' => -1,
]);
if (empty($authKey)) {
throw new RuntimeException("Auth key with ID {$sessionUser['authkey_id']} not exists.");
}
$user['authkey_id'] = $authKey['AuthKey']['id'];
$user['authkey_expiration'] = $authKey['AuthKey']['expiration'];
$user['allowed_ips'] = $authKey['AuthKey']['allowed_ips'];
$user = $this->AuthKey->updateUserData($user, $sessionUser['authkey_id']);
}
if (isset($sessionUser['logged_by_authkey'])) {
$user['logged_by_authkey'] = $sessionUser['logged_by_authkey'];

View File

@ -750,6 +750,7 @@ class AttributesController extends AppController
// check which attribute is newer
if (count($existingAttribute) && !$existingAttribute['Attribute']['deleted']) {
$this->request->data['Attribute']['id'] = $existingAttribute['Attribute']['id'];
$this->request->data['Attribute']['event_id'] = $existingAttribute['Attribute']['event_id'];
$dateObj = new DateTime();
$skipTimeCheck = false;
if (!isset($this->request->data['Attribute']['timestamp'])) {
@ -2571,8 +2572,7 @@ class AttributesController extends AppController
foreach ($this->Attribute->categoryDefinitions as $cat => $data) {
$result['category_type_mappings'][$cat] = $data['types'];
}
$this->set('result', $result);
$this->set('_serialize', array('result'));
return $this->RestResponse->viewData(['result' => $result], 'json');
}
public function attributeStatistics($type = 'type', $percentage = false)
@ -2597,12 +2597,7 @@ class AttributesController extends AppController
}
}
ksort($results);
$this->autoRender = false;
$this->layout = false;
$this->set('data', $results);
$this->set('flags', JSON_PRETTY_PRINT);
$this->response->type('json');
$this->render('/Servers/json/simple');
return $this->RestResponse->viewData($results, 'json');
}
public function addTag($id = false, $tag_id = false)

View File

@ -82,7 +82,7 @@ class AuthKeysController extends AppController
$authKey['AuthKey']['expiration'] = date('Y-m-d H:i:s', $authKey['AuthKey']['expiration']);
return $authKey;
},
'fields' => ['comment', 'allowed_ips', 'expiration'],
'fields' => ['comment', 'allowed_ips', 'expiration', 'read_only'],
'contain' => ['User.id', 'User.org_id']
]);
if ($this->IndexFilter->isRest()) {
@ -100,6 +100,7 @@ class AuthKeysController extends AppController
]);
$this->set('edit', true);
$this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
$this->set('title_for_layout', __('Edit auth key'));
$this->render('add');
}
@ -134,6 +135,7 @@ class AuthKeysController extends AppController
])
];
$this->set(compact('dropdownData'));
$this->set('title_for_layout', __('Add auth key'));
$this->set('menuData', [
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
'menuItem' => 'authKeyAdd',
@ -162,7 +164,7 @@ class AuthKeysController extends AppController
$this->set('uniqueIps', $uniqueIps);
}
$this->set('title_for_layout', __('Auth Key'));
$this->set('title_for_layout', __('Auth key'));
$this->set('menuData', [
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
'menuItem' => 'authKeyView',

View File

@ -71,9 +71,9 @@ class ACLComponent extends Component
'viewPicture' => array('*'),
),
'authKeys' => [
'add' => ['perm_auth'],
'delete' => ['perm_auth'],
'edit' => ['perm_auth'],
'add' => ['AND' => ['perm_auth', 'not_read_only_authkey']],
'delete' => ['AND' => ['perm_auth', 'not_read_only_authkey']],
'edit' => ['AND' => ['perm_auth', 'not_read_only_authkey']],
'index' => ['perm_auth'],
'view' => ['perm_auth']
],
@ -468,9 +468,9 @@ class ACLComponent extends Component
'display' => array('*'),
),
'posts' => array(
'add' => array('*'),
'delete' => array('*'),
'edit' => array('*'),
'add' => array('not_read_only_authkey'),
'delete' => array('not_read_only_authkey'),
'edit' => array('not_read_only_authkey'),
'pushMessageToZMQ' => array('perm_site_admin')
),
'regexp' => array(
@ -483,7 +483,7 @@ class ACLComponent extends Component
'index' => array('*'),
),
'restClientHistory' => array(
'delete' => array('*'),
'delete' => array('not_read_only_authkey'),
'index' => array('*')
),
'roles' => array(
@ -594,6 +594,7 @@ class ACLComponent extends Component
'quickDelete' => array('perm_sighting'),
'viewSightings' => array('*'),
'bulkSaveSightings' => array('OR' => array('perm_sync', 'perm_sighting')),
'filterSightingUuidsForPush' => ['perm_sync'],
'quickAdd' => array('perm_sighting')
),
'sightingdb' => array(
@ -651,6 +652,7 @@ class ACLComponent extends Component
'toggleRequired' => array('perm_site_admin'),
'update' => array(),
'import' => [],
'export' => ['*'],
'view' => array('*'),
'unhideTag' => array('perm_tagger'),
'hideTag' => array('perm_tagger'),
@ -693,7 +695,7 @@ class ACLComponent extends Component
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),
'attributehistogram' => array('*'),
'change_pw' => ['AND' => ['self_management_enabled', 'password_change_enabled']],
'change_pw' => ['AND' => ['self_management_enabled', 'password_change_enabled', 'not_read_only_authkey']],
'checkAndCorrectPgps' => array(),
'checkIfLoggedIn' => array('*'),
'dashboard' => array('*'),
@ -711,7 +713,7 @@ class ACLComponent extends Component
'register' => array('*'),
'registrations' => array('perm_site_admin'),
'resetAllSyncAuthKeys' => array(),
'resetauthkey' => ['AND' => ['self_management_enabled', 'perm_auth']],
'resetauthkey' => ['AND' => ['self_management_enabled', 'perm_auth', 'not_read_only_authkey']],
'request_API' => array('*'),
'routeafterlogin' => array('*'),
'statistics' => array('*'),
@ -727,10 +729,10 @@ class ACLComponent extends Component
'userSettings' => array(
'index' => array('*'),
'view' => array('*'),
'setSetting' => array('*'),
'setSetting' => array('not_read_only_authkey'),
'getSetting' => array('*'),
'delete' => array('*'),
'setHomePage' => array('*'),
'delete' => array('not_read_only_authkey'),
'setHomePage' => array('not_read_only_authkey'),
'eventIndexColumnToggle' => ['*'],
),
'warninglists' => array(
@ -793,6 +795,10 @@ class ACLComponent extends Component
$this->dynamicChecks['delegation_enabled'] = function (array $user) {
return (bool)Configure::read('MISP.delegation');
};
// Returns true if current user is not using advanced auth key or if authkey is not read only
$this->dynamicChecks['not_read_only_authkey'] = function (array $user) {
return !isset($user['authkey_read_only']) || !$user['authkey_read_only'];
};
}
private function __checkLoggedActions($user, $controller, $action)

View File

@ -558,7 +558,7 @@ class RestResponseComponent extends Component
}
}
// Do not pretty print response for automatic tools
$flags = $this->isAutomaticTool() ? JSON_UNESCAPED_UNICODE : JSON_PRETTY_PRINT;
$flags = $this->isAutomaticTool() ? JSON_UNESCAPED_UNICODE : (JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$response = json_encode($response, $flags);
} else {
if ($dumpSql) {

View File

@ -2415,11 +2415,13 @@ class EventsController extends AppController
$this->set('distributions', $distributions);
$this->set('sgs', $sgs);
$this->set('event', $event);
$this->set('title_for_layout', __('Event merge results'));
$this->set('title', __('Event merge results'));
$this->set('importComment', 'Merged from event ' . $source_id);
$this->render('resolved_misp_format');
} else {
$this->set('target_event', $target_event);
$this->set('title_for_layout', __('Merge data from event'));
}
}
@ -2691,7 +2693,7 @@ class EventsController extends AppController
throw new NotFoundException(__('Invalid event'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$result = $this->Event->publishRouter($event['Event']['id'], null, $this->Auth->user(), 'sightings');
$result = $this->Event->publishSightingsRouter($event['Event']['id'], $this->Auth->user());
if (!Configure::read('MISP.background_jobs')) {
if (!is_array($result)) {
// redirect to the view event page
@ -3674,9 +3676,6 @@ class EventsController extends AppController
*/
public function freeTextImport($id, $adhereToWarninglists = false, $returnMetaAttributes = false)
{
if (!$this->userRole['perm_add']) {
throw new MethodNotAllowedException(__('Event not found or you don\'t have permissions to create attributes'));
}
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
if (empty($event)) {
throw new MethodNotAllowedException(__('Invalid event.'));
@ -3750,7 +3749,8 @@ class EventsController extends AppController
$this->set('mapping', $typeCategoryMapping);
$this->set('resultArray', $resultArray);
$this->set('importComment', '');
$this->set('title', 'Freetext Import Results');
$this->set('title_for_layout', __('Freetext Import Results'));
$this->set('title', __('Freetext Import Results'));
$this->loadModel('Warninglist');
$this->set('missingTldLists', $this->Warninglist->missingTldLists());
$this->render('resolved_attributes');
@ -3798,9 +3798,6 @@ class EventsController extends AppController
public function saveFreeText($id)
{
if (!$this->userRole['perm_add']) {
throw new MethodNotAllowedException(__('Event not found or you don\'t have permissions to create attributes'));
}
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('This endpoint requires a POST request.');
}
@ -3970,9 +3967,6 @@ class EventsController extends AppController
public function exportChoice($id)
{
if (!is_numeric($id)) {
throw new MethodNotAllowedException(__('Invalid ID'));
}
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
if (empty($event)) {
throw new NotFoundException(__('Event not found or you are not authorised to view it.'));
@ -4111,9 +4105,6 @@ class EventsController extends AppController
public function importChoice($id = false, $scope = 'event')
{
if ($scope == 'event') {
if (!is_numeric($id)) {
throw new MethodNotAllowedException(__('Invalid ID'));
}
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
if (empty($event)) {
throw new NotFoundException(__('Event not found or you are not authorised to view it.'));
@ -4150,7 +4141,7 @@ class EventsController extends AppController
$this->loadModel('Module');
$modules = $this->Module->getEnabledModules($this->Auth->user(), false, 'Import');
if (is_array($modules) && !empty($modules)) {
foreach ($modules['modules'] as $k => $module) {
foreach ($modules['modules'] as $module) {
$imports[$module['name']] = array(
'url' => $this->baseurl . '/events/importModule/' . $module['name'] . '/' . $id,
'text' => Inflector::humanize($module['name']),
@ -4420,7 +4411,7 @@ class EventsController extends AppController
'metadata' => true,
));
if (empty($event)) {
throw new MethodNotAllowedException(__('Invalid Event.'));
throw new NotFoundException(__('Invalid Event.'));
}
$this->set('event', $event[0]);
@ -4511,16 +4502,14 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getDistributionGraph($id, $type = 'event')
{
$extended = isset($this->params['named']['extended']) ? 1 : 0;
$json = $this->genDistributionGraph($id, $type, $extended);
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getEventGraphReferences($id, $type = 'event')
@ -4544,8 +4533,7 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getEventGraphTags($id, $type = 'event')
@ -4569,8 +4557,7 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getEventGraphGeneric($id, $type = 'event')
@ -4599,8 +4586,7 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getReferenceData($uuid, $type = 'reference')
@ -4620,8 +4606,7 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function getObjectTemplate($type = 'templates')
@ -4642,8 +4627,7 @@ class EventsController extends AppController
$item = utf8_encode($item);
}
});
$this->response->type('json');
return new CakeResponse(array('body' => json_encode($json), 'status' => 200, 'type' => 'json'));
return $this->RestResponse->viewData($json, 'json');
}
public function viewGalaxyMatrix($scope_id, $galaxy_id, $scope='event', $disable_picking=false)
@ -4754,35 +4738,34 @@ class EventsController extends AppController
$this->Galaxy->sortMatrixByScore($tabs, $scores);
if ($this->_isRest()) {
$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 {
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('Invalid method.'));
}
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($scores);
$this->set('eventId', $eventId);
$this->set('target_type', $scope);
$this->set('columnOrders', $killChainOrders);
$this->set('tabs', $tabs);
$this->set('scores', $scores);
$this->set('maxScore', $maxScore);
if (!empty($colours)) {
$this->set('colours', $colours['mapping']);
$this->set('interpolation', $colours['interpolation']);
}
$this->set('pickingMode', !$disable_picking);
$this->set('target_id', $scope_id);
if ($matrixData['galaxy']['id'] == $mitreAttackGalaxyId) {
$this->set('defaultTabName', 'mitre-attack');
$this->set('removeTrailling', 2);
}
$this->render('/Elements/view_galaxy_matrix');
return $this->RestResponse->viewData($json, 'json');
}
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('Invalid method.'));
}
App::uses('ColourGradientTool', 'Tools');
$gradientTool = new ColourGradientTool();
$colours = $gradientTool->createGradientFromValues($scores);
$this->set('eventId', $eventId);
$this->set('target_type', $scope);
$this->set('columnOrders', $killChainOrders);
$this->set('tabs', $tabs);
$this->set('scores', $scores);
$this->set('maxScore', $maxScore);
if (!empty($colours)) {
$this->set('colours', $colours['mapping']);
$this->set('interpolation', $colours['interpolation']);
}
$this->set('pickingMode', !$disable_picking);
$this->set('target_id', $scope_id);
if ($matrixData['galaxy']['id'] == $mitreAttackGalaxyId) {
$this->set('defaultTabName', 'mitre-attack');
$this->set('removeTrailling', 2);
}
$this->render('/Elements/view_galaxy_matrix');
}
// Displays all the cluster relations for the provided event
@ -4899,7 +4882,7 @@ class EventsController extends AppController
$options = array();
foreach ($enabledModules['modules'] as $temp) {
if ($temp['name'] == $module) {
$format = (!empty($temp['mispattributes']['format']) ? $temp['mispattributes']['format'] : 'simplified');
$format = !empty($temp['mispattributes']['format']) ? $temp['mispattributes']['format'] : 'simplified';
if (isset($temp['meta']['config'])) {
foreach ($temp['meta']['config'] as $conf) {
$options[$conf] = Configure::read('Plugin.' . $type . '_' . $module . '_' . $conf);
@ -4915,6 +4898,8 @@ class EventsController extends AppController
}
$this->set('distributions', $distributions);
$this->set('sgs', $sgs);
$this->set('title_for_layout', __('Enrichment Results'));
$this->set('title', __('Enrichment Results'));
if ($format == 'misp_standard') {
$this->__queryEnrichment($attribute, $module, $options, $type);
} else {
@ -4935,7 +4920,7 @@ class EventsController extends AppController
}
$result = $this->Module->queryModuleServer($data, false, $type);
if (!$result) {
throw new MethodNotAllowedException(__('%s service not reachable.', $type));
throw new InternalErrorException(__('%s service not reachable.', $type));
}
if (isset($result['error'])) {
$this->Flash->error($result['error']);
@ -4964,7 +4949,8 @@ class EventsController extends AppController
}
$this->set('event', $event);
$this->set('menuItem', 'enrichmentResults');
$this->set('title', 'Enrichment Results');
$this->set('title_for_layout', __('Enrichment Results'));
$this->set('title', __('Enrichment Results'));
$this->render('resolved_misp_format');
}
}
@ -4980,7 +4966,7 @@ class EventsController extends AppController
}
$result = $this->Module->queryModuleServer($data, false, $type);
if (!$result) {
throw new MethodNotAllowedException(__('%s service not reachable.', $type));
throw new InternalErrorException(__('%s service not reachable.', $type));
}
if (isset($result['error'])) {
$this->Flash->error($result['error']);
@ -5023,7 +5009,6 @@ class EventsController extends AppController
$this->set('resultArray', $resultArray);
$this->set('typeDefinitions', $this->Event->Attribute->typeDefinitions);
$this->set('typeCategoryMapping', $typeCategoryMapping);
$this->set('title', 'Enrichment Results');
$this->set('importComment', $importComment);
$this->render('resolved_attributes');
}
@ -5158,7 +5143,7 @@ class EventsController extends AppController
}
$result = $this->Module->queryModuleServer($modulePayload, false, $moduleFamily = 'Import');
if (!$result) {
throw new Exception(__('Import service not reachable.'));
throw new InternalErrorException(__('Import service not reachable.'));
}
if (isset($result['error'])) {
$this->Flash->error($result['error']);
@ -5208,7 +5193,8 @@ class EventsController extends AppController
}
$this->set('distributions', $distributions);
$this->set('sgs', $sgs);
$this->set('title', 'Import Results');
$this->set('title', __('Import Results'));
$this->set('title_for_layout', __('Import Results'));
$this->set('importComment', $importComment);
$this->render($render_name);
}

View File

@ -646,7 +646,7 @@ class GalaxiesController extends AppController
$this->set('galaxy_id', $galaxyId);
}
public function relationsGraph($galaxyId)
public function relationsGraph($galaxyId, $includeInbound=0)
{
$clusters = $this->Galaxy->GalaxyCluster->fetchGalaxyClusters($this->Auth->user(), array('conditions' => array('GalaxyCluster.galaxy_id' => $galaxyId)), $full=true);
if (empty($clusters)) {
@ -658,13 +658,14 @@ class GalaxiesController extends AppController
));
App::uses('ClusterRelationsGraphTool', 'Tools');
$grapher = new ClusterRelationsGraphTool($this->Auth->user(), $this->Galaxy->GalaxyCluster);
$relations = $grapher->getNetwork($clusters);
$relations = $grapher->getNetwork($clusters, $includeInbound, $includeInbound);
if ($this->_isRest()) {
return $this->RestResponse->viewData($relations, $this->response->type());
}
$this->set('relations', $relations);
$this->set('galaxy', $galaxy);
$this->set('galaxy_id', $galaxyId);
$this->set('includeInbound', $includeInbound);
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
$this->set('distributionLevels', $distributionLevels);

View File

@ -323,6 +323,7 @@ class GalaxyClustersController extends AppController
}
}
}
$this->set('galaxy', ['Galaxy' => ['id' => $galaxyId]]);
$this->set('galaxy_id', $galaxyId);
$this->set('distributionLevels', $distributionLevels);
$this->set('initialDistribution', $initialDistribution);
@ -980,14 +981,14 @@ class GalaxyClustersController extends AppController
/**
* @param mixed $id ID or UUID of the cluster
*/
public function viewRelations($id)
public function viewRelations($id, $includeInbound=1)
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException('This function can only be reached via AJAX.');
}
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', true, true);
$existingRelations = $this->GalaxyCluster->GalaxyClusterRelation->getExistingRelationships();
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster);
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster, $includeInbound);
App::uses('ClusterRelationsTreeTool', 'Tools');
$grapher = new ClusterRelationsTreeTool();
@ -997,9 +998,16 @@ class GalaxyClustersController extends AppController
$this->set('existingRelations', $existingRelations);
$this->set('cluster', $cluster);
$relations = $cluster['GalaxyCluster']['GalaxyClusterRelation'];
if ($includeInbound && !empty($cluster['GalaxyCluster']['TargetingClusterRelation'])) {
foreach ($cluster['GalaxyCluster']['TargetingClusterRelation'] as $targetingCluster) {
$targetingCluster['isInbound'] = true;
$relations[] = $targetingCluster;
}
}
$this->set('passedArgs', json_encode([]));
$this->set('relations', $relations);
$this->set('tree', $tree);
$this->set('includeInbound', $includeInbound);
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
unset($distributionLevels[4]);
@ -1010,15 +1018,18 @@ class GalaxyClustersController extends AppController
/**
* @param mixed $id ID or UUID of the cluster
*/
public function viewRelationTree($id)
public function viewRelationTree($id, $includeInbound=1)
{
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors=true, $full=true);
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster);
$cluster = $this->GalaxyCluster->attachClusterToRelations($this->Auth->user(), $cluster, $includeInbound);
App::uses('ClusterRelationsTreeTool', 'Tools');
$grapher = new ClusterRelationsTreeTool();
$grapher->construct($this->Auth->user(), $this->GalaxyCluster);
$tree = $grapher->getTree($cluster);
$this->set('tree', $tree);
$this->set('cluster', $cluster);
$this->set('includeInbound', $includeInbound);
$this->set('testtest', 'testtest');
$this->render('/Elements/GalaxyClusters/view_relation_tree');
}
}

View File

@ -1,7 +1,9 @@
<?php
App::uses('AppController', 'Controller');
/**
* @property ObjectReference $ObjectReference
*/
class ObjectReferencesController extends AppController
{
public $components = array('Security' ,'RequestHandler', 'Session');
@ -24,20 +26,14 @@ class ObjectReferencesController extends AppController
throw new MethodNotAllowedException('No object defined.');
}
if (Validation::uuid($objectId)) {
$temp = $this->ObjectReference->Object->find('first', array(
'recursive' => -1,
'fields' => array('Object.id'),
'conditions' => array('Object.uuid' => $objectId, 'Object.deleted' => 0)
));
if (empty($temp)) {
throw new NotFoundException('Invalid Object');
}
$objectId = $temp['Object']['id'];
} elseif (!is_numeric($objectId)) {
throw new NotFoundException(__('Invalid object'));
$conditions = ['Object.uuid' => $objectId];
} else {
$conditions = ['Object.id' => $objectId];
}
$conditions['Object.deleted'] = 0;
$object = $this->ObjectReference->Object->find('first', array(
'conditions' => array('Object.id' => $objectId, 'Object.deleted' => 0),
'conditions' => $conditions,
'recursive' => -1,
'contain' => array(
'Event' => array(
@ -48,13 +44,12 @@ class ObjectReferencesController extends AppController
if (empty($object) || !$this->__canModifyEvent($object)) {
throw new NotFoundException('Invalid object.');
}
$this->set('objectId', $objectId);
$this->set('objectId', $object['Object']['id']);
if ($this->request->is('post')) {
$data = array();
if (!isset($this->request->data['ObjectReference'])) {
$this->request->data['ObjectReference'] = $this->request->data;
}
list($referenced_id, $referenced_uuid, $referenced_type) = $this->ObjectReference->getReferencedInfo($this->request->data['ObjectReference']['referenced_uuid'], $object, true, $this->Auth->user());
list($referenced_id, $referenced_uuid, $referenced_type) = $this->ObjectReference->getReferencedInfo(trim($this->request->data['ObjectReference']['referenced_uuid']), $object, true, $this->Auth->user());
$relationship_type = empty($this->request->data['ObjectReference']['relationship_type']) ? '' : $this->request->data['ObjectReference']['relationship_type'];
if (!empty($this->request->data['ObjectReference']['relationship_type_select']) && $this->request->data['ObjectReference']['relationship_type_select'] !== 'custom') {
$relationship_type = $this->request->data['ObjectReference']['relationship_type_select'];
@ -67,7 +62,7 @@ class ObjectReferencesController extends AppController
'event_id' => $object['Event']['id'],
'object_uuid' => $object['Object']['uuid'],
'source_uuid' => $object['Object']['uuid'],
'object_id' => $objectId,
'object_id' => $object['Object']['id'],
'referenced_type' => $referenced_type,
'uuid' => CakeText::uuid()
);
@ -115,7 +110,7 @@ class ObjectReferencesController extends AppController
'fields' => array('Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids')
),
'Object' => array(
'conditions' => array('NOT' => array('Object.id' => $objectId), 'Object.deleted' => 0),
'conditions' => array('NOT' => array('Object.id' => $object['Object']['id']), 'Object.deleted' => 0),
'fields' => array('Object.id', 'Object.uuid', 'Object.name', 'Object.meta-category'),
'Attribute' => array(
'conditions' => array('Attribute.deleted' => 0),
@ -142,20 +137,16 @@ class ObjectReferencesController extends AppController
}
}
$this->loadModel('ObjectRelationship');
$relationshipsTemp = $this->ObjectRelationship->find('all', array(
'recursive' => -1
$relationships = $this->ObjectRelationship->find('column', array(
'recursive' => -1,
'fields' => ['name'],
));
$relationships = array();
$relationshipMetadata = array();
foreach ($relationshipsTemp as $k => $v) {
$relationshipMetadata[$v['ObjectRelationship']['name']] = $v;
$relationships[$v['ObjectRelationship']['name']] = $v['ObjectRelationship']['name'];
}
$relationships = array_combine($relationships, $relationships);
$relationships['custom'] = 'custom';
ksort($relationships);
$this->set('relationships', $relationships);
$this->set('event', $event);
$this->set('objectId', $objectId);
$this->set('objectId', $object['Object']['id']);
$this->layout = 'ajax';
$this->render('ajax/add');
}
@ -164,21 +155,8 @@ class ObjectReferencesController extends AppController
public function delete($id, $hard = false)
{
if (Validation::uuid($id)) {
$temp = $this->ObjectReference->find('first', array(
'recursive' => -1,
'fields' => array('ObjectReference.id'),
'conditions' => array('ObjectReference.uuid' => $id)
));
if (empty($temp)) {
throw new NotFoundException('Invalid object reference');
}
$id = $temp['ObjectReference']['id'];
} elseif (!is_numeric($id)) {
throw new NotFoundException(__('Invalid object reference'));
}
$objectReference = $this->ObjectReference->find('first', array(
'conditions' => array('ObjectReference.id' => $id),
'conditions' => Validation::uuid($id) ? ['ObjectReference.uuid' => $id] : ['ObjectReference.id' => $id],
'recursive' => -1,
'contain' => array('Object' => array('Event'))
));
@ -188,6 +166,7 @@ class ObjectReferencesController extends AppController
if (!$this->__canModifyEvent($objectReference['Object'])) {
throw new ForbiddenException(__('Invalid object reference.'));
}
$id = $objectReference['ObjectReference']['id'];
if ($this->request->is('post') || $this->request->is('put') || $this->request->is('delete')) {
$result = $this->ObjectReference->smartDelete($objectReference['ObjectReference']['id'], $hard);
if ($result === true) {
@ -216,30 +195,20 @@ class ObjectReferencesController extends AppController
public function view($id)
{
if (Validation::uuid($id)) {
$temp = $this->ObjectReference->find('first', array(
'recursive' => -1,
'fields' => array('ObjectReference.id'),
'conditions' => array('ObjectReference.uuid' => $id)
));
if (empty($temp)) {
throw new NotFoundException('Invalid object reference');
}
$id = $temp['ObjectReference']['id'];
} elseif (!is_numeric($id)) {
throw new NotFoundException(__('Invalid object reference'));
}
$objectReference = $this->ObjectReference->find('first', array(
'conditions' => array('ObjectReference.id' => $id),
'conditions' => Validation::uuid($id) ? ['ObjectReference.uuid' => $id] : ['ObjectReference.id' => $id],
'recursive' => -1,
));
if (empty($objectReference)) {
throw new NotFoundException(__('Invalid object reference.'));
}
$event = $this->ObjectReference->Object->Event->fetchSimpleEvent($this->Auth->user(), $objectReference['ObjectReference']['event_id'], ['contain' => ['Orgc']]);
if (!$event) {
throw new NotFoundException(__('Invalid event'));
// Check if user can view object that contains this reference
$object = $this->ObjectReference->Object->find($this->Auth->user(), [
'conditions' => $objectReference['ObjectReference']['object_id'],
]);
if (!$object) {
throw new NotFoundException(__('Invalid object reference.'));
}
return $this->RestResponse->viewData($objectReference, 'application/json');
return $this->RestResponse->viewData($objectReference, 'json');
}
}

View File

@ -978,13 +978,6 @@ class ServersController extends AppController
$gpgErrors = array(0 => __('OK'), 1 => __('FAIL: settings not set'), 2 => __('FAIL: Failed to load GnuPG'), 3 => __('FAIL: Issues with the key/passphrase'), 4 => __('FAIL: sign failed'));
$proxyErrors = array(0 => __('OK'), 1 => __('not configured (so not tested)'), 2 => __('Getting URL via proxy failed'));
$zmqErrors = array(0 => __('OK'), 1 => __('not enabled (so not tested)'), 2 => __('Python ZeroMQ library not installed correctly.'), 3 => __('ZeroMQ script not running.'));
$stixOperational = array(0 => __('Some of the libraries related to STIX are not installed. Make sure that all libraries listed below are correctly installed.'), 1 => __('OK'));
$stixVersion = array(0 => __('Incorrect STIX version installed, found $current, expecting $expected'), 1 => __('OK'));
$stix2Version = array(0 => __('Incorrect STIX2 version installed, found $current, expecting $expected'), 1 => __('OK'));
$cyboxVersion = array(0 => __('Incorrect CyBox version installed, found $current, expecting $expected'), 1 => __('OK'));
$mixboxVersion = array(0 => __('Incorrect mixbox version installed, found $current, expecting $expected'), 1 => __('OK'));
$maecVersion = array(0 => __('Incorrect maec version installed, found $current, expecting $expected'), 1 => __('OK'));
$pymispVersion = array(0 => __('Incorrect PyMISP version installed, found $current, expecting $expected'), 1 => __('OK'));
$sessionErrors = array(0 => __('OK'), 1 => __('High'), 2 => __('Alternative setting used'), 3 => __('Test failed'));
$moduleErrors = array(0 => __('OK'), 1 => __('System not enabled'), 2 => __('No modules found'));
@ -1104,7 +1097,7 @@ class ServersController extends AppController
}
// check if the STIX and Cybox libraries are working and the correct version using the test script stixtest.py
$stix = $this->Server->stixDiagnostics($diagnostic_errors, $stixVersion, $cyboxVersion, $mixboxVersion, $maecVersion, $stix2Version, $pymispVersion);
$stix = $this->Server->stixDiagnostics($diagnostic_errors);
$yaraStatus = $this->Server->yaraDiagnostics($diagnostic_errors);
@ -1142,7 +1135,7 @@ class ServersController extends AppController
$securityAudit = (new SecurityAudit())->run($this->Server);
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'stixVersion', 'cyboxVersion', 'mixboxVersion', 'maecVersion', 'stix2Version', 'pymispVersion', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stixOperational', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'redisInfo', 'attachmentScan', 'securityAudit');
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'redisInfo', 'attachmentScan', 'securityAudit');
} else {
$view = [];
}
@ -1211,6 +1204,7 @@ class ServersController extends AppController
$this->set('pythonmin', $this->pythonmin);
$this->set('pythonrec', $this->pythonrec);
$this->set('pymisp', $this->pymisp);
$this->set('title_for_layout', __('Diagnostics'));
}
public function startWorker($type)
@ -1805,14 +1799,19 @@ class ServersController extends AppController
$versionArray = $this->Server->checkMISPVersion();
$response = [
'version' => $versionArray['major'] . '.' . $versionArray['minor'] . '.' . $versionArray['hotfix'],
'pymisp_recommended_version' => $this->pyMispVersion,
'perm_sync' => $this->userRole['perm_sync'],
'perm_sighting' => $this->userRole['perm_sighting'],
'perm_galaxy_editor' => $this->userRole['perm_galaxy_editor'],
'request_encoding' => $this->CompressedRequestHandler->supportedEncodings(),
'filter_sightings' => true, // check if Sightings::filterSightingUuidsForPush method is supported
];
return $this->RestResponse->viewData($response, $this->response->type());
}
/**
* @deprecated Use field `pymisp_recommended_version` from getVersion instead
*/
public function getPyMISPVersion()
{
$this->set('response', array('version' => $this->pyMispVersion));

View File

@ -355,4 +355,26 @@ class SightingsController extends AppController
throw new MethodNotAllowedException($e->getMessage());
}
}
public function filterSightingUuidsForPush($eventId)
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException('This method is only accessible via POST requests.');
}
$event = $this->Sighting->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
if (empty($event)) {
throw new NotFoundException("Event not found");
}
$incomingSightingUuids = $this->request->data;
$existingSightingUuids = $this->Sighting->find('column', [
'fields' => ['Sighting.uuid'],
'conditions' => [
'Sighting.uuid' => $incomingSightingUuids,
'Sighting.event_id' => $event['Event']['id']
],
]);
return $this->RestResponse->viewData($existingSightingUuids, $this->response->type());
}
}

View File

@ -700,12 +700,7 @@ class TagsController extends AppController
}
}
$results = array('tags' => $tags, 'taxonomies' => $taxonomies);
$this->autoRender = false;
$this->layout = false;
$this->set('data', $results);
$this->set('flags', JSON_PRETTY_PRINT);
$this->response->type('json');
$this->render('/Servers/json/simple');
return $this->RestResponse->viewData($results, 'json');
}
private function __findObjectByUuid($object_uuid, &$type, $scope = 'modify')
@ -859,16 +854,16 @@ class TagsController extends AppController
}
$result = $this->$objectType->$connectorObject->save($data);
if ($result) {
$tempObject = $this->$objectType->find('first', array(
'recursive' => -1,
'conditions' => array($objectType . '.id' => $object[$objectType]['id'])
));
$date = new DateTime();
$tempObject[$objectType]['timestamp'] = $date->getTimestamp();
$this->$objectType->save($tempObject);
if ($local) {
$message = 'Local tag ' . $existingTag['Tag']['name'] . '(' . $existingTag['Tag']['id'] . ') successfully attached to ' . $objectType . '(' . $object[$objectType]['id'] . ').';
} else {
$tempObject = $this->$objectType->find('first', array(
'recursive' => -1,
'conditions' => array($objectType . '.id' => $object[$objectType]['id'])
));
$date = new DateTime();
$tempObject[$objectType]['timestamp'] = $date->getTimestamp();
$this->$objectType->save($tempObject);
if ($objectType === 'Attribute') {
$this->$objectType->Event->unpublishEvent($object['Event']['id']);
} elseif ($objectType === 'Event') {
@ -950,12 +945,28 @@ class TagsController extends AppController
}
}
}
$local = $existingAssociation[$objectType . 'Tag']['local'];
$result = $this->$objectType->$connectorObject->delete($existingAssociation[$connectorObject]['id']);
if ($result) {
$message = 'Tag ' . $existingTag['Tag']['name'] . '(' . $existingTag['Tag']['id'] . ') successfully removed from ' . $objectType . '(' . $object[$objectType]['id'] . ').';
$message = sprintf(__('%s tag %s (%s) successfully removed from %s(%s).'), $local ? __('Local') : __('Global'), $existingTag['Tag']['name'], $existingTag['Tag']['id'], $objectType, $object[$objectType]['id']);
if (!$local) {
$tempObject = $this->$objectType->find('first', array(
'recursive' => -1,
'conditions' => array($objectType . '.id' => $object[$objectType]['id'])
));
$date = new DateTime();
$tempObject[$objectType]['timestamp'] = $date->getTimestamp();
$this->$objectType->save($tempObject);
if ($objectType === 'Attribute') {
$this->$objectType->Event->unpublishEvent($object['Event']['id']);
} elseif ($objectType === 'Event') {
$this->Event->unpublishEvent($object['Event']['id']);
}
}
return $this->RestResponse->saveSuccessResponse('Tags', 'removeTagFromObject', false, $this->response->type(), $message);
} else {
return $this->RestResponse->saveFailResponse('Tags', 'removeTagFromObject', false, 'Failed to remove tag from object.', $this->response->type());
$message = __('Failed to remove tag from object.');
return $this->RestResponse->saveFailResponse('Tags', 'removeTagFromObject', false, $message, $this->response->type());
}
}

View File

@ -113,6 +113,55 @@ class TaxonomiesController extends AppController
$this->set('title_for_layout', __('%s Taxonomy Library', h(strtoupper($taxonomy['Taxonomy']['namespace']))));
}
public function export($id)
{
$taxonomy = $this->Taxonomy->find('first', [
'recursive' => -1,
'contain' => ['TaxonomyPredicate' => ['TaxonomyEntry']],
'conditions' => is_numeric($id) ? ['Taxonomy.id' => $id] : ['LOWER(Taxonomy.namespace)' => mb_strtolower($id)],
]);
if (empty($taxonomy)) {
throw new NotFoundException(__('Taxonomy not found.'));
}
$data = [
'namespace' => $taxonomy['Taxonomy']['namespace'],
'description' => $taxonomy['Taxonomy']['description'],
'version' => (int)$taxonomy['Taxonomy']['version'],
'exclusive' => $taxonomy['Taxonomy']['exclusive'],
'predicates' => [],
];
foreach ($taxonomy['TaxonomyPredicate'] as $predicate) {
$predicateOutput = [];
foreach (['value', 'expanded', 'colour', 'description', 'exclusive', 'numerical_value'] as $field) {
if (isset($predicate[$field]) && !empty($predicate[$field])) {
$predicateOutput[$field] = $predicate[$field];
}
}
$data['predicates'][] = $predicateOutput;
if (!empty($predicate['TaxonomyEntry'])) {
$entries = [];
foreach ($predicate['TaxonomyEntry'] as $entry) {
$entryOutput = [];
foreach(['value', 'expanded', 'colour', 'description', 'exclusive', 'numerical_value'] as $field) {
if (isset($entry[$field]) && !empty($entry[$field])) {
$entryOutput[$field] = $entry[$field];
}
}
$entries[] = $entryOutput;
}
$data['values'][] = [
'predicate' => $predicate['value'],
'entry' => $entries,
];
}
}
return $this->RestResponse->viewData($data, 'json');
}
public function enable($id)
{
if (!$this->_isSiteAdmin() || !$this->request->is('Post')) {

View File

@ -74,7 +74,7 @@
'conditions' => array(
'referenced_galaxy_cluster_uuid' => $cluster['GalaxyCluster']['uuid']
),
'contain' => array('SharingGroup'),
'contain' => array('SharingGroup', 'GalaxyClusterRelationTag' => 'Tag'),
));
if (!empty($referencingRelations)) {
foreach ($referencingRelations as $relation) {
@ -92,9 +92,9 @@
$nodes[$referencingClusterUuid]['group'] = $referencingCluster['GalaxyCluster']['type'];
$links[] = array(
'source' => $referencingClusterUuid,
'target' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'],
'target' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_uuid'],
'type' => $relation['GalaxyClusterRelation']['referenced_galaxy_cluster_type'],
'tag' => isset($relation['Tag']) ? $relation['Tag'] : array(),
'tag' => isset($relation['GalaxyClusterRelationTag']) ? Hash::extract($relation, '{n}.Tag') : array(),
);
}
}

View File

@ -79,6 +79,8 @@ class HttpSocketResponseExtended extends HttpSocketResponse
/**
* Supports response compression and also decodes response as JSON
* @method HttpSocketResponseExtended get($uri = null, $query = array(), $request = array())
* @method HttpSocketResponseExtended post($uri = null, $data = array(), $request = array())
*/
class HttpSocketExtended extends HttpSocket
{

View File

@ -0,0 +1,170 @@
<?php
App::uses('SyncTool', 'Tools');
class ServerSyncTool
{
const FEATURE_BR = 'br',
FEATURE_GZIP = 'gzip',
FEATURE_FILTER_SIGHTINGS = 'filter_sightings';
/** @var array */
private $server;
/** @var array */
private $request;
/** @var HttpSocketExtended */
private $socket;
/** @var array|null */
private $info = null;
/**
* @param array $server
* @param array $request
* @throws Exception
*/
public function __construct(array $server, array $request)
{
$this->server = $server;
$this->request = $request;
$syncTool = new SyncTool();
$this->socket = $syncTool->setupHttpSocket($server);
}
/**
* Check if event exists on remote server.
* @param array $event
* @return bool
* @throws Exception
*/
public function eventExists(array $event)
{
$exists = $this->socket->head($this->server['Server']['url'] . '/events/view/' . $event['Event']['uuid'], [], $this->request);
if ($exists->code == '404') {
return false;
}
if ($exists->code == '200') {
return true;
}
throw new Exception("Not possible to check if event exists or not. Unexpected HTTP code $exists->code");
}
/**
* @param array $event
* @param array $sightingUuids
* @return array Sighting UUIDs that exists on remote side
* @throws HttpClientJsonException
*/
public function filterSightingUuidsForPush(array $event, array $sightingUuids)
{
if (!$this->isSupported(self::FEATURE_FILTER_SIGHTINGS)) {
return [];
}
$response = $this->post('/sightings/filterSightingUuidsForPush/' . $event['Event']['uuid'], $sightingUuids);
if ($response->isOk()) {
return $response->json();
}
throw new Exception("Not possible to filter sighting UUIDs. Unexpected HTTP code $response->code");
}
/**
* @param array $sightings
* @param string $eventUuid
* @throws Exception
*/
public function uploadSightings(array $sightings, $eventUuid)
{
foreach ($sightings as &$sighting) {
if (!isset($sighting['org_id'])) {
$sighting['org_id'] = '0';
}
}
$logMessage = "Pushing Sightings for Event #{$eventUuid} to Server #{$this->server['Server']['id']}";
$response = $this->post('/sightings/bulkSaveSightings/' . $eventUuid, $sightings, $logMessage);
if (!$response->isOk()) {
throw new Exception("HTTP error {$response->code}: $response->body");
}
}
/**
* @return array
* @throws HttpClientJsonException
*/
public function info()
{
if ($this->info) {
return $this->info;
}
$response = $this->socket->get($this->server['Server']['url'] . '/servers/getVersion', [], $this->request);
if (!$response->isOk()) {
throw new Exception("Invalid response when fetching server version. Error {$response->code}: $response->body");
}
$info = $response->json();
if (!isset($info['version'])) {
throw new Exception("Invalid response when fetching server version: version field missing");
}
$this->info = $info;
return $info;
}
/**
* @param string $url
* @param mixed $data
* @param string|null $logMessage
* @return HttpSocketResponseExtended
* @throws Exception
*/
private function post($url, $data, $logMessage = null)
{
$data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($logMessage && !empty(Configure::read('Security.sync_audit'))) {
$pushLogEntry = sprintf(
"==============================================================\n\n[%s] %s:\n\n%s\n\n",
date("Y-m-d H:i:s"),
$logMessage,
$data
);
file_put_contents(APP . 'files/scripts/tmp/debug_server_' . $this->server['Server']['id'] . '.log', $pushLogEntry, FILE_APPEND);
}
$request = $this->request;
if (strlen($data) > 1024) { // do not compress small body
if ($this->isSupported(self::FEATURE_BR) && function_exists('brotli_compress')) {
$request['header']['Content-Encoding'] = 'br';
$data = brotli_compress($data, 1, BROTLI_TEXT);
} else if ($this->isSupported(self::FEATURE_GZIP) && function_exists('gzencode')) {
$request['header']['Content-Encoding'] = 'gzip';
$data = gzencode($data, 1);
}
}
return $this->socket->post($this->server['Server']['url'] . $url, $data, $request);
}
/**
* @param string $flag
* @return bool
* @throws HttpClientJsonException
*/
private function isSupported($flag)
{
$info = $this->info();
switch ($flag) {
case self::FEATURE_BR:
return isset($info['request_encoding']) && in_array('br', $info['request_encoding'], true);
case self::FEATURE_GZIP:
return isset($info['request_encoding']) && in_array('gzip', $info['request_encoding'], true);
case self::FEATURE_FILTER_SIGHTINGS:
return isset($info['filter_sightings']) && $info['filter_sightings'];
default:
throw new InvalidArgumentException("Invalid flag provided");
}
}
}

View File

@ -90,7 +90,7 @@ class AppModel extends Model
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
69 => false, 70 => false, 71 => true,
69 => false, 70 => false, 71 => true, 72 => true,
);
public $advanced_updates_description = array(
@ -1608,6 +1608,9 @@ class AppModel extends Model
$sqlArray[] = "ALTER TABLE `warninglist_entries` ADD `comment` text DEFAULT NULL;";
$sqlArray[] = "ALTER TABLE `warninglists` ADD `default` tinyint(1) NOT NULL DEFAULT 1, ADD `category` varchar(20) NOT NULL DEFAULT 'false_positive', DROP COLUMN `warninglist_entry_count`";
break;
case 72:
$sqlArray[] = "ALTER TABLE `auth_keys` ADD `read_only` tinyint(1) NOT NULL DEFAULT 0 AFTER `expiration`;";
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;';
@ -3103,12 +3106,12 @@ class AppModel extends Model
* @param $query
* @param array $results
* @return array
* @throws Exception
* @throws InvalidArgumentException
*/
protected function _findColumn($state, $query, $results = array())
{
if ($state === 'before') {
if (count($query['fields']) === 1) {
if (isset($query['fields']) && count($query['fields']) === 1) {
if (strpos($query['fields'][0], '.') === false) {
$query['fields'][0] = $this->alias . '.' . $query['fields'][0];
}
@ -3119,8 +3122,10 @@ class AppModel extends Model
} else {
$query['fields'] = array($query['fields'][0]);
}
} else if (!isset($query['fields'])) {
throw new InvalidArgumentException("This method requires `fields` option defined.");
} else {
throw new Exception("Invalid number of column, expected one, " . count($query['fields']) . " given");
throw new InvalidArgumentException("Invalid number of column, expected one, " . count($query['fields']) . " given");
}
if (!isset($query['recursive'])) {

View File

@ -108,6 +108,24 @@ class AuthKey extends AppModel
return true;
}
/**
* @param array $user
* @param int $authKeyId
* @return array
*/
public function updateUserData(array $user, $authKeyId)
{
$authKey = $this->find('first', [
'conditions' => ['id' => $authKeyId, 'user_id' => $user['id']],
'fields' => ['id', 'expiration', 'allowed_ips', 'read_only'],
'recursive' => -1,
]);
if (empty($authKey)) {
throw new RuntimeException("Auth key with ID $authKeyId doesn't exist anymore.");
}
return $this->setUserData($user, $authKey);
}
/**
* @param string $authkey
* @return array|false
@ -118,7 +136,7 @@ class AuthKey extends AppModel
$end = substr($authkey, -4);
$possibleAuthkeys = $this->find('all', [
'recursive' => -1,
'fields' => ['id', 'authkey', 'user_id', 'expiration', 'allowed_ips'],
'fields' => ['id', 'authkey', 'user_id', 'expiration', 'allowed_ips', 'read_only'],
'conditions' => [
'OR' => [
'expiration >' => time(),
@ -133,9 +151,7 @@ class AuthKey extends AppModel
if ($passwordHasher->check($authkey, $possibleAuthkey['AuthKey']['authkey'])) {
$user = $this->User->getAuthUser($possibleAuthkey['AuthKey']['user_id']);
if ($user) {
$user['authkey_id'] = $possibleAuthkey['AuthKey']['id'];
$user['authkey_expiration'] = $possibleAuthkey['AuthKey']['expiration'];
$user['allowed_ips'] = $possibleAuthkey['AuthKey']['allowed_ips'];
$user = $this->setUserData($user, $possibleAuthkey);
}
return $user;
}
@ -143,6 +159,30 @@ class AuthKey extends AppModel
return false;
}
/**
* @param array $user
* @param array $authkey
* @return array
*/
private function setUserData(array $user, array $authkey)
{
$user['authkey_id'] = $authkey['AuthKey']['id'];
$user['authkey_expiration'] = $authkey['AuthKey']['expiration'];
$user['allowed_ips'] = $authkey['AuthKey']['allowed_ips'];
$user['authkey_read_only'] = (bool)$authkey['AuthKey']['read_only'];
if ($authkey['AuthKey']['read_only']) {
// Disable all permissions, keep just `perm_auth` unchanged
foreach ($user['Role'] as $key => &$value) {
if (substr($key, 0, 5) === 'perm_' && $key !== 'perm_auth') {
$value = 0;
}
}
}
return $user;
}
/**
* @param int $userId
* @param int|null $keyId
@ -264,7 +304,7 @@ class AuthKey extends AppModel
/**
* When key is modified, update `date_modified` for user that was assigned to that key, so session data
* will be realoaded.
* will be reloaded.
* @see AppController::_refreshAuth
*/
public function afterSave($created, $options = array())

View File

@ -9,6 +9,8 @@ App::uses('SendEmailTemplate', 'Tools');
/**
* @property User $User
* @property Attribute $Attribute
* @property MispObject $Object
* @property EventReport $EventReport
* @property ShadowAttribute $ShadowAttribute
* @property EventTag $EventTag
* @property SharingGroup $SharingGroup
@ -914,89 +916,50 @@ class Event extends AppModel
return $data;
}
private function __resolveErrorCode($code, &$event, &$server)
{
$error = false;
switch ($code) {
case 403:
return 'The distribution level of this event blocks it from being pushed.';
case 405:
$error = 'The sync user on the remote instance does not have the required privileges to handle this event.';
break;
}
if ($error) {
$newTextBody = 'Uploading Event (' . $event['Event']['id'] . ') to Server (' . $server['Server']['id'] . ')';
$this->__logUploadResult($server, $event, $newTextBody);
}
return $error;
}
private function __executeRestfulEventToServer($event, $server, $resourceId, &$newLocation, &$newTextBody, $HttpSocket, $scope)
{
$result = $this->restfulEventToServer($event, $server, $resourceId, $newLocation, $newTextBody, $HttpSocket, $scope);
if (is_numeric($result)) {
$error = $this->__resolveErrorCode($result, $event, $server);
if ($error) {
return $error . ' Error code: ' . $result;
}
}
return true;
}
public function uploadSightingsToServer($sightings, $server, $event_uuid, $HttpSocket = null)
{
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
$request = $this->setupSyncRequest($server);
$uri = $server['Server']['url'] . '/sightings/bulkSaveSightings/' . $event_uuid;
foreach ($sightings as &$sighting) {
if (!isset($sighting['org_id'])) {
$sighting['org_id'] = '0';
}
}
$data = json_encode($sightings);
if (!empty(Configure::read('Security.sync_audit'))) {
$pushLogEntry = sprintf(
"==============================================================\n\n[%s] Pushing Sightings for Event #%s to Server #%d:\n\n%s\n\n",
date("Y-m-d H:i:s"),
$event_uuid,
$server['Server']['id'],
$data
);
file_put_contents(APP . 'files/scripts/tmp/debug_server_' . $server['Server']['id'] . '.log', $pushLogEntry, FILE_APPEND);
}
$response = $HttpSocket->post($uri, $data, $request);
return $this->__handleRestfulEventToServerResponse($response, $newLocation, $newTextBody);
}
public function uploadEventToServer($event, $server, $HttpSocket = null, $scope = 'events')
/**
* @param array $event
* @param array $server
* @param HttpSocket $HttpSocket
* @return false|string
*/
public function uploadEventToServer(array $event, array $server, HttpSocket $HttpSocket)
{
$this->Server = ClassRegistry::init('Server');
$push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket);
if ($scope === 'events' && empty($push['canPush'])) {
return 'The remote user is not a sync user - the upload of the event has been blocked.';
} elseif ($scope === 'sightings' && empty($push['canPush']) && empty($push['canSight'])) {
return 'The remote user is not a sightings user - the upload of the sightings has been blocked.';
if (empty($this->Server->eventFilterPushableServers($event, [$server]))) {
return 'The server rules blocks it from being pushed.';
}
if (!$this->checkDistributionForPush($event, $server, 'Event')) {
return 'The distribution level of this event blocks it from being pushed.';
}
$push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket);
if (empty($push['canPush'])) {
return 'The remote user is not a sync user - the upload of the event has been blocked.';
}
if (!empty($server['Server']['unpublish_event'])) {
$event['Event']['published'] = 0;
}
$updated = null;
$newLocation = $newTextBody = '';
$result = $this->__executeRestfulEventToServer($event, $server, null, $newLocation, $newTextBody, $HttpSocket, $scope);
if ($result !== true) {
return $result;
}
if (strlen($newLocation)) { // HTTP/1.1 302 Found and Location: http://<newLocation>
$result = $this->__executeRestfulEventToServer($event, $server, $newLocation, $newLocation, $newTextBody, $HttpSocket, $scope);
if ($result !== true) {
return $result;
}
}
try {
$this->jsonDecode($newTextBody);
$this->restfulEventToServer($event, $server, $HttpSocket);
} catch (Exception $e) {
$this->logException("Invalid JSON returned when pushing to remote server {$server['Server']['id']}", $e);
return $this->__logUploadResult($server, $event, $newTextBody);
$errorMessage = $e->getMessage();
if ($e instanceof HttpException && $e->getCode() == 403) {
// Do not log errors that are expected
$errorJson = json_decode($errorMessage, true);
if (isset($errorJson['errors'])) {
$errorMessage = $errorJson['errors'];
if ($errorMessage === 'Event could not be saved: Event in the request not newer than the local copy.') {
return $errorMessage;
}
}
}
$this->logException("Could not push event '{$event['Event']['uuid']}' to remote server #{$server['Server']['id']}", $e);
$this->__logUploadResult($server, $event, $errorMessage);
return false;
}
return 'Success';
}
@ -1004,7 +967,7 @@ class Event extends AppModel
private function __prepareForPushToServer($event, $server)
{
if ($event['Event']['distribution'] == 4) {
if (empty($event['SharingGroup']['SharingGroup']['roaming']) && empty($server['Server']['internal'])) {
if (empty($event['SharingGroup']['roaming']) && empty($server['Server']['internal'])) {
$serverFound = false;
if (!empty($event['SharingGroup']['SharingGroupServer'])) {
foreach ($event['SharingGroup']['SharingGroupServer'] as $sgs) {
@ -1052,48 +1015,35 @@ class Event extends AppModel
return '';
}
private function __handleRestfulEventToServerResponse($response, &$newLocation, &$newTextBody)
{
switch ($response->code) {
case '200': // 200 (OK) + entity-action-result
$newTextBody = $response->body();
return true;
case '302': // Found
$newLocation = $response->headers['Location'];
$newTextBody = $response->body();
return true;
case '404': // Not Found
$newLocation = $response->headers['Location'];
$newTextBody = $response->body();
return 404;
case '405':
$newTextBody = $response->body();
return 405;
case '403': // Not authorised
$newTextBody = $response->body();
return 403;
}
}
// Uploads the event and the associated Attributes to another Server
public function restfulEventToServer($event, $server, $urlPath, &$newLocation, &$newTextBody, $HttpSocket = null, $scope)
/**
* Uploads the event and the associated Attributes to another Server.
* @param array $event
* @param array $server
* @param HttpSocket $HttpSocket
* @return array
* @throws JsonException
*/
private function restfulEventToServer(array $event, array $server, HttpSocket $HttpSocket)
{
// TODO: Replace by __updateEventForSync method in future
$event = $this->__prepareForPushToServer($event, $server);
if (is_numeric($event)) {
return $event;
throw new Exception("This should never happen.");
}
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
$request = $this->setupSyncRequest($server);
if ($scope === 'sightings') {
$scope .= '/bulkSaveSightings';
$urlPath = $event['Event']['uuid'];
}
$url = $server['Server']['url'];
$uri = $url . '/' . $scope . $this->__getLastUrlPathComponent($urlPath);
if ($scope === 'event') {
// After creating or editing event, it is not necessary to fetch full event
$uri .= '/metadata:1';
$serverUrl = $server['Server']['url'];
$exists = false;
try {
// Check if event exists on remote server to use proper endpoint
$response = $HttpSocket->head("$serverUrl/events/view/{$event['Event']['uuid']}", [], $request);
if ($response->code == '200') {
$exists = true;
}
} catch (Exception $e) {
$this->logException("Could not check if event {$event['Event']['uuid']} exists on remote server {$server['Server']['id']}", $e, LOG_NOTICE);
}
$data = json_encode($event);
if (!empty(Configure::read('Security.sync_audit'))) {
$pushLogEntry = sprintf(
@ -1105,8 +1055,33 @@ class Event extends AppModel
);
file_put_contents(APP . 'files/scripts/tmp/debug_server_' . $server['Server']['id'] . '.log', $pushLogEntry, FILE_APPEND);
}
$response = $HttpSocket->post($uri, $data, $request);
return $this->__handleRestfulEventToServerResponse($response, $newLocation, $newTextBody);
if ($exists) {
$url = "$serverUrl/events/edit/{$event['Event']['uuid']}/metadata:1";
} else {
$url = "$serverUrl/events/add/metadata:1";
}
$response = $HttpSocket->post($url, $data, $request);
// Maybe the check if event exists was not correct, try to create a new event
if ($exists && $response->code == '404') {
$url = "$serverUrl/events/add/metadata:1";
$response = $HttpSocket->post($url, $data, $request);
}
// There is bug in MISP API, that returns response code 404 with Location if event already exists
else if (!$exists && $response->code == '404' && $response->getHeader('Location')) {
$lastPart = $this->__getLastUrlPathComponent($response->getHeader('Location'));
$url = "$serverUrl/events/edit/$lastPart/metadata:1";
$response = $HttpSocket->post($url, $data, $request);
}
if (!$response->isOk()) {
throw new HttpException($response->body, $response->code);
}
return $this->jsonDecode($response->body);
}
private function __rearrangeEventStructureForSync($event)
@ -3348,7 +3323,7 @@ class Event extends AppModel
{
// fetch the event as user that requested more information. So if creators will reply to that email, no data
// that requestor could not access would be leaked.
$event = $this->fetchEvent($this->User->rearrangeToAuthForm($user), [
$event = $this->fetchEvent($user, [
'eventid' => $id,
'includeAllTags' => true,
'includeEventCorrelations' => true,
@ -3394,8 +3369,10 @@ class Event extends AppModel
$subject = "[" . Configure::read('MISP.org') . " MISP] Need info about event $id - " . strtoupper($tplColorString);
$result = true;
foreach ($orgMembers as $eventReporter) {
$body = $this->prepareContactAlertEmail($user, $eventReporter, $message, $event);
$result = $this->User->sendEmail($eventReporter, $body, false, $subject, $user) && $result;
$requestor = !empty($user['User']) ? $user : ['User' => $user];
$reporterForEmailTemplate = !empty($eventReporter['User']) ? $eventReporter['User'] : $eventReporter;
$body = $this->prepareContactAlertEmail($requestor, $reporterForEmailTemplate, $message, $event);
$result = $this->User->sendEmail($eventReporter, $body, false, $subject, ['User' => $user]) && $result;
}
return $result;
}
@ -3413,7 +3390,7 @@ class Event extends AppModel
$template->set('event', $event);
$template->set('requestor', $user);
$template->set('message', $message);
$template->set('user', $this->User->rearrangeToAuthForm($eventReporter));
$template->set('user', $eventReporter);
$template->set('baseurl', $this->__getAnnounceBaseurl());
$template->set('distributionLevels', $this->distributionLevels);
$template->set('analysisLevels', $this->analysisLevels);
@ -3949,9 +3926,7 @@ class Event extends AppModel
// zeroq: check if sightings are attached and add to event
if (isset($data['Sighting']) && !empty($data['Sighting'])) {
$this->Sighting = ClassRegistry::init('Sighting');
foreach ($data['Sighting'] as $s) {
$result = $this->Sighting->saveSightings($s['attribute_uuid'], false, $s['date_sighting'], $user, $s['type'], $s['source'], $s['uuid']);
}
$this->Sighting->captureSightings($data['Sighting'], null, $this->id, $user);
}
if ($fromXml) {
$created_id = $this->id;
@ -4212,9 +4187,7 @@ class Event extends AppModel
// zeroq: if sightings then attach to event
if (isset($data['Sighting']) && !empty($data['Sighting'])) {
$this->Sighting = ClassRegistry::init('Sighting');
foreach ($data['Sighting'] as $s) {
$result = $this->Sighting->saveSightings($s['attribute_uuid'], false, $s['date_sighting'], $user, $s['type'], $s['source'], $s['uuid']);
}
$this->Sighting->captureSightings($data['Sighting'], null, $this->id, $user);
}
// if published -> do the actual publishing
if ($changed && (!empty($data['Event']['published']) && 1 == $data['Event']['published'])) {
@ -4395,8 +4368,103 @@ class Event extends AppModel
return true;
}
// Uploads this specific event or sightings to all remote servers
public function uploadEventToServersRouter($id, $passAlong = null, $scope = 'events')
/**
* New variant of uploadEventToServersRouter (since 2.4.137) for pushing sightings.
* @param array $event with event tags and whole sharing group
* @param null|int $passAlong Server ID that should be skipped from uploading.
* @param array $sightingsUuidsToPush
* @return array|bool
* @throws Exception
*/
private function uploadEventSightingsToServersRouter(array $event, $passAlong, array $sightingsUuidsToPush)
{
$this->Server = ClassRegistry::init('Server');
$conditions = ['Server.push_sightings' => 1];
if ($passAlong) {
$conditions[] = array('Server.id !=' => $passAlong);
}
$servers = $this->Server->find('all', [
'conditions' => $conditions,
'contain' => ['RemoteOrg'], // remote org required for checkDistributionForPush
'recursive' => -1,
'order' => ['Server.priority ASC', 'Server.id ASC'],
]);
// TODO: This are new conditions, that was not used in old code
// Filter out servers that do not match server conditions for event push
$servers = $this->Server->eventFilterPushableServers($event, $servers);
// Filter out servers that do not match event distribution conditions for event push
$servers = array_filter($servers, function (array $server) use ($event) {
return $this->checkDistributionForPush($event, $server);
});
if (empty($servers)) {
return true;
}
$failedServers = [];
foreach ($servers as $server) {
try {
$this->pushSightingsToServer($server, $event, $sightingsUuidsToPush);
} catch (Exception $e) {
$this->logException("Uploading sightings to server {$server['Server']['id']} failed.", $e);
$failedServers[] = $server['Server']['url'];
}
}
if (!empty($failedServers)) {
return $failedServers;
}
return true;
}
/**
* @param array $server
* @param array $event
* @param array $sightingsUuidsToPush
* @throws HttpClientJsonException
* @throws JsonException
* @throws Exception
*/
private function pushSightingsToServer(array $server, array $event, array $sightingsUuidsToPush = [])
{
App::uses('ServerSyncTool', 'Tools');
if (!isset($this->Sighting)) {
$this->Sighting = ClassRegistry::init('Sighting');
}
$serverSync = new ServerSyncTool($server, $this->setupSyncRequest($server));
try {
if ($serverSync->eventExists($event) === false) {
return; // skip if event not exists on remote server
}
} catch (Exception $e) {}
$fakeSyncUser = [
'org_id' => $server['Server']['remote_org_id'],
'Role' => [
'perm_site_admin' => 0,
],
];
// Process sightings in batch to keep memory requirements low
foreach ($this->Sighting->fetchUuidsForEventToPush($event, $fakeSyncUser, $sightingsUuidsToPush) as $batch) {
// Filter out sightings that already exists on remote server
$existingSightings = $serverSync->filterSightingUuidsForPush($event, $batch);
$newSightings = array_diff($batch, $existingSightings);
if (empty($newSightings)) {
continue;
}
$conditions = ['Sighting.uuid' => $newSightings];
$sightings = $this->Sighting->attachToEvent($event, $fakeSyncUser, null, $conditions, true);
$serverSync->uploadSightings($sightings, $event['Event']['uuid']);
}
}
/**
* @param int $id
* @param int|null $passAlong
* @return array|bool
* @throws Exception
*/
public function uploadEventToServersRouter($id, $passAlong = null)
{
$eventOrgcId = $this->find('first', array(
'conditions' => array('Event.id' => $id),
@ -4422,11 +4490,8 @@ class Event extends AppModel
$event['Event']['locked'] = 1;
// get a list of the servers
$this->Server = ClassRegistry::init('Server');
if ($scope === 'sightings') {
$conditions = array('push_sightings' => 1);
} else {
$conditions = array('push' => 1);
}
$conditions = array('push' => 1);
if ($passAlong) {
$conditions[] = array('Server.id !=' => $passAlong);
}
@ -4441,16 +4506,14 @@ class Event extends AppModel
$uploaded = true;
$failedServers = array();
App::uses('SyncTool', 'Tools');
foreach ($servers as &$server) {
$syncTool = new SyncTool();
foreach ($servers as $server) {
if (
($scope === 'events' &&
(!isset($server['Server']['internal']) || !$server['Server']['internal']) && $event['Event']['distribution'] < 2) ||
($scope === 'sightings' &&
(!isset($server['Server']['push_sightings']) || !$server['Server']['push_sightings']))
(!isset($server['Server']['internal']) || !$server['Server']['internal']) && $event['Event']['distribution'] < 2
) {
continue;
}
$syncTool = new SyncTool();
$HttpSocket = $syncTool->setupHttpSocket($server);
// Skip servers where the event has come from.
if (($passAlong != $server['Server']['id'])) {
@ -4466,7 +4529,8 @@ class Event extends AppModel
'includeAttachments' => true,
'includeAllTags' => true,
'deleted' => array(0,1),
'excludeGalaxy' => 1
'excludeGalaxy' => 1,
'noSightings' => true, // sightings are pushed separately
));
if (!empty($server['Server']['internal'])) {
$params['excludeLocalTags'] = 0;
@ -4474,34 +4538,25 @@ class Event extends AppModel
$event = $this->fetchEvent($elevatedUser, $params);
$event = $event[0];
$event['Event']['locked'] = 1;
// attach sightings if needed
if ($scope === 'sightings') {
$this->Sighting = ClassRegistry::init('Sighting');
$fakeSyncUser = array(
'org_id' => $server['Server']['remote_org_id'],
'Role' => array(
'perm_site_admin' => 0
)
);
$sightings = $this->Sighting->attachToEvent($event, $fakeSyncUser, null, false, true);
if (!empty($sightings)) {
$thisUploaded = $this->uploadSightingsToServer($sightings, $server, $event['Event']['uuid'], $HttpSocket);
} else {
$thisUploaded = true;
}
} else {
$fakeSyncUser = array(
'org_id' => $server['Server']['remote_org_id'],
'Role' => array(
'perm_site_admin' => 0
)
);
$this->Server->syncGalaxyClusters($HttpSocket, $server, $fakeSyncUser, $technique=$event['Event']['id'], $event=$event);
$thisUploaded = $this->uploadEventToServer($event, $server, $HttpSocket, $scope);
if (isset($this->data['ShadowAttribute'])) {
$this->Server->syncProposals($HttpSocket, $server, null, $id, $this);
$fakeSyncUser = array(
'org_id' => $server['Server']['remote_org_id'],
'Role' => array(
'perm_site_admin' => 0
)
);
$this->Server->syncGalaxyClusters($HttpSocket, $server, $fakeSyncUser, $technique=$event['Event']['id'], $event=$event);
$thisUploaded = $this->uploadEventToServer($event, $server, $HttpSocket);
if ($thisUploaded === 'Success') {
try {
$this->pushSightingsToServer($server, $event); // push sighting by method that check for duplicates
} catch (Exception $e) {
$this->logException("Uploading sightings to server {$server['Server']['id']} failed.", $e);
}
}
if (isset($this->data['ShadowAttribute'])) {
$this->Server->syncProposals($HttpSocket, $server, null, $id, $this);
}
if (!$thisUploaded) {
$uploaded = !$uploaded ? $uploaded : $thisUploaded;
$failedServers[] = $server['Server']['url'];
@ -4518,48 +4573,81 @@ class Event extends AppModel
}
}
public function publishRouter($id, $passAlong = null, $user, $scope = 'events')
/**
* @param int $id Event ID
* @param array $user
* @param int|null $passAlong Server ID that should be skipped when pushing sightings.
* @param array $sightingUuids Push just sightings with these UUIDs
* @return array|bool
* @throws Exception
*/
public function publishSightingsRouter($id, array $user, $passAlong = null, array $sightingUuids = [])
{
if (Configure::read('MISP.background_jobs')) {
$job_type = 'publish_' . $scope;
$function = 'publish';
$message = 'Publishing.';
if ($scope === 'sightings') {
$message = 'Publishing sightings.';
$function = 'publish_sightings';
}
$job = ClassRegistry::init('Job');
$message = empty($sightingUuids) ? __('Publishing sightings.') : __('Publishing %s sightings.', count($sightingUuids));
$job->create();
$data = array(
$job->save([
'worker' => 'prio',
'job_type' => 'publish_event',
'job_input' => 'Event ID: ' . $id,
'status' => 0,
'retries' => 0,
'org_id' => $user['org_id'],
'org' => $user['Organisation']['name'],
'message' => $message
'message' => $message,
]);
$command = ['publish_sightings', $id, $passAlong, $job->id, $user['id']];
if (!empty($sightingUuids)) {
$randomFileName = $this->generateRandomFileName() . '.json';
App::uses('File', 'Utility');
$tempFile = new File(APP . 'tmp/cache/ingest' . DS . $randomFileName, true, 0644);
$writeResult = $tempFile->write(json_encode($sightingUuids));
if (!$writeResult) {
throw new Exception("Could not write file content");
}
$command[] = $randomFileName;
}
$processId = CakeResque::enqueue('prio', 'EventShell', $command, true);
$job->saveField('process_id', $processId);
return $processId;
}
return $this->publish_sightings($id, $passAlong, $sightingUuids);
}
public function publishRouter($id, $passAlong = null, $user)
{
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$data = array(
'worker' => 'prio',
'job_type' => 'publish_event',
'job_input' => 'Event ID: ' . $id,
'status' => 0,
'retries' => 0,
'org_id' => $user['org_id'],
'org' => $user['Organisation']['name'],
'message' => 'Publishing.'
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'prio',
'EventShell',
array($function, $id, $passAlong, $jobId, $user['id']),
array('publish', $id, $passAlong, $jobId, $user['id']),
true
);
$job->saveField('process_id', $process_id);
return $process_id;
} elseif ($scope === 'sightings') {
$result = $this->publish_sightings($id, $passAlong);
return $result;
} else {
$result = $this->publish($id, $passAlong);
return $result;
}
}
public function publish_sightings($id, $passAlong = null, $jobId = null)
public function publish_sightings($id, $passAlong = null, array $sightingsUuidsToPush = [])
{
if (is_numeric($id)) {
$condition = array('Event.id' => $id);
@ -4568,24 +4656,22 @@ class Event extends AppModel
}
$event = $this->find('first', array(
'recursive' => -1,
'conditions' => $condition
'conditions' => $condition,
'contain' => ['EventTag', 'SharingGroup' => ['SharingGroupServer', 'SharingGroupOrg' => ['Organisation']]],
));
if (empty($event)) {
return false;
}
if ($jobId) {
$this->Behaviors->unload('SysLogLogable.SysLogLogable');
} else {
// update the DB to set the sightings timestamp
// for background jobs, this should be done already
$fieldList = array('id', 'info', 'sighting_timestamp');
$event['Event']['sighting_timestamp'] = time();
$event['Event']['skip_zmq'] = 1;
$event['Event']['skip_kafka'] = 1;
$this->save($event, array('fieldList' => $fieldList));
}
$uploaded = $this->uploadEventToServersRouter($id, $passAlong, 'sightings');
return $uploaded;
// update the DB to set the sightings timestamp
// for background jobs, this should be done already
$fieldList = array('id', 'info', 'sighting_timestamp');
$event['Event']['sighting_timestamp'] = time();
$event['Event']['skip_zmq'] = 1;
$event['Event']['skip_kafka'] = 1;
$this->save($event, array('fieldList' => $fieldList));
return $this->uploadEventSightingsToServersRouter($event, $passAlong, $sightingsUuidsToPush);
}
// Performs all the actions required to publish an event
@ -4687,7 +4773,7 @@ class Event extends AppModel
$job->saveField('process_id', $process_id);
return true;
} else {
return $this->sendContactEmail($id, $message, $creator_only, array('User' => $user));
return $this->sendContactEmail($id, $message, $creator_only, $user);
}
}
@ -6302,9 +6388,11 @@ class Event extends AppModel
}
}
}
if (!empty($resolved_data['Attribute'])) {
$total_attributes = count($resolved_data['Attribute']);
foreach ($resolved_data['Attribute'] as $a => $attribute) {
$processedAttributes = 0;
foreach ($resolved_data['Attribute'] as $attribute) {
$this->Attribute->create();
if (empty($attribute['comment'])) {
$attribute['comment'] = $default_comment;
@ -6338,19 +6426,21 @@ class Event extends AppModel
}
}
if ($jobId) {
$current = ($a + 1);
$this->Job->saveField('message', 'Attribute ' . $current . '/' . $total_attributes);
$this->Job->saveField('progress', ($current * 100 / $items_count));
$processedAttributes++;
$this->Job->saveField('message', 'Attribute ' . $processedAttributes . '/' . $total_attributes);
$this->Job->saveField('progress', ($processedAttributes * 100 / $items_count));
}
}
} else {
$total_attributes = 0;
}
if (!empty($resolved_data['Object'])) {
$initial_object_id = isset($resolved_data['initialObject']) ? $resolved_data['initialObject']['Object']['id'] : "0";
$total_objects = count($resolved_data['Object']);
$processedObjects = 0;
$references = array();
foreach ($resolved_data['Object'] as $o => $object) {
foreach ($resolved_data['Object'] as $object) {
if (isset($object['meta_category']) && !isset($object['meta-category'])) {
$object['meta-category'] = $object['meta_category'];
unset($object['meta_category']);
@ -6393,7 +6483,7 @@ class Event extends AppModel
}
if (!empty($object['ObjectReference'])) {
foreach ($object['ObjectReference'] as $object_reference) {
array_push($references, array('objectId' => $initial_object_id, 'reference' => $object_reference));
$references[] = array('objectId' => $initial_object_id, 'reference' => $object_reference);
}
}
$saved_objects++;
@ -6444,24 +6534,25 @@ class Event extends AppModel
}
}
if (!empty($object['ObjectReference'])) {
foreach($object['ObjectReference'] as $object_reference) {
array_push($references, array('objectId' => $object_id, 'reference' => $object_reference));
foreach ($object['ObjectReference'] as $object_reference) {
$references[] = array('objectId' => $object_id, 'reference' => $object_reference);
}
}
}
if ($jobId) {
$current = ($o + 1);
$this->Job->saveField('message', 'Object ' . $current . '/' . $total_objects);
$this->Job->saveField('progress', (($current + $total_attributes) * 100 / $items_count));
$processedObjects++;
$this->Job->saveField('message', 'Object ' . $processedObjects . '/' . $total_objects);
$this->Job->saveField('progress', (($processedObjects + $total_attributes) * 100 / $items_count));
}
}
if (!empty($references)) {
$reference_errors = array();
foreach($references as $reference) {
foreach ($references as $reference) {
$object_id = $reference['objectId'];
$reference = $reference['reference'];
if (in_array($reference['object_uuid'], $failed) || in_array($reference['referenced_uuid'], $failed)) {
continue;
if (in_array($reference['object_uuid'], $failed)) {
continue; // if object that contains reference couldn't be added, skip
}
if (isset($recovered_uuids[$reference['object_uuid']])) {
$reference['object_uuid'] = $recovered_uuids[$reference['object_uuid']];
@ -6469,22 +6560,25 @@ class Event extends AppModel
if (isset($recovered_uuids[$reference['referenced_uuid']])) {
$reference['referenced_uuid'] = $recovered_uuids[$reference['referenced_uuid']];
}
$current_reference = $this->Object->ObjectReference->find('all', array(
'conditions' => array('ObjectReference.object_id' => $object_id,
'ObjectReference.referenced_uuid' => $reference['referenced_uuid'],
'ObjectReference.relationship_type' => $reference['relationship_type'],
'ObjectReference.event_id' => $id, 'ObjectReference.deleted' => 0),
$current_reference = $this->Object->ObjectReference->find('first', array(
'conditions' => [
'ObjectReference.object_id' => $object_id,
'ObjectReference.referenced_uuid' => $reference['referenced_uuid'],
'ObjectReference.relationship_type' => $reference['relationship_type'],
'ObjectReference.event_id' => $id,
'ObjectReference.deleted' => 0,
],
'recursive' => -1,
'fields' => ('ObjectReference.uuid')
'fields' => ['ObjectReference.id'],
));
if (!empty($current_reference)) {
continue;
continue; // Reference already exists, skip.
}
list($referenced_id, $referenced_uuid, $referenced_type) = $this->Object->ObjectReference->getReferencedInfo(
$reference['referenced_uuid'],
array('Event' => array('id' => $id)),
false,
$user
$reference['referenced_uuid'],
array('Event' => array('id' => $id)),
false,
$user
);
if (!$referenced_id && !$referenced_uuid && !$referenced_type) {
continue;
@ -6505,6 +6599,7 @@ class Event extends AppModel
}
}
}
if (!empty($resolved_data['EventReport'])) {
$total_reports = count($resolved_data['EventReport']);
foreach ($resolved_data['EventReport'] as $i => $report) {
@ -6522,9 +6617,8 @@ class Event extends AppModel
$this->Job->saveField('progress', ($current * 100 / $items_count));
}
}
} else {
$total_reports = 0;
}
if ($saved_attributes > 0 || $saved_objects > 0 || $saved_reports > 0) {
$event = $this->find('first', array(
'conditions' => array('Event.id' => $id),
@ -6533,8 +6627,7 @@ class Event extends AppModel
if ($event['Event']['published'] == 1) {
$event['Event']['published'] = 0;
}
$date = new DateTime();
$event['Event']['timestamp'] = $date->getTimestamp();
$event['Event']['timestamp'] = time();
$this->save($event);
}
if ($event_level) {

View File

@ -2064,7 +2064,7 @@ class GalaxyCluster extends AppModel
return $cluster;
}
public function attachClusterToRelations($user, $cluster)
public function attachClusterToRelations($user, $cluster, $both=true)
{
if (!empty($cluster['GalaxyCluster']['GalaxyClusterRelation'])) {
foreach ($cluster['GalaxyCluster']['GalaxyClusterRelation'] as $k => $relation) {
@ -2075,12 +2075,14 @@ class GalaxyCluster extends AppModel
}
}
}
if (!empty($cluster['GalaxyCluster']['TargetingClusterRelation'])) {
foreach ($cluster['GalaxyCluster']['TargetingClusterRelation'] as $k => $relation) {
$conditions = array('conditions' => array('GalaxyCluster.uuid' => $relation['galaxy_cluster_uuid']));
$relatedCluster = $this->fetchGalaxyClusters($user, $conditions, false);
if (!empty($relatedCluster)) {
$cluster['GalaxyCluster']['TargetingClusterRelation'][$k]['GalaxyCluster'] = $relatedCluster[0]['GalaxyCluster'];
if ($both) {
if (!empty($cluster['GalaxyCluster']['TargetingClusterRelation'])) {
foreach ($cluster['GalaxyCluster']['TargetingClusterRelation'] as $k => $relation) {
$conditions = array('conditions' => array('GalaxyCluster.uuid' => $relation['galaxy_cluster_uuid']));
$relatedCluster = $this->fetchGalaxyClusters($user, $conditions, false);
if (!empty($relatedCluster)) {
$cluster['GalaxyCluster']['TargetingClusterRelation'][$k]['GalaxyCluster'] = $relatedCluster[0]['GalaxyCluster'];
}
}
}
}

View File

@ -3,19 +3,19 @@ App::uses('AppModel', 'Model');
class Job extends AppModel
{
const STATUS_WAITING = 1;
const STATUS_RUNNING = 2;
const STATUS_FAILED = 3;
const STATUS_COMPLETED = 4;
const STATUS_WAITING = 1,
STATUS_RUNNING = 2,
STATUS_FAILED = 3,
STATUS_COMPLETED = 4;
public $belongsTo = array(
'Org' => array(
'className' => 'Organisation',
'foreignKey' => 'org_id',
'order' => array(),
'fields' => array('id', 'name', 'uuid')
),
);
'Org' => array(
'className' => 'Organisation',
'foreignKey' => 'org_id',
'order' => array(),
'fields' => array('id', 'name', 'uuid')
),
);
public function beforeValidate($options = array())
{

View File

@ -6,6 +6,7 @@ App::uses('TmpFileTool', 'Tools');
* @property Event $Event
* @property SharingGroup $SharingGroup
* @property Attribute $Attribute
* @property ObjectReference $ObjectReference
*/
class MispObject extends AppModel
{
@ -492,7 +493,7 @@ class MispObject extends AppModel
];
}
public function fetchObjectSimple($user, $options = array())
public function fetchObjectSimple(array $user, $options = array())
{
$params = array(
'conditions' => $this->buildConditions($user),

View File

@ -1,7 +1,9 @@
<?php
App::uses('AppModel', 'Model');
/**
* @property MispObject $Object
*/
class ObjectReference extends AppModel
{
public $actsAs = array(
@ -266,7 +268,14 @@ class ObjectReference extends AppModel
return true;
}
public function getReferencedInfo($referencedUuid, $object, $strict = true, $user=[])
/**
* @param string $referencedUuid
* @param array $object
* @param bool $strict When true, throw exception when referenced object not found.
* @param array $user
* @return array|int[]
*/
public function getReferencedInfo($referencedUuid, $object, $strict = true, $user = [])
{
$referenced_type = 1;
$target_object = $this->Object->find('first', array(

View File

@ -5,7 +5,6 @@ App::uses('AppModel', 'Model');
class ObjectRelationship extends AppModel
{
public $actsAs = array(
'AuditLog',
'Containable',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
@ -47,13 +46,13 @@ class ObjectRelationship extends AppModel
$relationsFile = APP . 'files/misp-objects/relationships/definition.json';
if (file_exists($relationsFile)) {
$file = new File($relationsFile);
$relations = json_decode($file->read(), true);
$relations = $this->jsonDecode($file->read());
if (!isset($relations['version'])) {
$relations['version'] = 1;
}
$this->deleteAll(array('version <' => $relations['version']));
foreach ($relations['values'] as $k => $relation) {
$relation['format'] = json_encode($relation['format'], true);
foreach ($relations['values'] as $relation) {
$relation['format'] = json_encode($relation['format']);
$relation['version'] = $relations['version'];
$this->create();
$this->save($relation);

View File

@ -773,9 +773,7 @@ class Server extends AppModel
$uri = $server['Server']['url'] . '/events/index';
$response = $HttpSocket->post($uri, json_encode($filterRules), $request);
if ($response === false) {
throw new Exception("Could not reach '$uri'.");
}
if (!$response->isOk()) {
throw new HttpException("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}", intval($response->code));
}
@ -1006,7 +1004,7 @@ class Server extends AppModel
'fields' => array('Event.id', 'Event.timestamp', 'Event.sighting_timestamp', 'Event.uuid', 'Event.orgc_id'), // array of field names
);
$eventIds = $this->Event->find('all', $findParams);
$eventUUIDsFiltered = $this->getEventIdsForPush($id, $HttpSocket, $eventIds, $user);
$eventUUIDsFiltered = $this->getEventIdsForPush($server, $HttpSocket, $eventIds);
if (!empty($eventUUIDsFiltered)) {
$eventCount = count($eventUUIDsFiltered);
// now process the $eventIds to push each of the events sequentially
@ -1099,11 +1097,8 @@ class Server extends AppModel
return true;
}
public function getEventIdsForPush($id, $HttpSocket, $eventIds, $user)
public function getEventIdsForPush(array $server, HttpSocket $HttpSocket, array $eventIds)
{
$server = $this->read(null, $id);
$this->Event = ClassRegistry::init('Event');
foreach ($eventIds as $k => $event) {
if (empty($this->eventFilterPushableServers($event, array($server)))) {
unset($eventIds[$k]);
@ -1111,17 +1106,14 @@ class Server extends AppModel
}
unset($eventIds[$k]['Event']['id']);
}
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
$request = $this->setupSyncRequest($server);
$data = json_encode($eventIds);
$uri = $server['Server']['url'] . '/events/filterEventIdsForPush';
$response = $HttpSocket->post($uri, $data, $request);
if ($response->code == '200') {
$uuidList = json_decode($response->body());
} else {
return false;
return $this->jsonDecode($response->body());
}
return $uuidList;
return false;
}
/**
@ -1166,7 +1158,7 @@ class Server extends AppModel
return $successes;
}
public function syncSightings($HttpSocket, $server, $user, $eventModel)
public function syncSightings($HttpSocket, array $server, array $user, Event $eventModel)
{
$successes = array();
if (!$server['Server']['push_sightings']) {
@ -1175,20 +1167,28 @@ class Server extends AppModel
$this->Sighting = ClassRegistry::init('Sighting');
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
try {
$eventIds = $this->getEventIdsFromServer($server, true, $HttpSocket, true, 'sightings');
$eventUuids = $this->getEventIdsFromServer($server, true, $HttpSocket, true, 'sightings');
} catch (Exception $e) {
$this->logException("Could not fetch event IDs from server {$server['Server']['name']}", $e);
return $successes;
}
// now process the $eventIds to push each of the events sequentially
// check each event and push sightings when needed
foreach ($eventIds as $k => $eventId) {
$event = $eventModel->fetchEvent($user, $options = array('event_uuid' => $eventId, 'metadata' => true));
foreach ($eventUuids as $eventUuid) {
$event = $eventModel->fetchEvent($user, ['event_uuid' => $eventUuid, 'metadata' => true]);
if (!empty($event)) {
$event = $event[0];
$event['Sighting'] = $this->Sighting->attachToEvent($event, $user, null, false, true);
$result = $eventModel->uploadEventToServer($event, $server, $HttpSocket, 'sightings');
if ($result === 'Success') {
if (empty($this->eventFilterPushableServers($event, [$server]))) {
continue;
}
if (!$eventModel->checkDistributionForPush($event, $server)) {
continue;
}
$sightings = $this->Sighting->attachToEvent($event, $user, null, false, true);
$result = $eventModel->uploadSightingsToServer($sightings, $server, $eventUuid, $HttpSocket);
if ($result) {
$successes[] = 'Sightings for event ' . $event['Event']['id'];
}
}
@ -3245,9 +3245,8 @@ class Server extends AppModel
return array('operational' => $scriptResult['success'], 'plyara' => $scriptResult['plyara']);
}
public function stixDiagnostics(&$diagnostic_errors, &$stixVersion, &$cyboxVersion, &$mixboxVersion, &$maecVersion, &$stix2Version, &$pymispVersion)
public function stixDiagnostics(&$diagnostic_errors)
{
$result = array();
$expected = array('stix' => '>1.2.0.9', 'cybox' => '>2.1.0.21', 'mixbox' => '1.0.3', 'maec' => '>4.1.0.14', 'stix2' => '>2.0', 'pymisp' => '>2.4.120');
// check if the STIX and Cybox libraries are working using the test script stixtest.py
$scriptResult = shell_exec($this->getPythonVersion() . ' ' . APP . 'files' . DS . 'scripts' . DS . 'stixtest.py');
@ -3256,7 +3255,8 @@ class Server extends AppModel
} catch (Exception $e) {
$this->logException('Invalid JSON returned from stixtest', $e);
return array(
'operational' => 0,
'operational' => -1,
'invalid_version' => false,
'stix' => array('expected' => $expected['stix']),
'cybox' => array('expected' => $expected['cybox']),
'mixbox' => array('expected' => $expected['mixbox']),
@ -3269,21 +3269,22 @@ class Server extends AppModel
if ($scriptResult['operational'] == 0) {
$diagnostic_errors++;
}
$result['operational'] = $scriptResult['operational'];
foreach ($expected as $package => $version) {
$result = [
'operational' => $scriptResult['operational'],
'invalid_version' => false,
];
foreach ($expected as $package => $expectedVersion) {
$result[$package]['version'] = $scriptResult[$package];
$result[$package]['expected'] = $expected[$package];
if ($expected[$package][0] === '>') {
$expected[$package] = trim($expected[$package], '>');
$result[$package]['status'] = (version_compare($result[$package]['version'], $expected[$package]) >= 0) ? 1 : 0;
$result[$package]['expected'] = $expectedVersion;
if ($expectedVersion[0] === '>') {
$result[$package]['status'] = version_compare($result[$package]['version'], trim($expectedVersion, '>')) >= 0 ? 1 : 0;
} else {
$result[$package]['status'] = $result[$package]['version'] == $result[$package]['expected'] ? 1 : 0;
$result[$package]['status'] = $result[$package]['version'] === $expectedVersion ? 1 : 0;
}
if ($result[$package]['status'] == 0) {
$diagnostic_errors++;
$result['invalid_version'] = true;
}
${$package . 'Version'}[0] = str_replace('$current', $result[$package]['version'], ${$package . 'Version'}[0]);
${$package . 'Version'}[0] = str_replace('$expected', $result[$package]['expected'], ${$package . 'Version'}[0]);
}
return $result;
}
@ -4250,9 +4251,8 @@ class Server extends AppModel
public function updateJSON()
{
$toUpdate = array('Galaxy', 'Noticelist', 'Warninglist', 'Taxonomy', 'ObjectTemplate');
$results = array();
foreach ($toUpdate as $target) {
foreach (['Galaxy', 'Noticelist', 'Warninglist', 'Taxonomy', 'ObjectTemplate', 'ObjectRelationship'] as $target) {
$this->$target = ClassRegistry::init($target);
$result = $this->$target->update();
$results[$target] = $result === false ? false : true;

View File

@ -127,7 +127,7 @@ class Sighting extends AppModel
/**
* @param array $sightings
* @param int $attributeId
* @param int|null $attributeId
* @param int $eventId
* @param array $user
* @return bool
@ -141,12 +141,34 @@ class Sighting extends AppModel
// Fetch existing organisations in bulk
$existingOrganisations = $this->existingOrganisations($sightings);
if ($attributeId === null) {
// If attribute ID is not set, check real ID and also check if user can access that attribute
$attributes = $this->Attribute->fetchAttributesSimple($user, [
'conditions' => [
'Attribute.uuid' => array_column($sightings, 'attribute_uuid'),
'Attribute.event_id' => $eventId,
],
'fields' => ['Attribute.id', 'Attribute.uuid'],
]);
$attributes = array_column(array_column($attributes, 'Attribute'), 'id', 'uuid');
}
$toSave = [];
foreach ($sightings as $sighting) {
if (isset($existingSighting[$sighting['uuid']])) {
continue; // already exists, skip
}
if ($attributeId === null) {
if (isset($attributes[$sighting['attribute_uuid']])) {
$sighting['attribute_id'] = $attributes[$sighting['attribute_uuid']];
} else {
continue; // attribute not exists ar user don't have permission to access it
}
} else {
$sighting['attribute_id'] = $attributeId;
}
$orgId = 0;
if (isset($sighting['Organisation'])) {
if (isset($existingOrganisations[$sighting['Organisation']['uuid']])) {
@ -161,7 +183,6 @@ class Sighting extends AppModel
$sighting['org_id'] = $orgId;
$sighting['event_id'] = $eventId;
$sighting['attribute_id'] = $attributeId;
$toSave[] = $sighting;
}
@ -529,6 +550,42 @@ class Sighting extends AppModel
/**
* @param array $event
* @param array $user
* @param array $sightingsUuidsToPush
* @return Generator<array>
*/
public function fetchUuidsForEventToPush(array $event, array $user, array $sightingsUuidsToPush = [])
{
$conditions = $this->createConditions($user, $event);
if ($conditions === false) {
return null;
}
$conditions['Sighting.event_id'] = $event['Event']['id'];
if (!empty($sightingsUuidsToPush)) {
$conditions['Sighting.uuid'] = $sightingsUuidsToPush;
}
while (true) {
$uuids = $this->find('column', [
'conditions' => $conditions,
'fields' => ['Sighting.uuid'],
'limit' => 250000,
'order' => ['Sighting.uuid'],
]);
$count = count($uuids);
if ($count === 0) {
return null;
}
yield $uuids;
if ($count !== 250000) {
return;
}
$conditions['Sighting.uuid >'] = $uuids[$count - 1];
}
}
/**
* @param array $event Just 'Event' object is enough
* @param array $user
* @param array|int|null $attribute Attribute model or attribute ID
* @param array|bool $extraConditions
* @param bool $forSync
@ -536,7 +593,12 @@ class Sighting extends AppModel
*/
public function attachToEvent(array $event, array $user, $attribute = null, $extraConditions = false, $forSync = false)
{
$conditions = array('Sighting.event_id' => $event['Event']['id']);
$conditions = $this->createConditions($user, $event);
if ($conditions === false) {
return [];
}
$conditions['Sighting.event_id'] = $event['Event']['id'];
if (isset($attribute['Attribute']['id'])) {
$conditions['Sighting.attribute_id'] = $attribute['Attribute']['id'];
} elseif (is_numeric($attribute)) {
@ -548,19 +610,6 @@ class Sighting extends AppModel
]);
}
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
if (!$ownEvent) {
$sightingsPolicy = $this->sightingsPolicy();
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
$conditions['Sighting.org_id'] = $user['org_id'];
} elseif ($sightingsPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
if (!$this->isReporter($event['Event']['id'], $user['org_id'])) {
return array();
}
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
$conditions['Sighting.org_id'] = [$user['org_id'], Configure::read('MISP.host_org_id')];
}
}
if ($extraConditions !== false) {
$conditions['AND'] = $extraConditions;
}
@ -672,7 +721,7 @@ class Sighting extends AppModel
}
++$sightingsAdded;
if ($publish) {
$this->Event->publishRouter($sighting['event_id'], null, $user, 'sightings');
$this->Event->publishSightingsRouter($sighting['event_id'], $user);
}
}
return $sightingsAdded;
@ -1044,7 +1093,8 @@ class Sighting extends AppModel
}
if ($this->saveMany($toSave)) {
$this->Event->publishRouter($event['Event']['id'], $passAlong, $user, 'sightings');
$existingUuids = array_column($toSave, 'uuid');
$this->Event->publishSightingsRouter($event['Event']['id'], $user, $passAlong, $existingUuids);
return count($toSave);
} else {
return 0;
@ -1195,4 +1245,27 @@ class Sighting extends AppModel
return "to_char(date(to_timestamp(Sighting.date_sighting)), 'YYYY-MM-DD')"; // PostgreSQL
}
}
/**
* @param array $user
* @param array $event
* @return array|false
*/
private function createConditions(array $user, array $event)
{
$sightingsPolicy = $this->sightingsPolicy();
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
if (!$ownEvent) {
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
return ['Sighting.org_id' => $user['org_id']];
} else if ($sightingsPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
if (!$this->isReporter($event['Event']['id'], $user['org_id'])) {
return false;
}
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
return ['Sighting.org_id' => [$user['org_id'], Configure::read('MISP.host_org_id')]];
}
}
return [];
}
}

View File

@ -251,11 +251,13 @@ class Warninglist extends AppModel
public function quickDelete($id)
{
$result = $this->WarninglistEntry->deleteAll(
array('WarninglistEntry.warninglist_id' => $id)
array('WarninglistEntry.warninglist_id' => $id),
false
);
if ($result) {
$result = $this->WarninglistType->deleteAll(
array('WarninglistType.warninglist_id' => $id)
array('WarninglistType.warninglist_id' => $id),
false
);
}
if ($result) {
@ -283,7 +285,9 @@ class Warninglist extends AppModel
}
$id = $this->__updateList($list, $existingWarninglist ? $existingWarninglist['Warninglist']: [], false);
$this->regenerateWarninglistCaches($id);
if (is_int($id)) {
$this->regenerateWarninglistCaches($id);
}
return $id;
}
@ -318,44 +322,44 @@ class Warninglist extends AppModel
}
$db = $this->getDataSource();
$values = array();
$warninglistId = (int)$this->id;
$result = true;
$keys = array_keys($list['list']);
if ($keys === array_keys($keys)) {
foreach ($list['list'] as $value) {
if (!empty($value)) {
$values[] = ['value' => $value, 'warninglist_id' => $warninglistId];
foreach (array_chunk($list['list'], 500) as $chunk) {
$valuesToInsert = [];
foreach ($chunk as $value) {
if (!empty($value)) {
$valuesToInsert[] = ['value' => $value, 'warninglist_id' => $warninglistId];
}
}
}
$result = true;
foreach (array_chunk($values, 500) as $chunk) {
$result = $db->insertMulti('warninglist_entries', ['value', 'warninglist_id'], $chunk);
$result = $db->insertMulti('warninglist_entries', ['value', 'warninglist_id'], $valuesToInsert);
}
} else { // import warninglist with comments
foreach ($list['list'] as $value => $comment) {
if (!empty($value)) {
$values[] = ['value' => $value, 'comment' => $comment, 'warninglist_id' => $warninglistId];
foreach (array_chunk($list['list'], 500, true) as $chunk) {
$valuesToInsert = [];
foreach ($chunk as $value => $comment) {
if (!empty($value)) {
$valuesToInsert[] = ['value' => $value, 'comment' => $comment, 'warninglist_id' => $warninglistId];
}
}
}
$result = true;
foreach (array_chunk($values, 500) as $chunk) {
$result = $db->insertMulti('warninglist_entries', ['value', 'comment', 'warninglist_id'], $chunk);
$result = $db->insertMulti('warninglist_entries', ['value', 'comment', 'warninglist_id'], $valuesToInsert);
}
}
if (!$result) {
return 'Could not insert values.';
}
if (!empty($list['matching_attributes'])) {
$values = array();
foreach ($list['matching_attributes'] as $type) {
$values[] = array('type' => $type, 'warninglist_id' => $warninglistId);
}
$this->WarninglistType->saveMany($values);
} else {
$this->WarninglistType->create();
$this->WarninglistType->save(array('WarninglistType' => array('type' => 'ALL', 'warninglist_id' => $warninglistId)));
if (empty($list['matching_attributes'])) {
$list['matching_attributes'] = ['ALL'];
}
$values = array();
foreach ($list['matching_attributes'] as $type) {
$values[] = array('type' => $type, 'warninglist_id' => $warninglistId);
}
$this->WarninglistType->saveMany($values);
return $warninglistId;
}

View File

@ -8,7 +8,7 @@
</div>
</div>
<?php
echo $this->Html->script('moment-with-locales');
echo $this->Html->script('moment.min');
echo $this->Html->script('doT');
echo $this->Html->script('extendext');
echo $this->Html->css('query-builder.default');

View File

@ -29,6 +29,11 @@ echo $this->element('genericElements/Form/genericForm', [
'class' => 'datepicker span6',
'placeholder' => "YYYY-MM-DD",
'type' => 'text'
],
[
'field' => 'read_only',
'label' => __('Read only (it will be not possible to do any change operation with this token)'),
'type' => 'checkbox',
]
],
'submit' => [

View File

@ -1,8 +1,8 @@
<?php
echo sprintf('<div%s>', empty($ajax) ? ' class="index"' : '');
if (!$advancedEnabled) {
echo '<div class="alert">' . __('Advanced auth keys are not enabled.') . '</div>';
}
echo sprintf('<div%s>', empty($ajax) ? ' class="index"' : '');
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
@ -14,6 +14,7 @@
'children' => [
'data' => [
'type' => 'simple',
'fa-icon' => 'plus',
'text' => __('Add authentication key'),
'class' => 'btn btn-primary',
'onClick' => 'openGenericModal',

View File

@ -63,6 +63,11 @@ echo $this->element('genericElements/SingleViews/single_view', [
'path' => 'AuthKey.expiration',
'type' => 'expiration'
],
[
'key' => __('Read only'),
'path' => 'AuthKey.read_only',
'type' => 'boolean'
],
[
'key' => __('Key usage'),
'type' => 'sparkline',

View File

@ -1,4 +1,8 @@
<div style="margin-bottom: 10px; position: relative">
<label>
<input type="checkbox" id="checkbox-include-inbound" <?= !empty($includeInbound) ? "checked=\"checked\"" : "" ?>></input>
<?= __('Include inbound relations from other galaxies') ?>
</label>
<div id="graphContainer" style="height: 70vh; border: 1px solid #ddd; "></div>
<div id="tooltipContainer" style="max-height: 450px; min-width: 200px; max-width:300px; position: absolute; top: 10px; right: 10px; border: 1px solid #999; border-radius: 3px; background-color: #f5f5f5ee; overflow: auto;"></div>
</div>
@ -45,8 +49,20 @@ $(document).ready( function() {
.text("<?= __('This galaxy does not have any relationships.') ?>")
);
}
$('#checkbox-include-inbound').click(function() {
var $container = $(this).parent().parent().parent();
var checked = $(this).prop('checked');
reloadGraph(checked);
})
});
function reloadGraph(checked) {
var uri = '<?= $baseurl ?>/galaxies/relationsGraph/<?= h($galaxy['Galaxy']['id']) ?>/' + (checked ? '1' : '0')
$.get(uri, function(data) {
$("#clusters_content").html(data);
})
}
function initGraph() {
var groupDomain = {};
graph.links.forEach(function(link) {

View File

@ -2,10 +2,16 @@
echo $this->element('genericElements/assetLoader', array(
'js' => array('d3')
));
$random = rand();
$randomClass = "relation-{$random}";
?>
<div style="padding: 5px; display: flex; position: absolute; top: 0; left: 0; right: 0; bottom: 0;">
<svg id="treeSVG" style="width: 100%; height: 100%;"></svg>
<label style="position: absolute;">
<input type="checkbox" id="checkbox-include-inbound" class="<?= $randomClass ?>" <?= !isset($includeInbound) || !empty($includeInbound) ? "checked=\"checked\"" : "" ?>></input>
<?= __('Include inbound relations') ?>
</label>
<svg id="treeSVG" class="<?= $randomClass ?>" style="width: 100%; height: 100%;"></svg>
</div>
<script>
@ -15,14 +21,23 @@ echo $this->element('genericElements/assetLoader', array(
var margin = {top: 10, right: 10, bottom: 30, left: 20};
var treeWidth, treeHeight;
var colors = d3.scale.category10();
var hasBeenBuilt = false;
var hasBeenBuilt<?= $random ?> = false;
$(document).ready(function() {
var $checkbox = $('#checkbox-include-inbound.<?= $randomClass ?>');
$checkbox.click(function() {
var $container = $(this).parent().parent().parent();
var checked = $(this).prop('checked');
reloadDiagram($container, checked);
})
})
function buildTree() {
if (hasBeenBuilt) {
if (hasBeenBuilt<?= $random ?>) {
return;
}
hasBeenBuilt = true;
var $tree = $('#treeSVG');
hasBeenBuilt<?= $random ?> = true;
var $tree = $('#treeSVG.<?= $randomClass ?>');
treeWidth = $tree.width() - margin.right - margin.left;
treeHeight = $tree.height() - margin.top - margin.bottom;
var leftShift;
@ -142,7 +157,7 @@ echo $this->element('genericElements/assetLoader', array(
+ " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
+ " " + d.target.y + "," + d.target.x;
};
var svg = d3.select("#treeSVG")
var svg = d3.select("#treeSVG.<?= $randomClass ?>")
.attr("width", treeWidth + margin.right + margin.left)
.attr("height", treeHeight + margin.top + margin.bottom)
.append("g")
@ -408,7 +423,7 @@ echo $this->element('genericElements/assetLoader', array(
}
function adaptContainerHeightIfNeeded(side) {
var $upperContainer = $('#treeSVG').parent().parent();
var $upperContainer = $('#treeSVG.<?= $randomClass ?>').parent().parent();
var leftNodeNumber = 0
var rightNodeNumber = 0
if (side == 'left') {
@ -436,4 +451,17 @@ echo $this->element('genericElements/assetLoader', array(
}
return id;
}
function reloadDiagram($container, checked) {
var url = '<?= $baseurl ?>/galaxy_clusters/viewRelationTree/<?= h($cluster['GalaxyCluster']['id']) ?>/' + (checked ? '1' : '0')
xhr({
dataType: "html",
success: function (data) {
hasBeenBuilt<?= $random ?> = false;
$container.html(data);
buildTree()
},
url: url,
});
}
</script>

View File

@ -0,0 +1,9 @@
<?php
$html = '';
if (!isset($data['requirement']) || $data['requirement']) {
foreach ($data['children'] as $child) {
$html .= empty($child['html']) ? '' : $child['html']; // this has to be sanitised beforehand!
}
}
echo $html;
?>

View File

@ -1378,7 +1378,7 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
'text' => __('Import Galaxy Clusters')
));
}
if ($menuItem === 'view' || $menuItem === 'export' || $menuItem === 'view_cluster') {
if ($menuItem === 'view' || $menuItem === 'export' || $menuItem === 'view_cluster' || $menuItem === 'add_cluster') {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'export',
'url' => $baseurl . '/galaxies/export/' . h($galaxy['Galaxy']['id']),

View File

@ -1,15 +1,11 @@
<div style="border:1px solid #dddddd; margin-top:1px; width:95%; padding:10px">
<?php
if (!$dbEncodingStatus):
?>
<div style="font-size:12pt;padding-left:3px;width:100%;background-color:red;color:white;font-weight:bold;"><?php echo __('Incorrect database encoding setting: Your database connection is currently NOT set to UTF-8. Please make sure to uncomment the \'encoding\' => \'utf8\' line in ') . APP; ?>Config/database.php</div>
<?php
endif;
?>
<h3><?php echo __('MISP version');?></h3>
<p><?php echo __('Every version of MISP includes a json file with the current version. This is checked against the latest tag on github, if there is a version mismatch the tool will warn you about it. Make sure that you update MISP regularly.');?></p>
<div style="background-color:#f7f7f9;width:100%;">
<span><?php echo __('Currently installed version…');?>
<?php if (!$dbEncodingStatus):?>
<div style="font-size:12pt;padding-left:3px;width:100%;background-color:red;color:white;font-weight:bold;"><?= __('Incorrect database encoding setting: Your database connection is currently NOT set to UTF-8. Please make sure to uncomment the \'encoding\' => \'utf8\' line in ') . APP; ?>Config/database.php</div>
<?php endif; ?>
<h3><?= __('MISP version');?></h3>
<p><?= __('Every version of MISP includes a JSON file with the current version. This is checked against the latest tag on GitHub, if there is a version mismatch the tool will warn you about it. Make sure that you update MISP regularly.');?></p>
<div class="diagnostics-box" style="width:100%">
<span><?= __('Currently installed version…');?>
<?php
$upToDate = isset($version['upToDate']) ? $version['upToDate'] : null;
switch ($upToDate) {
@ -31,7 +27,7 @@
}
?>
<span style="color:<?php echo $fontColour; ?>;">
<?= (isset($version['current']) ? $version['current'] : __('Unknown')) . ' (' . h($commit) . ')';
<?= (isset($version['current']) ? $version['current'] : __('Unknown')) . ' (' . ($commit ? h($commit) : __('Unknown')) . ')';
?>
<?php if ($commit === ''): ?>
<br>
@ -40,23 +36,23 @@
</span>
<?php endif; ?>
</span>
</span><br />
</span><br>
<span><?php echo __('Latest available version…');?>
<span style="color:<?php echo $fontColour; ?>;">
<?= (isset($version['newest']) ? $version['newest'] : __('Unknown')) . ' (' . (isset($latestCommit) ? $latestCommit : __('Unknown')) . ')' ?>
<?= (isset($version['newest']) ? $version['newest'] : __('Unknown')) . ' (' . ($latestCommit ? $latestCommit : __('Unknown')) . ')' ?>
</span>
</span><br />
</span><br>
<span><?php echo __('Status…');?>
<span style="color:<?php echo $fontColour; ?>;"><?= $versionText ?></span>
</span><br />
<span style="color:<?= $fontColour; ?>;"><?= $versionText ?></span>
</span><br>
<span><?php echo __('Current branch…');?>
<?php
$branchColour = $branch == '2.4' ? 'green' : 'red bold';
?>
<span class="<?php echo h($branchColour); ?>">
<?=($branch == '2.4') ? h($branch) : __('You are not on a branch, Update MISP will fail'); ?>
<?= $branch == '2.4' ? h($branch) : __('You are not on a branch, Update MISP will fail'); ?>
</span>
</span><br />
</span><br>
<pre class="hidden green bold" id="gitResult"></pre>
<button title="<?php echo __('Pull the latest MISP version from GitHub');?>" class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;" onClick = "updateMISP();"><?php echo __('Update MISP');?></button>
<a title="<?php echo __('Click the following button to go to the update progress page. This page lists all updates that are currently queued and executed.'); ?>" style="margin-left: 5px;" href="<?php echo $baseurl; ?>/servers/updateProgress/"><i class="fas fa-tasks"></i> <?php echo __('View Update Progress');?></a>
@ -96,7 +92,7 @@
$message = __('File ') . $message;
$colour = 'red';
}
echo $file . '…<span style="color:' . $colour . ';">' . $message . '</span><br />';
echo $file . '…<span style="color:' . $colour . ';">' . $message . '</span><br>';
}
?>
</div>
@ -110,7 +106,7 @@
$message = __('File ') . $message;
$colour = 'red';
}
echo $file . '…<span style="color:' . $colour . ';">' . $message . '</span><br />';
echo $file . '…<span style="color:' . $colour . ';">' . $message . '</span><br>';
}
?>
</div>
@ -299,6 +295,7 @@
<span class="red bold">Redis is not available. <?= $redisInfo['connection_error'] ?></span>
<?php endif; ?>
</div>
<h3><?php echo __('Advanced attachment handler');?></h3>
<?php echo __('The advanced attachment tools are used by the add attachment functionality to extract additional data about the uploaded sample.');?>
<div class="diagnostics-box">
@ -317,6 +314,7 @@
endif;
?>
</div>
<h3><?= __('Attachment scan module') ?></h3>
<div class="diagnostics-box">
<?php if ($attachmentScan['status']): ?>
@ -327,55 +325,45 @@
<b>Reason:</b> <?= $attachmentScan['error'] ?>
<?php endif; ?>
</div>
<h3><?php echo __('STIX and Cybox libraries');?></h3>
<p><?php echo __('Mitre\'s STIX and Cybox python libraries have to be installed in order for MISP\'s STIX export to work. Make sure that you install them (as described in the MISP installation instructions) if you receive an error below.');?><br />
<?php echo __('If you run into any issues here, make sure that both STIX and CyBox are installed as described in the INSTALL.txt file. The required versions are');?>:<br />
<b>STIX</b>: <?php echo $stix['stix']['expected'];?><br />
<b>CyBox</b>: <?php echo $stix['cybox']['expected'];?><br />
<b>mixbox</b>: <?php echo $stix['mixbox']['expected'];?><br />
<b>maec</b>: <?php echo $stix['maec']['expected'];?><br />
<b>STIX2</b>: <?php echo $stix['stix2']['expected'];?><br />
<b>PyMISP</b>: <?php echo $stix['pymisp']['expected'];?><br />
<?php echo __('Other versions might work but are not tested / recommended.');?></p>
<div class="diagnostics-box">
<?php
$colour = 'green';
$testReadError = false;
foreach ($readableFiles as $file => $data) {
if (substr($file, -strlen('/stixtest.py')) == '/stixtest.py') {
if ($data > 0) {
$colour = 'red';
echo __('STIX and CyBox') . '… <span class="red">' . __('Could not read test script (stixtest.py).') . '</span>';
$testReadError = true;
}
}
}
if (!$testReadError) {
$error_count = 0;
$libraries = '';
foreach (array('stix', 'cybox', 'mixbox', 'maec', 'stix2', 'pymisp') as $package) {
$lib_colour = 'green';
if ($stix[$package]['status'] == 0) {
$lib_colour = 'red';
$error_count += 1;
}
$libraries = $libraries . strtoupper($package) . __(' library version') . '…<span style="color:' . $lib_colour . ';">' . ${$package . 'Version'}[$stix[$package]['status']] . '</span><br />';
}
if ($stix['operational'] == 0) {
$colour = 'red';
echo '<b>Current libraries status</b>…<span style="color:' . $colour . ';">' . $stixOperational[$stix['operational']] . '</span><br />';
} else {
if ($error_count > 0) {
$colour = 'orange';
echo '<b>Current libraries status</b>…<span style="color:' . $colour . ';">Some versions should be updated</span>:<br />';
} else {
echo '<b>Current libraries status</b>…<span style="color:' . $colour . ';">' . $stixOperational[$stix['operational']] . '</span><br />';
}
}
echo $libraries;
}
?>
</div>
<h3><?= __('STIX and Cybox libraries');?></h3>
<p><?= __('Mitre\'s STIX and Cybox python libraries have to be installed in order for MISP\'s STIX export to work. Make sure that you install them (as described in the MISP installation instructions) if you receive an error below.');?><br />
<?= __('If you run into any issues here, make sure that both STIX and CyBox are installed as described in the INSTALL.txt file.');?><br>
<?php if ($stix['operational'] === -1): ?>
<b class="red"><?= __('Could not run test script (stixtest.py). Please check error logs for more details.') ?></b>
<?php else: ?>
<b><?= __('Current libraries status') ?>:</b>
<?php if ($stix['operational'] === 0): ?>
<b class="red bold"><?= __('Some of the libraries related to STIX are not installed. Make sure that all libraries listed below are correctly installed.') ?></b>
<?php elseif ($stix['invalid_version']): ?>
<span class="orange"><?= __('Some versions should be updated.') ?></span>
<?php else: ?>
<b class="green"><?= __('OK') ?></b>
<?php endif ?>
<table class="table table-condensed table-bordered" style="width: 400px">
<thead>
<tr>
<th><?= __('Library') ?></th>
<th><?= __('Expected version') ?></th>
<th><?= __('Installed version') ?></th>
<th><?= __('Status') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($stix as $name => $library): if (!isset($library['expected'])) continue; ?>
<tr>
<td><?= h($name) ?></td>
<td><?= h($library['expected']) ?></td>
<td><?= $library['version'] === 0 ? __('Not installed') : h($library['version']) ?></td>
<td><?= $library['status'] ? '<i class="green fa fa-check" role="img" aria-label="' . __('Correct') . '"></i>' : '<i class="red fa fa-times" role="img" aria-label="' . __('Incorrect') . '"></i>' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h3><?php echo __('Yara');?></h3>
<p><?php echo __('This tool tests whether plyara, the library used by the yara export tool is installed or not.');?></p>
<div class="diagnostics-box">
@ -402,6 +390,7 @@
}
?>
</div>
<h3><?php echo __('ZeroMQ');?></h3>
<p><?php echo __('This tool tests whether the ZeroMQ extension is installed and functional.');?></p>
<div class="diagnostics-box">

View File

@ -197,7 +197,7 @@
<?php
foreach ($object['ObjectReference'] as $reference) {
echo '&nbsp;&nbsp;<span class="ObjectReference">';
echo '<span class="Relationship">' . h($reference['relationship_type']) . ' </span>';
echo '<span class="Relationship">' . h($reference['relationship_type']) . '</span> ';
$referenced_uuid = $reference['referenced_uuid'];
foreach ($event['Object'] as $object_reference) {
if ($referenced_uuid === $object_reference['uuid']) {

View File

@ -3,6 +3,21 @@
'data' => array(
'skip_pagination' => true,
'data' => $relations,
'top_bar' => array(
'children' => array(
array(
'type' => 'raw',
'children' => array(
array(
'html' => '<label>' .
'<input type="checkbox" id="checkbox-include-inbound" ' . (!isset($includeInbound) || !empty($includeInbound) ? "checked=\"checked\"" : "") . '></input>' .
__('Include inbound relations') .
'</label>'
)
)
)
),
),
'fields' => array(
array(
'name' => __('Id'),
@ -15,6 +30,12 @@
'element' => 'boolean',
'data_path' => 'default',
),
array(
'name' => __('Is Inbound'),
'class' => 'short',
'element' => 'boolean',
'data_path' => 'isInbound',
),
array(
'name' => __('Galaxy Cluster Target (galaxy :: cluster)'),
'element' => 'galaxy_cluster_link',
@ -170,6 +191,14 @@ $(document).ready(function() {
$('#buttonAddRelationship').click(function() {
submitRelationshipForm();
})
var $checkbox = $('#referencesTable_div #checkbox-include-inbound');
$checkbox.click(function() {
var checked = $(this).prop('checked');
reloadRelationTable(checked, function () {
$('#references_div a[href="#tabularView"]').tab('show')
})
})
});
function pickerTarget() {
@ -234,13 +263,7 @@ function submitRelationshipForm() {
$.ajax({
data: $('#GalaxyClusterRelationAddForm').serialize(),
success:function (data) {
$.get("/galaxy_clusters/viewRelations/<?php echo $cluster['GalaxyCluster']['id']; ?>", function(data) {
$("#relations_container").html(data);
$('#references_div').show({
complete: buildTree,
duration: 0
});
});
reloadRelationTable()
},
error:function(jqXHR, textStatus, errorThrown) {
showMessage('fail', textStatus + ": " + errorThrown);
@ -266,4 +289,15 @@ function toggleLoadingButton(loading) {
$('#buttonAddRelationship > i').removeClass('fa-spinner fa-spin').addClass('fa-plus');
}
}
function reloadRelationTable(checked, callback) {
$.get("/galaxy_clusters/viewRelations/<?php echo $cluster['GalaxyCluster']['id']; ?>/" + (checked ? '1' : '0'), function(data) {
$("#relations_container").html(data);
$('#references_div').show({
complete: buildTree,
duration: 0
});
callback()
});
}
</script>

View File

@ -65,7 +65,8 @@ echo $this->element('/genericElements/IndexTable/index_table', [
[
'name' => __('Name'),
'sort' => 'name',
'data_path' => 'Organisation.name',
'data_path' => 'Organisation',
'element' => 'org'
],
[
'name' => __('UUID'),

View File

@ -1,2 +0,0 @@
<?php
echo json_encode($data, $flags);

View File

@ -1,5 +1,5 @@
<div class="roles view">
<h2><?= __('Sharing Group %s', $sg['SharingGroup']['name']);?></h2>
<h2><?= __('Sharing Group %s', h($sg['SharingGroup']['name']));?></h2>
<div class="row-fluid"><div class="span8" style="margin:0">
<?php
$tableData = [

View File

@ -22,6 +22,7 @@ import os
import re
import sys
import uuid
import traceback
import misp2stix2_mapping
from datetime import datetime
from stix2.base import STIXJSONEncoder
@ -38,7 +39,8 @@ _MISP_event_tags = ['Threat-Report', 'misp:tool="misp2stix2"']
_time_fields = {'indicator': ('valid_from', 'valid_until'),
'observed-data': ('first_observed', 'last_observed')}
class StixBuilder():
class StixBuilder:
def __init__(self):
self.orgs = []
self.galaxies = []
@ -60,7 +62,7 @@ class StixBuilder():
f.write(json.dumps(stix_packages, cls=STIXJSONEncoder))
print(json.dumps({'success': 1}))
except Exception as e:
print(json.dumps({'error': e.__str__()}))
print(json.dumps({'error': e.__str__(), 'traceback': ''.join(traceback.format_tb(e.__traceback__))}))
@staticmethod
def _get_event(events):
@ -1853,5 +1855,6 @@ def main(args):
stix_builder.loadEvent(args)
stix_builder.buildEvent()
if __name__ == "__main__":
main(sys.argv)

View File

@ -47,13 +47,5 @@ try:
except Exception:
results['success'] = 0
print(json.dumps({
'success': results['success'],
'stix': results['stix'],
'cybox': results['cybox'],
'mixbox': results['mixbox'],
'maec': results['maec'],
'stix2': results['stix2'],
'pymisp': results['pymisp']
}))
print(json.dumps(results))
sys.exit(0)

View File

@ -618,6 +618,17 @@
"column_default": null,
"extra": ""
},
{
"column_name": "read_only",
"is_nullable": "NO",
"data_type": "tinyint",
"character_maximum_length": null,
"numeric_precision": "3",
"collation_name": null,
"column_type": "tinyint(1)",
"column_default": "0",
"extra": ""
},
{
"column_name": "user_id",
"is_nullable": "NO",
@ -8191,5 +8202,5 @@
"id": true
}
},
"db_version": "71"
"db_version": "72"
}

View File

@ -1,10 +1,10 @@
# INSTALLATION INSTRUCTIONS for RHEL 8.x, CentOS8/Stream
# INSTALLATION INSTRUCTIONS for RHEL 8.x based distros
-------------------------
### -2/ RHEL8/CentOS8/CentOS_Stream/Fedora33 - status
### -2/ RHEL8/CentOS8/CentOS_Stream/Rocky8.4/Fedora34 - status
-------------------------
!!! notice
Tested fully working without SELinux by [@SteveClement](https://twitter.com/SteveClement) on 20210401
Tested fully working without SELinux by [@SteveClement](https://twitter.com/SteveClement) on 20210702
TODO: Fix SELinux permissions, *pull-requests welcome*.
{!generic/manual-install-notes.md!}
@ -24,6 +24,7 @@
!!! notice
Maintenance for CentOS 8 will end on: December 31st, 2021 [Source[0]](https://wiki.centos.org/About/Product) [Source[1]](https://linuxlifecycle.com/)
Consider using [Rocky Linux](https://rockylinux.org/)
CentOS 8 [NetInstallURL](http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=BaseOS)
{!generic/manual-install-notes.md!}
@ -92,14 +93,14 @@ sudo dnf install drpm -y
## 1.5.b/ Install vim (optional)
```bash
# Because (neo)vim is just so practical
sudo dnf install neovim -y
sudo dnf install neovim -y || sudo dnf install vim -y || echo "neovim is not in the catalog"
# For RHEL, it's vim and after enabling epel neovim is available too
```
## 1.5.c/ Install ntpdate (optional)
```bash
# In case you time is wrong, this will fix it.
sudo dnf install ntpdate -y
sudo dnf install ntpdate -y || sudo dnf install ntpsec -y
sudo ntpdate pool.ntp.org
```
@ -120,12 +121,12 @@ enableEPEL_REMI_8 () {
sudo dnf install http://rpms.remirepo.net/enterprise/remi-release-8.rpm -y
sudo dnf install dnf-utils -y
sudo dnf module enable php:remi-7.4 -y
[[ ${DISTRI} == "centos8stream" ]] && sudo dnf config-manager --set-enabled powertools
[[ ${DISTRI} == "centos8" ]] && sudo dnf config-manager --set-enabled powertools
([[ ${DISTRI} == "centos8stream" ]] || [[ ${DISTRI} == "centos8" ]] || [[ ${DISTRI} == "rocky8.4" ]]) && sudo dnf config-manager --set-enabled powertools
}
enableREMI_f33 () {
sudo dnf install http://rpms.remirepo.net/fedora/remi-release-33.rpm
enableREMI_fedora () {
[[ "${DISTRI%??}" == "fedora" ]] && sudo dnf install http://rpms.remirepo.net/fedora/remi-release-${DISTRI:6}.rpm -y
dnf list installed mod_lua && sudo dnf remove mod_lua -y
sudo dnf install dnf-utils -y
sudo dnf module enable php:remi-7.4 -y
}
@ -160,7 +161,7 @@ yumInstallCoreDeps8 () {
policycoreutils-python-utils \
langpacks-en glibc-all-langpacks \
libxslt-devel zlib-devel ssdeep-devel -y
sudo alternatives --set python /usr/bin/python3
readlink -f /usr/bin/python | grep python3 || sudo alternatives --set python /usr/bin/python3
# Enable and start redis
sudo systemctl enable --now redis.service
@ -272,9 +273,8 @@ installCoreRHEL8 () {
cd $PATH_TO_MISP/app/files/scripts/python-cybox
$SUDO_WWW git config core.filemode false
# If you umask is has been changed from the default, it is a good idea to reset it to 0022 before installing python modules
([[ ${DISTRI} == 'fedora33' ]] || [[ ${DISTRI} == 'rhel8.3' ]]) && sudo dnf install cmake3 -y && CMAKE_BIN='cmake3'
[[ ${DISTRI} == 'centos8stream' ]] && sudo dnf install cmake -y && CMAKE_BIN='cmake'
[[ ${DISTRI} == 'centos8' ]] && sudo dnf install cmake -y && CMAKE_BIN='cmake'
([[ ${DISTRI} == 'fedora33' ]] || [[ ${DISTRI} == 'fedora34' ]] || [[ ${DISTRI} == 'rhel8.3' ]]) && sudo dnf install cmake3 -y && CMAKE_BIN='cmake3'
([[ ${DISTRI} == 'centos8stream' ]] || [[ ${DISTRI} == 'centos8' ]] || [[ ${DISTRI} == 'rocky8.4' ]]) && sudo dnf install cmake -y && CMAKE_BIN='cmake'
UMASK=$(umask)
umask 0022

View File

@ -17,6 +17,8 @@ MISPvars
# $ MISPvars
MISPvars () {
debug "Setting generic ${LBLUE}MISP${NC} variables shared by all flavours" 2> /dev/null
# Some distros have no openssl installed by default, catch that exception.
$(openssl help 2> /dev/null) || (echo "No openssl, please install to continue"; exit -1)
# Local non-root MISP user
MISP_USER="${MISP_USER:-misp}"
MISP_PASSWORD="${MISP_PASSWORD:-$(openssl rand -hex 32)}"

View File

@ -7,7 +7,7 @@ mispmodulesRHEL () {
[[ "${DIST_VER}" =~ ^[7].* ]] && sudo dnf install openjpeg-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
# some misp-modules dependencies for RHEL8
([[ "${DISTRI}" == "fedora33" ]] || [[ "${DIST_VER}" =~ ^[8].* ]]) && sudo dnf install openjpeg2-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
([[ "${DISTRI}" == "fedora33" ]] || [[ "${DISTRI}" == "fedora34" ]] || [[ "${DIST_VER}" =~ ^[8].* ]]) && sudo dnf install openjpeg2-devel gcc-c++ poppler-cpp-devel pkgconfig python3-devel redhat-rpm-config -y
sudo chmod 2777 /usr/local/src
sudo chown root:users /usr/local/src

View File

@ -1,5 +1,5 @@
# INSTALLATION INSTRUCTIONS
## for OpenBSD 6.8-amd64
## for OpenBSD 6.9-amd64
!!! warning
This is not fully working yet. Mostly it is a template for our ongoing documentation efforts :spider:
@ -25,7 +25,7 @@
```bash
export AUTOMAKE_VERSION=1.16
export AUTOCONF_VERSION=2.69
export AUTOCONF_VERSION=2.71
```
### 1/ Minimal OpenBSD install
@ -86,7 +86,7 @@ doas pkg_add -v mariadb-server
#### Install misc dependencies
```bash
doas pkg_add -v curl git python--%3.8 redis libmagic autoconf--%2.69 automake--%1.16 libtool unzip--iconv
doas pkg_add -v curl git python--%3.8 redis libmagic autoconf--%2.71 automake--%1.16 libtool unzip--iconv rust
```
```bash
@ -232,7 +232,7 @@ doas pkg_add -v py3-virtualenv py3-pip
doas ln -sf /usr/local/bin/pip3.8 /usr/local/bin/pip
doas ln -s /usr/local/bin/python3.8 /usr/local/bin/python
doas mkdir /usr/local/virtualenvs
doas virtualenv-3 /usr/local/virtualenvs/MISP
doas /usr/local/bin/virtualenv /usr/local/virtualenvs/MISP
```
#### Install ssdeep
@ -338,7 +338,7 @@ ${SUDO_WWW} git submodule foreach --recursive git config core.filemode false
${SUDO_WWW} git config core.filemode false
doas pkg_add -v py3-pip libxml libxslt py3-jsonschema
doas /usr/local/virtualenvs/MISP/bin/pip install -U pip
doas /usr/local/virtualenvs/MISP/bin/pip install -U pip setuptools setuptools-rust
cd /var/www/htdocs/MISP/app/files/scripts
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/CybOXProject/python-cybox.git; done
@ -502,6 +502,7 @@ LoadModule rewrite_module /usr/local/lib/apache2/mod_rewrite.so
LoadModule ssl_module /usr/local/lib/apache2/mod_ssl.so
LoadModule proxy_module /usr/local/lib/apache2/mod_proxy.so
LoadModule proxy_fcgi_module /usr/local/lib/apache2/mod_proxy_fcgi.so
LoadModule socache_shmcb_module /usr/local/lib/apache2/socache_shmcb_module.so
Listen 443
DirectoryIndex index.php
```

View File

@ -5,7 +5,7 @@ import time
import json
import datetime
import unittest
from typing import Union, List
from typing import Union, List, Optional
import urllib3 # type: ignore
import logging
import uuid
@ -588,6 +588,87 @@ class TestSecurity(unittest.TestCase):
self.__delete_advanced_authkey(auth_key["id"])
def test_advanced_authkeys_read_only_false(self):
with self.__setting("Security.advanced_authkeys", True):
auth_key = self.__create_advanced_authkey(self.test_usr.id, {
"read_only": 0,
})
self.assertFalse(auth_key["read_only"])
# Try to login
logged_in = self.__login_by_advanced_authkey(auth_key)
# Create new event should not be possible with read only key
event = logged_in.add_event(self.__generate_event())
check_response(event)
self.__delete_advanced_authkey(auth_key["id"])
def test_advanced_authkeys_read_only(self):
with self.__setting("Security.advanced_authkeys", True):
auth_key = self.__create_advanced_authkey(self.test_usr.id, {
"read_only": 1,
})
self.assertTrue(auth_key["read_only"])
# Try to login
logged_in = self.__login_by_advanced_authkey(auth_key)
# Create new event should not be possible with read only key
event = logged_in.add_event(self.__generate_event())
with self.assertRaises(Exception):
check_response(event)
self.__delete_advanced_authkey(auth_key["id"])
def test_advanced_authkeys_read_only_edit_self(self):
with self.__setting("Security.advanced_authkeys", True):
auth_key = self.__create_advanced_authkey(self.test_usr.id, {
"read_only": 1,
})
self.assertTrue(auth_key["read_only"])
# Try to login
logged_in = self.__login_by_advanced_authkey(auth_key)
# Edit current auth key and set it to not read_only should be not possible
with self.assertRaises(Exception):
send(logged_in, "POST", f'authKeys/edit/{auth_key["id"]}', {"read_only": 0})
self.__delete_advanced_authkey(auth_key["id"])
def test_advanced_authkeys_read_only_create_new_authkey(self):
with self.__setting("Security.advanced_authkeys", True):
auth_key = self.__create_advanced_authkey(self.test_usr.id, {
"read_only": 1,
})
self.assertTrue(auth_key["read_only"])
# Try to login
logged_in = self.__login_by_advanced_authkey(auth_key)
# Create new auth key should be not possible
with self.assertRaises(Exception):
send(logged_in, "POST", f'authKeys/add/{logged_in._current_user.id}')
self.__delete_advanced_authkey(auth_key["id"])
def test_advanced_authkeys_read_only_reset_authkey(self):
with self.__setting("Security.advanced_authkeys", True):
auth_key = self.__create_advanced_authkey(self.test_usr.id, {
"read_only": 1,
})
self.assertTrue(auth_key["read_only"])
# Try to login
logged_in = self.__login_by_advanced_authkey(auth_key)
# Create new auth key should be not possible
with self.assertRaises(Exception):
send(logged_in, "POST", "users/resetauthkey/me")
self.__delete_advanced_authkey(auth_key["id"])
def test_authkey_keep_session(self):
with self.__setting("Security.authkey_keep_session", True):
logged_in = PyMISP(url, self.test_usr.authkey)
@ -1424,8 +1505,16 @@ class TestSecurity(unittest.TestCase):
self.assertEqual(int(role_id), int(user.role_id))
return user
def __create_advanced_authkey(self, user_id: int, data=None):
return send(self.admin_misp_connector, "POST", f'authKeys/add/{user_id}', data=data)["AuthKey"]
def __create_advanced_authkey(self, user_id: int, data: Optional[dict] = None) -> dict:
auth_key = send(self.admin_misp_connector, "POST", f'authKeys/add/{user_id}', data=data)["AuthKey"]
# it is not possible to call `assertEqual`, because we use this method in `setUpClass` method
assert user_id == auth_key["user_id"], "Key was created for different user"
return auth_key
def __login_by_advanced_authkey(self, auth_key: dict) -> PyMISP:
logged_in = PyMISP(url, auth_key["authkey_raw"])
self.assertEqual(logged_in._current_user.id, auth_key["user_id"], "Logged in by different user")
return logged_in
def __delete_advanced_authkey(self, key_id: int):
return send(self.admin_misp_connector, "POST", f'authKeys/delete/{key_id}')