new: [Warninglist] index

feature/3.x_Warninglists
Christophe Vandeplas 2024-04-29 09:59:44 +00:00
parent 1a14d2bc44
commit 37d332808b
16 changed files with 1385 additions and 140 deletions

View File

@ -3,6 +3,10 @@ parameters:
fileExtensions:
- php
- ctp
paths:
- src
- tests
- templates
ignoreErrors:
- message: '#Undefined variable: \$this#'
path: %currentWorkingDirectory%/templates

View File

@ -279,6 +279,20 @@ class ACLComponent extends Component
'view' => ['*'],
'preview_entries' => ['*']
],
'Warninglists' => [
'checkValue' => ['*'],
'delete' => ['perm_warninglist'],
'enableWarninglist' => ['perm_warninglist'],
'getToggleField' => ['perm_warninglist'],
'index' => ['*'],
'toggleEnable' => ['perm_warninglist'],
'update' => [], // TODO shouldn't perm_warninglist be allowed to update?
'view' => ['*'],
'edit' => ['perm_warninglist'],
'add' => ['perm_warninglist'],
'export' => ['*'],
'import' => ['perm_warninglist'],
],
'ObjectTemplates' => [
'activate' => [],
'add' => ['perm_object_template'],

View File

@ -50,11 +50,6 @@ class OrganisationsController extends AppController
'contextFilters' => [
'custom' => $customContextFilters,
],
'afterFind' => function ($entity) {
$entity->setVirtual(['user_count']);
return $entity;
},
'contain' => $this->containFields,
'statisticsFields' => $this->statisticsFields,
]

View File

