Merge branch '2.4' into feature/attribute-tagging

pull/1857/head
iglocska 2017-01-16 16:15:06 +01:00
commit 4ad022b03c
44 changed files with 736 additions and 254 deletions

2
.gitignore vendored
View File

@ -18,6 +18,7 @@
/app/tmp/cache/persistent/myapp*
/app/tmp/cache/views/myapp*
/app/files/*
/app/tmp/cache/feeds/*.cache
!/app/files/empty
!/app/files/scripts/
!/app/files/warninglists
@ -64,6 +65,7 @@
/app/tmp/cached_exports/csv_sig/*
/app/tmp/cached_exports/stix/*
/app/tmp/cached_exports/sha256/*
/app/tmp/cached_exports/bro/*
.gnupg
.smime
*.swp

View File

@ -672,6 +672,7 @@ CREATE TABLE IF NOT EXISTS `taxonomy_entries` (
`taxonomy_predicate_id` int(11) NOT NULL,
`value` text COLLATE utf8_bin NOT NULL,
`expanded` text COLLATE utf8_bin,
`colour` varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
INDEX `taxonomy_predicate_id` (`taxonomy_predicate_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@ -687,6 +688,7 @@ CREATE TABLE IF NOT EXISTS `taxonomy_predicates` (
`taxonomy_id` int(11) NOT NULL,
`value` text COLLATE utf8_bin NOT NULL,
`expanded` text COLLATE utf8_bin,
`colour` varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
INDEX `taxonomy_id` (`taxonomy_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

View File

@ -591,6 +591,7 @@ CREATE TABLE IF NOT EXISTS taxonomy_entries (
taxonomy_predicate_id bigint NOT NULL,
value text NOT NULL,
expanded text,
colour varchar(7) NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX idx_taxonomy_entries_taxonomy_predicate_id ON taxonomy_entries (taxonomy_predicate_id);
@ -606,6 +607,7 @@ CREATE TABLE IF NOT EXISTS taxonomy_predicates (
taxonomy_id bigint NOT NULL,
value text NOT NULL,
expanded text,
colour varchar(7) NOT NULL,
PRIMARY KEY (id)
);
CREATE INDEX idx_taxonomy_predicates_taxonomy_id ON taxonomy_predicates (taxonomy_id);

View File

@ -31,7 +31,7 @@ yum install centos-release-scl
yum install gcc git httpd zip redis mariadb mariadb-server python-devel python-pip libxslt-devel zlib-devel
# Install PHP 5.6 from SCL, see https://www.softwarecollections.org/en/scls/rhscl/rh-php56/
yum install rh-php56 rh-php56-php-fpm rh-php56-php-devel rh-php56-php-mysqlnd rh-php56-php-mbstring rh-php56-bcmath
yum install rh-php56 rh-php56-php-fpm rh-php56-php-devel rh-php56-php-mysqlnd rh-php56-php-mbstring rh-php56-php-bcmath
# rh-php56-php only provided mod_php for httpd24-httpd from SCL
# if we want to use httpd from CentOS base we can use rh-php56-php-fpm instead
@ -206,7 +206,7 @@ cp -a config.default.php config.php
# Configure the fields in the newly created files:
# config.php : baseurl (example: 'baseurl' => 'http://misp',) - don't use "localhost" it causes issues when browsing externally
# config.php : Uncomment and set the timezone: `// date_default_timezone_set('UTC');`
# core.php : Uncomment and set the timezone: `// date_default_timezone_set('UTC');`
# database.php : login, port, password, database
# Important! Change the salt key in /var/www/MISP/app/Config/config.php

2
PyMISP

@ -1 +1 @@
Subproject commit 8cec9377c1c4e2ea756282e9bfee7701200c94b6
Subproject commit 2f967268d1b249fe4ce5784b46e9392c2d2d483e

View File

@ -84,7 +84,7 @@ This software is licensed under [GNU Affero General Public License version 3](ht
* Copyright (C) 2012 Christophe Vandeplas
* Copyright (C) 2012 Belgian Defence
* Copyright (C) 2012 NATO / NCIRC
* Copyright (C) 2013-2016 Andras Iklody
* Copyright (C) 2015-2016 CIRCL - Computer Incident Response Center Luxembourg
* Copyright (C) 2013-2017 Andras Iklody
* Copyright (C) 2015-2017 CIRCL - Computer Incident Response Center Luxembourg
For more information, [the list of authors and contributors](AUTHORS) is available.

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":58}
{"major":2, "minor":4, "hotfix":59}

View File

@ -47,6 +47,7 @@ class AppController extends Controller {
public $helpers = array('Utility');
private $__jsVersion = '2.4.58';
public $pyMispVersion = '2.4.56';
public $phpmin = '5.5.9';
public $phprec = '5.6.0';

View File

@ -717,7 +717,7 @@ class AttributesController extends AppController {
$saved_attribute = $this->Attribute->find('first', array(
'conditions' => array('id' => $this->Attribute->id),
'recursive' => -1,
'fields' => array('id', 'type', 'to_ids', 'category', 'uuid', 'event_id', 'distribution', 'timestamp', 'comment', 'value'),
'fields' => array('id', 'type', 'to_ids', 'category', 'uuid', 'event_id', 'distribution', 'timestamp', 'comment', 'value', 'disable_correlation'),
));
$response = array('response' => array('Attribute' => $saved_attribute['Attribute']));
$this->set('response', $response);

View File

@ -140,6 +140,7 @@ class ACLComponent extends Component {
'fetchFromFeed' => array(),
'fetchSelectedFromFreetextIndex' => array(),
'getEvent' => array(),
'importFeeds' => array(),
'index' => array(),
'previewEvent' => array(),
'previewIndex' => array(),
@ -234,6 +235,7 @@ class ACLComponent extends Component {
'edit' => array(),
'fetchServersForSG' => array('*'),
'filterEventIndex' => array(),
'getPyMISPVersion' => array('*'),
'getVersion' => array('*'),
'index' => array('OR' => array('perm_sync', 'perm_admin')),
'previewEvent' => array(),
@ -282,6 +284,7 @@ class ACLComponent extends Component {
'sightings' => array(
'add' => array('perm_add'),
'delete' => array('perm_add'),
'index' => array('*')
),
'tags' => array(
'add' => array('perm_tag_editor'),
@ -351,7 +354,7 @@ class ACLComponent extends Component {
'delete' => array('perm_admin'),
'downloadTerms' => array('*'),
'edit' => array('*'),
'fetchPGPKey' => array('perm_admin'),
'fetchPGPKey' => array('*'),
'histogram' => array('*'),
'index' => array('*'),
'initiatePasswordReset' => array('perm_admin'),

View File

@ -1,7 +1,7 @@
<?php
class RestResponseComponent extends Component {
private $__descriptions = array(
'User' => array(
'admin_add' => array(
@ -15,7 +15,7 @@ class RestResponseComponent extends Component {
)
)
);
public function saveFailResponse($controller, $action, $id = false, $validationErrors, $format = false) {
$this->autoRender = false;
$response = array();
@ -24,9 +24,9 @@ class RestResponseComponent extends Component {
$response['message'] = $response['name'];
$response['url'] = $this->__generateURL($action, $controller, $id);
$response['errors'] = $validationErrors;
return $this->__sendResponse($response, 403, $format);
return $this->__sendResponse($response, 403, $format);
}
public function saveSuccessResponse($controller, $action, $id = false, $format = false, $message = false) {
$action = $this->__dissectAdminRouting($action);
if (!$message) {
@ -37,7 +37,7 @@ class RestResponseComponent extends Component {
$response['url'] = $this->__generateURL($action, $controller, $id);
return $this->__sendResponse($response, 200, $format);
}
private function __sendResponse($response, $code, $format = false) {
if (strtolower($format) === 'application/xml') {
$response = Xml::build($response);
@ -48,11 +48,11 @@ class RestResponseComponent extends Component {
}
return new CakeResponse(array('body'=> $response,'status' => $code, 'type' => $type));
}
private function __generateURL($action, $controller, $id) {
return ($action['admin'] ? '/admin' : '') . '/' . strtolower($controller) . '/' . $action['action'] . ($id ? '/' . $id : '');
}
private function __dissectAdminRouting($action) {
$admin = false;
if (strlen($action) > 6 && substr($action, 0, 6) == 'admin_') {
@ -61,11 +61,11 @@ class RestResponseComponent extends Component {
}
return array('action' => $action, 'admin' => $admin);
}
public function viewData($data, $format = false) {
return $this->__sendResponse($data, 200, $format);
}
public function describe($controller, $action, $id = false, $format = false) {
$actionArray = $this->__dissectAdminRouting($action);
$response['name'] = $this->__generateURL($actionArray, $controller, false) . ' API description';

View File

@ -1196,9 +1196,9 @@ class EventsController extends AppController {
if (isset($this->data['Event']['submittedfile'])) {
if (Configure::read('MISP.take_ownership_xml_import')
&& (isset($this->data['Event']['takeownership']) && $this->data['Event']['takeownership'] == 1)) {
$results = $this->_addMISPExportFile($ext, true);
$results = $this->_addMISPExportFile($ext, true, $this->data['Event']['publish']);
} else {
$results = $this->_addMISPExportFile($ext);
$results = $this->_addMISPExportFile($ext, false, $this->data['Event']['publish']);
}
}
}
@ -1280,6 +1280,13 @@ class EventsController extends AppController {
}
public function edit($id = null) {
if (Validation::uuid($id)) {
$temp = $this->Event->find('first', array('recursive' => -1, 'fields' => array('Event.id'), 'conditions' => array('Event.uuid' => $id)));
if (empty($temp)) throw new NotFoundException('Invalid event');
$id = $temp['Event']['id'];
} else if (!is_numeric($id)) {
throw new NotFoundException(__('Invalid event'));
}
$this->Event->id = $id;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
@ -2174,7 +2181,7 @@ class EventsController extends AppController {
}
}
public function _addMISPExportFile($ext, $take_ownership = false) {
public function _addMISPExportFile($ext, $take_ownership = false, $publish = false) {
App::uses('FileAccessTool', 'Tools');
$data = (new FileAccessTool())->readFromFile($this->data['Event']['submittedfile']['tmp_name'], $this->data['Event']['submittedfile']['size']);
@ -2212,6 +2219,7 @@ class EventsController extends AppController {
$event = array('Event' => $event);
$created_id = 0;
$event['Event']['locked'] = 1;
$event['Event']['published'] = $publish;
$result['result'] = $this->Event->_add($event, true, $this->Auth->user(), '', null, false, null, $created_id, $validationIssues);
$result['id'] = $created_id;
$result['validationIssues'] = $validationIssues;
@ -2225,6 +2233,7 @@ class EventsController extends AppController {
}
$created_id = 0;
$temp['Event']['locked'] = 1;
$temp['Event']['published'] = $publish;
$result = $this->Event->_add($temp, true, $this->Auth->user(), '', null, false, null, $created_id, $validationIssues);
$results = array(0 => array('info' => $temp['Event']['info'], 'result' => $result, 'id' => $created_id, 'validationIssues' => $validationIssues));
}
@ -3482,10 +3491,10 @@ class EventsController extends AppController {
$this->loadModel('Log');
$hashes = array('md5' => 'malware-sample', 'sha1' => 'filename|sha1', 'sha256' => 'filename|sha256');
$categoryDefinitions = $this->Event->Attribute->categoryDefinitions;
$types = array();
$categories = array();
foreach ($categoryDefinitions as $k => $v) {
if (in_array('malware-sample', $v['types']) && !in_array($k, $types)) {
$types[] = $k;
if (in_array('malware-sample', $v['types']) && !in_array($k, $categories)) {
$categories[] = $k;
}
}
$parameter_options = array(
@ -3494,7 +3503,7 @@ class EventsController extends AppController {
'analysis' => array('valid_options' => array(0, 1, 2), 'default' => 0),
'info' => array('default' => 'Malware samples uploaded on ' . date('Y-m-d')),
'to_ids' => array('valid_options' => array(0, 1), 'default' => 1),
'category' => array('valid_options' => $types, 'default' => 'Payload installation'),
'category' => array('valid_options' => $categories, 'default' => 'Payload installation'),
'comment' => array('default' => '')
);
@ -3556,6 +3565,8 @@ class EventsController extends AppController {
));
if (empty($event)) throw new NotFoundException('Event not found.');
$this->Event->id = $data['event_id'];
$date = new DateTime();
$this->Event->saveField('timestamp', $date->getTimestamp());
$this->Event->saveField('published', 0);
} else {
$this->Event->create();
@ -3592,69 +3603,44 @@ class EventsController extends AppController {
$successCount = 0;
$errors = array();
foreach ($data['files'] as $file) {
if ($data['to_ids']) {
$temp = $this->Event->Attribute->handleMaliciousBase64($data['event_id'], $file['filename'], $file['data'], array_keys($hashes));
if ($temp['success']) {
foreach ($hashes as $hash => $typeName) {
if ($temp[$hash] == false) continue;
$file[$hash] = $temp[$hash];
$file['data'] = $temp['data'];
$this->Event->Attribute->create();
$attribute = array(
'value' => $file['filename'] . '|' . $file[$hash],
'distribution' => $data['distribution'],
'category' => $data['category'],
'type' => $typeName,
'event_id' => $data['event_id'],
'to_ids' => $data['to_ids'],
'comment' => $data['comment']
);
if ($hash == 'md5') $attribute['data'] = $file['data'];
$result = $this->Event->Attribute->save($attribute);
if (!$result) {
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $data['event_id'],
'email' => $this->Auth->user('email'),
'action' => 'upload_sample',
'user_id' => $this->Auth->user('id'),
'title' => 'Error: Failed to create attribute using the upload sample functionality',
'change' => 'There was an issue creating an attribute (' . $typeName . ': ' . $file['filename'] . '|' . $file[$hash] . '). ' . 'The validation errors were: ' . json_encode($this->Event->Attribute->validationErrors),
));
if ($typeName == 'malware-sample') {
$errors[] = array('filename' => $file['filename'], 'hash' => $file[$hash], 'error' => $this->Event->Attribute->validationErrors);
}
} else if ($typeName == 'malware-sample') {
$successCount++;
$temp = $this->Event->Attribute->handleMaliciousBase64($data['event_id'], $file['filename'], $file['data'], array_keys($hashes));
if ($temp['success']) {
foreach ($hashes as $hash => $typeName) {
if ($temp[$hash] == false) continue;
$file[$hash] = $temp[$hash];
$file['data'] = $temp['data'];
$this->Event->Attribute->create();
$attribute = array(
'value' => $file['filename'] . '|' . $file[$hash],
'distribution' => $data['distribution'],
'category' => $data['category'],
'type' => $typeName,
'event_id' => $data['event_id'],
'to_ids' => $data['to_ids'],
'comment' => $data['comment']
);
if ($hash == 'md5') $attribute['data'] = $file['data'];
$result = $this->Event->Attribute->save($attribute);
if (!$result) {
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $data['event_id'],
'email' => $this->Auth->user('email'),
'action' => 'upload_sample',
'user_id' => $this->Auth->user('id'),
'title' => 'Error: Failed to create attribute using the upload sample functionality',
'change' => 'There was an issue creating an attribute (' . $typeName . ': ' . $file['filename'] . '|' . $file[$hash] . '). ' . 'The validation errors were: ' . json_encode($this->Event->Attribute->validationErrors),
));
if ($typeName == 'malware-sample') {
$errors[] = array('filename' => $file['filename'], 'hash' => $file[$hash], 'error' => $this->Event->Attribute->validationErrors);
}
} else if ($typeName == 'malware-sample') {
$successCount++;
}
} else {
$errors[] = array('filename' => $file['filename'], 'hash' => $file['hash'], 'error' => 'Failed to encrypt and compress the file.');
}
} else {
$this->Event->Attribute->create();
$attribute = array(
'value' => $file['filename'],
'distribution' => $data['distribution'],
'category' => $data['category'],
'type' => 'attachment',
'event_id' => $data['event_id'],
'to_ids' => $data['to_ids'],
'data' => $file['data'],
'comment' => $data['comment']
);
$result = $this->Event->Attribute->save($attribute);
$this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Event',
'model_id' => $data['event_id'],
'email' => $this->Auth->user('email'),
'action' => 'upload_sample',
'user_id' => $this->Auth->user('id'),
'title' => 'Error: Failed to create attribute using the upload sample functionality',
'change' => 'There was an issue creating an attribute (attachment: ' . $file['filename'] . '). ' . 'The validation errors were: ' . json_encode($this->Event->Attribute->validationErrors),
));
$errors[] = array('filename' => $file['filename'], 'hash' => $file['hash'], 'error' => 'Failed to encrypt and compress the file.');
}
}
if (!empty($errors)) {

View File

@ -25,6 +25,12 @@ class FeedsController extends AppController {
}
public function index() {
$scope = isset($this->passedArgs['scope']) ? $this->passedArgs['scope'] : 'all';
if ($scope !== 'all') {
$this->paginate['conditions'][] = array(
'Feed.default' => $scope == 'custom' ? 0 : 1
);
}
$data = $this->paginate();
$this->loadModel('Event');
foreach ($data as $key => $value) {
@ -35,6 +41,16 @@ class FeedsController extends AppController {
}
}
}
if ($this->_isRest()) {
foreach ($data as $k => $v) {
unset($data[$k]['SharingGroup']);
if (empty($data[$k]['Tag']['id'])) {
unset($data[$k]['Tag']);
}
}
return $this->RestResponse->viewData($data, $this->response->type());
}
$this->set('scope', $scope);
$this->set('feeds', $data);
$this->loadModel('Event');
$this->set('feed_types', $this->Feed->feed_types);
@ -42,10 +58,72 @@ class FeedsController extends AppController {
}
public function view($feedId) {
$feed = $this->Feed->find('first', array('conditions' => array('Feed.id' => $feedId)));
$feed = $this->Feed->find('first', array(
'conditions' => array('Feed.id' => $feedId),
'recursive' => -1,
'contain' => array('Tag')
));
if ($this->_isRest()) {
if (empty($feed['Tag']['id'])) {
unset($feed['Tag']);
}
return $this->RestResponse->viewData($feed, $this->response->type());
}
}
public function importFeeds() {
if ($this->request->is('post')) {
$feeds = json_decode($this->request->data['Feed']['json'], true);
if (!isset($feeds[0])) {
$feeds = array($feeds);
}
if (empty($feeds)) throw new NotFoundException('No valid ');
$existingFeeds = $this->Feed->find('all', array());
$fail = $success = 0;
foreach ($feeds as $feed) {
if (isset($feed['Feed']['id'])) {
unset($feed['Feed']['id']);
}
$found = false;
foreach ($existingFeeds as $existingFeed) {
if ($existingFeed['Feed']['url'] == $feed['Feed']['url']) {
$found = true;
}
}
if (!$found) {
$this->Feed->create();
if (!$this->Feed->save($feed, true, array('name', 'provider', 'url', 'rules', 'source_format', 'fixed_event', 'delta_merge', 'override_ids', 'publish', 'settings'))) {
$fail++;
$this->Session->setFlash('Could not save feeds. Reason: ' . json_encode($this->Feed->validationErros));
} else {
$success++;
}
}
}
$message = $success . ' new feeds added.';
if ($fail) {
$message .= ' ' . $fail . ' feeds could not be added (possibly because they already exist)';
}
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Feed', 'importFeeds', false, $this->response->type(), $message);
} else {
$this->Session->setFlash($message);
$this->redirect(array('controller' => 'Feeds', 'action' => 'index', 'all'));
}
}
}
public function add() {
$this->loadModel('Event');
$sgs = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
$distributionLevels = $this->Event->distributionLevels;
if (empty($sgs)) unset($distributionLevels[4]);
$this->set('distributionLevels', $distributionLevels);
$this->set('sharingGroups', $sgs);
$this->set('feed_types', $this->Feed->getFeedTypesOptions());
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->set('tags', $tags);
if ($this->request->is('post')) {
$error = false;
if (isset($this->request->data['Feed']['pull_rules'])) $this->request->data['Feed']['rules'] = $this->request->data['Feed']['pull_rules'];
@ -60,6 +138,14 @@ class FeedsController extends AppController {
}
if (!isset($this->request->data['Feed']['settings'])) {
$this->request->data['Feed']['settings'] = array();
} else {
if (!empty($this->request->data['Feed']['settings']['common']['excluderegex']) && !$this->__checkRegex($this->request->data['Feed']['settings']['common']['excluderegex'])) {
$this->Session->setFlash('Invalid exclude regex. Make sure it\'s a delimited PCRE regex pattern.');
return true;
}
}
if (isset($this->request->data['Feed']['settings']['delimiter']) && empty($this->request->data['Feed']['settings']['delimiter'])) {
$this->request->data['Feed']['settings']['delimiter'] = ',';
}
if (empty($this->request->data['Feed']['target_event'])) {
$this->request->data['Feed']['target_event'] = 0;
@ -75,22 +161,29 @@ class FeedsController extends AppController {
else $this->Session->setFlash('Feed could not be added. Invalid field: ' . array_keys($this->Feed->validationErrors)[0]);
}
}
$this->loadModel('Event');
$sgs = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
$distributionLevels = $this->Event->distributionLevels;
if (empty($sgs)) unset($distributionLevels[4]);
$this->set('distributionLevels', $distributionLevels);
$this->set('sharingGroups', $sgs);
$this->set('feed_types', $this->Feed->getFeedTypesOptions());
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->set('tags', $tags);
}
private function __checkRegex($pattern) {
if (@preg_match($pattern, null) === false) {
return false;
}
return true;
}
public function edit($feedId) {
$this->Feed->id = $feedId;
if (!$this->Feed->exists()) throw new NotFoundException('Invalid feed.');
$this->Feed->read();
$this->loadModel('Event');
$sgs = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
$distributionLevels = $this->Event->distributionLevels;
if (empty($sgs)) unset($distributionLevels[4]);
$this->set('distributionLevels', $distributionLevels);
$this->set('sharingGroups', $sgs);
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->set('feed_types', $this->Feed->getFeedTypesOptions());
$this->set('tags', $tags);
if (!empty($this->Feed->data['Feed']['settings'])) {
$this->Feed->data['Feed']['settings'] = json_decode($this->Feed->data['Feed']['settings'], true);
}
@ -109,6 +202,14 @@ class FeedsController extends AppController {
}
if (!isset($this->request->data['Feed']['settings'])) {
$this->request->data['Feed']['settings'] = array();
} else {
if (!empty($this->request->data['Feed']['settings']['common']['excluderegex']) && !$this->__checkRegex($this->request->data['Feed']['settings']['common']['excluderegex'])) {
$this->Session->setFlash('Invalid exclude regex. Make sure it\'s a delimited PCRE regex pattern.');
return true;
}
}
if (isset($this->request->data['Feed']['settings']['delimiter']) && empty($this->request->data['Feed']['settings']['delimiter'])) {
$this->request->data['Feed']['settings']['delimiter'] = ',';
}
$this->request->data['Feed']['settings'] = json_encode($this->request->data['Feed']['settings']);
$fields = array('id', 'name', 'provider', 'enabled', 'rules', 'url', 'distribution', 'sharing_group_id', 'tag_id', 'fixed_event', 'event_id', 'publish', 'delta_merge', 'override_ids', 'settings');
@ -120,6 +221,10 @@ class FeedsController extends AppController {
}
$result = $this->Feed->save($feed);
if ($result) {
$feedCache = APP . 'tmp' . DS . 'cache' . DS . 'misp_feed_' . intval($feedId) . '.cache';
if (file_exists($feedCache)) {
unlink($feedCache);
}
$this->Session->setFlash('Feed updated.');
$this->redirect(array('controller' => 'feeds', 'action' => 'index'));
} else {
@ -134,16 +239,6 @@ class FeedsController extends AppController {
}
$this->request->data['Feed']['pull_rules'] = $this->request->data['Feed']['rules'];
}
$this->loadModel('Event');
$sgs = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
$distributionLevels = $this->Event->distributionLevels;
if (empty($sgs)) unset($distributionLevels[4]);
$this->set('distributionLevels', $distributionLevels);
$this->set('sharingGroups', $sgs);
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->set('feed_types', $this->Feed->getFeedTypesOptions());
$this->set('tags', $tags);
}
public function delete($feedId) {
@ -241,7 +336,7 @@ class FeedsController extends AppController {
$this->__previewFreetext($this->Feed->data);
}
}
private function __previewIndex($feed) {
if (isset($this->passedArgs['pages'])) $currentPage = $this->passedArgs['pages'];
else $currentPage = 1;
@ -259,7 +354,7 @@ class FeedsController extends AppController {
$this->params->params['paging'] = array($this->modelClass => $params);
if (is_array($events)) $customPagination->truncateByPagination($events, $params);
else ($events = array());
$this->set('events', $events);
$this->loadModel('Event');
$threat_levels = $this->Event->ThreatLevel->find('all');
@ -275,13 +370,24 @@ class FeedsController extends AppController {
$this->set('passedArgs', json_encode($passedArgs));
$this->set('passedArgsArray', $passedArgs);
}
private function __previewFreetext($feed) {
if (isset($this->passedArgs['page'])) $currentPage = $this->passedArgs['page'];
else if (isset($this->passedArgs['page'])) $currentPage = $this->passedArgs['page'];
else $currentPage = 1;
$urlparams = '';
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
if (!in_array($feed['Feed']['source_format'], array('freetext', 'csv'))) throw new MethodNotAllowedException('Invalid feed type.');
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format']);
$params = array();
// params is passed as reference here, the pagination happens in the method, which isn't ideal but considering the performance gains here it's worth it
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage, 60, $params);
// we want false as a valid option for the split fetch, but we don't want it for the preview
if ($resultArray == false) {
$resultArray = array();
}
$this->params->params['paging'] = array($this->modelClass => $params);
$resultArray = $this->Feed->getFreetextFeedCorrelations($resultArray);
// remove all duplicates
foreach ($resultArray as $k => $v) {
@ -296,13 +402,19 @@ class FeedsController extends AppController {
$this->set('attributes', $resultArray);
$this->render('freetext_index');
}
private function __previewCSV($feed) {
if (isset($this->passedArgs['pages'])) $currentPage = $this->passedArgs['pages'];
else $currentPage = 1;
App::uses('SyncTool', 'Tools');
$syncTool = new SyncTool();
if ($feed['Feed']['source_format'] != 'csv') throw new MethodNotAllowedException('Invalid feed type.');
$HttpSocket = $syncTool->setupHttpSocketFeed($feed);
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket);
$resultArray = $this->Feed->getFreetextFeed($feed, $HttpSocket, $feed['Feed']['source_format'], $currentPage);
// we want false as a valid option for the split fetch, but we don't want it for the preview
if ($resultArray == false) {
$resultArray = array();
}
$resultArray = $this->Feed->getFreetextFeedCorrelations($resultArray);
// remove all duplicates
foreach ($resultArray as $k => $v) {
@ -395,7 +507,7 @@ class FeedsController extends AppController {
$result['message'] = $fail ? 'Could not ' . $action . ' feed.' : 'Feed ' . $action . 'd.';
return $result;
}
public function fetchSelectedFromFreetextIndex($id) {
if (!$this->request->is('Post')) {
throw new MethodNotAllowedException('Only POST requests are allowed.');

View File

@ -128,7 +128,7 @@ class OrganisationsController extends AppController {
if (!$this->Organisation->exists()) throw new NotFoundException('Invalid organisation');
$fullAccess = false;
$fields = array('id', 'name', 'date_created', 'date_modified', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid');
if ($this->_isSiteAdmin() || $this->Auth->user('Organisation')['id'] == $id) {
if ($this->_isSiteAdmin() || ($this->_isAdmin() && $this->Auth->user('Organisation')['id'] == $id)) {
$fullAccess = true;
$fields = array_merge($fields, array('created_by'));
}

View File

@ -910,6 +910,7 @@ class ServersController extends AppController {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $beforeResult)),'status'=>200));
}
}
$this->request->data['Server']['value'] = trim($this->request->data['Server']['value']);
if ($found['type'] == 'boolean') {
$this->request->data['Server']['value'] = ($this->request->data['Server']['value'] ? true : false);
}
@ -1154,4 +1155,9 @@ class ServersController extends AppController {
$this->set('response', array('version' => $versionArray['major'] . '.' . $versionArray['minor'] . '.' . $versionArray['hotfix'], 'perm_sync' => $this->userRole['perm_sync']));
$this->set('_serialize', 'response');
}
public function getPyMISPVersion() {
$this->set('response', array('version' => $this->pyMispVersion));
$this->set('_serialize', 'response');
}
}

View File

@ -181,6 +181,7 @@ class ShadowAttributesController extends AppController {
$response = $this->__accept($id);
if ($this->_isRest()) {
if (isset($response['success'])) {
$response['check_publish'] = true;
$this->set('name', $response['success']);
$this->set('message', $response['success']);
$this->set('url', '/shadow_attributes/accept/' . $id);

View File

@ -51,16 +51,9 @@ class SightingsController extends AppController {
}
} else {
if ($error) {
$this->set('errors', $error);
$this->set('name', 'Could not add the Sighting.');
$this->set('message', 'Could not add the Sighting.');
$this->set('_serialize', array('name', 'message', 'errors'));
return $this->RestResponse->saveFailResponse('Sighting', 'add', $id, $error);
} else {
$this->set('name', 'Sighting added.');
$this->set('message', $result . ' sighting' . (($result == 1) ? '' : 's') . ' successfuly added.');
$this->set('url', '/sightings/add/' . $id);
$this->set('id', $this->Sighting->id);
$this->set('_serialize', array('name', 'message', 'url', 'id'));
return $this->RestResponse->saveSuccessResponse('Sighting', 'add', $id, false, $result . ' sighting' . (($result == 1) ? '' : 's') . ' successfuly added.');
}
}
}
@ -76,15 +69,37 @@ class SightingsController extends AppController {
}
$result = $this->Sighting->delete($sighting['Sighting']['id']);
if (!$result) {
$this->set('errors', '');
$this->set('name', 'Failed');
$this->set('message', 'Could not delete the Sighting.');
$this->set('_serialize', array('name', 'message', 'errors'));
return $this->RestResponse->saveFailResponse('Sighting', 'delete', $id, 'Could not delete the Sighting.');
} else {
$this->set('name', 'Success');
$this->set('message', 'Sighting successfuly deleted.');
$this->set('url', '/sightings/delete/' . $id);
$this->set('_serialize', array('name', 'message', 'url'));
return $this->RestResponse->saveSuccessResponse('Sighting', 'delete', $id, false, 'Sighting successfuly deleted.');
}
}
public function index($eventid = false) {
$this->loadModel('Event');
$sightingConditions = array();
if ($eventid) {
$sightingConditions = array('Sighting.event_id' => $eventid);
}
$sightedEvents = $this->Sighting->find('list', array(
'group' => 'Sighting.event_id',
'fields' => array('Sighting.event_id'),
'conditions' => $sightingConditions
));
if (empty($sightedEvents)) {
$this->RestResponse->viewData(array());
}
$conditions = array('metadata' => true, 'contain' => false);
if ($eventid) {
$conditions['eventid'] = $sightedEvents;
}
$events = $this->Event->fetchEventIds($this->Auth->user(), false, false, false, false, false, false, $sightedEvents);
$sightings = array();
if (!empty($events)) {
foreach ($events as $k => $event) {
$sightings = array_merge($sightings, $this->Sighting->attachToEvent($event, $this->Auth->user()));
}
}
return $this->RestResponse->viewData($sightings);
}
}

View File

@ -1236,9 +1236,6 @@ class UsersController extends AppController {
}
public function fetchPGPKey($email = false) {
if (!$this->_isAdmin()) {
throw new Exception('Administrators only.');
}
if ($email == false) {
throw new NotFoundException('No email provided.');
}

View File

@ -99,6 +99,7 @@ class ComplexTypeTool {
public function checkCSV($input, $settings = array()) {
$delimiter = isset($settings['delimiter']) ? $settings['delimiter'] : ",";
$lines = explode("\n", $input);
unset($input);
$values = !empty($settings['value']) ? $settings['value'] : array();
if (!is_array($values)) {
$values = explode(',', $values);
@ -114,6 +115,11 @@ class ComplexTypeTool {
foreach ($elements as $elementPos => $element) {
if ((!empty($values) && in_array(($elementPos + 1), $values)) || empty($values)) {
$element = trim($element, " \t\n\r\0\x0B\"\'");
if (isset($settings['excluderegex']) && !empty($settings['excluderegex'])) {
if (preg_match($settings['excluderegex'], $element)) {
continue;
}
}
$resolvedResult = $this->__resolveType($element);
if (!empty($resolvedResult)) {
$iocArray[] = $resolvedResult;
@ -146,6 +152,11 @@ class ComplexTypeTool {
}
$ioc = preg_replace('/\p{C}+/u', '', $ioc);
if (empty($ioc)) continue;
if (isset($settings['excluderegex']) && !empty($settings['excluderegex'])) {
if (preg_match($settings['excluderegex'], $ioc)) {
continue;
}
}
$typeArray = $this->__resolveType($ioc);
if ($typeArray === false) continue;
$temp = $typeArray;
@ -172,7 +183,7 @@ class ComplexTypeTool {
if (count($compositeParts) == 2) {
if ($this->__resolveFilename($compositeParts[0])) {
foreach ($this->__hexHashTypes as $k => $v) {
if (strlen($compositeParts[1]) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $compositeParts[1])) return array('types' => $v['composite'], 'to_ids' => true, 'default_type' => $v['composite'][0]);
if (strlen($compositeParts[1]) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $compositeParts[1])) return array('types' => $v['composite'], 'to_ids' => true, 'default_type' => $v['composite'][0], 'value' => $input);
}
if (preg_match('#^[0-9]+:[0-9a-zA-Z\/\+]+:[0-9a-zA-Z\/\+]+$#', $compositeParts[1]) && !preg_match('#^[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}$#', $compositeParts[1])) {
return array('types' => array('ssdeep'), 'to_ids' => true, 'default_type' => 'filename|ssdeep', 'value' => $input);
@ -183,7 +194,7 @@ class ComplexTypeTool {
// check for hashes
foreach ($this->__hexHashTypes as $k => $v) {
if (strlen($input) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $input)) return array('types' => $v['single'], 'to_ids' => true, 'default_type' => $v['single'][0]);
if (strlen($input) == $k && preg_match("#[0-9a-f]{" . $k . "}$#i", $input)) return array('types' => $v['single'], 'to_ids' => true, 'default_type' => $v['single'][0], 'value' => $input);
}
if (preg_match('#^[0-9]+:[0-9a-zA-Z\/\+]+:[0-9a-zA-Z\/\+]+$#', $input) && !preg_match('#^[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}$#', $input)) return array('types' => array('ssdeep'), 'to_ids' => true, 'default_type' => 'ssdeep', 'value' => $input);
$inputRefanged = $input;

View File

@ -41,7 +41,11 @@ class AppModel extends Model {
32 => false, 33 => true, 38 => true, 39 => true, 40 => false,
42 => false, 44 => false, 45 => false, 49 => true, 50 => false,
51 => false, 52 => false, 55 => true, 56 => true, 57 => true,
<<<<<<< HEAD
58 => false, 60 => false
=======
58 => false, 59 => false
>>>>>>> 2.4
)
)
);
@ -524,6 +528,14 @@ class AppModel extends Model {
$this->__addIndex('correlations', '1_event_id');
$this->__addIndex('warninglist_entries', 'warninglist_id');
break;
case '2.4.58':
$sqlArray[] = "ALTER TABLE `events` ADD `disable_correlation` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "ALTER TABLE `attributes` ADD `disable_correlation` tinyint(1) NOT NULL DEFAULT 0;";
break;
case '2.4.59':
$sqlArray[] = "ALTER TABLE taxonomy_entries ADD colour varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';";
$sqlArray[] = "ALTER TABLE taxonomy_predicates ADD colour varchar(7) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';";
break;
case '2.4.60':
if ($dataSource == 'Database/Mysql') {
$sqlArray[] = 'CREATE TABLE IF NOT EXISTS `attribute_tags` (
@ -550,7 +562,7 @@ class AppModel extends Model {
}
$this->__dropIndex('attribute_tags', 'attribute_id');
$this->__dropIndex('attribute_tags', 'tag_id');
break;
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;';
@ -567,10 +579,6 @@ class AppModel extends Model {
$sqlArray[] = 'ALTER TABLE `threads` DROP `org`;';
$sqlArray[] = 'ALTER TABLE `users` DROP `org`;';
break;
case '2.4.58':
$sqlArray[] = "ALTER TABLE `events` ADD `disable_correlation` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "ALTER TABLE `attributes` ADD `disable_correlation` tinyint(1) NOT NULL DEFAULT 0;";
break;
default:
return false;
break;

View File

@ -2150,4 +2150,35 @@ class Attribute extends AppModel {
}
else return 'Could not save changes.';
}
public function saveAndEncryptAttribute($attribute, $user) {
$hashes = array('md5' => 'malware-sample', 'sha1' => 'filename|sha1', 'sha256' => 'filename|sha256');
if ($attribute['encrypt']) {
$result = $this->handleMaliciousBase64($attribute['event_id'], $attribute['value'], $attribute['data'], array_keys($hashes));
if (!$result['success']) {
return 'Could not handle the sample';
}
foreach ($hashes as $hash => $typeName) {
if (!$result[$hash]) continue;
$attributeToSave = array(
'Attribute' => array(
'value' => $attribute['value'] . '|' . $result[$hash],
'category' => $attribute['category'],
'type' => $typeName,
'event_id' => $attribute['event_id'],
'comment' => $attribute['comment'],
'to_ids' => 1,
'distribution' => $attribute['distribution'],
'sharing_group_id' => isset($attribute['sharing_group_id']) ? $attribute['sharing_group_id'] : 0,
)
);
if ($hash == 'md5') $attributeToSave['Attribute']['data'] = $result['data'];
$this->create();
if (!$this->save($attributeToSave)) {
return $this->validationErrors;
}
}
}
return true;
}
}

View File

@ -1078,7 +1078,7 @@ class Event extends AppModel {
}
}
public function fetchEventIds($user, $from = false, $to = false, $last = false, $list = false, $timestamp = false, $publish_timestamp = false) {
public function fetchEventIds($user, $from = false, $to = false, $last = false, $list = false, $timestamp = false, $publish_timestamp = false, $eventIdList = false) {
$conditions = array();
// restricting to non-private or same org if the user is not a site-admin.
if (!$user['Role']['perm_site_admin']) {
@ -1109,6 +1109,7 @@ class Event extends AppModel {
if ($last) $conditions['AND'][] = array('Event.publish_timestamp >=' => $last);
if ($timestamp) $conditions['AND'][] = array('Event.timestamp >=' => $timestamp);
if ($publish_timestamp) $conditions['AND'][] = array('Event.publish_timestamp >=' => $publish_timestamp);
if ($eventIdList) $conditions['AND'][] = array('Event.id' => $eventIdList);
if ($list) {
$params = array(
'conditions' => $conditions,
@ -2018,31 +2019,58 @@ class Event extends AppModel {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$attribute['event_id'] = $this->id;
unset($attribute['id']);
$this->Attribute->create();
$attributeSaveResult = $this->Attribute->save($attribute, array('fieldList' => $fieldList['Attribute']));
if ($attributeSaveResult) {
if (isset($attribute['AttributeTag'])) {
foreach ($attribute['AttributeTag'] as $at) {
$this->Attribute->AttributeTag->create();
$at['attribute_id'] = $this->Attribute->id;
$at['event_id'] = $this->id;
$this->Attribute->AttributeTag->save($at);
if (isset($attribute['encrypt'])) {
$saveResult = $this->Attribute->saveAndEncryptAttribute($attribute, $user);
if ($saveResult !== true) {
$validationErrors['Attribute'][$k] = $saveResult;
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'add',
'user_id' => $user['id'],
'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($saveResult ? $saveResult : array()),
));
} else {
if (isset($attribute['AttributeTag'])) {
foreach ($attribute['AttributeTag'] as $at) {
$this->Attribute->AttributeTag->create();
$at['attribute_id'] = $this->Attribute->id;
$at['event_id'] = $this->id;
$this->Attribute->AttributeTag->save($at);
}
}
}
} else {
$validationErrors['Attribute'][$k] = $this->Attribute->validationErrors;
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'add',
'user_id' => $user['id'],
'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($this->Attribute->validationErrors),
));
$this->Attribute->create();
if (!$this->Attribute->save($attribute, array('fieldList' => $fieldList['Attribute']))) {
$validationErrors['Attribute'][$k] = $this->Attribute->validationErrors;
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'add',
'user_id' => $user['id'],
'title' => 'Attribute dropped due to validation for Event ' . $this->id . ' failed: ' . $attribute_short,
'change' => json_encode($this->Attribute->validationErrors),
));
} else {
if (isset($attribute['AttributeTag'])) {
foreach ($attribute['AttributeTag'] as $at) {
$this->Attribute->AttributeTag->create();
$at['attribute_id'] = $this->Attribute->id;
$at['event_id'] = $this->id;
$this->Attribute->AttributeTag->save($at);
}
}
}
}
}
}
@ -2118,8 +2146,8 @@ class Event extends AppModel {
if (isset($data['Event']['published']) && $data['Event']['published'] && !$user['Role']['perm_publish']) $data['Event']['published'] = 0;
if (!isset($data['Event']['published'])) $data['Event']['published'] = 0;
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'distribution', 'timestamp', 'sharing_group_id'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment', 'sharing_group_id', 'deleted')
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'distribution', 'timestamp', 'sharing_group_id', 'disable_correlation'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp', 'comment', 'sharing_group_id', 'deleted', 'disable_correlation')
);
$saveResult = $this->save(array('Event' => $data['Event']), array('fieldList' => $fieldList['Event']));
$this->Log = ClassRegistry::init('Log');
@ -2127,37 +2155,75 @@ class Event extends AppModel {
$validationErrors = array();
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => $attribute) {
if (isset($attribute['uuid'])) {
$existingAttribute = $this->Attribute->findByUuid($attribute['uuid']);
if (count($existingAttribute)) {
if ($existingAttribute['Attribute']['event_id'] != $id) {
$result = $this->Log->save(array(
$attribute['event_id'] = $existingEvent['Event']['id'];
if (isset($attribute['encrypt'])) {
if (isset($attribute['uuid'])) {
$existingAttribute = $this->Attribute->findByUuid($attribute['uuid']);
if (!empty($existingAttribute)) {
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Event',
'model_id' => $id,
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'edit',
'action' => 'add',
'user_id' => $user['id'],
'title' => 'Duplicate UUID found in attribute',
'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'],
'title' => 'Attribute dropped because the encrypt parameter was passed along an attribute that already exists',
'change' => '',
));
unset($data['Event']['Attribute'][$k]);
} else {
// If a field is not set in the request, just reuse the old value
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id');
foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $data['Event']['Attribute'][$k][$rF] = $existingAttribute['Attribute'][$rF];
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if (isset($data['Event']['Attribute'][$k]['timestamp'])) {
if ($data['Event']['Attribute'][$k]['timestamp'] <= $existingAttribute['Attribute']['timestamp']) {
unset($data['Event']['Attribute'][$k]);
continue;
}
}
continue;
}
$saveResult = $this->Attribute->saveAndEncryptAttribute($attribute, $user);
if ($saveResult !== true) {
$attribute_short = (isset($attribute['category']) ? $attribute['category'] : 'N/A') . '/' . (isset($attribute['type']) ? $attribute['type'] : 'N/A') . ' ' . (isset($attribute['value']) ? $attribute['value'] : 'N/A');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'add',
'user_id' => $user['id'],
'title' => 'Attribute dropped due to validation for Event ' . $id . ' failed: ' . $attribute_short,
'change' => json_encode($saveResult ? $saveResult : array()),
));
}
} else {
if (isset($attribute['uuid'])) {
$existingAttribute = $this->Attribute->findByUuid($attribute['uuid']);
if (count($existingAttribute)) {
if ($existingAttribute['Attribute']['event_id'] != $id) {
$result = $this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => 'Attribute',
'model_id' => 0,
'email' => $user['email'],
'action' => 'edit',
'user_id' => $user['id'],
'title' => 'Duplicate UUID found in attribute',
'change' => 'An attribute was blocked from being saved due to a duplicate UUID. The uuid in question is: ' . $attribute['uuid'],
));
unset($data['Event']['Attribute'][$k]);
} else {
$data['Event']['Attribute'][$k]['timestamp'] = $date;
// If a field is not set in the request, just reuse the old value
$recoverFields = array('value', 'to_ids', 'distribution', 'category', 'type', 'comment', 'sharing_group_id');
foreach ($recoverFields as $rF) if (!isset($attribute[$rF])) $data['Event']['Attribute'][$k][$rF] = $existingAttribute['Attribute'][$rF];
$data['Event']['Attribute'][$k]['id'] = $existingAttribute['Attribute']['id'];
// Check if the attribute's timestamp is bigger than the one that already exists.
// If yes, it means that it's newer, so insert it. If no, it means that it's the same attribute or older - don't insert it, insert the old attribute.
// Alternatively, we could unset this attribute from the request, but that could lead with issues if we decide that we want to start deleting attributes that don't exist in a pushed event.
if (isset($data['Event']['Attribute'][$k]['timestamp'])) {
if ($data['Event']['Attribute'][$k]['timestamp'] <= $existingAttribute['Attribute']['timestamp']) {
unset($data['Event']['Attribute'][$k]);
continue;
}
} else {
$data['Event']['Attribute'][$k]['timestamp'] = $date;
}
}
} else {
$this->Attribute->create();
}
} else {
$this->Attribute->create();

View File

@ -63,7 +63,7 @@ class EventTag extends AppModel {
}
return true;
}
public function getSortedTagList($context = false) {
$conditions = array();
$tag_counts = $this->find('all', array(

View File

@ -33,7 +33,7 @@ class Feed extends AppModel {
'message' => 'Please enter a numeric event ID or leave this field blank.',
)
);
// currently we only have an internal name and a display name, but later on we can expand this with versions, default settings, etc
public $feed_types = array(
'misp' => array(
@ -46,7 +46,7 @@ class Feed extends AppModel {
'name' => 'Simple CSV Parsed Feed'
)
);
public function getFeedTypesOptions() {
$result = array();
foreach ($this->feed_types as $key => $value) {
@ -95,24 +95,55 @@ class Feed extends AppModel {
$events = $this->__filterEventsIndex($events, $feed);
return $events;
}
public function getFreetextFeed($feed, $HttpSocket, $type = 'freetext') {
public function getFreetextFeed($feed, $HttpSocket, $type = 'freetext', $page = 1, $limit = 60, &$params = array()) {
$result = array();
$response = $HttpSocket->get($feed['Feed']['url'], '', array());
if ($response->code == 200) {
App::uses('ComplexTypeTool', 'Tools');
$complexTypeTool = new ComplexTypeTool();
$this->Warninglist = ClassRegistry::init('Warninglist');
$complexTypeTool->setTLDs($this->Warninglist->fetchTLDLists());
$resultArray = $complexTypeTool->checkComplexRouter($response->body, $type, isset($feed['Feed']['settings'][$type]) ? $feed['Feed']['settings'][$type] : array());
$feedCache = APP . 'tmp' . DS . 'cache' . DS . 'misp_feed_' . intval($feed['Feed']['id']) . '.cache';
$doFetch = true;
if (file_exists($feedCache)) {
$file = new File($feedCache);
if (time() - $file->lastChange() < 600) {
$doFetch = false;
$data = file_get_contents($feedCache);
}
}
if ($doFetch) {
$response = $HttpSocket->get($feed['Feed']['url'], '', array());
if ($response->code == 200) {
$data = $response->body;
file_put_contents($feedCache, $data);
}
}
App::uses('ComplexTypeTool', 'Tools');
$complexTypeTool = new ComplexTypeTool();
$this->Warninglist = ClassRegistry::init('Warninglist');
$complexTypeTool->setTLDs($this->Warninglist->fetchTLDLists());
$settings = array();
if (isset($feed['Feed']['settings'][$type])) {
$settings = $feed['Feed']['settings'][$type];
}
if (isset($feed['Feed']['settings']['common'])) {
$settings = array_merge($settings, $feed['Feed']['settings']['common']);
}
$resultArray = $complexTypeTool->checkComplexRouter($data, $type, $settings);
$this->Attribute = ClassRegistry::init('Attribute');
foreach ($resultArray as $key => $value) {
$resultArray[$key]['category'] = $this->Attribute->typeDefinitions[$value['default_type']]['default_category'];
}
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$params = $customPagination->createPaginationRules($resultArray, array('page' => $page, 'limit' => $limit), 'Feed', $sort = false);
if (!empty($page) && $page != 'all') {
$start = ($page - 1) * $limit;
if ($start > count($resultArray)) {
return false;
}
$resultArray = array_slice($resultArray, $start, $limit);
}
return $resultArray;
}
public function getFreetextFeedCorrelations($data) {
$values = array();
foreach ($data as $key => $value) {
@ -129,7 +160,7 @@ class Feed extends AppModel {
$data[$key]['correlations'] = array_values($correlations[$value['value']]);
}
}
return $data;
return $data;
}
public function downloadFromFeed($actions, $feed, $HttpSocket, $user, $jobId = false) {
@ -189,9 +220,9 @@ class Feed extends AppModel {
try {
$commit = trim(shell_exec('git log --pretty="%H" -n1 HEAD'));
} catch (Exception $e) {
$commit = false;
$commit = false;
}
$result = array(
'header' => array(
'Accept' => 'application/json',
@ -416,6 +447,9 @@ class Feed extends AppModel {
$syncTool = new SyncTool();
$job = ClassRegistry::init('Job');
$this->read();
if (isset($this->data['Feed']['settings']) && !empty($this->data['Feed']['settings'])) {
$this->data['Feed']['settings'] = json_decode($this->data['Feed']['settings'], true);
}
$HttpSocket = $syncTool->setupHttpSocketFeed($this->data);
if ($this->data['Feed']['source_format'] == 'misp') {
if ($jobId) {
@ -437,28 +471,33 @@ class Feed extends AppModel {
$job->id = $jobId;
$job->saveField('message', 'Fetching data.');
}
$data = $this->getFreetextFeed($this->data, $HttpSocket, $this->data['Feed']['source_format']);
foreach ($data as $key => $value) {
$data[$key] = array(
$temp = $this->getFreetextFeed($this->data, $HttpSocket, $this->data['Feed']['source_format'], 'all');
foreach ($temp as $key => $value) {
$data[] = array(
'category' => $value['category'],
'type' => $value['default_type'],
'value' => $value['value'],
'to_ids' => $value['to_ids']
);
}
if ($jobId) {
$job->saveField('progress', 50);
$job->saveField('message', 'Saving data.');
}
$result = $this->saveFreetextFeedData($this->data, $data, $user);
$message = 'Job complete.';
if ($result !== true) {
return false;
}
if ($jobId) {
$job->saveField('progress', '100');
$job->saveField('message', 'Job complete.');
}
}
return $result;
}
public function saveFreetextFeedData($feed, $data, $user) {
public function saveFreetextFeedData($feed, $data, $user, $jobId = false) {
$this->Event = ClassRegistry::init('Event');
$event = false;
if ($feed['Feed']['fixed_event'] && $feed['Feed']['event_id']) {
@ -493,7 +532,7 @@ class Feed extends AppModel {
$to_delete = array();
foreach ($data as $k => $dataPoint) {
foreach ($event['Attribute'] as $attribute_key => $attribute) {
if ($dataPoint['value'] == $attribute['value']) {
if ($dataPoint['value'] == $attribute['value'] && $dataPoint['type'] == $attribute['type'] && $attribute['category'] == $dataPoint['category']) {
unset($data[$k]);
unset($event['Attribute'][$attribute_key]);
}
@ -518,8 +557,16 @@ class Feed extends AppModel {
$data[$key]['sharing_group_id'] = $feed['Feed']['sharing_group_id'];
$data[$key]['to_ids'] = $feed['Feed']['override_ids'] ? 0 : $data[$key]['to_ids'];
}
if (!$this->Event->Attribute->saveMany($data)) {
return 'Could not save the parsed attributes.';
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->id = $jobId;
}
$data = array_chunk($data, 100);
foreach ($data as $k => $chunk) {
$this->Event->Attribute->saveMany($chunk);
if ($jobId) {
$job->saveField('progress', 50 + round((50 * ((($k + 1) * 100) / count($data)))));
}
}
if ($feed['Feed']['publish']) {
$this->Event->publishRouter($event['Event']['id'], null, $user);

View File

@ -71,6 +71,13 @@ class Log extends AppModel {
'email' => array('values' => array('admin_email'))
);
public function beforeValidete() {
parent::beforeValidate();
if (!isset($this->data['Log']['org']) || empty($this->data['Log']['org'])) {
$this->data['Log']['org'] = 'SYSTEM';
}
}
public function beforeSave($options = array()) {
if (Configure::read('MISP.log_client_ip') && isset($_SERVER['REMOTE_ADDR'])) $this->data['Log']['ip'] = $_SERVER['REMOTE_ADDR'];
$setEmpty = array('title' => '', 'model' => '', 'model_id' => 0, 'action' => '', 'user_id' => 0, 'change' => '', 'email' => '', 'org' => '', 'description' => '');

View File

@ -911,6 +911,34 @@ class Server extends AppModel {
'type' => 'string',
),
),
'Session' => array(
'branch' => 1,
'autoRegenerate' => array(
'level' => 1,
'description' => 'Set to true to automatically regenerate sessions on activity. (Recommended)',
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
),
'defaults' => array(
'level' => 0,
'description' => 'The session type used by MISP. The default setting is database, which will use the MySQL tables for the session data (supported options: database, php). The recommended option is php and setting your PHP up to use redis sessions via your php.ini. Just add \'session.save_handler = redis\' and "session.save_path = \'tcp://localhost:6379\'" (replace the latter with your redis connection) to ',
'value' => '',
'errorMessage' => '',
'test' => 'testForSessionDefaults',
'type' => 'string',
'options' => array('php' => 'php', 'database' => 'database', 'cake' => 'cake', 'cache' => 'cache'),
),
'timeout' => array(
'level' => 0,
'description' => 'The timeout duration of sessions (in MINUTES). Keep in mind that autoregenerate can be used to extend the session on user activity.',
'value' => '',
'errorMessage' => '',
'test' => 'testForNumeric',
'type' => 'string',
)
),
'Plugin' => array(
'branch' => 1,
'RPZ_policy' => array(
@ -1308,7 +1336,8 @@ class Server extends AppModel {
'GnuPG' => 'Encryption',
'SMIME' => 'Encryption',
'misc' => 'Security',
'Security' => 'Security'
'Security' => 'Security',
'Session' => 'Security'
);
public $validEventIndexFilters = array('searchall', 'searchpublished', 'searchorg', 'searchtag', 'searcheventid', 'searchdate', 'searcheventinfo', 'searchthreatlevel', 'searchdistribution', 'searchanalysis', 'searchattribute');
@ -2054,6 +2083,14 @@ class Server extends AppModel {
return true;
}
public function testForSessionDefaults($value) {
if (empty($value) || !in_array($value, array('php', 'database', 'cake', 'cache'))) {
return 'Please choose a valid session handler. Recommended values: php or database. Alternate options are cake (cakephp file based sessions) and cache.';
} else {
return true;
}
}
public function testLocalOrg($value) {
$this->Organisation = ClassRegistry::init('Organisation');
if ($value == 0) return 'No organisation selected';
@ -2067,6 +2104,7 @@ class Server extends AppModel {
}
public function testForEmpty($value) {
$value = trim($value);
if ($value === '') return 'Value not set.';
return true;
}
@ -2122,7 +2160,6 @@ class Server extends AppModel {
}
public function testBool($value) {
if ($this->testForEmpty($value) !== true) return $this->testForEmpty($value);
if ($value !== true && $value !== false) return 'Value is not a boolean, make sure that you convert \'true\' to true for example.';
return true;
}
@ -2353,7 +2390,14 @@ class Server extends AppModel {
}
Configure::write('Security.auth', $authmethods);
}
Configure::dump('config.php', 'default', array('MISP', 'GnuPG', 'SMIME', 'Proxy', 'SecureAuth', 'Security', 'debug', 'site_admin_debug', 'Plugin', 'CertAuth', 'ApacheShibbAuth', 'ApacheSecureAuth'));
$settingsToSave = array('debug', 'MISP', 'GnuPG', 'SMIME', 'Proxy', 'SecureAuth', 'Security', 'Session.defaults', 'Session.timeout', 'Session.autoRegenerate', 'site_admin_debug', 'Plugin', 'CertAuth', 'ApacheShibbAuth', 'ApacheSecureAuth');
$settingsArray = array();
foreach ($settingsToSave as $setting) {
$settingsArray[$setting] = Configure::read($setting);
}
$settingsString = var_export($settingsArray, true);
$settingsString = '<?php' . "\n" . '$config = ' . $settingsString . ';';
file_put_contents(APP . 'Config' . DS . 'config.php', $settingsString);
}
public function checkVersion($newest) {

View File

@ -37,7 +37,7 @@ class Sighting extends AppModel {
return true;
}
public function attachToEvent(&$event, &$user) {
public function attachToEvent(&$event, $user) {
$ownEvent = false;
if ($user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id']) $ownEvent = true;
$conditions = array('Sighting.event_id' => $event['Event']['id']);

View File

@ -56,7 +56,7 @@ class Taxonomy extends AppModel {
'fields' => array('version', 'enabled', 'namespace')
));
if (empty($current) || $vocab['version'] > $current['Taxonomy']['version']) {
$result = $this->__updateVocab($vocab, $current, array('colour'));
$result = $this->__updateVocab($vocab, $current);
if (is_numeric($result)) {
$updated['success'][$result] = array('namespace' => $vocab['namespace'], 'new' => $vocab['version']);
if (!empty($current)) $updated['success'][$result]['old'] = $current['Taxonomy']['version'];
@ -112,11 +112,17 @@ class Taxonomy extends AppModel {
foreach ($predicate['TaxonomyEntry'] as $entry) {
$temp = array('tag' => $taxonomy['Taxonomy']['namespace'] . ':' . $predicate['value'] . '="' . $entry['value'] . '"');
$temp['expanded'] = (!empty($predicate['expanded']) ? $predicate['expanded'] : $predicate['value']) . ': ' . (!empty($entry['expanded']) ? $entry['expanded'] : $entry['value']);
if (isset($entry['colour']) && !empty($entry['colour'])) {
$temp['colour'] = $entry['colour'];
}
$entries[] = $temp;
}
} else {
$temp = array('tag' => $taxonomy['Taxonomy']['namespace'] . ':' . $predicate['value']);
$temp['expanded'] = !empty($predicate['expanded']) ? $predicate['expanded'] : $predicate['value'];
if (isset($predicate['colour']) && !empty($predicate['colour'])) {
$temp['colour'] = $predicate['colour'];
}
$entries[] = $temp;
}
}
@ -214,7 +220,7 @@ class Taxonomy extends AppModel {
if (isset($tags[strtoupper($entry['tag'])])) {
$temp = $tags[strtoupper($entry['tag'])];
if ((in_array('colour', $skipUpdateFields) && $temp['Tag']['colour'] != $colours[$k]) || (in_array('name', $skipUpdateFields) && $temp['Tag']['name'] !== $entry['tag'])) {
if (!in_array('colour', $skipUpdateFields)) $temp['Tag']['colour'] = $colours[$k];
if (!in_array('colour', $skipUpdateFields)) $temp['Tag']['colour'] = (isset($entry['colour']) && !empty($entry['colour'])) ? $entry['colour'] : $colours[$k];
if (!in_array('name', $skipUpdateFields)) $temp['Tag']['name'] = $entry['tag'];
$this->Tag->save($temp['Tag']);
}
@ -232,21 +238,25 @@ class Taxonomy extends AppModel {
$tags = $this->Tag->getTagsForNamespace($taxonomy['Taxonomy']['namespace']);
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
foreach ($taxonomy['entries'] as $k => $entry) {
$colour = $colours[$k];
if (isset($entry['colour']) && !empty($entry['colour'])) {
$colour = $entry['colour'];
}
if ($tagList) {
foreach ($tagList as $tagName) {
if ($tagName === $entry['tag']) {
if (isset($tags[strtoupper($entry['tag'])])) {
$this->Tag->quickEdit($tags[strtoupper($entry['tag'])], $tagName, $colours[$k]);
$this->Tag->quickEdit($tags[strtoupper($entry['tag'])], $tagName, $colour);
} else {
$this->Tag->quickAdd($tagName, $colours[$k]);
$this->Tag->quickAdd($tagName, $colour);
}
}
}
} else {
if (isset($tags[strtoupper($entry['tag'])])) {
$this->Tag->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colours[$k]);
$this->Tag->quickEdit($tags[strtoupper($entry['tag'])], $entry['tag'], $colour);
} else {
$this->Tag->quickAdd($entry['tag'], $colours[$k]);
$this->Tag->quickAdd($entry['tag'], $colour);
}
}
}

View File

@ -242,6 +242,7 @@ class User extends AppModel {
$this->data['User']['confirm_password'] = $this->data['User']['password'];
}
}
if (!isset($this->data['User']['certif_public']) || empty($this->data['User']['certif_public'])) $this->data['User']['certif_public'] = '';
if (!isset($this->data['User']['authkey']) || empty($this->data['User']['authkey'])) $this->data['User']['authkey'] = $this->generateAuthKey();
if (!isset($this->data['User']['nids_sid']) || empty($this->data['User']['nids_sid'])) $this->data['User']['nids_sid'] = mt_rand(1000000, 9999999);
return true;

View File

@ -105,7 +105,13 @@
$clusterList[$cluster['Galaxy']['id']][] = array('value' => $cluster['value'], 'id' => $cluster['id'], 'tag_id' => $cluster['tag_id']);
endforeach;
endif;
$first = true;
foreach ($clusterList as $galaxy_id => $clusters):
if (!$first) {
echo '<br />';
} else {
$first = false;
}
?>
<span class="blue bold"><a href="<?php echo $baseurl; ?>/galaxies/view/<?php echo h($galaxy_id); ?>"><?php echo h($galaxyList[$galaxy_id]); ?></a>:</span>
<?php

View File

@ -366,12 +366,14 @@
if ($mayChangeCorrelation && !$event['Event']['disable_correlation']):
if ($object['objectType'] == 0):
?>
<td class="short" style="padding-top:3px;">
<td class="short <?php echo $extra; ?>" style="padding-top:3px;">
<input class="correlation-toggle" type="checkbox" data-attribute-id="<?php echo h($object['id']); ?>" <?php echo $object['disable_correlation'] ? '' : 'checked'; ?>>
</td>
<?php
else:
echo '&nbsp;';
?>
<td class="short <?php echo $extra; ?>" style="padding-top:3px;">&nbsp;</td>
<?php
endif;
endif;
?>

View File

@ -312,6 +312,7 @@
case 'feeds': ?>
<li id='liindex'><a href="<?php echo $baseurl;?>/feeds/index">List Feeds</a></li>
<li id='liadd'><a href="<?php echo $baseurl;?>/feeds/add">Add Feed</a></li>
<li id='liadd'><a href="<?php echo $baseurl;?>/feeds/importFeeds">Import Feeds from JSON</a></li>
<?php if ($menuItem === 'edit'): ?>
<li class="active"><a href="#">Edit Feed</a></li>
<?php elseif ($menuItem === 'previewIndex'): ?>

View File

@ -7,16 +7,20 @@
'label' => '<b>MISP XML or JSON file</b>',
'type' => 'file',
));
if (Configure::read('MISP.take_ownership_xml_import')):
?>
?>
<div class="input clear"></div>
<?php
<?php
if (Configure::read('MISP.take_ownership_xml_import')):
echo $this->Form->input('Event.takeownership', array(
'checked' => false,
'label' => 'Take ownership of the event',
'title' => 'Warning: This will change the creator organisation of the event, tampering with the event\'s ownership and releasability and can lead to unexpected behaviour when synchronising the event with instances that have another creator for the same event.'
));
endif;
echo $this->Form->input('publish', array(
'checked' => false,
'label' => 'Publish imported events',
));
?>
</fieldset>
<?php

View File

@ -184,7 +184,7 @@
<dd class="background-red bold"><?php echo h($subject);?> requested that <?php echo h($target)?> take over this event. (<a href="#" style="color:white;" onClick="genericPopup('<?php echo $baseurl;?>/eventDelegations/view/<?php echo h($delegationRequest['EventDelegation']['id']);?>', '#confirmation_box');">View request details</a>)</dd>
<?php endif;?>
<?php
if (!Configure::read('MISP.completely_disable_correlation')):
if (!Configure::read('MISP.completely_disable_correlation') && Configure::read('MISP.allow_disabling_correlation')):
?>
<dt <?php echo $event['Event']['disable_correlation'] ? 'class="background-red bold"' : '';?>>Correlation</dt>
<dd <?php echo $event['Event']['disable_correlation'] ? 'class="background-red bold"' : '';?>>
@ -199,6 +199,12 @@
Enabled (<a onClick="getPopup('<?php echo h($event['Event']['id']); ?>', 'events', 'toggleCorrelation', '', '#confirmation_box');" style="cursor:pointer;" style="font-weight:normal;">disable</a>)
<?php
endif;
else:
if ($event['Event']['disable_correlation']):
echo 'Disabled';
else:
echo 'Enabled';
endif;
endif;
?>
</dd>

View File

@ -28,7 +28,7 @@
));
?>
<div id="TargetDiv" class="optionalField">
<?php
<?php
echo $this->Form->input('fixed_event', array(
'label' => 'Target Event',
'div' => 'input clear',
@ -58,6 +58,29 @@
));
?>
</div>
<div id="settingsCsvDelimiterDiv" class="optionalField">
<?php
echo $this->Form->input('Feed.settings.csv.delimiter', array(
'label' => 'Delimiter',
'title' => 'Set the default CSV delimiter (default = ",")',
'div' => 'input clear',
'placeholder' => ',',
'class' => 'form-control span6',
'value' => ','
));
?>
</div>
<div id="settingsCommonExcluderegexDiv" class="optionalField">
<?php
echo $this->Form->input('Feed.settings.common.excluderegex', array(
'label' => 'Exclusion Regex',
'title' => 'Add a regex pattern for detecting iocs that should be skipped (this can be useful to exclude any references to the actual report / feed for example)',
'div' => 'input clear',
'placeholder' => 'Regex pattern, for example: "/^https://myfeedurl/i',
'class' => 'form-control span6'
));
?>
</div>
<div id="PublishDiv" class="input clear optionalField">
<?php
echo $this->Form->input('publish', array(
@ -88,7 +111,7 @@
));
?>
</div>
<?php
<?php
echo $this->Form->input('distribution', array(
'options' => array($distributionLevels),
'div' => 'input clear',

View File

@ -28,7 +28,7 @@
));
?>
<div id="TargetDiv" class="optionalField">
<?php
<?php
echo $this->Form->input('fixed_event', array(
'label' => 'Target Event',
'div' => 'input clear',
@ -58,6 +58,28 @@
));
?>
</div>
<div id="settingsCsvDelimiterDiv" class="optionalField">
<?php
echo $this->Form->input('Feed.settings.csv.delimiter', array(
'label' => 'Delimiter',
'title' => 'Set the default CSV delimiter (default = ",")',
'div' => 'input clear',
'placeholder' => ',',
'class' => 'form-control span6'
));
?>
</div>
<div id="settingsCommonExcluderegexDiv" class="optionalField">
<?php
echo $this->Form->input('Feed.settings.common.excluderegex', array(
'label' => 'Exclusion Regex',
'title' => 'Add a regex pattern for detecting iocs that should be skipped (this can be useful to exclude any references to the actual report / feed for example)',
'div' => 'input clear',
'placeholder' => 'Regex pattern, for example: "/^https://myfeedurl/i"',
'class' => 'form-control span6'
));
?>
</div>
<div id="PublishDiv" class="input clear optionalField">
<?php
echo $this->Form->input('publish', array(
@ -87,7 +109,7 @@
));
?>
</div>
<?php
<?php
echo $this->Form->input('distribution', array(
'options' => array($distributionLevels),
'div' => 'input clear',

View File

@ -1,6 +1,6 @@
<div class="attributes index">
<h2>Parsed attributes from feed <?php echo h($feed['Feed']['name']);?></h2>
<?php
<?php
echo $this->Form->create('Feed', array('url' => array('controller' => 'feeds', 'action' => 'fetchSelectedFromFreetextIndex', $feed['Feed']['id'])));
echo $this->Form->input('data', array('style' => 'display:none;', 'label' => false, 'div' => false));
?>
@ -8,6 +8,23 @@
<?php
echo $this->Form->end();
?>
<div class="pagination">
<ul>
<?php
$url = array_merge(array('controller' => 'feeds', 'action' => 'previewIndex', $feed['Feed']['id']), $this->request->named);
$this->Paginator->options(array(
'url' => $url,
'update' => '.span12',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><input class="select_all" type="checkbox" onClick="toggleAllAttributeCheckboxes();" /></th>
@ -18,7 +35,7 @@
<th>Correlations</th>
<th>Distribution</th>
</tr>
<?php
<?php
foreach ($attributes as $key => $attribute):
?>
<tr>
@ -30,12 +47,12 @@
<td id="<?php echo h($key);?>_value"><?php echo h($attribute['value']);?></td>
<td class="short" id="<?php echo h($key);?>_to_ids" data-value="<?php echo h($attribute['to_ids']); ?>"><span class="icon-<?php echo $attribute['to_ids'] ? 'ok' : 'remove';?>"></span></td>
<td class="shortish">
<?php
<?php
if (isset($attribute['correlations'])):
foreach ($attribute['correlations'] as $correlation):
?>
<a href="<?php echo $baseurl; ?>/events/view/<?php echo h($correlation); ?>"><?php echo h($correlation); ?></a>
<?php
<?php
endforeach;
endif;
?>&nbsp;
@ -45,7 +62,7 @@
if ($feed['Feed']['distribution'] == 4):
?>
<a href="<?php echo $baseurl; ?>/sharing_groups/view/<?php echo h($feed['Feed']['sharing_group_id']); ?>"><?php echo h($feed['SharingGroup']['name']); ?></a>
<?php
<?php
else:
echo h($distributionLevels[$feed['Feed']['distribution']]);
endif;
@ -56,7 +73,22 @@
endforeach;
?>
</table>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'url' => $url,
'update' => '.span12',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'feeds', 'menuItem' => 'add'));
@ -90,4 +122,4 @@ function freetextFeedFetchSelected() {
}
</script>
</script>

View File

@ -0,0 +1,25 @@
<div class="feed form">
<?php echo $this->Form->create('Feed');?>
<fieldset>
<legend>Paste feed data</legend>
<p>Paste a MISP feed metadata JSON below to add feeds.</p>
<div>
<?php
echo $this->Form->input('json', array(
'div' => 'input clear',
'placeholder' => 'Feed metadata JSON',
'class' => 'form-control span6',
'type' => 'textarea',
'rows' => 18
));
?>
</div>
</fieldset>
<?php
echo $this->Form->button('Add', array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'feeds', 'menuItem' => 'import'));
?>

View File

@ -15,6 +15,11 @@
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<div class="tabMenuFixedContainer" style="display:inline-block;">
<span class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php echo $scope == 'default' ? 'tabMenuActive' : ''; ?>" onclick="window.location='/feeds/index/scope:default'">Default feeds</span>
<span class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php echo $scope == 'custom' ? 'tabMenuActive' : ''; ?> " onclick="window.location='/feeds/index/scope:custom'">Custom Feeds</span>
<span class="tabMenuFixed tabMenuFixedCenter tabMenuSides useCursorPointer <?php echo $scope == 'all' ? 'tabMenuActive' : ''; ?> " onclick="window.location='/feeds/index/scope:all'">All Feeds</span>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
@ -70,18 +75,18 @@ foreach ($feeds as $item):
<td><?php echo h($item['Feed']['provider']); ?>&nbsp;</td>
<td><?php echo h($item['Feed']['url']); ?>&nbsp;</td>
<td class="shortish">
<?php
<?php
if (in_array($item['Feed']['source_format'], array('freetext', 'csv'))):
if ($item['Feed']['fixed_event']):
if (isset($item['Feed']['event_error'])):
?>
<span class="red bold">Error: Invalid event!</span>
<?php
<?php
else:
if ($item['Feed']['event_id']):
?>
<a href="<?php echo $baseurl;?>/events/view/<?php echo h($item['Feed']['event_id']); ?>">Fixed event <?php echo h($item['Feed']['event_id']); ?></a>
<?php
<?php
else:
echo 'New fixed event';
endif;
@ -90,15 +95,15 @@ foreach ($feeds as $item):
else:
echo ' ';
endif;
?>
?>
</td>
<?php
if ($item['Feed']['source_format'] == 'freetext'):
<?php
if ($item['Feed']['source_format'] != 'misp'):
?>
<td><span class="<?php echo ($item['Feed']['publish'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td><span class="<?php echo ($item['Feed']['delta_merge'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<td><span class="<?php echo ($item['Feed']['override_ids'] ? 'icon-ok' : 'icon-remove'); ?>"></span></td>
<?php
<?php
else:
?>
<td>&nbsp;</td>
@ -129,6 +134,7 @@ foreach ($feeds as $item):
?>
<a href="<?php echo $baseurl;?>/feeds/edit/<?php echo h($item['Feed']['id']); ?>"><span class="icon-edit" title="edit">&nbsp;</span></a>
<?php echo $this->Form->postLink('', array('action' => 'delete', h($item['Feed']['id'])), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to permanently remove the feed (%s)?', h($item['Feed']['name']))); ?>
<a href="<?php echo $baseurl;?>/feeds/view/<?php echo h($item['Feed']['id']); ?>.json" title="Download feed metadata as JSON" download><span class="icon-download"></span></a>
</td>
</tr><?php
endforeach; ?>

@ -1 +1 @@
Subproject commit 0d97a18f2d871e8f507010c55015ce2aee5e3254
Subproject commit a42d4c4f4f45102ded4093f3868479a26dbf4607

@ -1 +1 @@
Subproject commit 40d96b6f2d19afc74eeb0ee3b10bd6a0b76da5fe
Subproject commit f65e0a9b6e27ac5118a8175fd01788982902c07d

@ -1 +1 @@
Subproject commit 338241e4999fa618909241e7e413c9fab5debca5
Subproject commit 88be67d44a5d7f79bad2862d4e4f8de3e2a7c1a7

0
app/tmp/cache/feeds/empty vendored Normal file
View File

View File

@ -2629,6 +2629,7 @@ function feedFormUpdate() {
$('#TargetEventDiv').show();
$('#DeltaMergeDiv').show();
}
$('#settingsCommonExcluderegexDiv').show();
break;
case 'csv':
$('#TargetDiv').show();
@ -2639,6 +2640,8 @@ function feedFormUpdate() {
$('#DeltaMergeDiv').show();
}
$('#settingsCsvValueDiv').show();
$('#settingsCsvDelimiterDiv').show();
$('#settingsCommonExcluderegexDiv').show();
break;
}
}
@ -2784,11 +2787,11 @@ function checkAndSetPublishedInfo() {
var id = $('#hiddenSideMenuData').data('event-id');
$.get( "/events/checkPublishedStatus/" + id, function(data) {
if (data == 1) {
$('.published').show();
$('.not-published').hide();
$('.published').removeClass('hidden');
$('.not-published').addClass('hidden');
} else {
$('.published').hide();
$('.not-published').show();
$('.published').addClass('hidden');
$('.not-published').removeClass('hidden');
}
});
}