Merge branch 'feature/CakeResque' into feature/test

pull/217/head
iglocska 2014-01-06 12:12:51 +01:00
commit 785f57143a
62 changed files with 4452 additions and 1170 deletions

11
.gitignore vendored
View File

@ -18,3 +18,14 @@
/app/Config/core.php
/cakephp
/app/webroot/gpg.asc
/app/Plugin/CakeResque/Config/bootstrap.php
/app/tmp/cached_exports/csv
/app/tmp/cached_exports/csv_all
/app/tmp/cached_exports/csv_sig
/app/tmp/cached_exports/md5
/app/tmp/cached_exports/sha1
/app/tmp/cached_exports/snort
/app/tmp/cached_exports/suricata
/app/tmp/cached_exports/text
/app/tmp/cached_exports/xml
/app/tmp/logs

View File

@ -26,3 +26,5 @@ CREATE TABLE IF NOT EXISTS `posts` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
ALTER TABLE `attributes` ADD `comment` TEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;
ALTER TABLE `shadow_attributes` ADD `event_org` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;
ALTER TABLE `shadow_attributes` ADD `comment` TEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL;

View File

@ -128,16 +128,18 @@ Configure::write('CyDefSig.cveurl', 'http://web.nvd.nist.gov/view/vuln/detail?vu
// The following 3 fields are optional
//Configure::write('MISP.welcome_text_top', 'Welcome to the Organisation community\'s'); // used in Events::login before the MISP logo
//Configure::write('MISP.welcome_text_bottom', 'instance'); // used in Events::login after the MISP logo
//Configure::write('MISP.welcome_logo', 'organisation'); // used in Events::login to the left of the MISP logo, place a .png file in app/webroot/img with the name specified here. In this case it would be organisation.png
//Configure::write('MISP.welcome_logo2', 'organisation2'); // used in Events::login to the right of the MISP logo, place a .png file in app/webroot/img with the name specified here. In this case it would be organisation2.png
// Configure::write('MISP.welcome_text_top', 'Welcome to the Organisation community\'s'); // used in Events::login before the MISP logo
// Configure::write('MISP.welcome_text_bottom', 'instance'); // used in Events::login after the MISP logo
// Configure::write('MISP.welcome_logo', 'organisation'); // used in Events::login to the left of the MISP logo, place a .png file in app/webroot/img with the name specified here. In this case it would be organisation.png
// Configure::write('MISP.welcome_logo2', 'organisation2'); // used in Events::login to the right of the MISP logo, place a .png file in app/webroot/img with the name specified here. In this case it would be organisation2.png
Configure::write('MISP.disablerestalert', 'false');
// Events will be created with the default distribution setting based on this. Valid options: '0', '1', '2', '3'
Configure::write('MISP.default_event_distribution', '3');
// Setting this to 'event' will create attributes that take the event's distribution as the initial setting. Valid options: '0', '1', '2', '3', 'event'
Configure::write('MISP.default_attribute_distribution', 'event');
// Configure::write('MISP.background_jobs', true);
/**
* The settings below can be used to set additional paths to models, views and controllers.
*

34
app/Console/AppShell.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* AppShell file
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::uses('Shell', 'Console', 'AppModel', 'Model');
/**
* Application Shell
*
* Add your application-wide methods in the class below, your shells
* will inherit them.
*
* @package app.Console.Command
*/
class AppShell extends Shell {
public function perform() {
$this->initialize();
$this->{array_shift($this->args)}();
}
}

View File

@ -0,0 +1,37 @@
<?php
App::uses('AppShell', 'Console/Command');
class AdminShell extends AppShell
{
public $uses = array('Event');
public function jobGenerateCorrelation() {
$this->loadModel('Job');
$this->Job->create();
$data = array(
'worker' => 'default',
'job_type' => 'generate correlation',
'job_input' => 'All attributes',
'status' => 0,
'retries' => 0,
'message' => 'Job created.',
);
$this->Job->save($data);
$jobID = $this->Job->id;
$this->loadModel('Correlation');
$this->Correlation->deleteAll(array('id !=' => ''), false);
$this->loadModel('Attribute');
$fields = array('Attribute.id', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.cluster', 'Event.date', 'Event.org');
// get all attributes..
$attributes = $this->Attribute->find('all', array('recursive' => -1));
// for all attributes..
$total = count($attributes);
foreach ($attributes as $k => $attribute) {
$this->Job->saveField('progress', $k/$total*100);
$this->Attribute->__afterSaveCorrelation($attribute['Attribute']);
}
$this->Job->saveField('progress', 100);
$this->Job->saveField('message', 'Job done.');
$this->Job->saveField('status', 1);
}
}

View File

@ -16,7 +16,7 @@
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
App::uses('Shell', 'Console');
App::uses('AppModel', 'Model');
/**
* Application Shell
@ -27,5 +27,8 @@ App::uses('Shell', 'Console');
* @package app.Console.Command
*/
class AppShell extends Shell {
public function perform() {
$this->initialize();
$this->{array_shift($this->args)}();
}
}

View File

@ -0,0 +1,292 @@
<?php
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
//App::uses('AppShell', 'Console/Command');
require_once 'AppShell.php';
class EventShell extends AppShell
{
public $uses = array('Event', 'Attribute', 'Job', 'User', 'Task');
public function doPublish() {
$id = $this->args[0];
$this->Event->id = $id;
if (!$this->Event->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$this->Job->create();
$data = array(
'worker' => 'default',
'job_type' => 'doPublish',
'job_input' => $id,
'status' => 0,
'retries' => 0,
//'org' => $jobOrg,
'message' => 'Job created.',
);
$this->Job->save($data);
//$jobID = $this->Job->id;
//$this->Job->add('default', 'Publish', 'Event published: ' . $id);
// update the event and set the from field to the current instance's organisation from the bootstrap. We also need to save id and info for the logs.
$this->Event->recursive = -1;
$event = $this->Event->read(null, $id);
$event['Event']['published'] = 1;
$fieldList = array('published', 'id', 'info');
$this->Event->save($event, array('fieldList' => $fieldList));
// only allow form submit CSRF protection.
$this->Job->saveField('status', 1);
$this->Job->saveField('message', 'Job done.');
}
public function cachexml() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$id = $this->args[2];
$this->Job->id = $id;
$eventIds = $this->Event->fetchEventIds($org, $isSiteAdmin);
$results = array();
$eventCount = count($eventIds);
foreach ($eventIds as $k => $eventId) {
$temp = $this->Event->fetchEvent($eventId['Event']['id'], null, $org, $isSiteAdmin, $this->Job->id);
$results[$k] = $temp[0];
$this->Job->saveField('progress', ($k+1) / $eventCount * 80);
}
// Whitelist check
$this->loadModel('Whitelist');
$results = $this->Whitelist->removeWhitelistedFromArray($results, false);
foreach ($results as $k => $result) {
$result['Event']['Attribute'] = $result['Attribute'];
$result['Event']['ShadowAttribute'] = $result['ShadowAttribute'];
$result['Event']['RelatedEvent'] = $result['RelatedEvent'];
//
// cleanup the array from things we do not want to expose
//
unset($result['Event']['user_id']);
// hide the org field is we are not in showorg mode
if ('true' != Configure::read('CyDefSIG.showorg') && !$isSiteAdmin) {
unset($result['Event']['org']);
unset($result['Event']['orgc']);
unset($result['Event']['from']);
}
// remove value1 and value2 from the output and remove invalid utf8 characters for the xml parser
foreach ($result['Event']['Attribute'] as $key => $value) {
$result['Event']['Attribute'][$key]['value'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $result['Event']['Attribute'][$key]['value']);
unset($result['Event']['Attribute'][$key]['value1']);
unset($result['Event']['Attribute'][$key]['value2']);
unset($result['Event']['Attribute'][$key]['category_order']);
}
// remove invalid utf8 characters for the xml parser
foreach($result['Event']['ShadowAttribute'] as $key => $value) {
$result['Event']['ShadowAttribute'][$key]['value'] = preg_replace ('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $result['Event']['ShadowAttribute'][$key]['value']);
}
if (isset($result['Event']['RelatedEvent'])) {
foreach ($result['Event']['RelatedEvent'] as $key => $value) {
unset($result['Event']['RelatedEvent'][$key]['user_id']);
if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) {
unset($result['Event']['RelatedEvent'][$key]['org']);
unset($result['Event']['RelatedEvent'][$key]['orgc']);
}
}
}
$xmlArray['response']['Event'][] = $result['Event'];
if ($k % 20 == 0) {
$this->Job->saveField('progress', (($k+1) / $eventCount * 10) + 79);
}
}
$xmlObject = Xml::fromArray($xmlArray, array('format' => 'tags'));
$dir = new Folder(APP . DS . '/tmp/cached_exports/xml');
if ($isSiteAdmin) {
$file = new File($dir->pwd() . DS . 'misp.xml' . '.ADMIN.xml');
} else {
$file = new File($dir->pwd() . DS . 'misp.xml' . '.' . $org . '.xml');
}
$file->write($xmlObject->asXML());
$file->close();
$this->Job->saveField('progress', '100');
}
public function cachehids() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$id = $this->args[2];
$this->Job->id = $id;
$extra = $this->args[3];
$this->Job->saveField('progress', 1);
$rules = $this->Attribute->hids($isSiteAdmin, $org, $extra);
$this->Job->saveField('progress', 80);
$dir = new Folder(APP . DS . '/tmp/cached_exports/' . $extra);
if ($isSiteAdmin) {
$file = new File($dir->pwd() . DS . 'misp.' . $extra . '.ADMIN.txt');
} else {
$file = new File($dir->pwd() . DS . 'misp.' . $extra . '.' . $org . '.txt');
}
$file->write('');
foreach ($rules as $rule) {
$file->append($rule . PHP_EOL);
}
$file->close();
$this->Job->saveField('progress', '100');
}
public function cachecsv() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$id = $this->args[2];
$this->Job->id = $id;
$extra = $this->args[3];
$eventIds = $this->Event->fetchEventIds($org, $isSiteAdmin);
$eventCount = count($eventIds);
foreach ($eventIds as $k => $eventId) {
$attributes = $this->Event->csv($org, $isSiteAdmin, $eventId['Event']['id'], $extra);
if ($k % 10 == 0) {
$this->Job->saveField('progress', $k / $eventCount * 80);
}
}
$this->loadModel('Whitelist');
$final = array();
$attributes = $this->Whitelist->removeWhitelistedFromArray($attributes, true);
foreach ($attributes as $attribute) {
$final[] = $attribute['Attribute']['uuid'] . ',' . $attribute['Attribute']['event_id'] . ',' . $attribute['Attribute']['category'] . ',' . $attribute['Attribute']['type'] . ',' . $attribute['Attribute']['value'];
}
$dir = new Folder(APP . DS . '/tmp/cached_exports/' . $extra);
if ($isSiteAdmin) {
$file = new File($dir->pwd() . DS . 'misp.' . $extra . '.ADMIN.csv');
} else {
$file = new File($dir->pwd() . DS . 'misp.' . $extra . '.' . $org . '.csv');
}
$file->write('');
foreach ($final as $line) {
$file->append($line . PHP_EOL);
}
$file->close();
$this->Job->saveField('progress', '100');
}
public function cachetext() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$id = $this->args[2];
$this->Job->id = $id;
$extra = $this->args[3];
$types = array_keys($this->Attribute->typeDefinitions);
$typeCount = count($types);
$dir = new Folder(APP . DS . '/tmp/cached_exports/text');
foreach ($types as $k => $type) {
$final = $this->Attribute->text($org, $isSiteAdmin, $type);
if ($isSiteAdmin) {
$file = new File($dir->pwd() . DS . 'misp.text_' . $type . '.ADMIN.txt');
} else {
$file = new File($dir->pwd() . DS . 'misp.text_' . $type . '.' . $org . '.txt');
}
$file->write('');
foreach ($final as $attribute) {
$file->append($attribute['Attribute']['value'] . PHP_EOL);
}
$file->close();
$this->Job->saveField('progress', $k / $typeCount * 100);
}
$this->Job->saveField('progress', 100);
}
public function cachenids() {
$org = $this->args[0];
$isSiteAdmin = $this->args[1];
$id = $this->args[2];
$this->Job->id = $id;
$format = $this->args[3];
$sid = $this->args[4];
$eventIds = $this->Event->fetchEventIds($org, $isSiteAdmin);
$eventCount = count($eventIds);
$dir = new Folder(APP . DS . '/tmp/cached_exports/' . $format);
if ($isSiteAdmin) {
$file = new File($dir->pwd() . DS . 'misp.' . $format . '.ADMIN.rules');
} else {
$file = new File($dir->pwd() . DS . 'misp.' . $format . '.' . $org . '.rules');
}
$file->write('');
foreach ($eventIds as $k => $eventId) {
if ($k == 0) {
$temp = $this->Attribute->nids($isSiteAdmin, $org, $format, $sid, $eventId['Event']['id']);
} else {
$temp = $this->Attribute->nids($isSiteAdmin, $org, $format, $sid, $eventId['Event']['id'], true);
}
foreach ($temp as $line) {
$file->append($line . PHP_EOL);
}
if ($k % 10 == 0) {
$this->Job->saveField('progress', $k / $eventCount * 80);
}
}
$file->close();
$this->Job->saveField('progress', '100');
}
public function alertemail() {
$org = $this->args[0];
$processId = $this->args[1];
$this->Job->id = $processId;
$eventId = $this->args[2];
$result = $this->Event->sendAlertEmail($eventId, $org, $processId);
$this->Job->saveField('progress', '100');
if ($result != true) $this->Job->saveField('message', 'Job done.');
}
public function contactemail() {
$id = $this->args[0];
$message = $this->args[1];
$all = $this->args[2];
$userId = $this->args[3];
$isSiteAdmin = $this->args[4];
$processId = $this->args[5];
$this->Job->id = $ProcessId;
$user = $this->User->read(null, $userId);
$eventId = $this->args[2];
$result = $this->Event->sendContactEmail($id, $message, $all, $user, $isSiteAdmin);
$this->Job->saveField('progress', '100');
if ($result != true) $this->Job->saveField('message', 'Job done.');
}
public function enqueueCaching() {
$timestamp = $this->args[0];
$task = $this->Task->findByType('cache_exports');
// If the next execution time and the timestamp don't match, it means that this task is no longer valid as the time for the execution has since being scheduled
// been updated.
if ($task['Task']['next_execution_time'] != $timestamp) return;
$orgs = $this->User->getOrgs();
// Queue a set of exports for admins. This "ADMIN" organisation. The organisation of the admin users doesn't actually matter, it is only used to indentify
// the special cache files containing all events
$i = 0;
foreach($this->Event->export_types as $k => $type) {
foreach ($orgs as $org) {
$this->Job->cache($k, false, $org, 'Events visible to: ' . $org, $org);
$i++;
}
$this->Job->cache($k, true, 'ADMIN', 'All events.', 'ADMIN');
$i++;
}
$task['Task']['message'] = $i . ' jobs started at ' . date('d/m/Y - H:i:s') . '.';
if ($task['Task']['timer'] > 0) {
$task['Task']['next_execution_time'] = strtotime('+' . $task['Task']['timer'] . ' hours', $task['Task']['next_execution_time']);
$task['Task']['scheduled_time'] = $this->Task->breakTime($task['Task']['scheduled_time'], $task['Task']['timer']);
}
$this->Task->save($task);
}
public function publish() {
$id = $this->args[0];
$passAlong = $this->args[1];
$processId = $this->args[2];
$this->Job->id = $processId;
$eventId = $this->args[2];
$result = $this->Event->publish($id, $passAlong, $processId);
$this->Job->saveField('progress', '100');
if ($result != true) $this->Job->saveField('message', 'Job done.');
}
}

View File

@ -0,0 +1,59 @@
<?php
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
//App::uses('AppShell', 'Console/Command');
require_once 'AppShell.php';
class ServerShell extends AppShell
{
public $uses = array('Server', 'Task', 'Job', 'User');
public function pull() {
//$user, $id = null, $technique=false, $server
$userId = $this->args[0];
$serverId = $this->args[1];
$technique = $this->args[2];
$jobId = $this->args[3];
$this->Job->read(null, $jobId);
$this->Server->id = $serverId;
$this->User->recursive = -1;
$user = $this->User->read(array('id', 'org', 'email'), $userId);
$server = $this->Server->read(null, $serverId);
$result = $this->Server->pull($user['User'], null, $technique, $server, $jobId);
if (is_numeric($result)) {
switch ($result) {
case '1' :
$this->Job->saveField('message', 'Not authorised. This is either due to an invalid auth key, or due to the sync user not having authentication permissions enabled on the remote server.');
return;
break;
case '2' :
$this->Job->saveField('message', 'Event Ids: ' . $eventIds);
return;
break;
case '3' :
$this->Job->saveField('message', 'Sorry, incremental pushes are not yet implemented.');
return;
break;
case '4' :
$this->Job->saveField('message', 'Invalid technique chosen.');
return;
break;
}
}
$this->Job->saveField('message', 'Job done.');
$this->Job->saveField('progress', 100);
$this->Job->saveField('status', 4);
}
public function push() {
$serverId = $this->args[0];
$technique = $this->args[1];
$jobId = $this->args[2];
$this->Job->read(null, $jobId);
App::uses('HttpSocket', 'Network/Http');
$HttpSocket = new HttpSocket();
$result = $this->Server->push($id, 'full', $jobId, $HttpSocket);
$this->Job->saveField('message', $result);
}
}

View File

@ -0,0 +1,5 @@
../cake CakeResque.CakeResque stop --all
../cake CakeResque.CakeResque start --interval 5 --queue default
../cake CakeResque.CakeResque start --interval 5 --queue cache
../cake CakeResque.CakeResque start --interval 5 --queue email
../cake CakeResque.CakeResque startscheduler -i 5

