new: Custom warninglist

pull/7464/head
Jakub Onderka 2021-06-01 17:49:45 +02:00
parent 76aceedb2b
commit 8dc740cc35
15 changed files with 470 additions and 86 deletions

View File

@ -734,14 +734,16 @@ class ACLComponent extends Component
'eventIndexColumnToggle' => ['*'], 'eventIndexColumnToggle' => ['*'],
), ),
'warninglists' => array( 'warninglists' => array(
'checkValue' => array('perm_auth'), 'checkValue' => array('perm_auth'),
'delete' => array(), 'delete' => ['perm_warninglist'],
'enableWarninglist' => array(), 'enableWarninglist' => ['perm_warninglist'],
'getToggleField' => array(), 'getToggleField' => ['perm_warninglist'],
'index' => array('*'), 'index' => array('*'),
'toggleEnable' => array(), 'toggleEnable' => ['perm_warninglist'],
'update' => array(), 'update' => array(),
'view' => array('*') 'view' => array('*'),
'edit' => ['perm_warninglist'],
'add' => ['perm_warninglist'],
), ),
'allowedlists' => array( 'allowedlists' => array(
'admin_add' => array('perm_regexp_access'), 'admin_add' => array('perm_regexp_access'),

View File

@ -85,9 +85,15 @@ class CRUDComponent extends Component
} else { } else {
$data = $input; $data = $input;
} }
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
}
/** @var Model $model */ /** @var Model $model */
$model = $this->Controller->{$modelName}; $model = $this->Controller->{$modelName};
if ($model->save($data)) { if ($model->save($data)) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
$data = $model->find('first', [ $data = $model->find('first', [
'recursive' => -1, 'recursive' => -1,
'conditions' => [ 'conditions' => [
@ -161,9 +167,15 @@ class CRUDComponent extends Component
if (!empty($params['conditions'])) { if (!empty($params['conditions'])) {
$query['conditions']['AND'][] = $params['conditions']; $query['conditions']['AND'][] = $params['conditions'];
} }
if (!empty($params['contain'])) {
$query['contain'] = $params['contain'];
}
/** @var Model $model */ /** @var Model $model */
$model = $this->Controller->{$modelName}; $model = $this->Controller->{$modelName};
$data = $model->find('first', $query); $data = $model->find('first', $query);
if (empty($data)) {
throw new NotFoundException(__('Invalid %s.', $modelName));
}
if (isset($params['afterFind'])) { if (isset($params['afterFind'])) {
$data = $params['afterFind']($data); $data = $params['afterFind']($data);
} }
@ -190,6 +202,9 @@ class CRUDComponent extends Component
$data = $params['beforeSave']($data); $data = $params['beforeSave']($data);
} }
if ($model->save($data)) { if ($model->save($data)) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
$message = __('%s updated.', $modelName); $message = __('%s updated.', $modelName);
if ($this->Controller->IndexFilter->isRest()) { if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json'); $this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');

View File

@ -29,12 +29,17 @@ class WarninglistsController extends AppController
'LOWER(Warninglist.name) LIKE' => '%' . strtolower($filters['value']) . '%', 'LOWER(Warninglist.name) LIKE' => '%' . strtolower($filters['value']) . '%',
'LOWER(Warninglist.description) LIKE' => '%' . strtolower($filters['value']) . '%', 'LOWER(Warninglist.description) LIKE' => '%' . strtolower($filters['value']) . '%',
'LOWER(Warninglist.type)' => strtolower($filters['value']), 'LOWER(Warninglist.type)' => strtolower($filters['value']),
] ]
]; ];
} }
if (isset($filters['enabled'])) { if (isset($filters['enabled'])) {
$this->paginate['conditions'][] = ['Warninglist.enabled' => $filters['enabled']]; $this->paginate['conditions'][] = ['Warninglist.enabled' => $filters['enabled']];
} }
$this->Warninglist->addCountField(
'warninglist_entry_count',
$this->Warninglist->WarninglistEntry,
['WarninglistEntry.warninglist_id = Warninglist.id']
);
$warninglists = $this->paginate(); $warninglists = $this->paginate();
foreach ($warninglists as &$warninglist) { foreach ($warninglists as &$warninglist) {
$validAttributes = array_column($warninglist['WarninglistType'], 'type'); $validAttributes = array_column($warninglist['WarninglistType'], 'type');
@ -46,6 +51,7 @@ class WarninglistsController extends AppController
} else { } else {
$this->set('warninglists', $warninglists); $this->set('warninglists', $warninglists);
$this->set('passedArgsArray', $filters); $this->set('passedArgsArray', $filters);
$this->set('possibleCategories', $this->Warninglist->categories());
} }
} }
@ -68,14 +74,14 @@ class WarninglistsController extends AppController
} }
$this->Log->create(); $this->Log->create();
$this->Log->save(array( $this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'], 'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Warninglist', 'model' => 'Warninglist',
'model_id' => $id, 'model_id' => $id,
'email' => $this->Auth->user('email'), 'email' => $this->Auth->user('email'),
'action' => 'update', 'action' => 'update',
'user_id' => $this->Auth->user('id'), 'user_id' => $this->Auth->user('id'),
'title' => __('Warning list updated'), 'title' => __('Warning list updated'),
'change' => $change, 'change' => $change,
)); ));
$successes++; $successes++;
} }
@ -84,14 +90,14 @@ class WarninglistsController extends AppController
foreach ($result['fails'] as $id => $fail) { foreach ($result['fails'] as $id => $fail) {
$this->Log->create(); $this->Log->create();
$this->Log->save(array( $this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'], 'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Warninglist', 'model' => 'Warninglist',
'model_id' => $id, 'model_id' => $id,
'email' => $this->Auth->user('email'), 'email' => $this->Auth->user('email'),
'action' => 'update', 'action' => 'update',
'user_id' => $this->Auth->user('id'), 'user_id' => $this->Auth->user('id'),
'title' => __('Warning list failed to update'), 'title' => __('Warning list failed to update'),
'change' => $fail['name'] . __(' could not be installed/updated. Error: ') . $fail['fail'], // TODO: needs to be optimized for non-SVO languages 'change' => __('%s could not be installed/updated. Error: %s', $fail['name'], $fail['fail']), // TODO: needs to be optimized for non-SVO languages
)); ));
$fails++; $fails++;
} }
@ -99,14 +105,14 @@ class WarninglistsController extends AppController
} else { } else {
$this->Log->create(); $this->Log->create();
$this->Log->save(array( $this->Log->save(array(
'org' => $this->Auth->user('Organisation')['name'], 'org' => $this->Auth->user('Organisation')['name'],
'model' => 'Warninglist', 'model' => 'Warninglist',
'model_id' => 0, 'model_id' => 0,
'email' => $this->Auth->user('email'), 'email' => $this->Auth->user('email'),
'action' => 'update', 'action' => 'update',
'user_id' => $this->Auth->user('id'), 'user_id' => $this->Auth->user('id'),
'title' => __('Warninglist update (nothing to update)'), 'title' => __('Warninglist update (nothing to update)'),
'change' => __('Executed an update of the warning lists, but there was nothing to update.'), 'change' => __('Executed an update of the warning lists, but there was nothing to update.'),
)); ));
} }
if ($successes == 0 && $fails == 0) { if ($successes == 0 && $fails == 0) {
@ -130,6 +136,91 @@ class WarninglistsController extends AppController
} }
} }
public function add()
{
$types = array_combine($this->Warninglist->validate['type']['rule'][1], $this->Warninglist->validate['type']['rule'][1]);
$this->set('possibleTypes', $types);
$this->set('possibleCategories', $this->Warninglist->categories());
$this->loadModel('Attribute');
$this->set('matchingAttributes', array_combine(array_keys($this->Attribute->typeDefinitions), array_keys($this->Attribute->typeDefinitions)));
$this->CRUD->add([
'beforeSave' => function (array $warninglist) {
if (isset($warninglist['Warninglist']['entries'])) {
$entries = $this->Warninglist->parseFreetext($warninglist['Warninglist']['entries']);
unset($warninglist['Warninglist']['entries']);
$warninglist['WarninglistEntry'] = $entries;
}
if (isset($warninglist['Warninglist']['matching_attributes']) && is_array($warninglist['Warninglist']['matching_attributes'])) {
$warninglist['WarninglistType'] = [];
foreach ($warninglist['Warninglist']['matching_attributes'] as $attribute) {
$warninglist['WarninglistType'][] = ['type' => $attribute];
}
}
$warninglist['Warninglist']['default'] = 0;
return $warninglist;
},
]);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
}
public function edit($id = null)
{
$types = array_combine($this->Warninglist->validate['type']['rule'][1], $this->Warninglist->validate['type']['rule'][1]);
$this->set('possibleTypes', $types);
$this->set('possibleCategories', $this->Warninglist->categories());
$this->loadModel('Attribute');
$this->set('matchingAttributes', array_combine(array_keys($this->Attribute->typeDefinitions), array_keys($this->Attribute->typeDefinitions)));
$this->CRUD->edit($id, [
'conditions' => ['default' => 0], // it is not possible to edit default warninglist
'contain' => ['WarninglistEntry', 'WarninglistType'],
'fields' => ['name', 'description', 'type', 'category', 'entries', 'matching_attributes'],
'redirect' => ['action' => 'view', $id],
'beforeSave' => function (array $warninglist) {
if (isset($warninglist['Warninglist']['entries'])) {
$entries = $this->Warninglist->parseFreetext($warninglist['Warninglist']['entries']);
unset($warninglist['Warninglist']['entries']);
$warninglist['WarninglistEntry'] = $entries;
}
if (isset($warninglist['Warninglist']['matching_attributes']) && is_array($warninglist['Warninglist']['matching_attributes'])) {
$warninglist['WarninglistType'] = [];
foreach ($warninglist['Warninglist']['matching_attributes'] as $attribute) {
$warninglist['WarninglistType'][] = ['type' => $attribute];
}
}
$warninglist['Warninglist']['version']++;
return $warninglist;
},
]);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
if (isset($this->request->data['WarninglistEntry'])) {
$entries = [];
foreach ($this->request->data['WarninglistEntry'] as $entry) {
$value = $entry['value'];
if ($entry['comment']) {
$value .= ' # ' . $entry['comment'];
}
$entries[] = $value;
}
$this->request->data['Warninglist']['entries'] = implode("\n", $entries);
}
if (isset($this->request->data['WarninglistType'])) {
$attributes = array_column($this->request->data['WarninglistType'], 'type');
$this->request->data['Warninglist']['matching_attributes'] = $attributes;
}
$this->render('add');
}
/* /*
* toggle warninglists on or offset * toggle warninglists on or offset
* Simply POST an ID or a list of IDs to toggle the current state * Simply POST an ID or a list of IDs to toggle the current state
@ -156,13 +247,12 @@ class WarninglistsController extends AppController
$names = $this->request->data['name']; $names = $this->request->data['name'];
} }
$conditions = array(); $conditions = array();
foreach ($names as $k => $name) { foreach ($names as $name) {
$conditions['OR'][] = array('LOWER(Warninglist.name) LIKE' => strtolower($name)); $conditions['OR'][] = array('LOWER(Warninglist.name) LIKE' => strtolower($name));
} }
$id = $this->Warninglist->find('list', array( $id = $this->Warninglist->find('column', array(
'conditions' => $conditions, 'conditions' => $conditions,
'recursive' => -1, 'fields' => array('Warninglist.id')
'fields' => array('Warninglist.id', 'Warninglist.id')
)); ));
} }
} }
@ -240,7 +330,10 @@ class WarninglistsController extends AppController
if (!is_numeric($id)) { if (!is_numeric($id)) {
throw new NotFoundException(__('Invalid ID.')); throw new NotFoundException(__('Invalid ID.'));
} }
$warninglist = $this->Warninglist->find('first', array('contain' => array('WarninglistEntry', 'WarninglistType'), 'conditions' => array('id' => $id))); $warninglist = $this->Warninglist->find('first', array(
'contain' => array('WarninglistEntry', 'WarninglistType'),
'conditions' => array('id' => $id))
);
if (empty($warninglist)) { if (empty($warninglist)) {
throw new NotFoundException(__('Warninglist not found.')); throw new NotFoundException(__('Warninglist not found.'));
} }
@ -251,6 +344,7 @@ class WarninglistsController extends AppController
$this->set('_serialize', array('Warninglist')); $this->set('_serialize', array('Warninglist'));
} else { } else {
$this->set('warninglist', $warninglist); $this->set('warninglist', $warninglist);
$this->set('possibleCategories', $this->Warninglist->categories());
} }
} }
@ -260,7 +354,7 @@ class WarninglistsController extends AppController
$id = intval($id); $id = intval($id);
$result = $this->Warninglist->quickDelete($id); $result = $this->Warninglist->quickDelete($id);
if ($result) { if ($result) {
$this->Flash->success(__('Warninglist successfuly deleted.')); $this->Flash->success(__('Warninglist successfully deleted.'));
$this->redirect(array('controller' => 'warninglists', 'action' => 'index')); $this->redirect(array('controller' => 'warninglists', 'action' => 'index'));
} else { } else {
$this->Flash->error(__('Warninglists could not be deleted.')); $this->Flash->error(__('Warninglists could not be deleted.'));

View File

@ -90,7 +90,7 @@ class AppModel extends Model
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false, 51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false, 57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false, 63 => true, 64 => false, 65 => false, 66 => false, 67 => false, 68 => false,
69 => false, 70 => false, 69 => false, 70 => false, 71 => true,
); );
public $advanced_updates_description = array( public $advanced_updates_description = array(
@ -1603,6 +1603,11 @@ class AppModel extends Model
case 70: case 70:
$sqlArray[] = "ALTER TABLE `galaxies` ADD `enabled` tinyint(1) NOT NULL DEFAULT 1 AFTER `namespace`;"; $sqlArray[] = "ALTER TABLE `galaxies` ADD `enabled` tinyint(1) NOT NULL DEFAULT 1 AFTER `namespace`;";
break; break;
case 71:
$sqlArray[] = "ALTER TABLE `roles` ADD `perm_warninglist` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "ALTER TABLE `warninglist_entries` ADD `comment` text DEFAULT NULL;";
$sqlArray[] = "ALTER TABLE `warninglists` ADD `default` tinyint(1) NOT NULL DEFAULT 1, ADD `category` varchar(20) NOT NULL DEFAULT 'false_positive', DROP COLUMN `warninglist_entry_count`";
break;
case 'fixNonEmptySharingGroupID': case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;'; $sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -26,7 +26,6 @@ class AuditLogBehavior extends ModelBehavior
'last_login' => true, // User 'last_login' => true, // User
'newsread' => true, // User 'newsread' => true, // User
'proposal_email_lock' => true, // Event 'proposal_email_lock' => true, // Event
'warninglist_entry_count' => true, // Warninglist
]; ];
private $modelInfo = [ private $modelInfo = [

View File

@ -319,6 +319,12 @@ class Role extends AppModel
'readonlyenabled' => false, 'readonlyenabled' => false,
'title' => __('Allow users to publish data to Kafka via the publish event to Kafka button.'), 'title' => __('Allow users to publish data to Kafka via the publish event to Kafka button.'),
), ),
'perm_warninglist' => array(
'id' => 'RolePermWarninglist',
'text' => 'Warninglist Editor',
'readonlyenabled' => false,
'title' => __('Allow to manage warninglists.'),
)
); );
} }
} }

View File

@ -8,6 +8,9 @@ App::uses('CidrTool', 'Tools');
*/ */
class Warninglist extends AppModel class Warninglist extends AppModel
{ {
const CATEGORY_FALSE_POSITIVE = 'false_positive',
CATEGORY_KNOWN = 'known';
public $useTable = 'warninglists'; public $useTable = 'warninglists';
public $recursive = -1; public $recursive = -1;
@ -19,7 +22,13 @@ class Warninglist extends AppModel
public $validate = array( public $validate = array(
'name' => array( 'name' => array(
'rule' => array('valueNotEmpty'), 'notEmpty' => [
'rule' => 'valueNotEmpty',
],
'unique' => [
'rule' => 'isUnique',
'message' => 'Warninglist with same name already exists.'
],
), ),
'description' => array( 'description' => array(
'rule' => array('valueNotEmpty'), 'rule' => array('valueNotEmpty'),
@ -27,6 +36,12 @@ class Warninglist extends AppModel
'version' => array( 'version' => array(
'rule' => array('numeric'), 'rule' => array('numeric'),
), ),
'type' => [
'rule' => ['inList', ['cidr', 'hostname', 'string', 'substring', 'regex']],
],
'category' => [
'rule' => ['inList', ['false_positive', 'known']],
],
); );
public $hasMany = array( public $hasMany = array(
@ -56,6 +71,31 @@ class Warninglist extends AppModel
$this->showForAll = Configure::read('MISP.warning_for_all'); $this->showForAll = Configure::read('MISP.warning_for_all');
} }
public function beforeValidate($options = array())
{
if (isset($this->data['WarninglistEntry'])) {
if ($this->data['Warninglist']['type'] === 'cidr') {
foreach ($this->data['WarninglistEntry'] as $entry) {
if (!CidrTool::validate($entry['value'])) {
$this->validationErrors['entries'][] = __('`%s` is not valid CIDR', $entry['value']);
}
}
} else if ($this->data['Warninglist']['type'] === 'regex') {
foreach ($this->data['WarninglistEntry'] as $entry) {
if (@preg_match($entry['value'], '') === false) {
$this->validationErrors['entries'][] = __('`%s` is not valid regular expression', $entry['value']);
}
}
}
if (!empty($this->validationErrors['entries'])) {
return false;
}
}
return true;
}
/** /**
* Attach warninglist matches to attributes or proposals with IDS mark. * Attach warninglist matches to attributes or proposals with IDS mark.
* *
@ -89,10 +129,10 @@ class Warninglist extends AppModel
return $eventWarnings; return $eventWarnings;
} }
$warninglistIdToName = []; $warninglists = [];
$enabledTypes = []; $enabledTypes = [];
foreach ($enabledWarninglists as $warninglist) { foreach ($enabledWarninglists as $warninglist) {
$warninglistIdToName[$warninglist['Warninglist']['id']] = $warninglist['Warninglist']['name']; $warninglists[$warninglist['Warninglist']['id']] = $warninglist['Warninglist'];
foreach ($warninglist['types'] as $type) { foreach ($warninglist['types'] as $type) {
$enabledTypes[$type] = true; $enabledTypes[$type] = true;
} }
@ -128,9 +168,10 @@ class Warninglist extends AppModel
'value' => $match['value'], 'value' => $match['value'],
'match' => $match['match'], 'match' => $match['match'],
'warninglist_id' => $warninglistId, 'warninglist_id' => $warninglistId,
'warninglist_name' => $warninglistIdToName[$warninglistId], 'warninglist_name' => $warninglists[$warninglistId]['name'],
'warninglist_category' => $warninglists[$warninglistId]['category'],
]; ];
$eventWarnings[$warninglistId] = $warninglistIdToName[$warninglistId]; $eventWarnings[$warninglistId] = $warninglists[$warninglistId]['name'];
$store[$warninglistId] = [$match['value'], $match['match']]; $store[$warninglistId] = [$match['value'], $match['match']];
} }
@ -146,9 +187,10 @@ class Warninglist extends AppModel
'value' => $matched[0], 'value' => $matched[0],
'match' => $matched[1], 'match' => $matched[1],
'warninglist_id' => $warninglistId, 'warninglist_id' => $warninglistId,
'warninglist_name' => $warninglistIdToName[$warninglistId], 'warninglist_name' => $warninglists[$warninglistId]['name'],
'warninglist_category' => $warninglists[$warninglistId]['category'],
]; ];
$eventWarnings[$warninglistId] = $warninglistIdToName[$warninglistId]; $eventWarnings[$warninglistId] = $warninglists[$warninglistId]['name'];
} }
} }
} }
@ -166,9 +208,11 @@ class Warninglist extends AppModel
public function update() public function update()
{ {
// Existing default warninglists
$existingWarninglist = $this->find('all', [ $existingWarninglist = $this->find('all', [
'fields' => ['id', 'name', 'version', 'enabled'], 'fields' => ['id', 'name', 'version', 'enabled'],
'recursive' => -1, 'recursive' => -1,
'condition' => ['default' => 1],
]); ]);
$existingWarninglist = array_column(array_column($existingWarninglist, 'Warninglist'), null, 'name'); $existingWarninglist = array_column(array_column($existingWarninglist, 'Warninglist'), null, 'name');
@ -228,6 +272,7 @@ class Warninglist extends AppModel
if ($current['enabled']) { if ($current['enabled']) {
$list['enabled'] = 1; $list['enabled'] = 1;
} }
$list['id'] = $current['id']; // keep list ID
$this->quickDelete($current['id']); $this->quickDelete($current['id']);
} }
$fieldsToSave = array('name', 'version', 'description', 'type', 'enabled'); $fieldsToSave = array('name', 'version', 'description', 'type', 'enabled');
@ -245,13 +290,11 @@ class Warninglist extends AppModel
} }
} }
unset($list['list']); unset($list['list']);
$count = count($values); $result = true;
foreach (array_chunk($values, 500) as $chunk) { foreach (array_chunk($values, 500) as $chunk) {
$result = $db->insertMulti('warninglist_entries', array('value', 'warninglist_id'), $chunk); $result = $db->insertMulti('warninglist_entries', array('value', 'warninglist_id'), $chunk);
} }
if ($result) { if (!$result) {
$this->saveField('warninglist_entry_count', $count);
} else {
return 'Could not insert values.'; return 'Could not insert values.';
} }
if (!empty($list['matching_attributes'])) { if (!empty($list['matching_attributes'])) {
@ -300,7 +343,7 @@ class Warninglist extends AppModel
$warninglists = $this->find('all', array( $warninglists = $this->find('all', array(
'contain' => array('WarninglistType'), 'contain' => array('WarninglistType'),
'conditions' => array('enabled' => 1), 'conditions' => array('enabled' => 1),
'fields' => ['id', 'name', 'type'], 'fields' => ['id', 'name', 'type', 'category'],
)); ));
$this->cacheWarninglists($warninglists); $this->cacheWarninglists($warninglists);
@ -365,9 +408,9 @@ class Warninglist extends AppModel
} }
} else { } else {
$warninglists = $this->find('all', array( $warninglists = $this->find('all', array(
'contain' => array('WarninglistType'), 'contain' => ['WarninglistType'],
'conditions' => array('enabled' => 1), 'conditions' => ['enabled' => 1],
'fields' => ['id', 'name', 'type'], 'fields' => ['id', 'name', 'type', 'category'],
)); ));
$this->cacheWarninglists($warninglists); $this->cacheWarninglists($warninglists);
} }
@ -457,8 +500,9 @@ class Warninglist extends AppModel
$object['warnings'][] = array( $object['warnings'][] = array(
'match' => $result[0], 'match' => $result[0],
'value' => $result[1], 'value' => $result[1],
'warninglist_name' => $list['Warninglist']['name'],
'warninglist_id' => $list['Warninglist']['id'], 'warninglist_id' => $list['Warninglist']['id'],
'warninglist_name' => $list['Warninglist']['name'],
'warninglist_category' => $list['Warninglist']['category'],
); );
} }
} }
@ -634,4 +678,92 @@ class Warninglist extends AppModel
} }
return $missingTldLists; return $missingTldLists;
} }
/**
* @param null $data
* @param bool $validate
* @param array $fieldList
* @return array|bool|mixed|null
* @throws Exception
*/
public function save($data = null, $validate = true, $fieldList = array())
{
$db = $this->getDataSource();
$transactionBegun = $db->begin();
$success = parent::save($data, $validate, $fieldList);
$db = $this->getDataSource();
try {
$id = (int)$this->id;
if (isset($data['WarninglistEntry'])) {
$this->WarninglistEntry->deleteAll(['warninglist_id' => $id]);
$entriesToInsert = [];
foreach ($data['WarninglistEntry'] as &$entry) {
$entriesToInsert[] = [$entry['value'], isset($entry['comment']) ? $entry['comment'] : null, $id];
}
$db->insertMulti(
$this->WarninglistEntry->table,
['value', 'comment', 'warninglist_id'],
$entriesToInsert
);
}
if (isset($data['WarninglistType'])) {
$this->WarninglistType->deleteAll(['warninglist_id' => $id]);
foreach ($data['WarninglistType'] as &$entry) {
$entry['warninglist_id'] = $id;
}
$this->WarninglistType->saveMany($data['WarninglistType']);
}
if ($transactionBegun) {
if ($success) {
$db->commit();
} else {
$db->rollback();
}
}
$this->regenerateWarninglistCaches($id);
} catch (Exception $e) {
if ($transactionBegun) {
$db->rollback();
}
throw $e;
}
return $success;
}
/**
* @param string $input
* @return array
*/
public function parseFreetext($input)
{
$input = trim($input);
if (empty($input)) {
return [];
}
$entries = [];
foreach (explode("\n", trim($input)) as $entry) {
$valueAndComment = explode("#", $entry, 2);
$entries[] = [
'value' => trim($valueAndComment[0]),
'comment' => count($valueAndComment) === 2 ? trim($valueAndComment[1]) : null,
];
}
return $entries;
}
public function categories()
{
return [
self::CATEGORY_FALSE_POSITIVE => __('False positive'),
self::CATEGORY_KNOWN => __('Known identifier'),
];
}
} }

View File

@ -79,7 +79,7 @@
if (!empty($fieldData['description'])) { if (!empty($fieldData['description'])) {
$temp .= sprintf('<small class="clear form-field-description apply_css_arrow">%s</small>', h($fieldData['description'])); $temp .= sprintf('<small class="clear form-field-description apply_css_arrow">%s</small>', h($fieldData['description']));
} }
$fieldsArrayForPersistence []= $modelForForm . Inflector::camelize($fieldData['field']); $fieldsArrayForPersistence[] = $modelForForm . Inflector::camelize($fieldData['field']);
if (!empty($fieldData['hidden'])) { if (!empty($fieldData['hidden'])) {
$temp = '<span class="hidden">' . $temp . '</span>'; $temp = '<span class="hidden">' . $temp . '</span>';
} }

View File

@ -506,11 +506,26 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
)); ));
break; break;
case 'warninglist': case 'warninglist':
if ($menuItem === 'view') { if ($menuItem === 'view' || $menuItem === 'edit') {
echo $this->element('/genericElements/SideMenu/side_menu_link', array( echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'view', 'element_id' => 'view',
'url' => $baseurl . '/warninglists/view/' . h($id),
'text' => __('View Warninglist') 'text' => __('View Warninglist')
)); ));
if (!$isDefault && $canAccess('warninglists', 'edit')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', [
'element_id' => 'edit',
'url' => $baseurl . '/warninglists/edit/' . h($id),
'text' => __('Edit Warninglist'),
]);
}
}
if ($canAccess('warninglists', 'add')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', [
'element_id' => 'add',
'url' => $baseurl . '/warninglists/add',
'text' => __('Add Warninglist'),
]);
} }
echo $this->element('/genericElements/SideMenu/side_menu_link', array( echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/warninglists/index', 'url' => $baseurl . '/warninglists/index',

View File

@ -158,17 +158,17 @@
'requirement' => !$isAclRegexp 'requirement' => !$isAclRegexp
), ),
array( array(
'text' => __('List Warninglists'), 'text' => __('Warninglists'),
'url' => $baseurl . '/warninglists/index' 'url' => $baseurl . '/warninglists/index'
), ),
array( array(
'text' => __('List Noticelists'), 'text' => __('Noticelists'),
'url' => $baseurl . '/noticelists/index' 'url' => $baseurl . '/noticelists/index'
), ),
array( array(
'text' => __('List Correlation Exclusions'), 'text' => __('Correlation Exclusions'),
'url' => $baseurl . '/correlation_exclusions/index', 'url' => $baseurl . '/correlation_exclusions/index',
'requirement' => $canAccess('correlation_exclusions', 'index'), 'requirement' => $canAccess('correlation_exclusions', 'index'),
) )
) )
), ),