@ -0,0 +1,501 @@
<?php
declare(strict_types=1);
namespace App\Controller;
class WarninglistsController extends AppController
{
public $paginate = [
'limit' => 60,
'maxLimit' => 9999,
'contain' => ['WarninglistTypes'],
'order' => [
'Warninglist.id' => 'DESC',
],
];
/**
* index page for warninglists
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$params = [
'filters' => ['name', 'description', 'type'],
'quickFilters' => ['name'],
];
$this->CRUD->index($params);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
// $filters = $this->harvestParameters(['value', 'enabled']);
// if (!empty($filters['value'])) {
// $this->paginate['conditions'] = [
// 'OR' => [
// 'LOWER(Warninglist.name) LIKE' => '%' . strtolower($filters['value']) . '%',
// 'LOWER(Warninglist.description) LIKE' => '%' . strtolower($filters['value']) . '%',
// 'LOWER(Warninglist.type)' => strtolower($filters['value']),
// ]
// ];
// }
// if (isset($filters['enabled'])) {
// $this->paginate['conditions'][] = ['Warninglist.enabled' => $filters['enabled']];
// }
// if ($this->ParamHandler->isRest()) {
// unset($this->paginate['limit']);
// $warninglists = $this->Warninglist->find('all', $this->paginate);
// } else {
// $warninglists = $this->paginate();
// }
// foreach ($warninglists as &$warninglist) {
// $validAttributes = array_column($warninglist['WarninglistType'], 'type');
// $warninglist['Warninglist']['valid_attributes'] = implode(', ', $validAttributes);
// unset($warninglist['WarninglistType']);
// }
// if ($this->ParamHandler->isRest()) {
// return $this->RestResponse->viewData(['Warninglists' => $warninglists], $this->response->getType());
// }
// $this->set('warninglists', $warninglists);
// $this->set('passedArgsArray', $filters);
$this->set('possibleCategories', $this->Warninglists->categories());
}
// public function update()
// {
// if (!$this->request->is('post')) {
// throw new MethodNotAllowedException(__('This action is only accessible via POST requests.'));
// }
// $result = $this->Warninglist->update();
// $this->Log = ClassRegistry::init('Log');
// $fails = 0;
// $successes = 0;
// if (!empty($result)) {
// if (isset($result['success'])) {
// foreach ($result['success'] as $id => $success) {
// if (isset($success['old'])) {
// $change = $success['name'] . ': updated from v' . $success['old'] . ' to v' . $success['new'];
// } else {
// $change = $success['name'] . ' v' . $success['new'] . ' installed';
// }
// $this->Log->create();
// $this->Log->saveOrFailSilently(array(
// 'org' => $this->Auth->user('Organisation')['name'],
// 'model' => 'Warninglist',
// 'model_id' => $id,
// 'email' => $this->Auth->user('email'),
// 'action' => 'update',
// 'user_id' => $this->Auth->user('id'),
// 'title' => __('Warning list updated'),
// 'change' => $change,
// ));
// $successes++;
// }
// }
// if (isset($result['fails'])) {
// foreach ($result['fails'] as $id => $fail) {
// $this->Log->create();
// $this->Log->saveOrFailSilently(array(
// 'org' => $this->Auth->user('Organisation')['name'],
// 'model' => 'Warninglist',
// 'model_id' => $id,
// 'email' => $this->Auth->user('email'),
// 'action' => 'update',
// 'user_id' => $this->Auth->user('id'),
// 'title' => __('Warning list failed to update'),
// 'change' => __('%s could not be installed/updated. Error: %s', $fail['name'], $fail['fail']), // TODO: needs to be optimized for non-SVO languages
// ));
// $fails++;
// }
// }
// } else {
// $this->Log->create();
// $this->Log->saveOrFailSilently(array(
// 'org' => $this->Auth->user('Organisation')['name'],
// 'model' => 'Warninglist',
// 'model_id' => 0,
// 'email' => $this->Auth->user('email'),
// 'action' => 'update',
// 'user_id' => $this->Auth->user('id'),
// 'title' => __('Warninglist update (nothing to update)'),
// 'change' => __('Executed an update of the warning lists, but there was nothing to update.'),
// ));
// }
// if ($successes == 0 && $fails == 0) {
// $flashType = 'info';
// $message = __('All warninglists are up to date already.');
// } elseif ($successes == 0) {
// $flashType = 'error';
// $message = __('Could not update any of the warning lists');
// } else {
// $flashType = 'success';
// $message = __('Successfully updated %s warninglists.', $successes);
// if ($fails != 0) {
// $message .= __(' However, could not update %s warninglists.', $fails); // TODO: non-SVO languages need to be considered
// }
// }
// if ($this->_isRest()) {
// return $this->RestResponse->saveSuccessResponse('Warninglist', 'update', false, $this->response->type(), $message);
// } else {
// $this->Flash->{$flashType}($message);
// $this->redirect(array('controller' => 'warninglists', 'action' => 'index'));
// }
// }
// 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 (empty($warninglist['WarninglistEntry'])) {
// $warninglist['Warninglist']['entries'] = ''; // Make model validation fails
// }
// if (empty($warninglist['Warninglist']['matching_attributes'])) {
// $warninglist['Warninglist']['matching_attributes'] = ['ALL'];
// }
// 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 (empty($warninglist['WarninglistEntry'])) {
// $warninglist['Warninglist']['entries'] = ''; // Make model validation fails
// }
// 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
// * Simply POST an ID or a list of IDs to toggle the current state
// * To control what state the warninglists should have after execution instead of just blindly toggling them, simply pass the enabled flag
// * Example:
// * {"id": [5, 8], "enabled": 1}
// * Alternatively search by a substring in the warninglist's named, such as:
// * {"name": ["%alexa%", "%iana%"], "enabled": 1}
// */
// public function toggleEnable()
// {
// if (!$this->request->is('post')) {
// return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('This function only accepts POST requests.'))), 'status' => 200, 'type' => 'json'));
// }
// if (isset($this->request->data['Warninglist']['data'])) {
// $id = $this->request->data['Warninglist']['data'];
// } else {
// if (!empty($this->request->data['id'])) {
// $id = $this->request->data['id'];
// } elseif (!empty($this->request->data['name'])) {
// if (!is_array($this->request->data['name'])) {
// $names = [$this->request->data['name']];
// } else {
// $names = $this->request->data['name'];
// }
// $conditions = array();
// foreach ($names as $name) {
// $conditions['OR'][] = ['LOWER(Warninglist.name] LIKE' => strtolower($name));
// }
// $id = $this->Warninglist->find('column', array(
// 'conditions' => $conditions,
// 'fields' => ['Warninglist.id']
// ));
// }
// }
// if (isset($this->request->data['enabled'])) {
// $enabled = $this->request->data['enabled'];
// }
// if (empty($id)) {
// return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Warninglist not found.'))), 'status' => 200, 'type' => 'json'));
// }
// $currentState = $this->Warninglist->find('all', ['conditions' => ['id' => $id], 'recursive' => -1]);
// if (empty($currentState)) {
// return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Warninglist(s) not found.'))), 'status' => 200, 'type' => 'json'));
// }
// $success = 0;
// foreach ($currentState as $warningList) {
// if (isset($enabled)) {
// $warningList['Warninglist']['enabled'] = $enabled;
// $message = $enabled ? 'enabled' : 'disabled';
// } else {
// if ($warningList['Warninglist']['enabled']) {
// $warningList['Warninglist']['enabled'] = 0;
// $message = 'disabled';
// } else {
// $warningList['Warninglist']['enabled'] = 1;
// $message = 'enabled';
// }
// if (!isset($enabled) && count($currentState) > 1) {
// $message = 'toggled';
// }
// }
// if ($this->Warninglist->save($warningList)) {
// $success += 1;
// }
// $this->Warninglist->regenerateWarninglistCaches($warningList['Warninglist']['id']);
// }
// if ($success) {
// return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $success . __(' warninglist(s) ') . $message)), 'status' => 200, 'type' => 'json')); // TODO: non-SVO lang considerations
// } else {
// return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => __('Warninglist(s) could not be toggled.'))), 'status' => 200, 'type' => 'json'));
// }
// }
// public function enableWarninglist($id, $enable = false)
// {
// $this->Warninglist->id = $id;
// if (!$this->Warninglist->exists()) {
// throw new NotFoundException(__('Invalid Warninglist.'));
// }
// // DBMS interoperability: convert boolean false to integer 0 so cakephp doesn't try to insert an empty string into the database
// if ($enable === false) {
// $enable = 0;
// }
// $this->Warninglist->saveField('enabled', $enable);
// $this->Warninglist->regenerateWarninglistCaches($id);
// if ($enable === 0) {
// $this->Flash->success(__('Warninglist disabled'));
// }
// else {
// $this->Flash->success(__('Warninglist enabled'));
// }
// $this->redirect(array('controller' => 'warninglists', 'action' => 'view', $id));
// }
// public function getToggleField()
// {
// if (!$this->request->is('ajax')) {
// throw new MethodNotAllowedException(__('This action is available via AJAX only.'));
// }
// $this->layout = false;
// $this->render('ajax/getToggleField');
// }
// public function view($id)
// {
// if (!is_numeric($id)) {
// throw new NotFoundException(__('Invalid ID.'));
// }
// $warninglist = $this->Warninglist->find('first', array(
// 'contain' => ['WarninglistEntry', 'WarninglistType'],
// 'conditions' => ['id' => $id])
// );
// if (empty($warninglist)) {
// throw new NotFoundException(__('Warninglist not found.'));
// }
// if ($this->IndexFilter->isCsv()) {
// $csv = [];
// foreach ($warninglist['WarninglistEntry'] as $entry) {
// $line = $entry['value'];
// if ($entry['comment']) {
// $line .= ';' . $entry['comment'];
// }
// $csv[] = $line;
// }
// return $this->RestResponse->viewData(implode("\n", $csv), 'csv');
// }
// if ($this->_isRest()) {
// $warninglist['Warninglist']['WarninglistEntry'] = $warninglist['WarninglistEntry'];
// $warninglist['Warninglist']['WarninglistType'] = $warninglist['WarninglistType'];
// return $this->RestResponse->viewData(['Warninglist' => $warninglist['Warninglist']], $this->response->type());
// }
// $this->set('warninglist', $warninglist);
// $this->set('possibleCategories', $this->Warninglist->categories());
// }
// public function import()
// {
// $this->request->allowMethod(['post']);
// if (empty($this->request->data)) {
// throw new BadRequestException(__('No valid data received.'));
// }
// foreach (['name', 'type', 'version', 'description', 'matching_attributes', 'list'] as $filed) {
// if (!isset($this->request->data[$filed])) {
// throw new BadRequestException(__('No valid data received: field `%s` is missing.', $filed));
// }
// }
// if (!is_array($this->request->data['list'])) {
// throw new BadRequestException(__('No valid data received: `list` field is not array'));
// }
// try {
// $id = $this->Warninglist->import($this->request->data);
// return $this->RestResponse->saveSuccessResponse('Warninglist', 'import', $id, false, __('Warninglist imported'));
// } catch (Exception $e) {
// return $this->RestResponse->saveFailResponse('Warninglist', 'import', false, $e->getMessage());
// }
// }
// public function export($id = null)
// {
// if (empty($id)) {
// throw new NotFoundException(__('Warninglist not found.'));
// }
// $warninglist = $this->Warninglist->find('first', [
// 'contain' => ['WarninglistType'],
// 'conditions' => ['id' => $id],
// ]);
// if (empty($warninglist)) {
// throw new NotFoundException(__('Warninglist not found.'));
// }
// $matchingAttributes = array_column($warninglist['WarninglistType'], 'type');
// $list = $this->Warninglist->WarninglistEntry->find('column', [
// 'conditions' => ['warninglist_id' => $warninglist['Warninglist']['id']],
// 'fields' => ['value'],
// ]);
// $output = [
// 'name' => $warninglist['Warninglist']['name'],
// 'type' => $warninglist['Warninglist']['type'],
// 'version' => $warninglist['Warninglist']['version'],
// 'description' => $warninglist['Warninglist']['description'],
// 'matching_attributes' => $matchingAttributes,
// 'list' => $list,
// ];
// return $this->RestResponse->viewData($output, 'json');
// }
// public function delete($id)
// {
// if ($this->request->is('post')) {
// $id = (int)$id;
// $result = $this->Warninglist->quickDelete($id);
// if ($result) {
// $this->Flash->success(__('Warninglist successfully deleted.'));
// } else {
// $this->Flash->error(__('Warninglist could not be deleted.'));
// }
// $this->redirect(['controller' => 'warninglists', 'action' => 'index']);
// } else {
// if ($this->request->is('ajax')) {
// $this->set('id', $id);
// $this->render('ajax/delete_confirmation');
// } else {
// throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
// }
// }
// }
// public function checkValue()
// {
// if ($this->request->is('post')) {
// if (empty($this->request->data)) {
// throw new NotFoundException(__('No valid data received.'));
// }
// $data = $this->request->data;
// if (is_array($data) && isset($data['Warninglist'])) {
// $data = $data['Warninglist'];
// }
// if (!is_array($data)) {
// $data = [$data];
// }
// if (array_key_exists('[]', $data)) {
// $data = $data['[]'];
// }
// $hits = array();
// $warninglists = $this->Warninglist->getEnabled();
// foreach ($data as $dataPoint) {
// $dataPoint = trim($dataPoint);
// foreach ($warninglists as $warninglist) {
// $values = $this->Warninglist->getFilteredEntries($warninglist);
// $result = $this->Warninglist->checkValue($values, $dataPoint, '', $warninglist['Warninglist']['type']);
// if ($result !== false) {
// $hits[$dataPoint][] = [
// 'id' => $warninglist['Warninglist']['id'],
// 'name' => $warninglist['Warninglist']['name'],
// 'matched' => $result[0],
// ];
// }
// }
// }
// if ($this->_isRest()) {
// return $this->RestResponse->viewData($hits, $this->response->type());
// }
// $this->set('hits', $hits);
// $this->set('data', $data);
// } else {
// if ($this->_isRest()) {
// return $this->RestResponse->describe('Warninglists', 'checkValue', false, $this->response->type());
// }
// }
// }
}