View File

@ -65,7 +65,7 @@ class AppController extends Controller {
'logoutRedirect' => array('controller' => 'users', 'action' => 'login'),
//'authorize' => array('Controller', // Added this line
//'Actions' => array('actionPath' => 'controllers')) // TODO ACL, 4: tell actionPath
)
),
);
public function beforeFilter() {
@ -138,6 +138,9 @@ class AppController extends Controller {
$this->debugMode = 'debugOff';
}
$this->set('debugMode', $this->debugMode);
$proposalCount = $this->_getProposalCount();
$this->set('proposalCount', $proposalCount[0]);
$this->set('proposalEventCount', $proposalCount[1]);
}
public $userRole = null;
@ -156,6 +159,25 @@ class AppController extends Controller {
return (isset($this->RequestHandler) && ($this->RequestHandler->isXml() || $this->isJson()));
}
private function _getProposalCount() {
$this->loadModel('ShadowAttribute');
$this->ShadowAttribute->recursive = -1;
$shadowAttributes = $this->ShadowAttribute->find('all', array(
'recursive' => -1,
'fields' => array('event_id', 'event_org'),
'conditions' => array(
'ShadowAttribute.event_org' => $this->Auth->user('org')
)));
$results = array();
$eventIds = array();
$results[0] = count($shadowAttributes);
foreach ($shadowAttributes as $sa) {
if (!in_array($sa['ShadowAttribute']['event_id'], $eventIds)) $eventIds[] = $sa['ShadowAttribute']['event_id'];
}
$results[1] = count($eventIds);
return $results;
}
/**
* Convert an array to the same array but with the values also as index instead of an interface_exists
*/
@ -199,7 +221,38 @@ class AppController extends Controller {
$this->Auth->login($user['User']);
}
public function queuegenerateCorrelation() {
if (!$this->_isSiteAdmin()) throw new NotFoundException();
$process_id = CakeResque::enqueue(
'default',
'AdminShell',
array('jobGenerateCorrelation'),
true
);
debug($process_id);
debug(CakeResque::getJobStatus($process_id));
debug(CakeResque::getJobStatus('f80f51ee76dd22194a0dd6cd28c15f46'));
throw new Exception();
$this->Session->setFlash('Job queued.');
$this->redirect(array('controller' => 'pages', 'action' => 'display', 'administration'));
}
public function generateCorrelation() {
$this->loadModel('Correlation');
$this->Correlation->deleteAll(array('id !=' => ''), false);
$this->loadModel('Attribute');
$fields = array('Attribute.id', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.type', 'Attribute.category', 'Attribute.value1', 'Attribute.value2');
// get all attributes..
$attributes = $this->Attribute->find('all', array('recursive' => -1, 'fields' => $fields));
// for all attributes..
foreach ($attributes as $attribute) {
$this->Attribute->__afterSaveCorrelation($attribute['Attribute']);
}
}
/*public function generateCorrelation() {
if (!self::_isSiteAdmin()) throw new NotFoundException();
$this->loadModel('Correlation');
@ -214,8 +267,8 @@ class AppController extends Controller {
}
$this->Session->setFlash(__('All done.'));
$this->redirect(array('controller' => 'events', 'action' => 'index', 'admin' => false));
}
}*/
public function generateLocked() {
if (!self::_isSiteAdmin()) throw new NotFoundException();
$this->loadModel('User');

View File

@ -1279,4 +1279,28 @@ class AttributesController extends AppController {
}
$this->__downloadAttachment($this->Attribute->data['Attribute']);
}
public function text($key, $type="") {
if ($key != 'download') {
// check if the key is valid -> search for users based on key
$user = $this->checkAuthUser($key);
if (!$user) {
throw new UnauthorizedException('This authentication key is not authorized to be used for exports. Contact your administrator.');
}
$this->response->type('txt'); // set the content type
$this->header('Content-Disposition: inline; filename="misp.' . $type . '.txt"');
$this->layout = 'text/default';
} else {
if (!$this->Auth->user('id')) {
throw new UnauthorizedException('You have to be logged in to do that.');
}
$this->response->type('txt'); // set the content type
$this->header('Content-Disposition: download; filename="misp.' . $type . '.txt"');
$this->layout = 'text/default';
}
$attributes = $this->Attribute->text($this->_checkOrg(), $this->_isSiteAdmin(), $type);
$this->loadModel('Whitelist');
$attributes = $this->Whitelist->removeWhitelistedFromArray($attributes, true);
$this->set('attributes', $attributes);
}
}

View File

@ -1,62 +0,0 @@
<?php
class HidsSha1ExportComponent extends Component {
public $rules = array();
public function explain() {
// unshift add in reverse order
array_unshift($this->rules, '# ');
array_unshift($this->rules, '# Keep in mind SHA-1 still has a theoretical collision possibility');
array_unshift($this->rules, '# These HIDS export contains SHA-1 checksums.');
}
public function export($items) {
$itemsDone = array();
foreach ($items as &$item) {
# sha-1
$ruleFormat = '%s';
$attribute = &$item['Attribute'];
switch ($attribute['type']) {
case 'sha1':
if (!in_array ($attribute['value1'], $itemsDone)) {
$this->checksumRule($ruleFormat, $attribute);
$itemsDone[] = $attribute['value1'];
}
break;
case 'filename|sha1':
if (!in_array ($attribute['value2'], $itemsDone)) {
$this->partRule($ruleFormat, $attribute);
$itemsDone[] = $attribute['value2'];
}
break;
default:
break;
}
}
sort($this->rules);
$this->explain();
return $this->rules;
}
public function checksumRule($ruleFormat, $attribute) {
$this->rules[] = sprintf($ruleFormat,
$attribute['value1'] // md5
);
}
public function partRule($ruleFormat, $attribute) {
$this->rules[] = sprintf($ruleFormat,
$attribute['value2'] // md5
);
}
}

View File

@ -1,17 +0,0 @@
<?php
App::uses('NidsExportComponent', 'Controller/Component');
class NidsSnortExportComponent extends NidsExportComponent {
public function export($items, $startSid) {
// set the specific format
$this->format = 'snort';
// call the generic function
return parent::export($items, $startSid);
}
// below overwrite functions from NidsExportComponent
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
<?php
App::uses('AppController', 'Controller');
/**
* Jobs Controller
*
* @property Job $Job
*/
class JobsController extends AppController {
public $components = array('Security' ,'RequestHandler', 'Session');
public $paginate = array(
'limit' => 20,
'order' => array(
'Job.id' => 'desc'
)
);
public function beforeFilter() {
parent::beforeFilter();
}
public function index() {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
if (!Configure::read('MISP.background_jobs')) throw new NotFoundException('Background jobs are not enabled on this instance.');
$this->recursive = 0;
$jobs = $this->paginate();
foreach($jobs as &$job) {
if ($job['Job']['process_id']) {
$job['Job']['status'] = $this->__jobStatusConverter(CakeResque::getJobStatus($job['Job']['process_id']));
} else {
$job['Job']['status'] = '???';
}
}
$this->set('list', $jobs);
}
private function __jobStatusConverter($status) {
switch ($status) {
case 1:
return 'In progress...';
break;
case 2:
return 'Unknown';
break;
case 3:
return 'Unknown';
break;
case 4:
return 'Completed';
break;
}
}
public function getGenerateCorrelationProgress($id) {
//if (!self::_isSiteAdmin()) throw new NotFoundException();
$progress = $this->Job->findById($id);
if (!$progress) {
$progress = 0;
} else {
$progress = $progress['Job']['progress'];
}
return new CakeResponse(array('body' => json_encode($progress)));
}
public function getProgress($type) {
$org = $this->Auth->user('org');
if ($this->_isSiteAdmin()) $org = 'ADMIN';
$progress = $this->Job->find('first', array(
'conditions' => array(
'job_type' => $type,
'org' => $org
),
'fields' => array('id', 'progress'),
'order' => array('Job.id' => 'desc'),
));
if (!$progress) {
$progress = 0;
} else {
$progress = $progress['Job']['progress'];
}
return new CakeResponse(array('body' => json_encode($progress)));
}
public function cache($type) {
if ($this->_isSiteAdmin()) {
$target = 'All events.';
$jobOrg = 'ADMIN';
} else {
$target = 'Events visible to: '.$this->Auth->user('org');
$jobOrg = $this->Auth->user('org');
}
$id = $this->Job->cache($type, $this->_isSiteAdmin(), $this->Auth->user('org'), $target, $jobOrg, $this->Auth->user('sid'));
return new CakeResponse(array('body' => json_encode($id)));
}
}

View File

@ -107,6 +107,7 @@ class LogsController extends AppController {
'order' => array('Log.id' => 'DESC'),
'fields' => $fieldList
);
$this->set('event', $this->Event->data);
$this->set('list', $this->paginate());
$this->set('eventId', $id);
$this->set('mayModify', $mayModify);

View File

@ -156,154 +156,62 @@ class ServersController extends AppController {
throw new NotFoundException(__('Invalid server'));
}
App::uses('HttpSocket', 'Network/Http');
$this->Server->read(null, $id);
$s = $this->Server->read(null, $id);
if (false == $this->Server->data['Server']['pull']) {
$this->Session->setFlash(__('Pull setting not enabled for this server.'));
$this->redirect(array('action' => 'index'));
}
$eventIds = array();
if ("full" == $technique) {
// get a list of the event_ids on the server
$eventIds = $this->Event->getEventIdsFromServer($this->Server->data);
// FIXME this is not clean at all ! needs to be refactored with try catch error handling/communication
if ($eventIds === 403) {
$this->Session->setFlash(__('Not authorised. This is either due to an invalid auth key, or due to the sync user not having authentication permissions enabled on the remote server.'));
$this->redirect(array('action' => 'index'));
} else if (is_string($eventIds)) {
$this->Session->setFlash($eventIds);
$this->redirect(array('action' => 'index'));
if (!Configure::read('MISP.background_jobs')) {
$result = $this->Server->pull($this->Auth->user(), $id, $technique, $s);
// error codes
if (is_numeric($result)) {
switch ($result) {
case '1' :
$this->Session->setFlash(__('Not authorised. This is either due to an invalid auth key, or due to the sync user not having authentication permissions enabled on the remote server.'));
$this->redirect(array('action' => 'index'));
break;
case '2' :
$this->Session->setFlash($eventIds);
$this->redirect(array('action' => 'index'));
break;
case '3' :
throw new NotFoundException('Sorry, this is not yet implemented');
break;
case '4' :
$this->redirect(array('action' => 'index'));
break;
}
} else {
$this->set('successes', $result[0]);
$this->set('fails', $result[1]);
$this->set('pulledProposals', $result[2]);
$this->set('lastpulledid', $result[3]);
}
// reverse array of events, to first get the old ones, and then the new ones
$eventIds = array_reverse($eventIds);
} elseif ("incremental" == $technique) {
// TODO incremental pull
throw new NotFoundException('Sorry, this is not yet implemented');
} elseif (true == $technique) {
$eventIds[] = intval($technique);
} else {
$this->loadModel('Job');
$this->Job->create();
$data = array(
'worker' => 'default',
'job_type' => 'pull',
'job_input' => 'Server: ' . $id,
'status' => 0,
'retries' => 0,
'org' => $this->Auth->user('org'),
'message' => 'Pulling.',
);
$this->Job->save($data);
$jobId = $this->Job->id;
$process_id = CakeResque::enqueue(
'default',
'ServerShell',
array('pull', $this->Auth->user('id'), $id, $technique, $jobId)
);
$this->Job->saveField('process_id', $process_id);
$this->Session->setFlash('Pull queued for background execution.');
$this->redirect(array('action' => 'index'));
}
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventIds)) {
$successes = array();
$fails = array();
// download each event
if (null != $eventIds) {
App::import('Controller', 'Events');
$HttpSocket = new HttpSocket();
foreach ($eventIds as &$eventId) {
$event = $this->Event->downloadEventFromServer(
$eventId,
$this->Server->data);
if (null != $event) {
// we have an Event array
// The event came from a pull, so it should be locked.
$event['Event']['locked'] = true;
if (!isset($event['Event']['distribution'])) { // version 1
$event['Event']['distribution'] = '1';
}
// Distribution
switch($event['Event']['distribution']) {
case 1:
case 'This community only': // backwards compatibility
// if community only, downgrade to org only after pull
$event['Event']['distribution'] = '0';
break;
case 2:
case 'Connected communities': // backwards compatibility
// if connected communities downgrade to community only
$event['Event']['distribution'] = '1';
break;
case 'All communities': // backwards compatibility
$event['Event']['distribution'] = '3';
break;
case 'Your organisation only': // backwards compatibility
$event['Event']['distribution'] = '0';
break;
}
// correct $event if just one Attribute
if (is_array($event['Event']['Attribute']) && isset($event['Event']['Attribute']['id'])) {
$tmp = $event['Event']['Attribute'];
unset($event['Event']['Attribute']);
$event['Event']['Attribute'][0] = $tmp;
}
if (is_array($event['Event']['Attribute'])) {
$size = is_array($event['Event']['Attribute']) ? count($event['Event']['Attribute']) : 0;
for ($i = 0; $i < $size; $i++) {
if (!isset($event['Event']['Attribute'][$i]['distribution'])) { // version 1
$event['Event']['Attribute'][$i]['distribution'] = 1;
}
switch($event['Event']['Attribute'][$i]['distribution']) {
case 1:
case 'This community only': // backwards compatibility
// if community only, downgrade to org only after pull
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
case 2:
case 'Connected communities': // backwards compatibility
// if connected communities downgrade to community only
$event['Event']['Attribute'][$i]['distribution'] = '1';
break;
case 'All communities': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '3';
break;
case 'Your organisation only': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
}
}
$event['Event']['Attribute'] = array_values($event['Event']['Attribute']);
} else {
unset($event['Event']['Attribute']);
}
// Distribution, set reporter of the event, being the admin that initiated the pull
$event['Event']['user_id'] = $this->Auth->user('id');
// check if the event already exist (using the uuid)
$existingEvent = null;
$existingEvent = $this->Event->find('first', array('conditions' => array('Event.uuid' => $event['Event']['uuid'])));
$eventsController = new EventsController();
$eventsController->constructClasses();
if (!$existingEvent) {
// add data for newly imported events
$passAlong = $this->Server->data['Server']['url'];
$result = $eventsController->_add($event, $fromXml = true, $this->Server->data['Server']['organization'], $passAlong, true);
if ($result) $successes[] = $eventId;
else {
$fails[$eventId] = 'Failed (partially?) because of validation errors: '. print_r($eventsController->Event->validationErrors, true);
}
} else {
$result = $eventsController->_edit($event, $existingEvent['Event']['id']);
if ($result === 'success') $successes[] = $eventId;
else $fails[$eventId] = $result;
}
} else {
// error
$fails[$eventId] = 'failed downloading the event';
}
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpulledid = min(array_keys($fails));
} else {
// no fails, take the highest success
$lastpulledid = count($successes) > 0 ? max($successes) : 0;
}
// increment lastid based on the highest ID seen
$this->Server->set('lastpulledid', $lastpulledid);
$this->Server->save($event, array('fieldList' => array('lastpulledid', 'url')));
}
}
$this->set('successes', $successes);
$this->set('fails', $fails);
}
public function push($id = null, $technique=false) {
@ -312,76 +220,35 @@ class ServersController extends AppController {
if (!$this->Server->exists()) {
throw new NotFoundException(__('Invalid server'));
}
App::uses('HttpSocket', 'Network/Http');
$this->Server->read(null, $id);
if (false == $this->Server->data['Server']['push']) {
$this->Session->setFlash(__('Push setting not enabled for this server.'));
$this->redirect(array('action' => 'index'));
}
if ("full" == $technique) {
$eventid_conditions_key = 'Event.id >';
$eventid_conditions_value = 0;
} elseif ("incremental" == $technique) {
$eventid_conditions_key = 'Event.id >';
$eventid_conditions_value = $this->Server->data['Server']['lastpushedid'];
} elseif (true == $technique) {
$eventIds[] = array('Event' => array ('id' => intval($technique)));
} else {
$this->redirect(array('action' => 'index'));
}
if (!isset($eventIds)) {
$findParams = array(
'conditions' => array(
$eventid_conditions_key => $eventid_conditions_value,
'Event.distribution >' => 0,
'Event.published' => 1,
'Event.attribute_count >' => 0
), //array of conditions
'recursive' => -1, //int
'fields' => array('Event.id'), //array of field names
);
$eventIds = $this->Event->find('all', $findParams);
}
//debug($eventIds);
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventIds)) {
$successes = array();
$fails = array();
$lowestfailedid = null;
if (!Configure::read('MISP.background_jobs')) {
App::uses('HttpSocket', 'Network/Http');
$HttpSocket = new HttpSocket();
foreach ($eventIds as $eventId) {
$this->Event->recursive=1;
$event = $this->Event->findById($eventId['Event']['id']);
$event['Event']['locked'] = true;
unset($event['User']);
$result = $this->Event->uploadEventToServer(
$event,
$this->Server->data,
$HttpSocket);
if ('Success' === $result) {
$successes[] = $event['Event']['id'];
} else {
$fails[$event['Event']['id']] = $result;
}
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpushedid = min(array_keys($fails));
} else {
// no fails, take the highest success
$lastpushedid = max($successes);
}
// increment lastid based on the highest ID seen
// Save the entire Server data instead of just a single field, so that the logger can be fed with the extra fields.
$this->Server->data['Server']['lastpushedid'] = $lastpushedid;
$this->Server->save($this->Server->data);
$result = $this->Server->push($id, $technique, false, $HttpSocket);
$this->set('successes', $result[0]);
$this->set('fails', $result[1]);
} else {
$this->loadModel('Job');
$this->Job->create();
$data = array(
'worker' => 'default',
'job_type' => 'push',
'job_input' => 'Server: ' . $id,
'status' => 0,
'retries' => 0,
'org' => $this->Auth->user('org'),
'message' => 'Pushing.',
);
$this->Job->save($data);
$jobId = $this->Job->id;
$process_id = CakeResque::enqueue(
'default',
'ServerShell',
array('push', $id, $technique, $jobId)
);
$this->Job->saveField('process_id', $process_id);
$this->Session->setFlash('Push queued for background execution.');
$this->redirect(array('action' => 'index'));
}
if (!isset($successes)) $successes = null;
if (!isset($fails)) $fails = null;
$this->set('successes', $successes);
$this->set('fails', $fails);
}
}