View File

@ -0,0 +1,55 @@
<?php
$action = $this->request->params['action'];
echo $this->element('genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'title' => $action === 'add' ? __('Add warninglist') : __('Edit warninglist'),
'model' => 'Warninglist',
'fields' => array(
array(
'field' => 'name',
'class' => 'input span6',
),
array(
'field' => 'description',
'class' => 'input span6',
'rows' => 1,
),
array(
'field' => 'type',
'class' => 'input',
'options' => $possibleTypes,
),
array(
'field' => 'category',
'class' => 'input',
'options' => $possibleCategories,
),
array(
'label' => __('Accepted attribute types'),
'field' => 'matching_attributes',
'type' => 'select',
'multiple' => 'multiple',
),
array(
'label' => __('Values (one value per line, for value comment use #)'),
'field' => 'entries',
'type' => 'textarea',
'rows' => 10,
),
),
'submit' => array(
'action' => $action
)
)
));
echo $this->element('/genericElements/SideMenu/side_menu', [
'menuList' => 'warninglist',
'menuItem' => $action === 'add' ? 'add' : 'edit',
'id' => $action === 'add' ? null : $entity['Warninglist']['id'],
'isDefault' => false,
]);
?>
<script type="text/javascript">
$('#WarninglistMatchingAttributes').chosen();
</script>