View File

@ -1,13 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Entity;
class AppModel extends Entity
{
const BROTLI_HEADER = "\xce\xb2\xcf\x81";
const BROTLI_MIN_LENGTH = 200;
public const BROTLI_HEADER = "\xce\xb2\xcf\x81";
public const BROTLI_MIN_LENGTH = 200;
public const ACTION_ADD = 'add',
ACTION_EDIT = 'edit',
@ -22,12 +24,22 @@ class AppModel extends Entity
ACTION_LOGIN_FAIL = 'login_fail',
ACTION_LOGOUT = 'logout';
/**
* getConstant
*
* @param mixed $name Name of the constant to get
* @return mixed the value of the constant
*/
public function getConstant($name)
{
return constant('self::' . $name);
}
/**
* getAccessibleFieldForNew
*
* @return array
*/
public function getAccessibleFieldForNew(): array
{
return $this->_accessibleOnNew ?? [];
@ -64,9 +76,10 @@ class AppModel extends Entity
$tag = [
'id' => $tag['id'],
'name' => $tag['name'],
'colour' => $tag['colour']
'colour' => $tag['colour'],
];
}
return $tags;
}
@ -77,13 +90,13 @@ class AppModel extends Entity
$alignmentDataToKeep = [
'individual' => [
'id',
'email'
'email',
],
'organisation' => [
'id',
'uuid',
'name'
]
'name',
],
];
foreach ($alignments as $alignment) {
foreach (array_keys($alignmentDataToKeep) as $type) {
@ -97,6 +110,7 @@ class AppModel extends Entity
}
}
}
return $rearrangedAlignments;
}
@ -106,15 +120,35 @@ class AppModel extends Entity
$this->organisation = [
'id' => $this->organisation['id'],
'name' => $this->organisation['name'],
'uuid' => $this->organisation['uuid']
'uuid' => $this->organisation['uuid'],
];
}
if (in_array('individual', $typesToRearrange) && isset($this->individual)) {
$this->individual = [
'id' => $this->individual['id'],
'email' => $this->individual['email'],
'uuid' => $this->individual['uuid']
'uuid' => $this->individual['uuid'],
];
}
}
/**
* @param string $field The field name to add
* @param \App\Model\Entity\AppModel $model The model to use
* @param array $conditions The conditions for the query
*/
public function addCountField($field, AppModel $model, array $conditions)
{
$dataSource = ConnectionManager::get('default')->config['datasource'];
$subQuery = $dataSource->buildStatement(
[
'fields' => ['COUNT(*)'],
'table' => $dataSource->fullTableName($model),
'alias' => $model->alias,
'conditions' => $conditions,
],
$model
);
$this->virtualFields[$field] = $subQuery;
}
}

View File

@ -1,16 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
use Cake\ORM\TableRegistry;
class Warninglist extends AppModel
{
public const CATEGORY_FALSE_POSITIVE = 'false_positive',
CATEGORY_KNOWN = 'known';
public const TLDS = array(
'TLDs as known by IANA'
);
public const TLDS = [
'TLDs as known by IANA',
];
/**
* _getWarninglistEntryCount adds virtual field counting the number of entries in the warninglist
*
* @return int the amount of entries in the warninglist
*/
protected function _getWarninglistEntryCount()
{
$warninglist_entries_table = TableRegistry::getTableLocator()->get('WarninglistEntries');
$entry_count = $warninglist_entries_table->find('all')
->where(['warninglist_id' => $this->id])
->count();
return $entry_count;
}
}

View File