View File

@ -98,7 +98,9 @@ class ShadowAttributesController extends AppController {
$this->Event->recursive = -1;
// Unpublish the event, accepting a proposal is modifying the event after all. Also, reset the lock.
$event = $this->Event->read(null, $activeAttribute['Attribute']['event_id']);
$fieldList = array('proposal_email_lock', 'id', 'info', 'published');
$fieldList = array('proposal_email_lock', 'id', 'info', 'published', 'timestamp');
$date = new DateTime();
$event['Event']['timestamp'] = $date->getTimestamp();
$event['Event']['proposal_email_lock'] = 0;
$event['Event']['published'] = 0;
$this->Event->save($event, array('fieldList' => $fieldList));
@ -198,13 +200,15 @@ class ShadowAttributesController extends AppController {
*/
public function add($eventId = null) {
if ($this->request->is('post')) {
// Give error if someone tried to submit a attribute with attachment or malware-sample type.
// TODO change behavior attachment options - this is bad ... it should rather by a messagebox or should be filtered out on the view level
if (isset($this->request->data['ShadowAttribute']['type']) && $this->ShadowAttribute->typeIsAttachment($this->request->data['ShadowAttribute']['type'])) {
$this->Session->setFlash(__('Attribute has not been added: attachments are added by "Add attachment" button', true), 'default', array(), 'error');
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
}
$temp = $this->_getEventData($this->request->data['ShadowAttribute']['event_id']);
$event_uuid = $temp['uuid'];
$event_org = $temp['orgc'];
//
// multiple attributes in batch import
//
@ -224,6 +228,8 @@ class ShadowAttributesController extends AppController {
$this->request->data['ShadowAttribute']['value'] = $attribute; // set the value as the content of the single line
$this->request->data['ShadowAttribute']['email'] = $this->Auth->user('email');
$this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org');
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
// TODO loop-holes,
// there seems to be a loop-hole in misp here
// be it an create and not an update
@ -265,8 +271,10 @@ class ShadowAttributesController extends AppController {
$savedId = $this->ShadowAttribute->getId();
$this->request->data['ShadowAttribute']['email'] = $this->Auth->user('email');
$this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org');
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
if ($this->ShadowAttribute->save($this->request->data)) {
$this->__sendProposalAlertEmail($eventId);
$this->__sendProposalAlertEmail($this->request->data['ShadowAttribute']['event_id']);
// inform the user and redirect
$this->Session->setFlash(__('The proposal has been saved'));
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
@ -333,7 +341,6 @@ class ShadowAttributesController extends AppController {
*/
public function add_attachment($eventId = null) {
if ($this->request->is('post')) {
$this->loadModel('Event');
// Check if there were problems with the file upload
// only keep the last part of the filename, this should prevent directory attacks
$filename = basename($this->request->data['ShadowAttribute']['value']['name']);
@ -347,8 +354,9 @@ class ShadowAttributesController extends AppController {
$this->Session->setFlash(__('There was a problem to upload the file.', true), 'default', array(), 'error');
$this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['ShadowAttribute']['event_id']));
}
$this->Event->id = $this->request->data['ShadowAttribute']['event_id'];
$temp = $this->_getEventData($this->request->data['ShadowAttribute']['event_id']);
$event_uuid = $temp['uuid'];
$event_org = $temp['orgc'];
// save the file-info in the database
$this->ShadowAttribute->create();
if ($this->request->data['ShadowAttribute']['malware']) {
@ -368,6 +376,8 @@ class ShadowAttributesController extends AppController {
$this->request->data['ShadowAttribute']['batch_import'] = 0;
$this->request->data['ShadowAttribute']['email'] = $this->Auth->user('email');
$this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org');
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
if ($this->ShadowAttribute->save($this->request->data)) {
$this->__sendProposalAlertEmail($eventId);
} else {
@ -434,8 +444,6 @@ class ShadowAttributesController extends AppController {
} else {
// set the event_id in the form
$this->request->data['ShadowAttribute']['event_id'] = $eventId;
$this->loadModel('Event');
$events = $this->Event->findById($eventId);
}
// combobox for categories
@ -488,7 +496,7 @@ class ShadowAttributesController extends AppController {
}
$uuid = $this->Attribute->data['Attribute']['uuid'];
if (!$this->_isSiteAdmin()) {
if (($this->Attribute->data['Attribute']['distribution'] == 0) || ($this->Attribute->data['Event']['org'] == $this->Auth->user('org'))) {
if (($this->Attribute->data['Attribute']['distribution'] == 0) || ($this->Attribute->data['Event']['orgc'] == $this->Auth->user('org'))) {
$this->Session->setFlash(__('Invalid Attribute.'));
$this->redirect(array('controller' => 'events', 'action' => 'index'));
}
@ -506,9 +514,14 @@ class ShadowAttributesController extends AppController {
if ($this->request->is('post') || $this->request->is('put')) {
$existingAttribute = $this->Attribute->findByUuid($uuid);
$temp = $this->_getEventData($eventId);
$event_uuid = $temp['uuid'];
$event_org = $temp['orgc'];
$this->request->data['ShadowAttribute']['old_id'] = $existingAttribute['Attribute']['id'];
$this->request->data['ShadowAttribute']['uuid'] = $existingAttribute['Attribute']['uuid'];
$this->request->data['ShadowAttribute']['event_id'] = $existingAttribute['Attribute']['event_id'];
$this->request->data['ShadowAttribute']['event_uuid'] = $event_uuid;
$this->request->data['ShadowAttribute']['event_org'] = $event_org;
if ($attachment) $this->request->data['ShadowAttribute']['value'] = $existingAttribute['Attribute']['value'];
if ($attachment) $this->request->data['ShadowAttribute']['type'] = $existingAttribute['Attribute']['type'];
$this->request->data['ShadowAttribute']['org'] = $this->Auth->user('org');
@ -662,7 +675,7 @@ class ShadowAttributesController extends AppController {
'conditions' => $conditions,
'fields' => array('id', 'org', 'old_id'),
'contain' => array(
'Event' =>array(
'Event' => array(
'fields' => array('id', 'org', 'info', 'orgc'),
),
),
@ -670,4 +683,39 @@ class ShadowAttributesController extends AppController {
);
$this->set('shadowAttributes', $this->paginate());
}
public function eventIndex() {
$result = $this->ShadowAttribute->find('all', array(
'fields' => array('event_id'),
'group' => 'event_id',
'conditions' => array(
'ShadowAttribute.event_org =' => $this->Auth->user('org'),
)));
$this->loadModel('Event');
foreach ($result as $eventId) {
}
}
private function _getEventData($event_id) {
$this->loadModel('Event');
$this->Event->recursive = -1;
$this->Event->read(array('id', 'uuid', 'orgc'), $event_id);
return $this->Event->data['Event'];
}
// takes a uuid and finds all proposals that belong to an event with the given uuid. These are then returned.
public function getProposalsByUuid($uuid) {
if (!$this->_isRest()) {
throw new MethodNotAllowedException(__('This feature is only available for REST users'));
}
if (strlen($uuid) != 36) {
throw new NotFoundException(__('Invalid UUID'));
}
$this->ShadowAttribute->recursive = -1;
$temp = $this->ShadowAttribute->findAllByEventUuid($uuid);
if ($temp == null) throw new NotFoundException(__('Invalid event'));
$this->set('proposal', $temp);
$this->render('get_proposals_by_uuid');
}
}

View File

@ -0,0 +1,96 @@
<?php
App::uses('AppController', 'Controller');
/**
* Jobs Controller
*
* @property Job $Job
*/
class TasksController extends AppController {
public $components = array('Security' ,'RequestHandler', 'Session');
public $paginate = array(
'limit' => 20,
'order' => array(
'Task.id' => 'desc'
)
);
public function beforeFilter() {
parent::beforeFilter();
}
public function index() {
if (!$this->_isSiteAdmin()) throw new MethodNotAllowedException();
if (!Configure::read('MISP.background_jobs')) throw new NotFoundException('Background jobs are not enabled on this instance.');
$this->__checkTasks();
$this->recursive = 0;
$tasks = $this->paginate();
$this->set('list', $tasks);
$this->set('time', time());
}
// checks if all the mandatory tasks exist, and if not, creates them
// default tasks are:
// 'cache_exports'
private function __checkTasks() {
foreach ($this->Task->tasks as $default_task) {
if (!$this->Task->findByType($default_task['type'], array('id', 'type'))) {
$this->Task->save($default_task);
}
}
}
public function setTask() {
if (!$this->_isSiteAdmin()) {
throw new MethodNotAllowedException('You are not authorised to do that.');
}
$today = $this->_getTodaysTimestamp();
if ($this->request->is('post') || $this->request->is('put')) {
$tasks = $this->Task->find('all', array('fields' => array('id', 'timer', 'scheduled_time', 'type', 'next_execution_time')));
foreach ($tasks as $k => $task) {
if ($this->request->data['Task'][$task['Task']['id']]['timer'] == $task['Task']['timer']) unset($this->request->data['Task'][$task['Task']['id']]['timer']);
if ($this->request->data['Task'][$task['Task']['id']]['scheduled_time'] == $task['Task']['scheduled_time']) unset($this->request->data['Task'][$task['Task']['id']]['scheduled_time']);
if (empty($this->request->data['Task'][$task['Task']['id']])) {
unset($this->request->data['Task'][$task['Task']['id']]);
} else {
$this->request->data['Task'][$task['Task']['id']]['id'] = $task['Task']['id'];
if (isset($this->request->data['Task'][$task['Task']['id']]['next_execution_time'])) {
$temp = $this->request->data['Task'][$task['Task']['id']]['next_execution_time'];
} else {
$temp = date("Y-m-d", $task['Task']['next_execution_time']);
}
if (isset($this->request->data['Task'][$task['Task']['id']]['scheduled_time'])) {
$this->request->data['Task'][$task['Task']['id']]['next_execution_time'] = strtotime($temp . ' ' . $this->request->data['Task'][$task['Task']['id']]['scheduled_time']);
} else {
$this->request->data['Task'][$task['Task']['id']]['next_execution_time'] = strtotime($temp . ' ' . $task['Task']['scheduled_time']);
}
// schedule task
$this->_jobScheduler($task['Task']['type'], $this->request->data['Task'][$task['Task']['id']]['next_execution_time']);
$this->Task->save($this->request->data['Task'][$task['Task']['id']]);
}
}
$this->Session->setFlash('Task edited');
$this->redirect(array('action' => 'index'));
}
}
private function _getTodaysTimestamp() {
return strtotime(date("d/m/Y") . ' 00:00:00');
}
private function _jobScheduler($type, $timestamp) {
if ($type === 'cache_exports') $this->_cacheScheduler($timestamp);
}
private function _cacheScheduler($timestamp) {
CakeResque::enqueueAt(
$timestamp,
'cache',
'EventShell',
array('enqueueCaching', $timestamp),
true
);
}
}

View File

@ -433,7 +433,7 @@ class UsersController extends AppController {
$this->Session->setFlash(__('Good-Bye'));
$this->redirect($this->Auth->logout());
}
public function resetauthkey($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid id for user', true), 'default', array(), 'error');
@ -464,7 +464,7 @@ class UsersController extends AppController {
$params = array('recursive' => 0,
'fields' => $fields,
'group' => array('User.org'),
'order' => array('User.org'),
'order' => array('UPPER(User.org)'),
);
$orgs = $this->User->find('all', $params);
$this->set('orgs', $orgs);
@ -743,5 +743,4 @@ class UsersController extends AppController {
}
// User didn't see the contact form yet. Present it to him.
}
}

View File

@ -0,0 +1,23 @@
<?php
App::uses('AppController', 'Controller');
/**
* WorkerLogs Controller
*
* @property WorkerLog $WorkerLog
*/
class WorkerLogsController extends AppController {
public function beforeFilter() {
parent::beforeFilter();
}
public function index() {
$this->recursive = 0;
$this->set('list', $this->paginate());
}
public function add() {
}
}

View File

@ -1,17 +1,21 @@
<?php
class HidsMd5ExportComponent extends Component {
class HidsExport {
public $rules = array();
public function explain() {
public function explain($type) {
// unshift add in reverse order
array_unshift($this->rules, '# ');
array_unshift($this->rules, '# Keep in mind MD5 is not collision resistant');
array_unshift($this->rules, '# These HIDS export contains MD5 checksums.');
if ($type === 'MD5') {
array_unshift($this->rules, '# Keep in mind MD5 is not collision resistant');
} else if ($type === 'SHA1') {
array_unshift($this->rules, '# Keep in mind SHA-1 still has a theoretical collision possibility');
}
array_unshift($this->rules, '# These HIDS export contains ' . $type . ' checksums.');
}
public function export($items) {
public function export($items, $type = 'MD5') {
$itemsDone = array();
foreach ($items as &$item) {
@ -22,6 +26,7 @@ class HidsMd5ExportComponent extends Component {
switch ($attribute['type']) {
case 'md5':
case 'sha1':
if (!in_array ($attribute['value1'], $itemsDone)) {
$this->checksumRule($ruleFormat, $attribute);
$itemsDone[] = $attribute['value1'];
@ -29,6 +34,7 @@ class HidsMd5ExportComponent extends Component {
break;
case 'filename|md5':
case 'malware-sample':
case 'filename|sha1':
if (!in_array ($attribute['value2'], $itemsDone)) {
$this->partRule($ruleFormat, $attribute);
$itemsDone[] = $attribute['value2'];
@ -42,7 +48,7 @@ class HidsMd5ExportComponent extends Component {
}
sort($this->rules);
$this->explain();
$this->explain($type);
return $this->rules;
}

View File

@ -1,6 +1,6 @@
<?php
class NidsExportComponent extends Component {
class NidsExport {
public $rules = array();
@ -24,13 +24,15 @@ class NidsExportComponent extends Component {
private $whitelist = null;
public function export($items, $startSid, $format="suricata") {
public function export($items, $startSid, $format="suricata", $continue = false) {
$this->format = $format;
$this->Whitelist = ClassRegistry::init('Whitelist');
$this->whitelist = $this->Whitelist->getBlockedValues();
// output a short explanation
$this->explain();
if (!$continue) {
$this->explain();
}
// generate the rules
foreach ($items as &$item) {
/*switch ($item['Event']['risk']) {
@ -106,7 +108,7 @@ class NidsExportComponent extends Component {
public function ipDstRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'ip', // proto
@ -125,7 +127,7 @@ class NidsExportComponent extends Component {
public function ipSrcRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'ip', // proto
@ -144,7 +146,7 @@ class NidsExportComponent extends Component {
public function emailSrcRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:established,to_server; content:"MAIL FROM|3a|"; nocase; content:"' . $attribute['value'] . '"; fast_pattern; nocase; content:"|0D 0A 0D 0A|"; within:8192;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -164,7 +166,7 @@ class NidsExportComponent extends Component {
public function emailDstRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:established,to_server; content:"RCPT TO|3a|"; nocase; content:"' . $attribute['value'] . '"; fast_pattern; nocase; content:"|0D 0A 0D 0A|"; within:8192;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -185,7 +187,7 @@ class NidsExportComponent extends Component {
public function emailSubjectRule($ruleFormat, $attribute, &$sid) {
// LATER nids - email-subject rule might not match because of line-wrapping
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:established,to_server; content:"Subject|3a|"; nocase; content:"' . $attribute['value'] . '"; fast_pattern; nocase; content:"|0D 0A 0D 0A|"; within:8192;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -206,7 +208,7 @@ class NidsExportComponent extends Component {
public function emailAttachmentRule($ruleFormat, $attribute, &$sid) {
// LATER nids - email-attachment rule might not match because of line-wrapping
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:established,to_server; content:"Content-Disposition|3a| attachment|3b| filename|3d 22|"; content:"' . $attribute['value'] . '|22|"; fast_pattern; content:"|0D 0A 0D 0A|"; within:8192;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -226,8 +228,8 @@ class NidsExportComponent extends Component {
public function hostnameRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExportComponent::dnsNameToRawFormat($attribute['value'], 'hostname') . '"; fast_pattern; nocase;';
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExport::dnsNameToRawFormat($attribute['value'], 'hostname') . '"; fast_pattern; nocase;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
@ -278,8 +280,8 @@ class NidsExportComponent extends Component {
public function domainRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExportComponent::dnsNameToRawFormat($attribute['value']) . '"; fast_pattern; nocase;';
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExport::dnsNameToRawFormat($attribute['value']) . '"; fast_pattern; nocase;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
@ -333,7 +335,7 @@ class NidsExportComponent extends Component {
//$hostpart = parse_url($attribute['value'], PHP_URL_HOST);
//$overruled = $this->checkNames($hostpart);
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:to_server,established; content:"' . $attribute['value'] . '"; nocase; http_uri;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -353,7 +355,7 @@ class NidsExportComponent extends Component {
public function userAgentRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:to_server,established; content:"' . $attribute['value'] . '"; http_header;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',

View File

@ -0,0 +1,14 @@
<?php
App::uses('NidsExport', 'Export');
class NidsSnortExport extends NidsExport {
public function export($items, $startSid, $format = "suricata", $continue = false) {
// set the specific format
$this->format = 'snort';
// call the generic function
return parent::export($items, $startSid, $format, $continue);
}
}

View File

@ -1,22 +1,20 @@
<?php
App::uses('NidsExport', 'Export');
App::uses('NidsExportComponent', 'Controller/Component');
class NidsSuricataExport extends NidsExport {
class NidsSuricataExportComponent extends NidsExportComponent {
public function export($items, $startSid) {
public function export($items, $startSid, $format = "suricata", $continue = false) {
// set the specific format
$this->format = 'suricata';
$this->format = "suricata";
// call the generic function
return parent::export($items, $startSid);
return parent::export($items, $startSid, $format, $continue);
}
// below overwrite functions from NidsExportComponent
// below overwrite functions from NidsExport
public function hostnameRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExportComponent::dnsNameToRawFormat($attribute['value'], 'hostname') . '"; fast_pattern; nocase;';
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExport::dnsNameToRawFormat($attribute['value'], 'hostname') . '"; fast_pattern; nocase;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
@ -68,8 +66,8 @@ class NidsSuricataExportComponent extends NidsExportComponent {
public function domainRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExportComponent::dnsNameToRawFormat($attribute['value']) . '"; fast_pattern; nocase;';
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'content:"|01 00 00 01 00 00 00 00 00 00|"; depth:10; offset:2; content:"' . NidsExport::dnsNameToRawFormat($attribute['value']) . '"; fast_pattern; nocase;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
'udp', // proto
@ -125,7 +123,7 @@ class NidsSuricataExportComponent extends NidsExportComponent {
//$overruled = $this->checkNames($hostpart);
// warning: only suricata compatible
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$content = 'flow:to_server,established; content:"' . $attribute['value'] . '"; fast_pattern; nocase; http_uri;';
$this->rules[] = sprintf($ruleFormat,
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
@ -145,7 +143,7 @@ class NidsSuricataExportComponent extends NidsExportComponent {
public function userAgentRule($ruleFormat, $attribute, &$sid) {
$overruled = $this->checkWhitelist($attribute['value']);
$attribute['value'] = NidsExportComponent::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
// warning: only suricata compatible
$content = 'flow:to_server,established; content:"' . $attribute['value'] . '"; fast_pattern; http_user_agent;';
$this->rules[] = sprintf($ruleFormat,

7
app/Lib/Export/test.php Normal file
View File

@ -0,0 +1,7 @@
<?php
class test {
public function yay() {
return 'hello';
}
}

View File

@ -1083,4 +1083,92 @@ class Attribute extends AppModel {
}
return $fails;
}
public function hids($isSiteAdmin, $org ,$type) {
// check if it's a valid type
if ($type != 'md5' && $type != 'sha1') {
throw new UnauthorizedException('Invalid hash type.');
}
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array('Attribute.to_ids' => 1, 'Event.published' => 1);
if (!$isSiteAdmin) {
$temp = array();
$distribution = array();
array_push($temp, array('Attribute.distribution >' => 0));
array_push($temp, array('(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org));
$conditions['OR'] = $temp;
}
$params = array(
'conditions' => $conditions, //array of conditions
'recursive' => 0, //int
'group' => array('Attribute.type', 'Attribute.value1'), //fields to GROUP BY
);
$items = $this->find('all', $params);
App::uses('HidsExport', 'Export');
$export = new HidsExport();
$rules = $export->export($items, strtoupper($type));
return $rules;
}
public function nids($isSiteAdmin, $org, $format, $sid, $id = null, $continue = false) {
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array('Attribute.to_ids' => 1, "Event.published" => 1);
if (!$isSiteAdmin) {
$temp = array();
$distribution = array();
array_push($temp, array('Attribute.distribution >' => 0));
array_push($temp, array('(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org));
$conditions['OR'] = $temp;
}
if ($id) {
array_push($conditions['AND'], array('Event.id' => $id));
}
$params = array(
'conditions' => $conditions, //array of conditions
'recursive' => 0, //int
'group' => array('Attribute.type', 'Attribute.value1'), //fields to GROUP BY
);
unset($this->virtualFields['category_order']); // not needed for IDS export and speeds things up
$items = $this->find('all', $params);
// export depending of the requested type
switch ($format) {
case 'suricata':
- App::uses('NidsSuricataExport', 'Export');
$export = new NidsSuricataExport();
break;
case 'snort':
App::uses('NidsSnortExport', 'Export');
$export = new NidsSnortExport();
break;
}
$rules = $export->export($items, $sid, $format, $continue);
return $rules;
}
public function text($org, $isSiteAdmin, $type) {
//restricting to non-private or same org if the user is not a site-admin.
$conditions['AND'] = array('Attribute.type' => $type, 'Attribute.to_ids =' => 1, 'Event.published =' => 1);
if (!$isSiteAdmin) {
$temp = array();
$distribution = array();
array_push($temp, array('Attribute.distribution >' => 0));
array_push($temp, array('(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org));
$conditions['OR'] = $temp;
}
$params = array(
'conditions' => $conditions, //array of conditions
'recursive' => 0, //int
'fields' => array('Attribute.value'), //array of field names
'order' => array('Attribute.value'), //string or array defining order
'group' => array('Attribute.value'), //fields to GROUP BY
'contain' => array('Event.id', 'Event.published'),
);
return $this->find('all', $params);
}
}

View File

@ -1,6 +1,6 @@
<?php
App::uses('AppModel', 'Model');
App::uses('CakeEmail', 'Network/Email');
App::import('Controller', 'Attributes');
/**
* Event Model
@ -70,6 +70,49 @@ class Event extends AppModel {
0 => 'Your organisation only', 1 => 'This community only', 2 => 'Connected communities', 3 => 'All communities'
);
public $export_types = array(
'xml' => array(
'extension' => '.xml',
'type' => 'XML',
'description' => 'Click this to download all events and attributes that you have access to <small>(except file attachments)</small> in a custom XML format.',
),
'csv_sig' => array(
'extension' => '.csv',
'type' => 'CSV_Sig',
'description' => 'Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.',
),
'csv_all' => array(
'extension' => '.csv',
'type' => 'CSV_All',
'description' => 'Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.',
),
'suricata' => array(
'extension' => '.rules',
'type' => 'Suricata',
'description' => 'Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'snort' => array(
'extension' => '.rules',
'type' => 'Snort',
'description' => 'Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a whitelist containing host, domain name and IP numbers to exclude from the NIDS export.',
),
'md5' => array(
'extension' => '.txt',
'type' => 'MD5',
'description' => 'Click on one of these two buttons to download all MD5 checksums contained in file-related attributes. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.',
),
'sha1' => array(
'extension' => '.txt',
'type' => 'SHA1',
'description' => 'Click on one of these two buttons to download all SHA1 checksums contained in file-related attributes. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.',
),
'text' => array(
'extension' => '.txt',
'type' => 'TEXT',
'description' => 'Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.'
)
);
/**
* Validation rules
*
@ -665,7 +708,7 @@ class Event extends AppModel {
* TODO move this to a component
* @return array|NULL
*/
public function downloadEventFromServer($eventId, $server, $HttpSocket=null) {
public function downloadEventFromServer($eventId, $server, $HttpSocket=null, $propsalDownload = false) {
$url = $server['Server']['url'];
$authkey = $server['Server']['authkey'];
if (null == $HttpSocket) {
@ -683,7 +726,11 @@ class Event extends AppModel {
//'Connection' => 'keep-alive' // LATER followup cakephp ticket 2854 about this problem http://cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/2854
)
);
$uri = $url . '/events/' . $eventId;
if (!$propsalDownload) {
$uri = $url . '/events/' . $eventId;
} else {
$uri = $url . '/shadow_attributes/getProposalsByUuid/' . $eventId;
}
$response = $HttpSocket->get($uri, $data = '', $request);
if ($response->isOk()) {
$xmlArray = Xml::toArray(Xml::build($response->body));
@ -758,4 +805,736 @@ class Event extends AppModel {
// error, so return null
return null;
}
public function fetchEventIds($org, $isSiteAdmin) {
$conditions = array();
if (!$isSiteAdmin) {
$conditions['OR'] = array(
'Event.distribution >' => 0,
'Event.org LIKE' => $org
);
}
$fields = array('Event.id', 'Event.org', 'Event.distribution');
$params = array(
'conditions' => $conditions,
'recursive' => -1,
'fields' => $fields,
);
$results = $this->find('all', $params);
return $results;
}
//Once the data about the user is gathered from the appropriate sources, fetchEvent is called from the controller.
public function fetchEvent($eventid = null, $idList = null, $org, $isSiteAdmin, $bkgrProcess = null) {
if (isset($eventid)) {
$this->id = $eventid;
if (!$this->exists()) {
throw new NotFoundException(__('Invalid event'));
}
$conditions = array("Event.id" => $eventid);
} else {
$conditions = array();
}
$me['org'] = $org;
// if we come from automation, we may not be logged in - instead we used an auth key in the URL.
$conditionsAttributes = array();
$conditionsShadowAttributes = array();
//restricting to non-private or same org if the user is not a site-admin.
if (!$isSiteAdmin) {
$conditions['AND']['OR'] = array(
'Event.distribution >' => 0,
'Event.org LIKE' => $org
);
$conditionsAttributes['OR'] = array(
'Attribute.distribution >' => 0,
'(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org
);
$conditionsShadowAttributes['OR'] = array(
// We are currently looking at events.org matching the user's org, but later on, once we start syncing shadow attributes, we may want to change this to orgc
// Right now the org that currently owns the event on an instance can see, accept and decline these requests, but in the long run once we can distribute
// the requests back to the creator, we may want to leave these decisions up to them.
array('(SELECT events.org FROM events WHERE events.id = ShadowAttribute.event_id) LIKE' => $org),
array('ShadowAttribute.org LIKE' => $org),
);
}
if ($idList) {
$conditions['AND'][] = array('Event.id' => $idList);
}
// removing this for now, we export the to_ids == 0 attributes too, since there is a to_ids field indicating it in the .xml
// $conditionsAttributes['AND'] = array('Attribute.to_ids =' => 1);
// Same idea for the published. Just adjust the tools to check for this
// TODO: It is important to make sure that this is documented
// $conditions['AND'][] = array('Event.published =' => 1);
// do not expose all the data ...
$fields = array('Event.id', 'Event.org', 'Event.date', 'Event.threat_level_id', 'Event.info', 'Event.published', 'Event.uuid', 'Event.attribute_count', 'Event.analysis', 'Event.timestamp', 'Event.distribution', 'Event.proposal_email_lock', 'Event.orgc', 'Event.user_id', 'Event.locked');
$fieldsAtt = array('Attribute.id', 'Attribute.type', 'Attribute.category', 'Attribute.value', 'Attribute.to_ids', 'Attribute.uuid', 'Attribute.event_id', 'Attribute.distribution', 'Attribute.timestamp', 'Attribute.comment');
$fieldsShadowAtt = array('ShadowAttribute.id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.value', 'ShadowAttribute.to_ids', 'ShadowAttribute.uuid', 'ShadowAttribute.event_id', 'ShadowAttribute.old_id', 'ShadowAttribute.comment');
$params = array('conditions' => $conditions,
'recursive' => 0,
'fields' => $fields,
'contain' => array(
'ThreatLevel' => array(
'fields' => array('ThreatLevel.name')
),
'Attribute' => array(
'fields' => $fieldsAtt,
'conditions' => $conditionsAttributes,
),
'ShadowAttribute' => array(
'fields' => $fieldsShadowAtt,
'conditions' => $conditionsShadowAttributes,
),
)
);
if ($isSiteAdmin) $params['contain']['User'] = array('fields' => 'email');
$results = $this->find('all', $params);
// Do some refactoring with the event
foreach ($results as $eventKey => &$event) {
// Let's find all the related events and attach it to the event itself
$results[$eventKey]['RelatedEvent'] = $this->getRelatedEvents($me, $isSiteAdmin, $event['Event']['id']);
// Let's also find all the relations for the attributes - this won't be in the xml export though
$results[$eventKey]['RelatedAttribute'] = $this->getRelatedAttributes($me, $isSiteAdmin, $event['Event']['id']);
foreach ($event['Attribute'] as $key => &$attribute) {
$attribute['ShadowAttribute'] = array();
// If a shadowattribute can be linked to an attribute, link it to it then remove it from the event
// This is to differentiate between proposals that were made to an attribute for modification and between proposals for new attributes
foreach ($event['ShadowAttribute'] as $k => &$sa) {
if(!empty($sa['old_id'])) {
if ($sa['old_id'] == $attribute['id']) {
$results[$eventKey]['Attribute'][$key]['ShadowAttribute'][] = $sa;
unset($results[$eventKey]['ShadowAttribute'][$k]);
}
}
}
}
}
return $results;
}
public function csv($org, $isSiteAdmin, $eventid=0, $ignore=0, $attributeIDList = array()) {
$final = array();
$attributeList = array();
$conditions = array();
$econditions = array();
$this->recursive = -1;
// If we are not in the search result csv download function then we need to check what can be downloaded. CSV downloads are already filtered by the search function.
if ($eventid !== 'search') {
// This is for both single event downloads and for full downloads. Org has to be the same as the user's or distribution not org only - if the user is no siteadmin
if(!$isSiteAdmin) {
$econditions['AND']['OR'] = array('Event.distribution >' => 0, 'Event.org =' => $org);
}
if ($eventid == 0 && $ignore == 0) {
$econditions['AND'][] = array('Event.published =' => 1);
}
// If it's a full download (eventid == null) and the user is not a site admin, we need to first find all the events that the user can see and save the IDs
if ($eventid == 0) {
$this->recursive = -1;
// let's add the conditions if we're dealing with a non-siteadmin user
$params = array(
'conditions' => $econditions,
'fields' => array('id', 'distribution', 'org', 'published'),
);
$events = $this->find('all', $params);
}
// if we have items in events, add their IDs to the conditions. If we're a site admin, or we have a single event selected for download, this should be empty
if (isset($events)) {
foreach ($events as $event) {
$conditions['AND']['OR'][] = array('Attribute.event_id' => $event['Event']['id']);
}
}
// if we're downloading a single event, set it as a condition
if ($eventid!=0) {
$conditions['AND'][] = array('Attribute.event_id' => $eventid);
}
//restricting to non-private or same org if the user is not a site-admin.
if ($ignore == 0) {
$conditions['AND'][] = array('Attribute.to_ids =' => 1);
}
if (!$isSiteAdmin) {
$temp = array();
$distribution = array();
array_push($temp, array('Attribute.distribution >' => 0));
array_push($temp, array('(SELECT events.org FROM events WHERE events.id = Attribute.event_id) LIKE' => $org));
$conditions['OR'] = $temp;
}
}
if ($eventid === 'search') {
foreach ($attributeIDList as $aID) {
$conditions['AND']['OR'][] = array('Attribute.id' => $aID);
}
}
$params = array(
'conditions' => $conditions, //array of conditions
'fields' => array('Attribute.event_id', 'Attribute.distribution', 'Attribute.category', 'Attribute.type', 'Attribute.value', 'Attribute.uuid'),
);
$attributes = $this->Attribute->find('all', $params);
foreach ($attributes as $attribute) {
$attribute['Attribute']['value'] = str_replace("\r", "", $attribute['Attribute']['value']);
$attribute['Attribute']['value'] = str_replace("\n", "", $attribute['Attribute']['value']);
}
return $attributes;
}
public function sendAlertEmailRouter($id, $user) {
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$data = array(
'worker' => 'default',
'job_type' => 'publish_alert_email',
'job_input' => 'Event: ' . $id,
'status' => 0,
'retries' => 0,
'org' => $user['org'],
'message' => 'Sending...',
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'default',
'EventShell',
array('alertemail', $user['org'], $jobId, $id)
);
$job->saveField('process_id', $process_id);
return true;
} else {
return ($this->sendAlertEmail($id, $user['org']));
}
}
public function sendAlertEmail($id, $org, $processId = null) {
$this->recursive = 1;
$event = $this->read(null, $id);
// Initialise the Job class if we have a background process ID
// This will keep updating the process's progress bar
if ($processId) {
$this->Job = ClassRegistry::init('Job');
}
// The mail body, h() is NOT needed as we are sending plain-text mails.
$body = "";
$body .= '----------------------------------------------' . "\n";
$appendlen = 20;
$body .= 'URL : ' . Configure::read('CyDefSIG.baseurl') . '/events/view/' . $event['Event']['id'] . "\n";
$body .= 'Event : ' . $event['Event']['id'] . "\n";
$body .= 'Date : ' . $event['Event']['date'] . "\n";
if ('true' == Configure::read('CyDefSIG.showorg')) {
$body .= 'Reported by : ' . $event['Event']['org'] . "\n";
}
$body .= 'Risk : ' . $event['ThreatLevel']['name'] . "\n";
$body .= 'Analysis : ' . $this->analysisLevels[$event['Event']['analysis']] . "\n";
$body .= 'Info : ' . "\n";
$body .= $event['Event']['info'] . "\n";
$user['org'] = $org;
$relatedEvents = $this->getRelatedEvents($user, false);
if (!empty($relatedEvents)) {
$body .= '----------------------------------------------' . "\n";
$body .= 'Related to : '. "\n";
foreach ($relatedEvents as &$relatedEvent) {
$body .= Configure::read('CyDefSIG.baseurl') . '/events/view/' . $relatedEvent['Event']['id'] . ' (' . $relatedEvent['Event']['date'] . ') ' ."\n";
}
$body .= '----------------------------------------------' . "\n";
}
$body .= 'Attributes :' . "\n";
$bodyTempOther = "";
if (isset($event['Attribute'])) {
foreach ($event['Attribute'] as &$attribute) {
$line = '- ' . $attribute['type'] . str_repeat(' ', $appendlen - 2 - strlen($attribute['type'])) . ': ' . $attribute['value'] . "\n";
if ('other' == $attribute['type']) // append the 'other' attribute types to the bottom.
$bodyTempOther .= $line;
else $body .= $line;
}
}
if (!empty($bodyTempOther)) {
$body .= "\n";
}
$body .= $bodyTempOther; // append the 'other' attribute types to the bottom.
$body .= '----------------------------------------------' . "\n";
// find out whether the event is private, to limit the alerted user's list to the org only
if ($event['Event']['distribution'] == 0) {
$eventIsPrivate = true;
} else {
$eventIsPrivate = false;
}
// sign the body
require_once 'Crypt/GPG.php';
try {
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); // , 'debug' => true
$gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
$bodySigned = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR);
//
// Build a list of the recipients that get a non-encrypted mail
// But only do this if it is allowed in the bootstrap.php file.
//
if ($eventIsPrivate) {
$conditions = array('User.autoalert' => 1, 'User.gpgkey =' => "", 'User.org =' => $event['Event']['org']);
} else {
$conditions = array('User.autoalert' => 1, 'User.gpgkey =' => "");
}
if ('false' == Configure::read('GnuPG.onlyencrypted')) {
$alertUsers = $this->User->find('all', array(
'conditions' => $conditions,
'recursive' => 0,
));
$max = count($alertUsers);
foreach ($alertUsers as $k => &$user) {
// prepare the the unencrypted email
$Email = new CakeEmail();
$Email->from(Configure::read('CyDefSIG.email'));
$Email->to($user['User']['email']);
$Email->subject("[" . Configure::read('CyDefSIG.org') . " " . Configure::read('CyDefSIG.name') . "] Event " . $id . " - " . $event['ThreatLevel']['name'] . " - TLP Amber");
$Email->emailFormat('text'); // both text or html
// send it
$Email->send($bodySigned);
$Email->reset();
if ($processId) {
$this->Job->saveField('progress', $k / $max * 50);
}
}
}
//
// Build a list of the recipients that wish to receive encrypted mails.
//
if ($eventIsPrivate) {
$conditions = array('User.autoalert' => 1, 'User.gpgkey !=' => "", 'User.org =' => $event['Event']['org']);
} else {
$conditions = array('User.autoalert' => 1, 'User.gpgkey !=' => "");
}
$alertUsers = $this->User->find('all', array(
'conditions' => $conditions,
'recursive' => 0,
)
);
// encrypt the mail for each user and send it separately
foreach ($alertUsers as &$user) {
// send the email
$Email = new CakeEmail();
$Email->from(Configure::read('CyDefSIG.email'));
$Email->to($user['User']['email']);
$Email->subject("[" . Configure::read('CyDefSIG.org') . " " . Configure::read('CyDefSIG.name') . "] Event " . $id . " - " . $event['ThreatLevel']['name'] . " - TLP Amber");
$Email->emailFormat('text'); // both text or html
// import the key of the user into the keyring
// this is not really necessary, but it enables us to find
// the correct key-id even if it is not the same as the emailaddress
$keyImportOutput = $gpg->importKey($user['User']['gpgkey']);
// say what key should be used to encrypt
try {
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir')));
$gpg->addEncryptKey($keyImportOutput['fingerprint']); // use the key that was given in the import
$bodyEncSig = $gpg->encrypt($bodySigned, true);
$Email->send($bodyEncSig);
} catch (Exception $e){
// catch errors like expired PGP keys
$this->log($e->getMessage());
// no need to return here, as we want to send out mails to the other users if GPG encryption fails for a single user
}
// If you wish to send multiple emails using a loop, you'll need
// to reset the email fields using the reset method of the Email component.
$Email->reset();
if ($processId) {
$this->Job->saveField('progress', ($k / $max * 50) + 50);
}
}
} catch (Exception $e){
// catch errors like expired PGP keys
$this->log($e->getMessage());
return $e->getMessage();
}
// LATER check if sending email succeeded and return appropriate result
return true;
}
public function sendContactEmail($id, $message, $all, $user, $isSiteAdmin) {
// fetch the event
$event = $this->read(null, $id);
$this->User = ClassRegistry::init('User');
if (!$all) {
//Insert extra field here: alertOrg or something, then foreach all the org members
//limit this array to users with contactalerts turned on!
$orgMembers = array();
$this->User->recursive = 0;
$temp = $this->User->findAllByOrg($event['Event']['org'], array('email', 'gpgkey', 'contactalert', 'id'));
foreach ($temp as $tempElement) {
if ($tempElement['User']['contactalert'] || $tempElement['User']['id'] == $event['Event']['user_id']) {
array_push($orgMembers, $tempElement);
}
}
} else {
$orgMembers = $this->User->findAllById($event['Event']['user_id'], array('email', 'gpgkey'));
}
// The mail body, h() is NOT needed as we are sending plain-text mails.
$body = "";
$body .= "Hello, \n";
$body .= "\n";
$body .= "Someone wants to get in touch with you concerning a MISP event. \n";
$body .= "\n";
$body .= "You can reach him at " . $user['User']['email'] . "\n";
if (!$user['User']['gpgkey'])
$body .= "His GPG/PGP key is added as attachment to this email. \n";
$body .= "\n";
$body .= "He wrote the following message: \n";
$body .= $message . "\n";
$body .= "\n";
$body .= "\n";
$body .= "The event is the following: \n";
// print the event in mail-format
// LATER place event-to-email-layout in a function
$appendlen = 20;
$body .= 'URL : ' . Configure::read('CyDefSIG.baseurl') . '/events/view/' . $event['Event']['id'] . "\n";
$body .= 'Event : ' . $event['Event']['id'] . "\n";
$body .= 'Date : ' . $event['Event']['date'] . "\n";
if ('true' == Configure::read('CyDefSIG.showorg')) {
$body .= 'Reported by : ' . $event['Event']['org'] . "\n";
}
$body .= 'Risk : ' . $event['ThreatLevel']['name'] . "\n";
$body .= 'Analysis : ' . $event['Event']['analysis'] . "\n";
$relatedEvents = $this->getRelatedEvents($user, $isSiteAdmin);
if (!empty($relatedEvents)) {
foreach ($relatedEvents as &$relatedEvent) {
$body .= 'Related to : ' . Configure::read('CyDefSIG.baseurl') . '/events/view/' . $relatedEvent['Event']['id'] . ' (' . $relatedEvent['Event']['date'] . ')' . "\n";
}
}
$body .= 'Info : ' . "\n";
$body .= $event['Event']['info'] . "\n";
$body .= "\n";
$body .= 'Attributes :' . "\n";
$bodyTempOther = "";
if (!empty($event['Attribute'])) {
foreach ($event['Attribute'] as &$attribute) {
$line = '- ' . $attribute['type'] . str_repeat(' ', $appendlen - 2 - strlen( $attribute['type'])) . ': ' . $attribute['value'] . "\n";
if ('other' == $attribute['type']) // append the 'other' attribute types to the bottom.
$bodyTempOther .= $line;
else $body .= $line;
}
}
$body .= "\n";
$body .= $bodyTempOther; // append the 'other' attribute types to the bottom.
$Email = new CakeEmail();
// sign the body
require_once 'Crypt/GPG.php';
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); // , 'debug' => true
$gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password'));
$bodySigned = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR);
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
if ($user['User']['gpgkey'] != null) {
// save the gpg key to a temporary file
$tmpfname = tempnam(TMP, "GPGkey");
$handle = fopen($tmpfname, "w");
fwrite($handle, $user['User']['gpgkey']);
fclose($handle);
// attach it
$Email->attachments(array(
'gpgkey.asc' => $tmpfname
));
}
foreach ($orgMembers as &$reporter) {
if (!empty($reporter['User']['gpgkey'])) {
// import the key of the user into the keyring
// this isn't really necessary, but it gives it the fingerprint necessary for the next step
$keyImportOutput = $gpg->importKey($reporter['User']['gpgkey']);
// say what key should be used to encrypt
try {
$gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir')));
$gpg->addEncryptKey($keyImportOutput['fingerprint']); // use the key that was given in the import
$bodyEncSig = $gpg->encrypt($bodySigned, true);
} catch (Exception $e){
// catch errors like expired PGP keys
$this->log($e->getMessage());
// no need to return here, as we want to send out mails to the other users if GPG encryption fails for a single user
}
} else {
$bodyEncSig = $bodySigned;
// FIXME should I allow sending unencrypted "contact" mails to people if they didn't import they GPG key?
}
$Email->from(Configure::read('CyDefSIG.email'));
$Email->replyTo($user['User']['email']);
$Email->to($reporter['User']['email']);
$Email->subject("[" . Configure::read('CyDefSIG.org') . " " . Configure::read('CyDefSIG.name') . "] Need info about event " . $id . " - TLP Amber");
//$this->Email->delivery = 'debug'; // do not really send out mails, only display it on the screen
$Email->emailFormat('text'); // both text or html
// Add the GPG key of the user as attachment
// LATER sign the attached GPG key
if ($user['User']['gpgkey'] != null) {
// attach the gpg key
$Email->attachments(array(
'gpgkey.asc' => $tmpfname
));
}
// send it
$result = $Email->send($bodyEncSig);
// If you wish to send multiple emails using a loop, you'll need
// to reset the email fields using the reset method of the Email component.
$Email->reset();
}
// remove the temporary gpg file
if ($user['User']['gpgkey'] != null) unlink($tmpfname);
return $result;
}
/**
* Low level function to add an Event based on an Event $data array
*
* @return bool true if success
*/
public function _add(&$data, $fromXml, $user, $or='', $passAlong = null, $fromPull = false) {
$this->create();
// force check userid and orgname to be from yourself
$data['Event']['user_id'] = $user['id'];
$date = new DateTime();
//if ($this->checkAction('perm_sync')) $data['Event']['org'] = Configure::read('CyDefSIG.org');
//else $data['Event']['org'] = $auth->user('org');
$data['Event']['org'] = $user['org'];
// set these fields if the event is freshly created and not pushed from another instance.
// Moved out of if (!$fromXML), since we might get a restful event without the orgc/timestamp set
if (!isset ($data['Event']['orgc'])) $data['Event']['orgc'] = $data['Event']['org'];
if ($fromXml) {
// Workaround for different structure in XML/array than what CakePHP expects
$this->cleanupEventArrayFromXML($data);
// the event_id field is not set (normal) so make sure no validation errors are thrown
// LATER do this with $this->validator()->remove('event_id');
unset($this->Attribute->validate['event_id']);
unset($this->Attribute->validate['value']['unique']); // otherwise gives bugs because event_id is not set
}
unset ($data['Event']['id']);
if (isset($data['Event']['uuid'])) {
// check if the uuid already exists
$existingEventCount = $this->find('count', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
if ($existingEventCount > 0) {
// RESTfull, set responce location header..so client can find right URL to edit
if ($fromPull) return false;
$existingEvent = $this->find('first', array('conditions' => array('Event.uuid' => $data['Event']['uuid'])));
return $existingEvent['Event']['id'];
}
}
if (isset($data['Attribute'])) {
foreach ($data['Attribute'] as &$attribute) {
unset ($attribute['id']);
}
}
// FIXME chri: validatebut the necessity for all these fields...impact on security !
$fieldList = array(
'Event' => array('org', 'orgc', 'date', 'threat_level_id', 'analysis', 'info', 'user_id', 'published', 'uuid', 'timestamp', 'distribution', 'locked'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'timestamp', 'distribution')
);
$saveResult = $this->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList,
'atomic' => true));
// FIXME chri: check if output of $saveResult is what we expect when data not valid, see issue #104
if ($saveResult) {
if (!empty($data['Event']['published']) && 1 == $data['Event']['published']) {
// do the necessary actions to publish the event (email, upload,...)
if ('true' != Configure::read('MISP.disablerestalert')) {
$this->sendAlertEmailRouter($this->getId(), $user);
}
$this->publish($this->getId(), $passAlong);
}
return true;
} else {
//throw new MethodNotAllowedException("Validation ERROR: \n".var_export($this->Event->validationErrors, true));
return false;
}
}
public function _edit(&$data, $id) {
$this->read(null, $id);
if (!isset ($data['Event']['orgc'])) $data['Event']['orgc'] = $data['Event']['org'];
if ($this->data['Event']['timestamp'] < $data['Event']['timestamp']) {
} else {
return 'Event exists and is the same or newer.';
}
if (!$this->data['Event']['locked']) {
return 'Event originated on this instance, any changes to it have to be done locally.';
}
$fieldList = array(
'Event' => array('date', 'threat_level_id', 'analysis', 'info', 'published', 'uuid', 'from', 'distribution', 'timestamp'),
'Attribute' => array('event_id', 'category', 'type', 'value', 'value1', 'value2', 'to_ids', 'uuid', 'revision', 'distribution', 'timestamp')
);
$data['Event']['id'] = $this->data['Event']['id'];
if (isset($data['Event']['Attribute'])) {
foreach ($data['Event']['Attribute'] as $k => &$attribute) {
$existingAttribute = $this->__searchUuidInAttributeArray($attribute['uuid'], $this->data);
if (count($existingAttribute)) {
$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 ($data['Event']['Attribute'][$k]['timestamp'] > $existingAttribute['Attribute']['timestamp']) {
} else {
unset($data['Event']['Attribute'][$k]);
}
} else {
unset($data['Event']['Attribute'][$k]['id']);
}
}
}
$this->cleanupEventArrayFromXML($data);
$saveResult = $this->saveAssociated($data, array('validate' => true, 'fieldList' => $fieldList));
if ($saveResult) return 'success';
else return 'Saving the event has failed.';
}
private function __searchUuidInAttributeArray($uuid, &$attr_array) {
foreach ($attr_array['Attribute'] as &$attr) {
if ($attr['uuid'] == $uuid) return array('Attribute' => $attr);
}
return false;
}
/**
* Uploads this specific event to all remote servers
* TODO move this to a component
*
* @return bool true if success, false if, partly, failed
*/
public function uploadEventToServersRouter($id, $passAlong = null) {
// make sure we have all the data of the Event
$this->id = $id;
$this->recursive = 1;
$this->read();
$this->data['Event']['locked'] = 1;
// get a list of the servers
$server = ClassRegistry::init('Server');
$servers = $server->find('all', array(
'conditions' => array('Server.push' => true)
));
// iterate over the servers and upload the event
if(empty($servers))
return true;
$uploaded = true;
$failedServers = array();
App::uses('HttpSocket', 'Network/Http');
$HttpSocket = new HttpSocket();
foreach ($servers as &$server) {
//Skip servers where the event has come from.
if (($passAlong != $server)) {
$thisUploaded = $this->uploadEventToServer($this->data, $server, $HttpSocket);
if (!$thisUploaded) {
$uploaded = !$uploaded ? $uploaded : $thisUploaded;
$failedServers[] = $server['Server']['url'];
}
}
}
if (!$uploaded) {
return $failedServers;
} else {
return true;
}
}
public function publishRouter($id, $passAlong = null) {
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$data = array(
'worker' => 'default',
'job_type' => 'publish_event',
'job_input' => 'Event ID: ' . $id,
'status' => 0,
'retries' => 0,
'org' => $this->Auth->user('org'),
'message' => 'Publishing.',
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'default',
'EventShell',
array('publish', $id, $passAlong, $process_id)
);
$job->saveField('process_id', $process_id);
return true;
} else {
$result = $this->publish($id, $passAlong);
return $result;
}
}
/**
* Performs all the actions required to publish an event
*
* @param unknown_type $id
*/
public function publish($id, $passAlong = null, $processId) {
$this->id = $id;
$this->recursive = 0;
$event = $this->read(null, $id);
// update the DB to set the published flag
$fieldList = array('published', 'id', 'info');
$event['Event']['published'] = 1;
$this->save($event, array('fieldList' => $fieldList));
$uploaded = false;
if ('true' == Configure::read('CyDefSIG.sync') && $event['Event']['distribution'] > 1) {
$uploaded = $this->uploadEventToServersRouter($id, $passAlong);
if (($uploaded == false) || (is_array($uploaded))) {
$this->saveField('published', 0);
}
} else {
return true;
}
return $uploaded;
}
/**
*
* Sends out an email to all people within the same org
* with the request to be contacted about a specific event.
* @todo move __sendContactEmail($id, $message) to a better place. (components?)
*
* @param unknown_type $id The id of the event for wich you want to contact the org.
* @param unknown_type $message The custom message that will be appended to the email.
* @param unknown_type $all, true: send to org, false: send to person.
*
* @codingStandardsIgnoreStart
* @throws \UnauthorizedException as well. // TODO Exception NotFoundException
* @codingStandardsIgnoreEnd
*
* @return True if success, False if error
*/
public function sendContactEmailRouter($id, $message, $all, $user, $isSiteAdmin, $JobId = false) {
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$data = array(
'worker' => 'default',
'job_type' => 'contact_alert',
'job_input' => 'To entire org: ' . $all,
'status' => 0,
'retries' => 0,
'org' => $user['org'],
'message' => 'Contacting.',
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'default',
'EventShell',
array('contactemail', $id, $message, $all, $user['id'], $isSiteAdmin, $jobId)
);
$job->saveField('process_id', $process_id);
return true;
} else {
$userMod['User'] = $user;
$result = $this->sendContactEmail($id, $message, $all, $userMod, $isSiteAdmin);
return $result;
}
}
}

48
app/Model/Job.php Normal file
View File

@ -0,0 +1,48 @@
<?php
App::uses('AppModel', 'Model');
/**
* Job Model
*
* @property Job $Job
*/
class Job extends AppModel {
public function cache($type, $isSiteAdmin, $org, $target, $jobOrg, $sid) {
$extra = null;
$extra2 = null;
$shell = 'Event';
$this->create();
$data = array(
'worker' => 'cache',
'job_type' => 'cache_' . $type,
'job_input' => $target,
'status' => 0,
'retries' => 0,
'org' => $jobOrg,
'message' => 'Fetching events.',
);
if ($type === 'md5' || $type === 'sha1') {
$extra = $type;
$type = 'hids';
}
if ($type === 'csv_all' || $type === 'csv_sig') {
$extra = $type;
$type = 'csv';
}
if ($type === 'suricata' || $type === 'snort') {
$extra = $type;
$type = 'nids';
$extra2 = $sid;
}
$this->save($data);
$id = $this->id;
$process_id = CakeResque::enqueue(
'cache',
$shell . 'Shell',
array('cache' . $type, $org, $isSiteAdmin, $id, $extra, $extra2),
true
);
$this->saveField('process_id', $process_id);
return $id;
}
}

67
app/Model/Pivot.php Normal file
View File

@ -0,0 +1,67 @@
<?php
App::uses('AppModel', 'Model');
/**
* Pivot Model
*
*/
class Pivot extends AppModel {
/**
* hasOne associations
*
* @var array
*/
public $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
),
'Event' => array(
'className' => 'Event',
'foreignKey' => 'event_id',
),
'Parent' => array(
'className' => 'Pivot',
'foreignKey' => 'pivot_id',
)
);
public function startPivoting($userID, $eventID, $eventInfo, $eventDate) {
$this->deleteAll(array('Pivot.user_id' => $userID), false);
$this->create();
$this->save(array(
'event_id' => $eventID,
'user_id' => $userID,
'event_info' => $eventInfo,
'event_date' => $eventDate,
'pivot_id' => 0));
return $this->getPivots($userID);
}
public function continuePivoting($userID, $eventID, $eventInfo, $eventDate) {
$this->recursive = -1;
$lastPivot = $this->find(
'first',
array(
'fields' => array('MAX(Pivot.id) as last_id'),
'conditions' => array('Pivot.user_id' => $userID),
));
$this->save(array(
'event_id' => $eventID,
'user_id' => $userID,
'pivot_id' => $lastPivot[0]['last_id']));
return $this->getPivots($userID);
}
public function getPivots($userID) {
$this->recursive = -1;
$allPivots = $this->find(
'all',
array(
'fields' => array('Pivot.event_id', 'Pivot.event_info', 'Pivot.event_date'),
'conditions' => array('Pivot.user_id' => $userID),
));
return $allPivots;
}
}
?>

View File

@ -66,7 +66,6 @@ class Regexp extends AppModel {
}
public function replaceSpecific($string, $allRegexp = null, $type) {
if(!$this->_isSiteAdmin()) $this->redirect(array('controller' => 'regexp', 'action' => 'index', 'admin' => false));
$orig = $string;
foreach ($allRegexp as $regexp) {
if (strlen($regexp['Regexp']['replacement']) && strlen($regexp['Regexp']['regexp']) && ($regexp['Regexp']['type'] === 'ALL' || $regexp['Regexp']['type'] === $type)) {

View File

@ -107,4 +107,266 @@ class Server extends AppModel {
public function isOwnedByOrg($serverid, $org) {
return $this->field('id', array('id' => $serverid, 'org' => $org)) === $serverid;
}
public function pull($user, $id = null, $technique=false, $server, $jobId = false) {
$eventModel = ClassRegistry::init('Event');
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->read(null, $jobId);
}
App::uses('HttpSocket', 'Network/Http');
$eventIds = array();
if ("full" == $technique) {
// get a list of the event_ids on the server
$eventIds = $eventModel->getEventIdsFromServer($server);
// FIXME this is not clean at all ! needs to be refactored with try catch error handling/communication
if ($eventIds === 403) {
return 1;
} else if (is_string($eventIds)) {
return 2;
}
// reverse array of events, to first get the old ones, and then the new ones
$eventIds = array_reverse($eventIds);
$eventCount = count($eventIds);
} elseif ("incremental" == $technique) {
// TODO incremental pull
return 3;
} elseif (true == $technique) {
$eventIds[] = intval($technique);
} else {
return 4;
}
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventIds)) {
$successes = array();
$fails = array();
$pulledProposals = array();
// download each event
if (null != $eventIds) {
$HttpSocket = new HttpSocket();
foreach ($eventIds as $k => &$eventId) {
$event = $eventModel->downloadEventFromServer(
$eventId,
$server);
if (null != $event) {
// we have an Event array
// The event came from a pull, so it should be locked.
$event['Event']['locked'] = true;
if (!isset($event['Event']['distribution'])) { // version 1
$event['Event']['distribution'] = '1';
}
// Distribution
switch($event['Event']['distribution']) {
case 1:
case 'This community only': // backwards compatibility
// if community only, downgrade to org only after pull
$event['Event']['distribution'] = '0';
break;
case 2:
case 'Connected communities': // backwards compatibility
// if connected communities downgrade to community only
$event['Event']['distribution'] = '1';
break;
case 'All communities': // backwards compatibility
$event['Event']['distribution'] = '3';
break;
case 'Your organisation only': // backwards compatibility
$event['Event']['distribution'] = '0';
break;
}
// correct $event if just one Attribute
if (is_array($event['Event']['Attribute']) && isset($event['Event']['Attribute']['id'])) {
$tmp = $event['Event']['Attribute'];
unset($event['Event']['Attribute']);
$event['Event']['Attribute'][0] = $tmp;
}
if (is_array($event['Event']['Attribute'])) {
$size = is_array($event['Event']['Attribute']) ? count($event['Event']['Attribute']) : 0;
for ($i = 0; $i < $size; $i++) {
if (!isset($event['Event']['Attribute'][$i]['distribution'])) { // version 1
$event['Event']['Attribute'][$i]['distribution'] = 1;
}
switch($event['Event']['Attribute'][$i]['distribution']) {
case 1:
case 'This community only': // backwards compatibility
// if community falseonly, downgrade to org only after pull
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
case 2:
case 'Connected communities': // backwards compatibility
// if connected communities downgrade to community only
$event['Event']['Attribute'][$i]['distribution'] = '1';
break;
case 'All communities': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '3';
break;
case 'Your organisation only': // backwards compatibility
$event['Event']['Attribute'][$i]['distribution'] = '0';
break;
}
}
$event['Event']['Attribute'] = array_values($event['Event']['Attribute']);
} else {
unset($event['Event']['Attribute']);
}
// Distribution, set reporter of the event, being the admin that initiated the pull
$event['Event']['user_id'] = $user['id'];
// check if the event already exist (using the uuid)
$existingEvent = null;
$existingEvent = $eventModel->find('first', array('conditions' => array('Event.uuid' => $event['Event']['uuid'])));
if (!$existingEvent) {
// add data for newly imported events
$passAlong = $server['Server']['url'];
$result = $eventModel->_add($event, $fromXml = true, $user, $server['Server']['organization'], $passAlong, true);
if ($result) $successes[] = $eventId;
else {
$fails[$eventId] = 'Failed (partially?) because of validation errors: '. print_r($eventModel->validationErrors, true);
}
} else {
$result = $eventModel->_edit($event, $existingEvent['Event']['id']);
if ($result === 'success') $successes[] = $eventId;
else $fails[$eventId] = $result;
}
} else {
// error
$fails[$eventId] = 'failed downloading the event';
}
if ($jobId && $k%10 == 0) {
$job->saveField('progress', $k * 100 / $eventCount);
}
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpulledid = min(array_keys($fails));
} else {
// no fails, take the highest success
$lastpulledid = count($successes) > 0 ? max($successes) : 0;
}
// increment lastid based on the highest ID seen
$this->save($event, array('fieldList' => array('lastpulledid', 'url')));
// grab all of the shadow attributes that are relevant to us
$events = $eventModel->find('all', array(
'fields' => array('id', 'uuid'),
'recursive' => -1,
));
$shadowAttribute = ClassRegistry::init('ShadowAttribute');
$shadowAttribute->recursive = -1;
foreach ($events as &$event) {
$proposals = $eventModel->downloadEventFromServer($event['Event']['uuid'], $server, null, true);
if (null != $proposals) {
if (isset($proposals['ShadowAttribute']['id'])) {
$temp = $proposals['ShadowAttribute'];
$proposals['ShadowAttribute'] = array(0 => $temp);
}
foreach($proposals['ShadowAttribute'] as &$proposal) {
unset($proposal['id']);
$proposal['event_id'] = $event['Event']['id'];
if (!$shadowAttribute->findByUuid($proposal['uuid'])) {
if (isset($pulledProposals[$event['Event']['id']])) {
$pulledProposals[$event['Event']['id']]++;
} else {
$pulledProposals[$event['Event']['id']] = 1;
}
$shadowAttribute->create();
$shadowAttribute->save($proposal);
}
}
}
}
}
}
return array($successes, $fails, $pulledProposals, $lastpulledid);
}
public function push($id = null, $technique=false, $jobId = false, $HttpSocket) {
if ($jobId) {
$job = ClassRegistry::init('Job');
$job->read(null, $jobId);
}
$eventModel = ClassRegistry::init('Event');
$this->read(null, $id);
if ("full" == $technique) {
$eventid_conditions_key = 'Event.id >';
$eventid_conditions_value = 0;
} elseif ("incremental" == $technique) {
$eventid_conditions_key = 'Event.id >';
$eventid_conditions_value = $this->data['Server']['lastpushedid'];
} elseif (true == $technique) {
$eventIds[] = array('Event' => array ('id' => intval($technique)));
} else {
$this->redirect(array('action' => 'index'));
}
if (!isset($eventIds)) {
$findParams = array(
'conditions' => array(
$eventid_conditions_key => $eventid_conditions_value,
'Event.distribution >' => 0,
'Event.published' => 1,
'Event.attribute_count >' => 0
), //array of conditions
'recursive' => -1, //int
'fields' => array('Event.id'), //array of field names
);
$eventIds = $eventModel->find('all', $findParams);
}
$eventCount = count($eventIds);
//debug($eventIds);
// now process the $eventIds to pull each of the events sequentially
if (!empty($eventIds)) {
$successes = array();
$fails = array();
$lowestfailedid = null;
foreach ($eventIds as $k => $eventId) {
$eventModel->recursive=1;
$event = $eventModel->findById($eventId['Event']['id']);
$event['Event']['locked'] = true;
unset($event['User']);
$result = $eventModel->uploadEventToServer(
$event,
$this->data,
$HttpSocket);
if ('Success' === $result) {
$successes[] = $event['Event']['id'];
} else {
$fails[$event['Event']['id']] = $result;
}
if ($jobId && $k%10 == 0) {
$job->saveField('progress', $k * 100 / $eventCount);
}
}
if (count($fails) > 0) {
// there are fails, take the lowest fail
$lastpushedid = min(array_keys($fails));
} else {
// no fails, take the highest success
$lastpushedid = max($successes);
}
// increment lastid based on the highest ID seen
// Save the entire Server data instead of just a single field, so that the logger can be fed with the extra fields.
$this->data['Server']['lastpushedid'] = $lastpushedid;
$this->save($this->data);
}
if (!isset($successes)) $successes = null;
if (!isset($fails)) $fails = null;
if ($jobId) {
$temp = 'Fails: ';
$failCount = count($fails);
foreach ($fails as $k => $fail) {
if ($k < $failCount) {
$temp .= $fail . ', ';
} else {
$temp .= $fail;
}
}
$job->saveField('message', $temp);
return array($temp);
} else {
return array($successes, $fails);
}
}
}

27
app/Model/Task.php Normal file
View File

@ -0,0 +1,27 @@
<?php
App::uses('AppModel', 'Model');
/**
* Task Model
*
* @property Task $Task
*/
class Task extends AppModel {
public $tasks = array(
'cache_exports' => array(
'type' => 'cache_exports',
'timer' => 0,
'scheduled_time' => 0,
'recurring' => false,
'description' => 'Generates export caches for every export type and for every organisation. This process is heavy, schedule so it might be a good idea to schedule this outside of working hours and before your daily automatic imports on connected services are scheduled.'
));
// takes a time in the 24h format (13:49) and an integer representing the number of hours
// by which it needs to be incremeneted. Returns a string in the first parameters format
public function breakTime($time, $timeToAdd) {
$temp = explode(':', $time);
$hours = $timeToAdd%24;
$temp[0] = $temp[0] + $hours;
if ($temp[0] > 23) $temp[0] = $temp[0] - 24;
return $temp[0] . ':' . $temp[1];
}
}

View File

@ -356,4 +356,16 @@ class User extends AppModel {
}
return $fails;
}
public function getOrgs() {
$this->recursive = -1;
$orgs = $this->find('all', array(
'fields' => array('DISTINCT (User.org) AS org'),
));
$orgNames = array();
foreach ($orgs as $org) {
$orgNames[] = $org['User']['org'];
}
return $orgNames;
}
}

1
app/Plugin/CakeResque Submodule

@ -0,0 +1 @@
Subproject commit 247c06cb8647b812eea4cf868a2b1f134159b48b

View File

@ -40,7 +40,7 @@ class UrlCacheAppHelper extends Helper {
*
* @return void
*/
function afterLayout($layoutFile = null) {
function afterLayout($layoutFile) {
if (!Configure::read('UrlCache.active') || Configure::read('UrlCache.runtime.afterLayout')) {
return;
}

View File

View File

@ -22,6 +22,7 @@
<li><a href="/attributes/search">Search Attributes</a></li>
<li class="divider"></li>
<li><a href="/shadow_attributes/index">View Proposals</a></li>
<li><a href="/events/proposalEventIndex">Events with proposals</a></li>
<li class="divider"></li>
<li><a href="/events/export">Export</a></li>
<?php if ($isAclAuth): ?>
@ -91,10 +92,16 @@
<?php endif; ?>
<li><a href="/admin/roles/index">List Roles</a></li>
<?php if($isSiteAdmin): ?>
<li class="divider"></li>
<li><a href="/admin/users/email">Contact Users</a></li>
<li class="divider"></li>
<li><a href="/pages/display/administration">Administrative tools</a></li>
<li class="divider"></li>
<li><a href="/admin/users/email">Contact Users</a></li>
<li class="divider"></li>
<li><a href="/pages/display/administration">Administrative tools</a></li>
<?php if (Configure::read('MISP.background_jobs')): ?>
<li class="divider"></li>
<li><a href="/jobs/index">Jobs</a></li>
<li class="divider"></li>
<li><a href="/tasks">Scheduled Tasks</a></li>
<?php endif; ?>
<?php endif; ?>
</ul>
</li>
@ -132,6 +139,11 @@
<div class="nav-collapse collapse pull-right" style="margin-top:10px">
<div class="nav" style="font-weight:bold">
<?php if ($proposalCount > 0): ?>
<span class="proposal_span"><a href="/events/proposalEventIndex" class="proposal_link"><?php echo $proposalCount . ' proposals in ' . $proposalEventCount; ?> events</a></span>
<?php else: ?>
<span><a href="/events/proposalEventIndex" class="proposal_link"><?php echo $proposalCount . ' proposals in ' . $proposalEventCount; ?> events</a></span>
<?php endif;?>
<span class="logoBlue">M</span><span class="logoGray">alware</span>
<span class="logoBlue">I</span><span class="logoGray">nformation </span>
<span class="logoBlue">S</span><span class="logoGray">haring</span>

View File

@ -2,7 +2,17 @@
<ul class="nav nav-list">
<?php
switch ($menuList) {
case 'event': ?>
case 'event':
if ($menuItem === 'addAttribute' ||
$menuItem === 'addAttachment' ||
$menuItem === 'addIOC' ||
$menuItem === 'addThreatConnect'
) {
// we can safely assume that mayModify is true if comming from these actions, as they require it in the controller and the user has already passed that check
$mayModify = true;
if ($isAclPublish) $mayPublish = true;
}
?>
<li <?php if ($menuItem === 'viewEvent') echo 'class="active";'?>><a href="/events/view/<?php echo $event['Event']['id'];?>">View Event</a></li>
<li <?php if ($menuItem === 'eventLog') echo 'class="active";'?>><a href="/logs/event_index/<?php echo $event['Event']['id'];?>">View Event History</a></li>
<?php if ($isSiteAdmin || (isset($mayModify) && $mayModify)): ?>
@ -52,6 +62,7 @@
<?php endif; ?>
<li class="divider"></li>
<li <?php if ($menuItem === 'viewProposals') echo 'class="active";'?>><a href="/shadow_attributes/index">View Proposals</a></li>
<li <?php if ($menuItem === 'viewProposalIndex') echo 'class="active";'?>><a href="/events/proposalEventIndex">Events with proposals</a></li>
<li class="divider"></li>
<li <?php if ($menuItem === 'export') echo 'class="active";'?>><a href="/events/export">Export</a></li>
<?php if ($isAclAuth): ?>
@ -106,7 +117,7 @@
<li class="divider"></li>
<?php endif; ?>
<li <?php if ($menuItem === 'index') echo 'class="active";'?>><?php echo $this->Html->link('List Servers', array('controller' => 'servers', 'action' => 'index'));?></li>
<li <?php if ($menuItem === 'add') echo 'class="active";'?>><?php if ($isAclAdd && $me['org'] == 'ADMIN') echo $this->Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add')); ?></li>
<li <?php if ($menuItem === 'add') echo 'class="active";'?>><?php if ($isSiteAdmin) echo $this->Html->link(__('New Server'), array('controller' => 'servers', 'action' => 'add')); ?></li>
<?php
break;
@ -130,10 +141,16 @@
<?php endif; ?>
<li <?php if ($menuItem === 'indexRole') echo 'class="active";'?>><?php echo $this->Html->link('List Roles', array('controller' => 'roles', 'action' => 'index', 'admin' => true)); ?> </li>
<?php if ($isSiteAdmin): ?>
<li class="divider"></li>
<li <?php if ($menuItem === 'contact') echo 'class="active";'?>><?php echo $this->Html->link('Contact users', array('controller' => 'users', 'action' => 'email', 'admin' => true)); ?> </li>
<li <?php if ($menuItem === 'adminTools') echo 'class="active";'?>><a href="/pages/display/administration">Administrative tools</a></li>
<?php endif;
<li class="divider"></li>
<li <?php if ($menuItem === 'contact') echo 'class="active";'?>><?php echo $this->Html->link('Contact users', array('controller' => 'users', 'action' => 'email', 'admin' => true)); ?> </li>
<li <?php if ($menuItem === 'adminTools') echo 'class="active";'?>><a href="/pages/display/administration">Administrative tools</a></li>
<li class="divider"></li>
<?php if (Configure::read('MISP.background_jobs')): ?>
<li <?php if ($menuItem === 'jobs') echo 'class="active";'?>><a href="/jobs/index">Jobs</a></li>
<li class="divider"></li>
<li <?php if ($menuItem === 'tasks') echo 'class="active";'?>><a href="/tasks">Scheduled Tasks</a></li>
<?php endif;
endif;
break;
case 'logs': ?>

View File

@ -1,3 +1,7 @@
<?php
$mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Event']['orgc'] == $me['org']) || ($isAclModifyOrg && $event['Event']['orgc'] == $me['org']));
$mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']);
?>
<div class="events form">
<?php echo $this->Form->create('Event');?>
<fieldset>
@ -33,7 +37,7 @@ echo $this->Form->end();
?>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'event', 'menuItem' => 'editEvent'));
echo $this->element('side_menu', array('menuList' => 'event', 'menuItem' => 'editEvent', 'mayModify' => $mayModify, 'mayPublish' => $mayPublish));
?>
<script type="text/javascript">

View File

@ -4,66 +4,153 @@
Note that not all attribute types are applicable for signature generation, currently we only support NIDS signature generation for IP, domains, host names, user agents etc., and hash list generation for MD5/SHA1 values of file artifacts. Support for more attribute types is planned.
<br/>
<p>Simply click on any of the following buttons to download the appropriate data.</p>
<?php $i = 0;?>
<script type="text/javascript">
var jobsArray = new Array();
var intervalArray = new Array();
function queueInterval(i, k, id, progress, modified) {
jobsArray[i] = id;
//if (id != -1) alert (i + " - "+ k + " - " + progress + " - " + id + " - " + modified);
intervalArray[i] = setInterval(function(){
if (id != -1 && progress < 100 && modified != "N/A") {
queryTask(k, i);
}
}, 1000);
}
function editMessage(id, text) {
document.getElementById("message" + id).innerHTML = text;
}
</script>
<table class="table table-striped table-hover table-condensed">
<tr>
<th style="text-align:center;">Type</th>
<th style="text-align:center;">Last Update</th>
<th style="text-align:center;">Description</th>
<th style="text-align:center;">Outdated</th>
<th style="text-align:center;">Progress</th>
<th style="text-align:center;">Actions</th>
</tr>
<?php foreach ($export_types as $k => $type): ?>
<tr>
<td class="short"><?php echo $type['type']; ?></td>
<td id="update<?php echo $i; ?>" class="short" style="color:red;"><?php echo $type['lastModified']; ?></td>
<td><?php echo $type['description']; ?></td>
<td id="outdated<?php echo $i; ?>">
<?php
if ($type['recommendation']) {
echo '<span style="color:red;">Yes</span>';
} else {
echo 'No';
}
?>
</td>
<td style="width:150px;">
<div id="barFrame<?php echo $i; ?>" class="progress progress-striped active" style="margin-bottom: 0px;display:none;">
<div id="bar<?php echo $i; ?>" class="bar" style="width: <?php echo $type['progress']; ?>%;">
<?php
if ($type['progress'] > 0 && $type['progress'] < 100) echo $type['progress'] . '%';
?>
</div>
</div>
<div id="message<?php echo $i; ?>" style="text-align:center;display:block;">Loading...</div>
<?php $temp = $i . "','" . $k . "','" . $type['job_id'] . "','" . $type['progress'] . "','" . $type['lastModified']; ?>
<script type="text/javascript">
if ("<?php echo $type['progress']; ?>" == 0) {
if ("<?php echo $type['lastModified']; ?>" != "N/A") {
editMessage(<?php echo $i; ?>, "Queued.");
} else {
editMessage(<?php echo $i; ?>, '<span style="color:red;">N/A</span>');
}
}
if ("<?php echo $type['progress']; ?>" == 100) editMessage(<?php echo $i; ?>, "Completed.");
queueInterval('<?php echo $temp;?>');
</script>
</td>
<td style="width:150px;">
<?php
if ($k !== 'text') {
echo $this->Html->link('Download', array('action' => 'downloadExport', $k), array('class' => 'btn btn-inverse toggle-left btn.active qet'));
?>
<button class = "btn btn-inverse toggle-right btn.active qet" id=button<?php echo $i;?> onClick = "generate('<?php echo $temp; ?>')" <?php if (!$type['recommendation']) echo 'disabled';?>>Generate</button>
<?php
} else {
?>
<button class = "btn btn-inverse btn.active qet" id=button<?php echo $i;?> onClick = "generate('<?php echo $temp; ?>')" <?php if (!$type['recommendation']) echo 'disabled';?>>Generate</button>
<?php
}
?>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as XML', array('action' => 'xml', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all events and attributes that you have access to <small>(except file attachments)</small> in a custom XML format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all signatures as CSV', array('action' => 'csv', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as CSV', array('action' => 'csv', 'download', '0','1'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download Suricata rules', array('action' => 'nids', 'suricata', 'download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download Snort rules', array('action' => 'nids', 'snort', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all network related attributes that you
have access to under the Snort rule format. Only <em>published</em>
events and attributes marked as <em>IDS Signature</em> are exported.
Administration is able to maintain a whitelist containing host,
domain name and IP numbers to exclude from the NIDS export.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all MD5 hashes', array('action' => 'hids', 'md5','download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download all SHA1 hashes', array('action' => 'hids', 'sha1','download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click on one of these two buttons to download all MD5 or SHA1
checksums contained in file-related attributes. This list can be
used to feed forensic software when searching for susipicious files.
Only <em>published</em> events and attributes marked as <em>IDS
Signature</em> are exported.
</div>
</div>
<p>
Click on one of these buttons to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only <em>published</em> events and attributes marked as <em>IDS Signature</em> are exported.
</p>
</td>
</tr>
<?php
$i++;
endforeach; ?>
</table>
<ul class="inline">
<?php
foreach ($sigTypes as $sigType): ?>
<li class="actions" style="text-align:center; width: auto; padding: 7px 2px;">
<?php echo $this->Html->link($sigType, array('action' => 'text', 'download' ,$sigType), array('class' => 'btn')) ?>
<?php echo $this->Html->link($sigType, array('action' => 'downloadExport', $k, $sigType), array('class' => 'btn btn-inverse btn.active qet')); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'event-collection', 'menuItem' => 'export'));
?>
<script type="text/javascript">
function generate(i, type, id, progress, modified) {
$.ajax({
url: "/jobs/cache/" + type,
})
.done(function(data) {
jobsArray[i] = data;
editMessage(i, "Adding...");
queueInterval(i, type, data, 1, "Just now");
disableButton(i);
});
}
function queryTask(type, i){
$.getJSON('/jobs/getProgress/cache_' + type, function(data) {
var x = document.getElementById("bar" + i);
x.style.width = data+"%";
if (data > -1 && data < 100) {
x.innerHTML = data + "%";
showDiv("barFrame" + i);
hideDiv("message" + i);
}
if (data == 100) {
clearInterval(intervalArray[i]);
hideDiv("barFrame" + i);
showDiv("message" + i);
updateTime(i);
editMessage(i, "Completed.");
updateOutdated(i);
}
if (data == -1) {
alert("Warning, the background worker is not responding!");
}
});
}
function showDiv(id) {
document.getElementById(id).style.display = 'block';
}
function hideDiv(id) {
document.getElementById(id).style.display = 'none';
}
function updateTime(id) {
document.getElementById("update" + id).innerHTML = "0 seconds ago";
}
function updateOutdated(id) {
document.getElementById("outdated" + id).innerHTML = "No";
}
function disableButton(id) {
$('#button' + id).prop('disabled', true);
}
</script>

View File

@ -0,0 +1,81 @@
<div class="event index">
<h2>Export</h2>
<p>Export functionality is designed to automatically generate signatures for intrusion detection systems. To enable signature generation for a given attribute, Signature field of this attribute must be set to Yes.
Note that not all attribute types are applicable for signature generation, currently we only support NIDS signature generation for IP, domains, host names, user agents etc., and hash list generation for MD5/SHA1 values of file artifacts. Support for more attribute types is planned.
<br/>
<p>Simply click on any of the following buttons to download the appropriate data.</p>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as XML', array('action' => 'xml', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all events and attributes that you have access to <small>(except file attachments)</small> in a custom XML format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all signatures as CSV', array('action' => 'csv', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all as CSV', array('action' => 'csv', 'download', '0','1'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download NIDS signatures', array('action' => 'nids', 'download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click this to download all network related attributes that you
have access to under the Snort rule format. Only <em>published</em>
events and attributes marked as <em>IDS Signature</em> are exported.
Administration is able to maintain a whitelist containing host,
domain name and IP numbers to exclude from the NIDS export.
</div>
</div>
<div class="row bottom-buffer">
<div class="span3">
<?php echo $this->Html->link('Download all MD5 hashes', array('action' => 'hids', 'md5','download'), array('class' => 'btn btn-block full-width')); ?>
<?php echo $this->Html->link('Download all SHA1 hashes', array('action' => 'hids', 'sha1','download'), array('class' => 'btn btn-block full-width')); ?>
</div>
<div class="span9">Click on one of these two buttons to download all MD5 or SHA1
checksums contained in file-related attributes. This list can be
used to feed forensic software when searching for susipicious files.
Only <em>published</em> events and attributes marked as <em>IDS
Signature</em> are exported.
</div>
</div>
<p>
Click on one of these buttons to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only <em>published</em> events and attributes marked as <em>IDS Signature</em> are exported.
</p>
<ul class="inline">
<?php
foreach ($sigTypes as $sigType): ?>
<li class="actions" style="text-align:center; width: auto; padding: 7px 2px;">
<?php echo $this->Html->link($sigType, array('action' => 'text', 'download' ,$sigType), array('class' => 'btn')) ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<div class="actions <?php echo $debugMode;?>">
<ul class="nav nav-list">
<li><a href="/events/index">List Events</a></li>
<?php if ($isAclAdd): ?>
<li><a href="/events/add">Add Event</a></li>
<?php endif; ?>
<li class="divider"></li>
<li><a href="/attributes/index">List Attributes</a></li>
<li><a href="/attributes/search">Search Attributes</a></li>
<li class="divider"></li>
<li class="active"><a href="/events/export">Export</a></li>
<?php if ($isAclAuth): ?>
<li><a href="/events/automation">Automation</a></li>
<?php endif;?>
</ul>
</div>

View File

@ -195,7 +195,7 @@
<th class="actions">Actions</th>
</tr>
<?php foreach ($events as $event):?>
<?php foreach ($events as $event): ?>
<tr <?php if($event['Event']['distribution'] == 0) echo 'class = "privateRed"'?>>
<td class="short" onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php
@ -308,6 +308,7 @@ $(document).ready( function () {
});
function toggleField(field) {
$(field).toggle();
$(field +" input").focus();

View File

@ -0,0 +1,110 @@
<div class="events index">
<h2>Event with proposals</h2>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'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 class="filter">
<?php echo $this->Paginator->sort('published', 'Valid.');?>
</th>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('attribute_count', 'Proposals');?></th>
<?php if ($isSiteAdmin): ?>
<th><?php echo $this->Paginator->sort('user_id', 'Email');?></th>
<?php endif; ?>
<th class="filter">
<?php echo $this->Paginator->sort('date');?>
</th>
<th title="<?php echo $eventDescriptions['threat_level_id']['desc'];?>">
<?php echo $this->Paginator->sort('threat_level_id');?>
</th>
<th title="<?php echo $eventDescriptions['analysis']['desc'];?>">
<?php echo $this->Paginator->sort('analysis');?>
</th>
<th class="filter">
<?php echo $this->Paginator->sort('info');?>
</th>
<?php if ('true' == Configure::read('CyDefSIG.sync')): ?>
<th title="<?php echo $eventDescriptions['distribution']['desc'];?>">
<?php echo $this->Paginator->sort('distribution');?>
</th>
<?php endif; ?>
</tr>
<?php foreach ($events as $event):?>
<tr <?php if($event['Event']['distribution'] == 0) echo 'class = "privateRed"'?>>
<td class="short" onclick="document.location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php
if ($event['Event']['published'] == 1) {
?>
<a href="/events/view/<?php echo $event['Event']['id'] ?>" class = "icon-ok" title = "View"></a>
<?php
} else {
?>
<a href="/events/view/<?php echo $event['Event']['id'] ?>" class = "icon-remove" title = "View"></a>
<?php
}?>&nbsp;
</td>
<td class="short">
<a href="/events/view/<?php echo $event['Event']['id'] ?>"><?php echo $event['Event']['id'];?></a>
</td>
<td class="short" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'" style="color:red;font-weight:bold;">
<?php echo count($event['ShadowAttribute']); ?>&nbsp;
</td>
<?php if ('true' == $isSiteAdmin): ?>
<td class="short" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo h($event['User']['email']); ?>&nbsp;
</td>
<?php endif; ?>
<td class="short" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo $event['Event']['date']; ?>&nbsp;
</td>
<td class="short" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo $event['ThreatLevel']['name']; ?>&nbsp;
</td>
<td class="short" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo $analysisLevels[$event['Event']['analysis']]; ?>&nbsp;
</td>
<td onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo nl2br(h($event['Event']['info'])); ?>&nbsp;
</td>
<?php if ('true' == Configure::read('CyDefSIG.sync')): ?>
<td class="short <?php if ($event['Event']['distribution'] == 0) echo 'privateRedText';?>" onclick="location.href ='/events/view/<?php echo $event['Event']['id'];?>'">
<?php echo $event['Event']['distribution'] != 3 ? $distributionLevels[$event['Event']['distribution']] : 'All';?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
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' => 'event-collection', 'menuItem' => 'viewProposalIndex'));
?>

View File

@ -1,9 +1,9 @@
<?php
$mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id']) || ($isAclModifyOrg && $event['Event']['orgc'] == $me['org']));
$mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Event']['orgc'] == $me['org']) || ($isAclModifyOrg && $event['Event']['orgc'] == $me['org']));
$mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']);
?>
<?php
echo $this->element('side_menu', array('menuList' => 'event', 'menuItem' => 'viewEvent'));
echo $this->element('side_menu', array('menuList' => 'event', 'menuItem' => 'viewEvent', 'mayModify' => $mayModify, 'mayPublish' => $mayPublish));
?>
@ -293,6 +293,9 @@ if (!empty($event['Attribute'])):?>
}
?></td>
<td class="short highlight2">
<?php
echo h($shadowAttribute['comment']);
?>
</td>
<td class="short highlight2">
</td>
@ -368,6 +371,9 @@ if (!empty($event['Attribute'])):?>
}
?></td>
<td class="short highlight2">
<?php
echo h($remain['comment']);
?>
</td>
<td class="short highlight2">
</td>

View File

@ -34,6 +34,9 @@ App::uses('UrlCacheAppHelper', 'UrlCache.View/Helper');
*/
class AppHelper extends UrlCacheAppHelper {
public function afterLayout($layoutFile) {
}
public function url($url = null, $full = false) {
if (is_array($url) && !isset($url['admin'])) {
$url['admin'] = false;

View File

@ -7,6 +7,7 @@ App::uses('AppHelper', 'View/Helper');
$data = null;
$text = $pivot['id'] . ': ';
$active = '';
$pivot['info'] = h($pivot['info']);
// Truncate string if longer than (11 - length of event id) chars to fit the pivot bubble
if (strlen($pivot['info']) > (11 - strlen((string)$pivot['id']))) {
$text .= substr($pivot['info'], 0, 7) . '...';

103
app/View/Jobs/index.ctp Normal file
View File

@ -0,0 +1,103 @@
<div class="jobs index">
<h2><?php echo __('Jobs');?></h2>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'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>
<script type="text/javascript">
var intervalArray = new Array();
function queueInterval(k, id) {
intervalArray[k] = setInterval(function(){
$.getJSON('/jobs/getGenerateCorrelationProgress/' + id, function(data) {
var x = document.getElementById("bar" + id);
x.style.width = data+"%";
if (data > 0 && data < 100) {
x.innerHTML = data + "%";
}
if (data == 100) {
x.innerHTML = "Completed.";
clearInterval(intervalArray[k]);
}
});
}, 3000);
}
</script>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('process_id');?></th>
<th><?php echo $this->Paginator->sort('worker');?></th>
<th><?php echo $this->Paginator->sort('job_type');?></th>
<th><?php echo $this->Paginator->sort('job_input');?></th>
<th><?php echo $this->Paginator->sort('org');?></th>
<th><?php echo $this->Paginator->sort('status');?></th>
<th><?php echo $this->Paginator->sort('retries');?></th>
<th><?php echo $this->Paginator->sort('progress');?></th>
</tr><?php
foreach ($list as $k => $item): ?>
<tr>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['id']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['process_id']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['worker']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['job_type']); ?>&nbsp;</td>
<td title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['job_input']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['org']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['status']); ?>&nbsp;</td>
<td class="short" title="Message: <?php echo h($item['Job']['message']); ?>"><?php echo h($item['Job']['retries']); ?>&nbsp;</td>
<td style="width:200px;">
<div class="progress progress-striped active" style="margin-bottom: 0px;">
<div id="bar<?php echo h($item['Job']['id']); ?>" class="bar" style="width: <?php echo h($item['Job']['progress']); ?>%;">
<?php
if ($item['Job']['progress'] > 0 && $item['Job']['progress'] < 100) echo h($item['Job']['progress']) . '%';
if ($item['Job']['progress'] == 100) echo 'Completed.';
?>
</div>
</div>
<?php if ($item['Job']['progress'] != 100): ?>
<script type="text/javascript">
queueInterval("<?php echo $k; ?>", "<?php echo h($item['Job']['id']); ?>");
</script>
<?php endif; ?>
</td>
</tr><?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
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>
<div class="actions <?php echo $debugMode;?>">
<ul class="nav nav-list">
</ul>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'admin', 'menuItem' => 'jobs'));
?>

View File

@ -13,6 +13,7 @@
echo $this->Html->css('roboto');
echo $this->Html->css('bootstrap'); // see http://twitter.github.io/bootstrap/base-css.html
echo $this->Html->css('datepicker');
echo $this->Html->css('bootstrap-timepicker');
echo $this->Html->css('main');
// FIXME chri: re-add print stylesheet
@ -22,7 +23,7 @@
echo $this->fetch('css');
echo $this->fetch('script');
echo $this->Html->script('jquery-1.9.1.min'); // Include jQuery library
echo $this->Html->script('jquery-2.0.3.min'); // Include jQuery library
?>
<!--?php echo $scripts_for_layout; ?-->
@ -57,6 +58,7 @@
echo $this->element('sql_dump');
echo $this->Html->script('bootstrap');
// echo $this->Html->script('bootstrap.min');
echo $this->Html->script('bootstrap-timepicker');
echo $this->Html->script('bootstrap-datepicker');
echo $this->Html->script('main');
?>

View File

@ -1,3 +1,7 @@
<?php
$mayModify = (($isAclModify && $event['Event']['user_id'] == $me['id'] && $event['Event']['orgc'] == $me['org']) || ($isAclModifyOrg && $event['Event']['orgc'] == $me['org']));
$mayPublish = ($isAclPublish && $event['Event']['orgc'] == $me['org']);
?>
<div class="logs index">
<h2>Logs</h2>
<div class="pagination">
@ -52,5 +56,5 @@
<?php
// We mimic the $event from some other views to pass the ID back to the sidemenu
$event['Event']['id'] = $eventId;
echo $this->element('side_menu', array('menuList' => 'event', 'event' => $event, 'menuItem' => 'eventLog'));
echo $this->element('side_menu', array('menuList' => 'event', 'event' => $event, 'menuItem' => 'eventLog', 'mayModify' => $mayModify, 'mayPublish' => $mayPublish));
?>

View File

@ -21,6 +21,18 @@ else:?>
</ul>
<?php
endif;?>
<h2>Proposals pulled</h2>
<?php
if (0 == count($pulledProposals)):?>
<p>No proposals pulled</p>
<?php
else:?>
<ul>
<?php foreach ($pulledProposals as $e => $p) echo '<li>Event ' . $e . ' : ' . $p . ' proposal(s).</li>'; ?>
</ul>
<?php
endif;?>
</div>
<?php
echo $this->element('side_menu', array('menuList' => 'sync', 'menuItem' => 'pull'));

View File

@ -19,6 +19,13 @@
'error' => array('escape' => false),
'class' => 'input-xxlarge clear'
));
echo $this->Form->input('comment', array(
'type' => 'text',
'label' => 'Contextual Comment',
'error' => array('escape' => false),
'div' => 'input clear',
'class' => 'input-xxlarge'
));
?>
<div class="input clear"></div>
<?php

View File

@ -21,6 +21,13 @@
'error' => array('escape' => false),
'class' => 'input-xxlarge clear'
));
echo $this->Form->input('comment', array(
'type' => 'text',
'label' => 'Contextual Comment',
'error' => array('escape' => false),
'div' => 'input clear',
'class' => 'input-xxlarge'
));
?>
<div class="input clear"></div>
<?php

View File

@ -0,0 +1,28 @@
<?php
$xmlArray = array();
//
// cleanup the array from things we do not want to expose
//
if (isset($proposal['ShadowAttribute']['id'])) {
$temp = $proposal['ShadowAttribute'];
unset ($proposal['ShadowAttribute']);
$proposal['ShadowAttribute'][0] = $temp;
unset ($temp);
}
$xmlArray['response']['ShadowAttribute'] = array();
foreach ($proposal as &$temp) {
unset($temp['ShadowAttribute']['email']);
unset($temp['ShadowAttribute']['category_order']);
unset($temp['ShadowAttribute']['value1']);
unset($temp['ShadowAttribute']['value2']);
// hide the org field is we are not in showorg mode
if ('true' != Configure::read('CyDefSIG.showorg') && !$isAdmin) {
unset($temp['ShadowAttribute']['org']);
unset($temp['ShadowAttribute']['event_org']);
}
$xmlArray['response']['ShadowAttribute'][] = $temp['ShadowAttribute'];
}
// display the XML to the user
$xmlObject = Xml::fromArray($xmlArray, array('format' => 'tags'));
echo $xmlObject->asXML();

123
app/View/Tasks/index.ctp Normal file
View File

@ -0,0 +1,123 @@
<div class="task index">
<h2>Scheduled Tasks</h2>
<p>Here you can schedule pre-defined tasks that will be executed every x hours. You can alter the date and time of the next scheduled execution and the frequency at which it will be repeated (expressed in hours). If you set the frequency to 0 then the task will not be repeated. To change and of the above mentioned settings just click on the appropriate field and hit update all when you are done editing the scheduled tasks.</p>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'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>
<?php
echo $this->Form->create('Task', array(
'action' => 'setTask',
'controller' => 'Tasks',
'inputDefaults' => array(
'label' => false
)));
?>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('type');?></th>
<th><?php echo $this->Paginator->sort('timer', 'Frequency (h)');?></th>
<th><?php echo $this->Paginator->sort('scheduled_time');?></th>
<th><?php echo $this->Paginator->sort('next_execution_time', 'Next Run');?></th>
<th><?php echo $this->Paginator->sort('description');?></th>
<th><?php echo $this->Paginator->sort('message');?></th>
</tr><?php
foreach ($list as $item):?>
<tr>
<td class="short"><?php echo h($item['Task']['id']);?>&nbsp;</td>
<td class="short"><?php echo h($item['Task']['type']);?>&nbsp;</td>
<td class="short">
<?php
echo $this->Form->input($item['Task']['id'] . '.timer', array(
'style' => 'display:none',
'class' => 'input-mini',
'default' => h($item['Task']['timer']),
'id' => $item['Task']['id'] . '-timer-active'
));
?>
<div id="<?php echo $item['Task']['id'];?>-timer-passive" onClick="activate1(<?php echo $item['Task']['id'];?>, 'timer')">
<?php echo h($item['Task']['timer']); ?>
</div>
</td>
<td class="short">
<div class="input-append bootstrap-timepicker" id="<?php echo $item['Task']['id'] . '-scheduled_time-active';?>" style="display:none">
<?php
echo $this->Form->input($item['Task']['id'] . '.scheduled_time', array(
'class' => 'input-small',
'type' => 'text',
'default' => h($item['Task']['scheduled_time']),
'id' => 'timepicker' . $item['Task']['id']
));
?>
</div>
<div id="<?php echo $item['Task']['id'];?>-scheduled_time-passive" onClick="activate2(<?php echo $item['Task']['id'];?>, 'scheduled_time', '<?php echo h($item['Task']['scheduled_time']);?>')">
<?php echo h($item['Task']['scheduled_time']); ?>
</div>
</td>
<td style="width:250px;">
<div class="input-append bootstrap-datepicker" id="<?php echo $item['Task']['id'] . '-next_execution_time-active';?>" style="display:none">
<?php
echo $this->Form->input($item['Task']['id'] . '.next_execution_time', array(
'type' => 'text',
'class' => 'datepicker',
'default' => h(date("Y-m-d", $item['Task']['next_execution_time'])),
'id' => 'datepicker' . $item['Task']['id']
));
?>
</div>
<div id="<?php echo $item['Task']['id'];?>-next_execution_time-passive" onClick="activate1(<?php echo $item['Task']['id'];?>, 'next_execution_time')">
<?php echo h(date("Y-m-d", $item['Task']['next_execution_time'])); ?>
</div>
</td>
<td><?php echo h($item['Task']['description']);?>&nbsp;</td>
<td><?php echo h($item['Task']['message']); ?></td>
</tr><?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Form->button('Update all', array('class' => 'btn btn-primary'));
echo $this->Form->end();
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
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' => 'admin', 'menuItem' => 'tasks'));
?>
<script type="text/javascript">
function activate1(id, type){
$("#"+id+"-"+type+"-active").show();
$("#"+id+"-"+type+"-passive").hide();
}
function activate2(id, type, defaultValue){
$("#"+id+"-"+type+"-active").show();
$("#"+id+"-"+type+"-passive").hide();
$('#timepicker'+id).timepicker({defaultTime: defaultValue, minuteStep: 1, showMeridian: false});
}
</script>

View File

@ -0,0 +1,146 @@
/*!
* Timepicker Component for Twitter Bootstrap
*
* Copyright 2013 Joris de Wit
*
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
.bootstrap-timepicker {
position: relative;
}
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu {
left: auto;
right: 0;
}
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before {
left: auto;
right: 12px;
}
.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after {
left: auto;
right: 13px;
}
.bootstrap-timepicker .add-on {
cursor: pointer;
}
.bootstrap-timepicker .add-on i {
display: inline-block;
width: 16px;
height: 16px;
}
.bootstrap-timepicker-widget.dropdown-menu {
padding: 4px;
}
.bootstrap-timepicker-widget.dropdown-menu.open {
display: inline-block;
}
.bootstrap-timepicker-widget.dropdown-menu:before {
border-bottom: 7px solid rgba(0, 0, 0, 0.2);
border-left: 7px solid transparent;
border-right: 7px solid transparent;
content: "";
display: inline-block;
position: absolute;
}
.bootstrap-timepicker-widget.dropdown-menu:after {
border-bottom: 6px solid #FFFFFF;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
content: "";
display: inline-block;
position: absolute;
}
.bootstrap-timepicker-widget.timepicker-orient-left:before {
left: 6px;
}
.bootstrap-timepicker-widget.timepicker-orient-left:after {
left: 7px;
}
.bootstrap-timepicker-widget.timepicker-orient-right:before {
right: 6px;
}
.bootstrap-timepicker-widget.timepicker-orient-right:after {
right: 7px;
}
.bootstrap-timepicker-widget.timepicker-orient-top:before {
top: -7px;
}
.bootstrap-timepicker-widget.timepicker-orient-top:after {
top: -6px;
}
.bootstrap-timepicker-widget.timepicker-orient-bottom:before {
bottom: -7px;
border-bottom: 0;
border-top: 7px solid #999;
}
.bootstrap-timepicker-widget.timepicker-orient-bottom:after {
bottom: -6px;
border-bottom: 0;
border-top: 6px solid #ffffff;
}
.bootstrap-timepicker-widget a.btn,
.bootstrap-timepicker-widget input {
border-radius: 4px;
}
.bootstrap-timepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-timepicker-widget table td {
text-align: center;
height: 30px;
margin: 0;
padding: 2px;
}
.bootstrap-timepicker-widget table td:not(.separator) {
min-width: 30px;
}
.bootstrap-timepicker-widget table td span {
width: 100%;
}
.bootstrap-timepicker-widget table td a {
border: 1px transparent solid;
width: 100%;
display: inline-block;
margin: 0;
padding: 8px 0;
outline: 0;
color: #333;
}
.bootstrap-timepicker-widget table td a:hover {
text-decoration: none;
background-color: #eee;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border-color: #ddd;
}
.bootstrap-timepicker-widget table td a i {
margin-top: 2px;
font-size: 18px;
}
.bootstrap-timepicker-widget table td input {
width: 25px;
margin: 0;
text-align: center;
}
.bootstrap-timepicker-widget .modal-content {
padding: 4px;
}
@media (min-width: 767px) {
.bootstrap-timepicker-widget.modal {
width: 200px;
margin-left: -100px;
}
}
@media (max-width: 767px) {
.bootstrap-timepicker {
width: 100%;
}
.bootstrap-timepicker .dropdown-menu {
width: 100%;
}
}

View File

@ -573,6 +573,30 @@ dd {
float:left !important;
}
.proposal_span {
padding-right: 25px;
}
a.proposal_link, a.proposal_link_red {
text-decoration:none;
font-weight:normal;
}
a.proposal_link:visited, a.proposal_link:link {
color:#999999;
}
a.proposal_link:hover {
color:white;
}
a.proposal_link_red:visited, a.proposal_link:link {
color:#999999;
}
a.proposal_link_red:hover {
color:red;
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(359deg);}

View File

@ -56,7 +56,7 @@ if (!defined('APP_DIR')) {
*
* Leaving this constant undefined will result in it being defined in Cake/bootstrap.php
*/
//define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib');
define('CAKE_CORE_INCLUDE_PATH', '/var/www/MISP_sync_test/lib');
/**
* Editing below this line should NOT be necessary.

File diff suppressed because it is too large Load Diff

6
app/webroot/js/jquery-2.0.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
curl -i -H "Accept: application/xml" -H "content-type: text/xml" -H "Authorization: vlf4o42bYSVVWLm28jLB85my4HBZWXTri8vGdySb" \
--data "@input/event.xml" -X POST http://localhost/events
curl -i -H "Accept: application/xml" -H "content-type: text/xml" -H "Authorization: FoKoqC6hr5Ke3Sb81iDBVuvem9HSCRAhflGFXMXp" \
--data "@input/event.xml" -X POST http://192.168.14.10:3600/events
#curl -i -H "Accept: application/json" -H "content-type: text/json" -H "Authorization: vlf4o42bYSVVWLm28jLB85my4HBZWXTri8vGdySb" \
#--data "@input/event.json" -X POST http://localhost/events
#--data "@input/event.json" -X POST http://localhost/events

View File

@ -1,65 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<Event>
<id>14</id>
<org>ORG</org>
<date>2012-04-12</date>
<risk>Medium</risk>
<info>info</info>
<user_id>1</user_id>
<alerted>0</alerted>
<uuid>4f8c2c4e-00dc-42c9-83ad-76e9ff32448e</uuid>
<private>0</private>
<id>25</id>
<org>NCIRC</org>
<date>2013-12-12</date>
<threat_level_id>1</threat_level_id>
<info>test5</info>
<published>0</published>
<uuid>52b9bca5-2e04-4fed-acf3-1f60c0a80e0a</uuid>
<attribute_count>1</attribute_count>
<analysis>0</analysis>
<timestamp>1386855599</timestamp>
<distribution>1</distribution>
<proposal_email_lock>0</proposal_email_lock>
<orgc>NCIRC</orgc>
<locked>0</locked>
<Attribute>
<id>116</id>
<event_id>14</event_id>
<type>ip-dst</type>
<id>7</id>
<type>ip-src</type>
<category>Network activity</category>
<to_ids>1</to_ids>
<uuid>4f8c2cc3-0410-4bf0-8559-5b9dff32448e</uuid>
<revision>1</revision>
<private>0</private>
<value>1.1.1.111</value>
<category_order>g</category_order>
</Attribute>
<Attribute>
<id>117</id>
<event_id>14</event_id>
<type>malware-sample</type>
<category>Payload delivery</category>
<to_ids>0</to_ids>
<uuid>4f8c2d08-7e6c-4648-8730-50a7ff32448e</uuid>
<revision>1</revision>
<private>0</private>
<value>A.doc|3f6f1aaab6171925c81de9b34a8fcf8e</value>
<category_order>c</category_order>
<data />
<uuid>52a9bcbe-d87c-4c5e-8408-22e8c0a80e0a</uuid>
<event_id>9</event_id>
<distribution>1</distribution>
<timestamp>1386855599</timestamp>
<comment/>
<value>1.1.1.2</value>
<ShadowAttribute/>
</Attribute>
<Attribute>
<id>115</id>
<event_id>14</event_id>
<type>vulnerability</type>
<category>Payload delivery</category>
<to_ids>1</to_ids>
<uuid>4f8c2c69-9bf8-4279-8d03-2138ff32448e</uuid>
<revision>1</revision>
<private>0</private>
<value>CVE-XXXX-XXXX</value>
<category_order>c</category_order>
</Attribute>
<RelatedEvent>
<id>11</id>
<date>2011-01-03</date>
<uuid>4f8812ff-ded0-4592-9227-0615ff32448e</uuid>
</RelatedEvent>
<RelatedEvent>
<id>9</id>
<date>2011-02-02</date>
<uuid>4f85981e-d044-4b16-bc16-0a35ff32448e</uuid>
</RelatedEvent>
<RelatedEvent>
<id>6</id>
<date>2011-03-01</date>
<uuid>4f7a9faa-91d4-4c91-8ec6-0878ff32448e</uuid>
</RelatedEvent>
<ShadowAttribute/>
</Event>