View File

@ -1,5 +1,5 @@
<div class="warninglist view"> <div class="warninglist view">
<h2><?= __('Search in Warninglists') ?></h2> <h2><?= __('Search in enabled Warninglists') ?></h2>
<?php <?php
echo $this->Form->create('Warninglist'); echo $this->Form->create('Warninglist');
echo sprintf('<div class="input-append">%s%s</div>', echo sprintf('<div class="input-append">%s%s</div>',
@ -28,4 +28,4 @@
<?php endif; ?> <?php endif; ?>
</div> </div>
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'warninglist', 'menuItem' => 'check_value')); ?> <?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'warninglist', 'menuItem' => 'check_value')); ?>

View File

@ -67,26 +67,37 @@
'name' => __('Description'), 'name' => __('Description'),
'data_path' => 'Warninglist.description', 'data_path' => 'Warninglist.description',
), ),
array(
'name' => __('Category'),
'sort' => 'category',
'class' => 'short',
'element' => 'custom',
'function' => function (array $row) use ($possibleCategories) {
return $possibleCategories[$row['Warninglist']['category']];
}
),
array( array(
'name' => __('Type'), 'name' => __('Type'),
'sort' => 'type', 'sort' => 'type',
'class' => 'short', 'class' => 'short',
'data_path' => 'Warninglist.type', 'data_path' => 'Warninglist.type',
), ),
array(
'name' => __('Valid attributes'),
'class' => 'short',
'sort' => 'Warninglist.valid_attributes',
'data_path' => 'Warninglist.valid_attributes',
),
array( array(
'name' => __('Entries'), 'name' => __('Entries'),
'sort' => 'warninglist_entry_count', 'sort' => 'warninglist_entry_count',
'class' => 'short', 'class' => 'short',
'data_path' => 'Warninglist.warninglist_entry_count', 'data_path' => 'Warninglist.warninglist_entry_count',
), ),
array(
'name' => __('Default'),
'sort' => 'default',
'class' => 'short',
'element' => 'boolean',
'data_path' => 'Warninglist.default',
),
array( array(
'name' => __('Enabled'), 'name' => __('Enabled'),
'sort' => 'enabled',
'class' => 'short', 'class' => 'short',
'element' => 'boolean', 'element' => 'boolean',
'data_path' => 'Warninglist.enabled', 'data_path' => 'Warninglist.enabled',
@ -128,11 +139,25 @@
) )
), ),
), ),
array(
'url' => $baseurl . '/warninglists/edit',
'url_params_data_paths' => array(
'Warninglist.id'
),
'title' => __('Edit'),
'icon' => 'edit',
'complex_requirement' => [
'function' => function($row) use ($me) {
return $row['Warninglist']['default'] == 0 && $me['Role']['perm_warninglist'];
}
]
),
array( array(
'url' => $baseurl . '/warninglists/view', 'url' => $baseurl . '/warninglists/view',
'url_params_data_paths' => array( 'url_params_data_paths' => array(
'Warninglist.id' 'Warninglist.id'
), ),
'title' => __('View'),
'icon' => 'eye', 'icon' => 'eye',
'dbclickAction' => true 'dbclickAction' => true
), ),
@ -153,7 +178,6 @@
echo '</div>'; echo '</div>';
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'warninglist', 'menuItem' => 'index')); echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'warninglist', 'menuItem' => 'index'));
?> ?>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
$('#quickFilterButton').click(function() { $('#quickFilterButton').click(function() {

View File

@ -1,13 +1,14 @@
<?php <?php
$data = $warninglist['Warninglist']; $data = $warninglist['Warninglist'];
$types = array_column($warninglist['WarninglistType'], 'type'); $types = implode(', ', array_column($warninglist['WarninglistType'], 'type'));
$table_data = array( $table_data = array(
array('key' => __('ID'), 'value' => $data['id']), array('key' => __('ID'), 'value' => $data['id']),
array('key' => __('Name'), 'value' => $data['name']), array('key' => __('Name'), 'value' => $data['name']),
array('key' => __('Description'), 'value' => $data['description']), array('key' => __('Description'), 'value' => $data['description']),
array('key' => __('Version'), 'value' => $data['version']), array('key' => __('Version'), 'value' => $data['version']),
array('key' => __('Category'), 'value' => $possibleCategories[$data['category']]),
array('key' => __('Type'), 'value' => $data['type']), array('key' => __('Type'), 'value' => $data['type']),
array('key' => __('Accepted attribute types'), 'value' => implode(', ', $types)), array('key' => __('Accepted attribute types'), 'value' => $types),
array( array(
'key' => __('Enabled'), 'key' => __('Enabled'),
'boolean' => $data['enabled'], 'boolean' => $data['enabled'],
@ -21,15 +22,29 @@
) )
), ),
); );
$values = [];
foreach ($warninglist['WarninglistEntry'] as $entry) {
$value = h($entry['value']);
if ($entry['comment']) {
$value .= ' <span style="color: gray"># ' . h($entry['comment']) . '</span>';
}
$values[] = $value;
}
echo sprintf( echo sprintf(
'<div class="warninglist view"><div class="row-fluid"><div class="span8" style="margin:0;">%s</div></div><h4>%s</h4>%s</div>', '<div class="warninglist view"><div class="row-fluid"><div class="span8" style="margin:0;">%s</div></div><h4>%s</h4>%s</div>',
sprintf( sprintf(
'<h2>%s</h2>%s', '<h2>%s</h2>%s',
h(strtoupper($warninglist['Warninglist']['name'])), h(mb_strtoupper($warninglist['Warninglist']['name'])),
$this->element('genericElements/viewMetaTable', array('table_data' => $table_data)) $this->element('genericElements/viewMetaTable', array('table_data' => $table_data))
), ),
__('Values'), __('Values'),
implode('<br>', array_column($warninglist['WarninglistEntry'], 'value')) implode('<br>', $values)
); );
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'warninglist', 'menuItem' => 'view')); echo $this->element('/genericElements/SideMenu/side_menu', [
'menuList' => 'warninglist',
'menuItem' => 'view',
'id' => $data['id'],
'isDefault' => $data['default'] == 1,
]);