@ -1,10 +1,8 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class WarninglistEntry extends AppModel
{
public $virtualFields = [];

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
class WarninglistType extends AppModel
{
public $virtualFields = [];
}

View File

@ -1,13 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class WarninglistEntriesTable extends AppTable
{
/**
* initialize
*
* @param mixed $config Configuration
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
@ -16,17 +21,24 @@ class WarninglistEntriesTable extends AppTable
'Warninglist',
[
'dependent' => true,
'propertyName' => 'Warninglist'
'propertyName' => 'Warninglist',
]
);
$this->setDisplayField('value');
}
/**
* validationDefault
*
* @param mixed $validator Validator
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('value')
->requirePresence(['value', 'warninglist_id'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\Validation\Validator;
class WarninglistTypesTable extends AppTable
{
/**
* initialize
*
* @param mixed $config Configuration
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
// $this->addBehavior('AuditLog');
$this->belongsTo(
'Warninglist',
[
'dependent' => true,
'propertyName' => 'Warninglist',
]
);
$this->setDisplayField('value');
}
/**
* validationDefault
*
* @param mixed $validator Validator
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('type')
->requirePresence(['type', 'warninglist_id'], 'create');
return $validator;
}
}

View File

@ -1,54 +1,81 @@
<?php
declare(strict_types=1);
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\Validation\Validator;
use App\Lib\Tools\CidrTool;
use App\Lib\Tools\FileAccessTool;
use App\Lib\Tools\RedisTool;
use App\Model\Entity\Warninglist;
use Cake\Core\Configure;
use Cake\ORM\RulesChecker;
use App\Lib\Tools\CidrTool;
use App\Lib\Tools\RedisTool;
use App\Lib\Tools\FileAccessTool;
use App\Model\Entity\Warninglist;
use Cake\Validation\Validator;
use Exception;
class WarninglistsTable extends AppTable
{
/** @var array */
/**
* @var array
*/
private $entriesCache = [];
/** @var array|null */
/**
* @var array|null
*/
private $enabledCache = null;
private $showForAll;
/**
* initialize
*
* @param mixed $config the config
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
$this->showForAll = Configure::read('MISP.warning_for_all');
$this->hasMany(
'WarninglistEntries',
[
'dependent' => true,
'propertyName' => 'WarninglistEntry'
'propertyName' => 'WarninglistEntry',
]
);
$this->hasMany(
'WarninglistTypes',
[
'dependent' => true,
'propertyName' => 'WarninglistType'
'propertyName' => 'WarninglistType',
]
);
parent::initialize($config);
$this->setTable('warninglists');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
}
/**
* buildRules
*
* @param \Cake\ORM\RulesChecker $rules the rules
* @return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->isUnique(['name']));
return $rules;
}
/**
* validationDefault
*
* @param \Cake\Validation\Validator $validator the validator
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
@ -71,11 +98,12 @@ class WarninglistsTable extends AppTable
]
)
->requirePresence(['name', 'description', 'entries'], 'create');
return $validator;
}
// TODO: [3.x-MIGRATION] re-implement this as a custom validation rule
// public function beforeValidate($options = array())
// public function beforeValidate($options = [])
// {
// if (isset($this->data['WarninglistEntry'])) {
// if ($this->data['Warninglist']['type'] === 'cidr') {
@ -103,7 +131,7 @@ class WarninglistsTable extends AppTable
/**
* Attach warninglist matches to attributes or proposals with IDS mark.
*
* @param array $attributes
* @param array $attributes the attributes to attach to
* @return array Warninglist ID => name
*/
public function attachWarninglistToAttributes(array &$attributes)
@ -133,6 +161,7 @@ class WarninglistsTable extends AppTable
if (!empty($eventWarnings)) {
$this->assignComments($attributes);
}
return $eventWarnings;
}
@ -218,7 +247,9 @@ class WarninglistsTable extends AppTable
/**
* Assign comments to warninglist hits.
* @param array $attributes
*
* @param array $attributes the attributes to assign comments to
* @return void
*/
private function assignComments(array &$attributes)
{
@ -233,19 +264,24 @@ class WarninglistsTable extends AppTable
$conditions = [];
foreach ($toFetch as $warninglistId => $values) {
$conditions[] = ['AND' => [
'warninglist_id' => $warninglistId,
'value' => array_unique($values),
]];
$conditions[] = [
'AND' => [
'warninglist_id' => $warninglistId,
'value' => array_unique($values),
]
];
}
$entries = $this->WarninglistEntry->find('all', [
'conditions' => [
'OR' => $conditions,
'comment !=' => '',
],
'fields' => ['value', 'warninglist_id', 'comment'],
]);
$entries = $this->WarninglistEntry->find(
'all',
[
'conditions' => [
'OR' => $conditions,
'comment !=' => '',
],
'fields' => ['value', 'warninglist_id', 'comment'],
]
);
if (empty($entries)) {
return;
}
@ -267,14 +303,22 @@ class WarninglistsTable extends AppTable
}
}
/**
* update the warninglists
*
* @return array the result of the update
*/
public function update()
{
// Fetch existing default warninglists
$existingWarninglist = $this->find('all', [
'fields' => ['id', 'name', 'version', 'enabled'],
'recursive' => -1,
'conditions' => ['default' => 1],
])->toArray();
$existingWarninglist = $this->find(
'all',
[
'fields' => ['id', 'name', 'version', 'enabled'],
'recursive' => -1,
'conditions' => ['default' => 1],
]
)->toArray();
$existingWarninglist = array_column(array_column($existingWarninglist, 'Warninglist'), null, 'name');
$directories = glob(APP . '..' . DS . 'libraries' . DS . 'misp-warninglists' . DS . 'lists' . DS . '*', GLOB_ONLYDIR);
@ -306,40 +350,52 @@ class WarninglistsTable extends AppTable
if (!empty($result['success']) || !empty($result['fails'])) {
$this->regenerateWarninglistCaches();
}
return $result;
}
/**
* quickDelete
*
* @param mixed $id the id of the list to delete
* @return array the result of the deletion
*/
public function quickDelete($id)
{
$result = $this->WarninglistEntry->deleteAll(
array('WarninglistEntry.warninglist_id' => $id),
['WarninglistEntry.warninglist_id' => $id],
false
);
if ($result) {
$result = $this->WarninglistType->deleteAll(
array('WarninglistType.warninglist_id' => $id),
['WarninglistType.warninglist_id' => $id],
false
);
}
if ($result) {
$result = $this->delete($id, false);
}
return $result;
}
/**
* Import single warninglist
* @param array $list
*
* @param array $list a list to import
* @return int Warninglist ID
* @throws Exception
* @throws \Exception
*/
public function import(array $list)
{
$existingWarninglist = $this->find('first', [
'fields' => ['id', 'name', 'version', 'enabled', 'default'],
'recursive' => -1,
'conditions' => ['name' => $list['name']],
]);
$existingWarninglist = $this->find(
'first',
[
'fields' => ['id', 'name', 'version', 'enabled', 'default'],
'recursive' => -1,
'conditions' => ['name' => $list['name']],
]
);
if ($existingWarninglist && $existingWarninglist['Warninglist']['default']) {
throw new Exception('It is not possible to modify default warninglist.');
@ -352,11 +408,11 @@ class WarninglistsTable extends AppTable
}
/**
* @param array $list
* @param array $existing
* @param bool $default
* @param array $list List to update
* @param array $existing Existing list
* @param bool $default Whether the list is default
* @return int Warninglist ID
* @throws Exception
* @throws \Exception
*/
private function __updateList(array $list, array $existing, $default = true)
{
@ -371,7 +427,7 @@ class WarninglistsTable extends AppTable
$this->WarninglistEntry->deleteAll(['WarninglistEntry.warninglist_id' => $existing['id']], false);
$this->WarninglistType->deleteAll(['WarninglistType.warninglist_id' => $existing['id']], false);
}
$fieldsToSave = array('name', 'version', 'description', 'type', 'enabled');
$fieldsToSave = ['name', 'version', 'description', 'type', 'enabled'];
foreach ($fieldsToSave as $fieldToSave) {
$warninglist['Warninglist'][$fieldToSave] = $list[$fieldToSave];
}
@ -380,7 +436,7 @@ class WarninglistsTable extends AppTable
}
$this->create();
if (!$this->save($warninglist)) {
throw new Exception("Could not save warninglist because of validation errors: " . json_encode($this->validationErrors));
throw new Exception('Could not save warninglist because of validation errors: ' . json_encode($this->validationErrors));
}
$db = $this->getDataSource();
@ -418,7 +474,7 @@ class WarninglistsTable extends AppTable
}
$values = [];
foreach ($list['matching_attributes'] as $type) {
$values[] = array('type' => $type, 'warninglist_id' => $warninglistId);
$values[] = ['type' => $type, 'warninglist_id' => $warninglistId];
}
$this->WarninglistType->saveMany($values);
@ -428,9 +484,10 @@ class WarninglistsTable extends AppTable
/**
* Regenerate the warninglist caches, but if an ID is passed along, only regen the entries for the given ID.
* This allows us to enable/disable a single warninglist without regenerating all caches.
* @param int|null $id
*
* @param int|null $id the ID of the warninglist to regenerate
* @return bool
* @throws RedisException
* @throws \RedisException
*/
public function regenerateWarninglistCaches($id = null)
{
@ -453,26 +510,34 @@ class WarninglistsTable extends AppTable
if ($id && $warninglist['Warninglist']['id'] != $id) {
continue;
}
$entries = $this->WarninglistEntry->find('column', array(
'conditions' => array('warninglist_id' => $warninglist['Warninglist']['id']),
'fields' => array('value')
));
$entries = $this->WarninglistEntry->find(
'column',
[
'conditions' => ['warninglist_id' => $warninglist['Warninglist']['id']],
'fields' => ['value'],
]
);
$this->cacheWarninglistEntries($entries, $warninglist['Warninglist']['id']);
}
return true;
}
/**
* Get enable warninglists and cache them.
* @return array
*
* @return array the warning lists
*/
private function getEnabledAndCacheWarninglist()
{
$warninglists = $this->find('all', [
'contain' => ['WarninglistType'],
'conditions' => ['enabled' => 1],
'fields' => ['id', 'name', 'type', 'category'],
]);
$warninglists = $this->find(
'all',
[
'contain' => ['WarninglistType'],
'conditions' => ['enabled' => 1],
'fields' => ['id', 'name', 'type', 'category'],
]
);
// Convert type to array
foreach ($warninglists as &$warninglist) {
@ -491,6 +556,13 @@ class WarninglistsTable extends AppTable
return $warninglists;
}
/**
* cacheWarninglistEntries
*
* @param mixed $warninglistEntries the entries
* @param mixed $id the id of the warninglist
* @return bool true if the cache was successful
*/
private function cacheWarninglistEntries(array $warninglistEntries, $id)
{
try {
@ -508,12 +580,13 @@ class WarninglistsTable extends AppTable
$redis->sAdd($key, $entry);
}
}
return true;
}
/**
* @return array
* @throws JsonException
* @throws \JsonException
*/
public function getEnabled()
{
@ -533,11 +606,14 @@ class WarninglistsTable extends AppTable
}
$this->enabledCache = $warninglists;
return $warninglists;
}
/**
* @param int $id
* get warninglist entries (from cache if possible) and cache if needed
*
* @param int $id the ID of the warninglist
* @return array
*/
private function getWarninglistEntries($id)
@ -550,18 +626,22 @@ class WarninglistsTable extends AppTable
} catch (Exception $e) {
}
$entries = $this->WarninglistEntry->find('column', array(
'conditions' => array('warninglist_id' => $id),
'fields' => array('WarninglistEntry.value')
));
$entries = $this->WarninglistEntry->find(
'column',
[
'conditions' => ['warninglist_id' => $id],
'fields' => ['WarninglistEntry.value'],
]
);
$this->cacheWarninglistEntries($entries, $id);
return $entries;
}
/**
* For 'hostname', 'string' and 'cidr' warninglist type, values are just in keys to save memory.
*
* @param array $warninglist
* @param array $warninglist the warninglist
* @return array
*/
public function getFilteredEntries(array $warninglist)
@ -579,13 +659,13 @@ class WarninglistsTable extends AppTable
$output[$v] = true;
}
$values = $output;
} else if ($warninglist['Warninglist']['type'] === 'string') {
} elseif ($warninglist['Warninglist']['type'] === 'string') {
$output = [];
foreach ($values as $v) {
$output[$v] = true;
}
$values = $output;
} else if ($warninglist['Warninglist']['type'] === 'cidr') {
} elseif ($warninglist['Warninglist']['type'] === 'cidr') {
$values = new CidrTool($values);
}
@ -595,7 +675,7 @@ class WarninglistsTable extends AppTable
}
/**
* @param array $object
* @param array $object the object to check
* @param array|null $warninglists If null, all enabled warninglists will be used
* @return array
*/
@ -610,25 +690,26 @@ class WarninglistsTable extends AppTable
if (in_array('ALL', $list['types'], true) || in_array($object['type'], $list['types'], true)) {
$result = $this->checkValue($this->getFilteredEntries($list), $object['value'], $object['type'], $list['Warninglist']['type']);
if ($result !== false) {
$object['warnings'][] = array(
$object['warnings'][] = [
'match' => $result[0],
'value' => $result[1],
'warninglist_id' => $list['Warninglist']['id'],
'warninglist_name' => $list['Warninglist']['name'],
'warninglist_category' => $list['Warninglist']['category'],
);
];
}
}
}
}
return $object;
}
/**
* @param array|CidrTool $listValues
* @param string $value
* @param string $type
* @param string $listType
* @param array|\App\Lib\Tools\CidrTool $listValues the values of the list
* @param string $value the value to check
* @param string $type the type of the attribute
* @param string $listType the type of the list
* @return array|false [Matched value, attribute value that matched]
*/
public function checkValue($listValues, $value, $type, $listType)
@ -636,7 +717,7 @@ class WarninglistsTable extends AppTable
if ($type === 'malware-sample' || strpos($type, '|') !== false) {
$value = explode('|', $value, 2);
} else {
$value = array($value);
$value = [$value];
}
foreach ($value as $v) {
if ($listType === 'cidr') {
@ -656,14 +737,15 @@ class WarninglistsTable extends AppTable
return [$result, $v];
}
}
return false;
}
/**
* Check for exact match.
*
* @param array $listValues
* @param string $value
* @param array $listValues the values of the list
* @param string $value the value to check
* @return false
*/
private function __evalString($listValues, $value)
@ -671,9 +753,17 @@ class WarninglistsTable extends AppTable
if (isset($listValues[$value])) {
return $value;
}
return false;
}
/**
* Check for substring match.
*
* @param array $listValues the values of the list
* @param string $value the value to check
* @return false
*/
private function __evalSubString($listValues, $value)
{
foreach ($listValues as $listValue) {
@ -681,9 +771,17 @@ class WarninglistsTable extends AppTable
return $listValue;
}
}
return false;
}
/**
* Check for hostname match.
*
* @param array $listValues the values of the list
* @param string $value the value to check
* @return false
*/
private function __evalHostname($listValues, $value)
{
// php's parse_url is dumb, so let's use some hacky workarounds
@ -711,9 +809,17 @@ class WarninglistsTable extends AppTable
return $rebuilt;
}
}
return false;
}
/**
* Check for regex match.
*
* @param array $listValues the values of the list
* @param string $value the value to check
* @return false
*/
private function __evalRegex($listValues, $value)
{
foreach ($listValues as $listValue) {
@ -721,6 +827,7 @@ class WarninglistsTable extends AppTable
return $listValue;
}
}
return false;
}
@ -729,10 +836,13 @@ class WarninglistsTable extends AppTable
*/
public function fetchTLDLists()
{
$tldLists = $this->find('column', array(
'conditions' => array('name IN' => Warninglist::TLDS),
'fields' => array('id')
));
$tldLists = $this->find(
'column',
[
'conditions' => ['name IN' => Warninglist::TLDS],
'fields' => ['id'],
]
);
$tlds = [];
foreach ($tldLists as $warninglistId) {
$tlds = array_merge($tlds, $this->getWarninglistEntries($warninglistId));
@ -741,6 +851,7 @@ class WarninglistsTable extends AppTable
if (!in_array('onion', $tlds, true)) {
$tlds[] = 'onion';
}
return $tlds;
}
@ -749,19 +860,23 @@ class WarninglistsTable extends AppTable
*/
public function fetchSecurityVendorDomains()
{
$securityVendorList = $this->find('column', array(
'conditions' => array('name' => 'List of known domains used by automated malware analysis services & security vendors'),
'fields' => array('id')
));
$securityVendorList = $this->find(
'column',
[
'conditions' => ['name' => 'List of known domains used by automated malware analysis services & security vendors'],
'fields' => ['id'],
]
);
$domains = [];
foreach ($securityVendorList as $warninglistId) {
$domains = array_merge($domains, $this->getWarninglistEntries($warninglistId));
}
return $domains;
}
/**
* @param array $attribute
* @param array $attribute the attribute to filter
* @param array|null $warninglists If null, all enabled warninglists will be used
* @return bool
*/
@ -779,33 +894,35 @@ class WarninglistsTable extends AppTable
}
}
}
return true;
}
public function missingTldLists()
{
$missingTldLists = array();
foreach (Warninglist::TLDS as $tldList) {
$temp = $this->find('first', array(
'recursive' => -1,
'conditions' => array('Warninglist.name' => $tldList),
'fields' => array('Warninglist.id')
));
if (empty($temp)) {
$missingTldLists[] = $tldList;
}
}
return $missingTldLists;
}
// public function missingTldLists()
// {
// $missingTldLists = [];
// foreach (Warninglist::TLDS as $tldList) {
// $temp = $this->find('first', [
// 'recursive' => -1,
// 'conditions' => ['Warninglist.name' => $tldList],
// 'fields' => ['Warninglist.id'],
// ]);
// if (empty($temp)) {
// $missingTldLists[] = $tldList;
// }
// }
// return $missingTldLists;
// }
/**
* @param array|null $data
* @param bool $validate
* @param array $fieldList
* @param array|null $data the data to save
* @param bool $validate whether to validate the data
* @param array $fieldList the fields to save
* @return array|bool|mixed|null
* @throws Exception
* @throws \Exception
*/
public function save($data = null, $validate = true, $fieldList = array())
public function save($data = null, $validate = true, $fieldList = [])
{
$db = $this->getDataSource();
$transactionBegun = $db->begin();
@ -825,7 +942,7 @@ class WarninglistsTable extends AppTable
$this->WarninglistEntry->deleteAll(['warninglist_id' => $id], false);
$entriesToInsert = [];
foreach ($data['WarninglistEntry'] as $entry) {
$entriesToInsert[] = [$entry['value'], isset($entry['comment']) ? $entry['comment'] : null, $id];
$entriesToInsert[] = [$entry['value'], $entry['comment'] ?? null, $id];
}
$db->insertMulti(
$this->WarninglistEntry->table,
@ -864,7 +981,10 @@ class WarninglistsTable extends AppTable
}
/**
* @param bool $created
* post full save actions
*
* @param bool $created True for a new creation, false for an edit
* @param array $data the data that was saved
* @return void
*/
private function afterFullSave($created, array $data)
@ -874,17 +994,20 @@ class WarninglistsTable extends AppTable
}
if ($this->pubToZmq('warninglist')) {
$warninglist = $this->find('first', [
'conditions' => ['id' => $data['Warninglist']['id']],
'contains' => ['WarninglistEntry', 'WarninglistType'],
]);
$warninglist = $this->find(
'first',
[
'conditions' => ['id' => $data['Warninglist']['id']],
'contains' => ['WarninglistEntry', 'WarninglistType'],
]
);
$pubSubTool = $this->getPubSubTool();
$pubSubTool->warninglist_save($warninglist, $created ? 'add' : 'edit');
}
}
/**
* @param string $input
* @param string $input the input to parse
* @return array
*/
public function parseFreetext($input)
@ -896,15 +1019,21 @@ class WarninglistsTable extends AppTable
$entries = [];
foreach (explode("\n", trim($input)) as $entry) {
$valueAndComment = explode("#", $entry, 2);
$valueAndComment = explode('#', $entry, 2);
$entries[] = [
'value' => trim($valueAndComment[0]),
'comment' => count($valueAndComment) === 2 ? trim($valueAndComment[1]) : null,
];
}
return $entries;
}
/**
* get the categories
*
* @return array
*/
public function categories()
{
return [

View File

@ -0,0 +1,175 @@
<?php
$fields = [
[
'name' => __('ID'),
'sort' => 'id',
'class' => 'short',
'data_path' => 'id',
// 'element' => 'links',
// 'url' => $baseurl . '/warninglists/view/%s',
],
[
'name' => __('Name'),
'sort' => 'name',
'data_path' => 'name',
],
[
'name' => __('Version'),
'sort' => 'version',
'class' => 'short',
'data_path' => 'version',
],
[
'name' => __('Description'),
'data_path' => 'description',
],
[
'name' => __('Category'),
'sort' => 'category',
'class' => 'short',
'element' => 'custom',
'function' => function (array|\App\Model\Entity\Warninglist $row) use ($possibleCategories) {
return $possibleCategories[$row['category']];
},
],
[
'name' => __('Type'),
'sort' => 'type',
'class' => 'short',
'data_path' => 'type',
],
[
'name' => __('Entries'),
'sort' => 'warninglist_entry_count',
'class' => 'short',
'data_path' => 'warninglist_entry_count',
],
[
'name' => __('Default'),
'sort' => 'default',
'class' => 'short',
'element' => 'boolean',
'data_path' => 'default',
],
[
'name' => __('Enabled'),
'sort' => 'enabled',
'class' => 'short',
'element' => 'toggle',
'data_path' => 'enabled',
'url' => '/warninglists/toggleEnable',
'url_params_vars' => [['datapath' => ['id']]],
'requirement' => $loggedUser['Role']['perm_site_admin'],
],
];
// echo '<div class="index">';
// if ($isSiteAdmin) {
// echo '<div id="hiddenFormDiv">';
// echo $this->Form->create('Warninglist', ['url' => $baseurl . '/warninglists/toggleEnable']);
// echo $this->Form->input('data', ['label' => false, 'style' => 'display:none;']);
// echo $this->Form->end();
// echo '</div>';
// }
echo $this->element(
'/genericElements/IndexTable/index_table',
[
'data' => [
'data' => $data,
'top_bar' => [
'children' => [
// FIXME chri filtering
// [
// 'type' => 'simple',
// 'children' => [
// [
// 'url' => $baseurl . '/warninglists/index',
// 'text' => __('All'),
// 'active' => !isset($passedArgsArray['enabled']),
// ],
// [
// 'url' => $baseurl . '/warninglists/index/enabled:1',
// 'text' => __('Enabled'),
// 'active' => isset($passedArgsArray['enabled']) && $passedArgsArray['enabled'] === '1',
// ],
// [
// 'url' => $baseurl . '/warninglists/index/enabled:0',
// 'text' => __('Disabled'),
// 'active' => isset($passedArgsArray['enabled']) && $passedArgsArray['enabled'] === '0',
// ],
// ],
// ],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value',
'allowFilering' => true,
],
],
],
'title' => __('Warninglists'),
'primary_id_path' => 'id',
'fields' => $fields,
'actions' => [
[
'url' => '/warninglists/view',
'url_params_data_paths' => ['id'],
'icon' => 'eye',
],
// [
// 'title' => __('Enable'),
// 'icon' => 'play',
// 'onclick' => sprintf('toggleSetting(%s, \'%s\', \'%s\')', 'event', 'warninglist_enable', '[onclick_params_data_path]'),
// 'onclick_params_data_path' => 'id',
// 'complex_requirement' => [
// 'function' => function ($row, $options) use ($loggedUser) {
// return $loggedUser['Role']['perm_site_admin'] && !$options['datapath']['enabled'];
// },
// 'options' => [
// 'datapath' => [
// 'orgc' => 'Event.orgc_id',
// 'enabled' => 'enabled',
// ],
// ],
// ],
// ],
// [
// 'title' => __('Disable'),
// 'icon' => 'stop',
// 'onclick' => sprintf('toggleSetting(%s, \'%s\', \'%s\')', 'event', 'warninglist_enable', '[onclick_params_data_path]'),
// 'onclick_params_data_path' => 'id',
// 'complex_requirement' => [
// 'function' => function ($row, $options) use ($loggedUser) {
// return $loggedUser['Role']['perm_site_admin'] && $options['datapath']['enabled'];
// },
// 'options' => [
// 'datapath' => [
// 'enabled' => 'enabled',
// ],
// ],
// ],
// ],
[
'open_modal' => '/warninglists/edit/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'edit',
'complex_requirement' => [
'function' => function ($row) use ($loggedUser) {
return $row['default'] == 0 && ($loggedUser['Role']['perm_warninglist'] || $loggedUser['Role']['perm_site_admin']);
},
],
],
[
'open_modal' => '/warninglists/delete/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'trash',
'requirement' => $loggedUser['Role']['perm_site_admin'],
],
],
],
]
);
// echo '</div>';
// echo $this->element('/genericElements/SideMenu/side_menu', ['menuList' => 'warninglist', 'menuItem' => 'index']);

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class WarninglistEntriesFixture extends TestFixture
{
public const WARNINGLIST_ENTRY_1_ID = 1;
public const WARNINGLIST_ENTRY_2_ID = 2;
public const WARNINGLIST_ENTRY_3_ID = 3;
public const WARNINGLIST_ENTRY_4_ID = 4;
public const WARNINGLIST_ENTRY_5_ID = 5;
public const WARNINGLIST_ENTRY_6_ID = 6;
public const WARNINGLIST_ENTRY_7_ID = 7;
public const WARNINGLIST_ENTRY_8_ID = 8;
public const WARNINGLIST_ENTRY_9_ID = 9;
public const WARNINGLIST_ENTRY_10_ID = 10;
public const WARNINGLIST_ENTRY_11_ID = 11;
public const WARNINGLIST_ENTRY_12_ID = 12;
public const WARNINGLIST_ENTRY_13_ID = 13;
public const WARNINGLIST_ENTRY_14_ID = 14;
public function init(): void
{
$this->records = [
// enabled
[
'id' => self::WARNINGLIST_ENTRY_1_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'value' => '1.1.1.0/24',
'comment' => '',
],
[
'id' => self::WARNINGLIST_ENTRY_2_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'value' => '2.2.0.0/16',
'comment' => '',
],
[
'id' => self::WARNINGLIST_ENTRY_3_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'value' => '1.1.1.0/32',
'comment' => '',
],
// disabled
[
'id' => self::WARNINGLIST_ENTRY_4_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_2_ID,
'value' => '4.4.4.0/24',
'comment' => '',
],
// hostname
[
'id' => self::WARNINGLIST_ENTRY_5_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_HOSTNAME_ID,
'value' => 'vm.misp-project.org',
'comment' => '',
],
// substr
[
'id' => self::WARNINGLIST_ENTRY_6_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_SUBSTR_ID,
'value' => 'misp',
'comment' => '',
],
// str
[
'id' => self::WARNINGLIST_ENTRY_7_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_STR_ID,
'value' => 'vm.misp-project.org',
'comment' => '',
],
// regex
[
'id' => self::WARNINGLIST_ENTRY_8_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_REGEX_ID,
'value' => '/misp-[a-z]\.org$/',
'comment' => '',
],
];
// TODO import warninglists from files to have larger datasets
parent::init();
}
}

View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class WarninglistTypesFixture extends TestFixture
{
public const WARNINGLIST_TYPE_1_ID = 1;
public const WARNINGLIST_TYPE_2_ID = 2;
public const WARNINGLIST_TYPE_3_ID = 3;
public const WARNINGLIST_TYPE_4_ID = 4;
public const WARNINGLIST_TYPE_5_ID = 5;
public const WARNINGLIST_TYPE_6_ID = 6;
public const WARNINGLIST_TYPE_7_ID = 7;
public const WARNINGLIST_TYPE_8_ID = 8;
public const WARNINGLIST_TYPE_9_ID = 9;
public const WARNINGLIST_TYPE_10_ID = 10;
public function init(): void
{
$this->records = [
// CIDR
[
'id' => self::WARNINGLIST_TYPE_1_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'type' => 'ip-src',
],
[
'id' => self::WARNINGLIST_TYPE_2_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'type' => 'ip-dst',
],
[
'id' => self::WARNINGLIST_TYPE_3_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_CIDR_1_ID,
'type' => 'domain|ip',
],
// HOSTNAME
[
'id' => self::WARNINGLIST_TYPE_4_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_HOSTNAME_ID,
'type' => 'url',
],
[
'id' => self::WARNINGLIST_TYPE_5_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_HOSTNAME_ID,
'type' => 'domain|ip',
],
[
'id' => self::WARNINGLIST_TYPE_6_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_HOSTNAME_ID,
'type' => 'hostname',
],
[
'id' => self::WARNINGLIST_TYPE_7_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_HOSTNAME_ID,
'type' => 'domain',
],
// SUBSTR
[
'id' => self::WARNINGLIST_TYPE_8_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_SUBSTR_ID,
'type' => 'domain',
],
// STR
[
'id' => self::WARNINGLIST_TYPE_9_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_STR_ID,
'type' => 'domain',
],
// REGEX
[
'id' => self::WARNINGLIST_TYPE_10_ID,
'warninglist_id' => WarninglistsFixture::WARNINGLIST_REGEX_ID,
'type' => 'domain',
],
];
parent::init();
}
}

View File

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use App\Model\Entity\Warninglist;
use Cake\TestSuite\Fixture\TestFixture;
class WarninglistsFixture extends TestFixture
{
public $connection = 'test';
public const WARNINGLIST_CIDR_1_ID = 1;
public const WARNINGLIST_CIDR_1_NAME = 'disabled cidr warninglist';
public const WARNINGLIST_CIDR_2_ID = 2;
public const WARNINGLIST_CIDR_2_NAME = 'enabled cidr warninglist';
public const WARNINGLIST_HOSTNAME_ID = 3;
public const WARNINGLIST_HOSTNAME_NAME = 'enabled hostname warninglist';
public const WARNINGLIST_SUBSTR_ID = 4;
public const WARNINGLIST_SUBSTR_NAME = 'enabled substr warninglist';
public const WARNINGLIST_STR_ID = 5;
public const WARNINGLIST_STR_NAME = 'enabled string warninglist';
public const WARNINGLIST_REGEX_ID = 6;
public const WARNINGLIST_REGEX_NAME = 'enabled regex warninglist';
public function init(): void
{
$this->records = [
[
'id' => self::WARNINGLIST_CIDR_1_ID,
'name' => self::WARNINGLIST_CIDR_1_NAME,
'type' => 'cidr',
'description' => 'test disabled cidr warninglist description',
'version' => 1,
'enabled' => false,
'default' => false,
'category' => Warninglist::CATEGORY_FALSE_POSITIVE,
],
[
'id' => self::WARNINGLIST_CIDR_2_ID,
'name' => self::WARNINGLIST_CIDR_2_NAME,
'type' => 'cidr',
'description' => 'test enabled cidr warninglist description',
'version' => 1,
'enabled' => true,
'default' => false,
'category' => Warninglist::CATEGORY_KNOWN,
],
[
'id' => self::WARNINGLIST_HOSTNAME_ID,
'name' => self::WARNINGLIST_HOSTNAME_NAME,
'type' => 'hostname',
'description' => 'test enabled hostname warninglist description',
'version' => 1,
'enabled' => true,
'default' => false,
'category' => Warninglist::CATEGORY_FALSE_POSITIVE,
],
[
'id' => self::WARNINGLIST_SUBSTR_ID,
'name' => self::WARNINGLIST_SUBSTR_NAME,
'type' => 'hostname',
'description' => 'test enabled substring warninglist description',
'version' => 1,
'enabled' => true,
'default' => false,
'category' => Warninglist::CATEGORY_FALSE_POSITIVE,
],
[
'id' => self::WARNINGLIST_STR_ID,
'name' => self::WARNINGLIST_STR_NAME,
'type' => 'hostname',
'description' => 'test enabled string warninglist description',
'version' => 1,
'enabled' => true,
'default' => false,
'category' => Warninglist::CATEGORY_FALSE_POSITIVE,
],
[
'id' => self::WARNINGLIST_REGEX_ID,
'name' => self::WARNINGLIST_REGEX_NAME,
'type' => 'hostname',
'description' => 'test enabled regex warninglist description',
'version' => 1,
'enabled' => true,
'default' => false,
'category' => Warninglist::CATEGORY_FALSE_POSITIVE,
],
];
parent::init();
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Warninglists;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\WarninglistsFixture;
use App\Test\Helper\ApiTestTrait;
use Cake\TestSuite\TestCase;
class IndexWarninglistsApiTest extends TestCase
{
use ApiTestTrait;
protected const ENDPOINT = '/warninglists/index';
protected $fixtures = [
'app.Organisations',
'app.Roles',
'app.Users',
'app.AuthKeys',
'app.Warninglists',
'app.WarninglistEntries',
'app.WarninglistTypes',
];
public function testIndexWarninglistsAsAdmin(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->get(self::ENDPOINT);
$this->assertResponseOk();
$this->assertResponseContains(sprintf('"name": "%s"', WarninglistsFixture::WARNINGLIST_CIDR_1_NAME));
}
public function testIndexWarninglistsAsUser(): void
{
$this->skipOpenApiValidations();
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$this->get(self::ENDPOINT);
$this->assertResponseOk();
$this->assertResponseContains(sprintf('"name": "%s"', WarninglistsFixture::WARNINGLIST_CIDR_1_NAME));
}
}