View File

@ -7609,6 +7609,17 @@
"column_default": "string", "column_default": "string",
"extra": "" "extra": ""
}, },
{
"column_name": "category",
"is_nullable": "NO",
"data_type": "varchar",
"character_maximum_length": "20",
"numeric_precision": null,
"collation_name": "utf8_general_ci",
"column_type": "varchar(20)",
"column_default": "false_positive",
"extra": ""
},
{ {
"column_name": "description", "column_name": "description",
"is_nullable": "NO", "is_nullable": "NO",
@ -7643,14 +7654,14 @@
"extra": "" "extra": ""
}, },
{ {
"column_name": "warninglist_entry_count", "column_name": "default",
"is_nullable": "NO", "is_nullable": "NO",
"data_type": "int", "data_type": "tinyint",
"character_maximum_length": null, "character_maximum_length": null,
"numeric_precision": "10", "numeric_precision": "3",
"collation_name": null, "collation_name": null,
"column_type": "int(11) unsigned", "column_type": "tinyint(1)",
"column_default": "0", "column_default": "1",
"extra": "" "extra": ""
} }
], ],
@ -7677,6 +7688,17 @@
"column_default": null, "column_default": null,
"extra": "" "extra": ""
}, },
{
"column_name": "comment",
"is_nullable": "YES",
"data_type": "text",
"character_maximum_length": "65535",
"numeric_precision": null,
"collation_name": "utf8_unicode_ci",
"column_type": "text",
"column_default": null,
"extra": ""
},
{ {
"column_name": "warninglist_id", "column_name": "warninglist_id",
"is_nullable": "NO", "is_nullable": "NO",
@ -8169,5 +8191,5 @@
"id": true "id": true
} }
}, },
"db_version": "70" "db_version": "71"
} }