2016-04-21 22:58:49 +02:00
|
|
|
<?php
|
|
|
|
App::uses('AppModel', 'Model');
|
2021-01-28 19:19:34 +01:00
|
|
|
App::uses('CidrTool', 'Tools');
|
2022-02-26 17:45:02 +01:00
|
|
|
App::uses('FileAccessTool', 'Tools');
|
2020-09-03 15:24:03 +02:00
|
|
|
|
2020-09-07 10:06:33 +02:00
|
|
|
/**
|
|
|
|
* @property WarninglistType $WarninglistType
|
2020-09-07 12:03:04 +02:00
|
|
|
* @property WarninglistEntry $WarninglistEntry
|
2020-09-07 10:06:33 +02:00
|
|
|
*/
|
2018-07-19 11:48:22 +02:00
|
|
|
class Warninglist extends AppModel
|
|
|
|
{
|
2021-06-01 17:49:45 +02:00
|
|
|
const CATEGORY_FALSE_POSITIVE = 'false_positive',
|
|
|
|
CATEGORY_KNOWN = 'known';
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public $useTable = 'warninglists';
|
|
|
|
|
|
|
|
public $recursive = -1;
|
|
|
|
|
|
|
|
public $actsAs = array(
|
2021-01-22 13:01:23 +01:00
|
|
|
'AuditLog',
|
2021-06-12 17:59:17 +02:00
|
|
|
'Containable',
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
public $validate = array(
|
|
|
|
'name' => array(
|
2021-06-01 17:49:45 +02:00
|
|
|
'notEmpty' => [
|
|
|
|
'rule' => 'valueNotEmpty',
|
|
|
|
],
|
|
|
|
'unique' => [
|
|
|
|
'rule' => 'isUnique',
|
|
|
|
'message' => 'Warninglist with same name already exists.'
|
|
|
|
],
|
2018-07-19 11:48:22 +02:00
|
|
|
),
|
|
|
|
'description' => array(
|
|
|
|
'rule' => array('valueNotEmpty'),
|
|
|
|
),
|
|
|
|
'version' => array(
|
|
|
|
'rule' => array('numeric'),
|
|
|
|
),
|
2021-06-01 17:49:45 +02:00
|
|
|
'type' => [
|
|
|
|
'rule' => ['inList', ['cidr', 'hostname', 'string', 'substring', 'regex']],
|
|
|
|
],
|
|
|
|
'category' => [
|
|
|
|
'rule' => ['inList', ['false_positive', 'known']],
|
|
|
|
],
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
public $hasMany = array(
|
2020-09-04 17:11:45 +02:00
|
|
|
'WarninglistEntry' => array(
|
|
|
|
'dependent' => true
|
|
|
|
),
|
|
|
|
'WarninglistType' => array(
|
|
|
|
'dependent' => true
|
|
|
|
)
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
|
2021-10-04 11:01:58 +02:00
|
|
|
const TLDS = array(
|
2018-07-19 11:48:22 +02:00
|
|
|
'TLDs as known by IANA'
|
|
|
|
);
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/** @var array */
|
|
|
|
private $entriesCache = [];
|
|
|
|
|
|
|
|
/** @var array|null */
|
|
|
|
private $enabledCache = null;
|
2018-07-19 11:48:22 +02:00
|
|
|
|
2020-09-07 10:06:33 +02:00
|
|
|
private $showForAll;
|
|
|
|
|
|
|
|
public function __construct($id = false, $table = null, $ds = null)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-09-07 10:06:33 +02:00
|
|
|
parent::__construct($id, $table, $ds);
|
|
|
|
$this->showForAll = Configure::read('MISP.warning_for_all');
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2021-06-01 17:49:45 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/**
|
2020-09-04 17:13:39 +02:00
|
|
|
* Attach warninglist matches to attributes or proposals with IDS mark.
|
2020-09-03 15:24:03 +02:00
|
|
|
*
|
|
|
|
* @param array $attributes
|
|
|
|
* @return array Warninglist ID => name
|
|
|
|
*/
|
|
|
|
public function attachWarninglistToAttributes(array &$attributes)
|
|
|
|
{
|
|
|
|
if (empty($attributes)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2020-09-07 10:20:30 +02:00
|
|
|
$enabledWarninglists = $this->getEnabled();
|
|
|
|
if (empty($enabledWarninglists)) {
|
|
|
|
return []; // no warninglist is enabled
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
try {
|
|
|
|
$redis = $this->setupRedisWithException();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
// fallback to default implementation when redis is not available
|
|
|
|
$eventWarnings = [];
|
|
|
|
foreach ($attributes as $pos => $attribute) {
|
2020-09-07 10:20:30 +02:00
|
|
|
$attributes[$pos] = $this->checkForWarning($attribute, $enabledWarninglists);
|
|
|
|
if (isset($attributes[$pos]['warnings'])) {
|
2020-09-03 15:24:03 +02:00
|
|
|
foreach ($attribute['warnings'] as $match) {
|
|
|
|
$eventWarnings[$match['warninglist_id']] = $match['warninglist_name'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-17 18:13:57 +02:00
|
|
|
if (!empty($eventWarnings)) {
|
|
|
|
$this->assignComments($attributes);
|
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
return $eventWarnings;
|
|
|
|
}
|
|
|
|
|
2021-06-01 17:49:45 +02:00
|
|
|
$warninglists = [];
|
2020-09-03 15:24:03 +02:00
|
|
|
$enabledTypes = [];
|
2020-09-07 10:20:30 +02:00
|
|
|
foreach ($enabledWarninglists as $warninglist) {
|
2021-06-01 17:49:45 +02:00
|
|
|
$warninglists[$warninglist['Warninglist']['id']] = $warninglist['Warninglist'];
|
2020-09-03 15:24:03 +02:00
|
|
|
foreach ($warninglist['types'] as $type) {
|
|
|
|
$enabledTypes[$type] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$redisResultToAttributePos = [];
|
2020-10-02 19:58:18 +02:00
|
|
|
$keysToGet = [];
|
2020-09-03 15:24:03 +02:00
|
|
|
foreach ($attributes as $pos => $attribute) {
|
2020-09-07 10:06:33 +02:00
|
|
|
if (($attribute['to_ids'] || $this->showForAll) && (isset($enabledTypes[$attribute['type']]) || isset($enabledTypes['ALL']))) {
|
2020-09-03 15:24:03 +02:00
|
|
|
$redisResultToAttributePos[] = $pos;
|
2020-10-03 11:07:33 +02:00
|
|
|
// Use hash as binary string to save memory and CPU time
|
|
|
|
// Hash contains just attribute type and value, so can be reused in another event attributes
|
2020-10-02 19:58:18 +02:00
|
|
|
$keysToGet[] = 'misp:wlc:' . md5($attribute['type'] . ':' . $attribute['value'], true);
|
2020-09-03 15:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
2020-10-02 19:58:18 +02:00
|
|
|
|
|
|
|
if (empty($keysToGet)) {
|
|
|
|
return []; // no attribute suitable for warninglist check
|
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
|
|
|
|
$eventWarnings = [];
|
|
|
|
$saveToCache = [];
|
2020-10-02 19:58:18 +02:00
|
|
|
foreach ($redis->mget($keysToGet) as $pos => $result) {
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($result === false) { // not in cache
|
|
|
|
$attribute = $attributes[$redisResultToAttributePos[$pos]];
|
2020-09-07 19:40:06 +02:00
|
|
|
$attribute = $this->checkForWarning($attribute, $enabledWarninglists);
|
2020-09-03 15:24:03 +02:00
|
|
|
|
|
|
|
$store = [];
|
|
|
|
if (isset($attribute['warnings'])) {
|
|
|
|
foreach ($attribute['warnings'] as $match) {
|
|
|
|
$warninglistId = $match['warninglist_id'];
|
|
|
|
$attributes[$redisResultToAttributePos[$pos]]['warnings'][] = [
|
|
|
|
'value' => $match['value'],
|
|
|
|
'match' => $match['match'],
|
|
|
|
'warninglist_id' => $warninglistId,
|
2021-06-01 17:49:45 +02:00
|
|
|
'warninglist_name' => $warninglists[$warninglistId]['name'],
|
|
|
|
'warninglist_category' => $warninglists[$warninglistId]['category'],
|
2020-09-03 15:24:03 +02:00
|
|
|
];
|
2021-06-01 17:49:45 +02:00
|
|
|
$eventWarnings[$warninglistId] = $warninglists[$warninglistId]['name'];
|
2020-09-03 15:24:03 +02:00
|
|
|
|
2020-09-07 19:40:06 +02:00
|
|
|
$store[$warninglistId] = [$match['value'], $match['match']];
|
2020-09-03 15:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-02 19:58:18 +02:00
|
|
|
$attributeKey = $keysToGet[$pos];
|
|
|
|
$saveToCache[$attributeKey] = empty($store) ? '' : json_encode($store);
|
2020-09-03 15:24:03 +02:00
|
|
|
|
2020-10-03 11:07:33 +02:00
|
|
|
} elseif (!empty($result)) { // skip empty string that means no warning list match
|
2020-09-03 15:24:03 +02:00
|
|
|
$matchedWarningList = json_decode($result, true);
|
|
|
|
foreach ($matchedWarningList as $warninglistId => $matched) {
|
|
|
|
$attributes[$redisResultToAttributePos[$pos]]['warnings'][] = [
|
|
|
|
'value' => $matched[0],
|
|
|
|
'match' => $matched[1],
|
|
|
|
'warninglist_id' => $warninglistId,
|
2021-06-01 17:49:45 +02:00
|
|
|
'warninglist_name' => $warninglists[$warninglistId]['name'],
|
|
|
|
'warninglist_category' => $warninglists[$warninglistId]['category'],
|
2020-09-03 15:24:03 +02:00
|
|
|
];
|
2021-06-01 17:49:45 +02:00
|
|
|
$eventWarnings[$warninglistId] = $warninglists[$warninglistId]['name'];
|
2020-09-03 15:24:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!empty($saveToCache)) {
|
2021-08-17 18:13:57 +02:00
|
|
|
$pipe = $redis->pipeline();
|
2020-10-02 19:58:18 +02:00
|
|
|
foreach ($saveToCache as $attributeKey => $json) {
|
2021-02-02 12:34:55 +01:00
|
|
|
$redis->setex($attributeKey, 8 * 3600, $json); // cache for eight hour
|
2020-09-03 15:24:03 +02:00
|
|
|
}
|
|
|
|
$pipe->exec();
|
|
|
|
}
|
|
|
|
|
2021-08-17 18:13:57 +02:00
|
|
|
if (!empty($eventWarnings)) {
|
|
|
|
$this->assignComments($attributes);
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
return $eventWarnings;
|
|
|
|
}
|
|
|
|
|
2021-08-17 18:13:57 +02:00
|
|
|
/**
|
|
|
|
* Assign comments to warninglist hits.
|
|
|
|
* @param array $attributes
|
|
|
|
*/
|
|
|
|
private function assignComments(array &$attributes)
|
|
|
|
{
|
|
|
|
$toFetch = [];
|
|
|
|
foreach ($attributes as $attribute) {
|
|
|
|
if (isset($attribute['warnings'])) {
|
|
|
|
foreach ($attribute['warnings'] as $warning) {
|
|
|
|
$toFetch[$warning['warninglist_id']][] = $warning['match'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$conditions = [];
|
|
|
|
foreach ($toFetch as $warninglistId => $values) {
|
|
|
|
$conditions[] = ['AND' => [
|
|
|
|
'warninglist_id' => $warninglistId,
|
|
|
|
'value' => array_unique($values),
|
|
|
|
]];
|
|
|
|
}
|
|
|
|
|
|
|
|
$entries = $this->WarninglistEntry->find('all', [
|
|
|
|
'conditions' => [
|
|
|
|
'OR' => $conditions,
|
|
|
|
'comment !=' => '',
|
|
|
|
],
|
|
|
|
'fields' => ['value', 'warninglist_id', 'comment'],
|
|
|
|
]);
|
|
|
|
if (empty($entries)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$comments = [];
|
|
|
|
foreach ($entries as $entry) {
|
|
|
|
$entry = $entry['WarninglistEntry'];
|
|
|
|
$comments[$entry['warninglist_id']][$entry['value']] = $entry['comment'];
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($attributes as &$attribute) {
|
|
|
|
if (isset($attribute['warnings'])) {
|
|
|
|
foreach ($attribute['warnings'] as &$warning) {
|
|
|
|
if (isset($comments[$warning['warninglist_id']][$warning['match']])) {
|
|
|
|
$warning['comment'] = $comments[$warning['warninglist_id']][$warning['match']];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
public function update()
|
|
|
|
{
|
2021-06-01 17:49:45 +02:00
|
|
|
// Existing default warninglists
|
2021-02-10 21:31:39 +01:00
|
|
|
$existingWarninglist = $this->find('all', [
|
|
|
|
'fields' => ['id', 'name', 'version', 'enabled'],
|
|
|
|
'recursive' => -1,
|
2021-06-12 17:59:17 +02:00
|
|
|
'conditions' => ['default' => 1],
|
2021-02-10 21:31:39 +01:00
|
|
|
]);
|
|
|
|
$existingWarninglist = array_column(array_column($existingWarninglist, 'Warninglist'), null, 'name');
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
$directories = glob(APP . 'files' . DS . 'warninglists' . DS . 'lists' . DS . '*', GLOB_ONLYDIR);
|
2020-05-15 18:26:06 +02:00
|
|
|
$updated = array('success' => [], 'fails' => []);
|
2018-07-19 11:48:22 +02:00
|
|
|
foreach ($directories as $dir) {
|
2022-02-26 17:45:02 +01:00
|
|
|
$list = FileAccessTool::readJsonFromFile($dir . DS . 'list.json');
|
2018-07-19 11:48:22 +02:00
|
|
|
if (!isset($list['version'])) {
|
|
|
|
$list['version'] = 1;
|
|
|
|
}
|
|
|
|
if (!isset($list['type'])) {
|
|
|
|
$list['type'] = 'string';
|
|
|
|
} elseif (is_array($list['type'])) {
|
|
|
|
$list['type'] = $list['type'][0];
|
|
|
|
}
|
2021-02-10 21:31:39 +01:00
|
|
|
if (!isset($existingWarninglist[$list['name']]) || $list['version'] > $existingWarninglist[$list['name']]['version']) {
|
|
|
|
$current = isset($existingWarninglist[$list['name']]) ? $existingWarninglist[$list['name']] : [];
|
2018-07-19 11:48:22 +02:00
|
|
|
$result = $this->__updateList($list, $current);
|
|
|
|
if (is_numeric($result)) {
|
|
|
|
$updated['success'][$result] = array('name' => $list['name'], 'new' => $list['version']);
|
|
|
|
if (!empty($current)) {
|
2021-02-10 21:31:39 +01:00
|
|
|
$updated['success'][$result]['old'] = $current['version'];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$updated['fails'][] = array('name' => $list['name'], 'fail' => json_encode($result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->regenerateWarninglistCaches();
|
|
|
|
return $updated;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function quickDelete($id)
|
|
|
|
{
|
|
|
|
$result = $this->WarninglistEntry->deleteAll(
|
2021-06-30 10:58:56 +02:00
|
|
|
array('WarninglistEntry.warninglist_id' => $id),
|
|
|
|
false
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
if ($result) {
|
|
|
|
$result = $this->WarninglistType->deleteAll(
|
2021-06-30 10:58:56 +02:00
|
|
|
array('WarninglistType.warninglist_id' => $id),
|
|
|
|
false
|
2018-07-19 11:48:22 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
if ($result) {
|
|
|
|
$result = $this->delete($id, false);
|
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2021-06-12 17:59:17 +02:00
|
|
|
/**
|
|
|
|
* Import single warninglist
|
|
|
|
* @param array $list
|
|
|
|
* @return array|int|string
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public function import(array $list)
|
|
|
|
{
|
|
|
|
$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.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$id = $this->__updateList($list, $existingWarninglist ? $existingWarninglist['Warninglist']: [], false);
|
2021-06-30 14:52:41 +02:00
|
|
|
if (is_int($id)) {
|
|
|
|
$this->regenerateWarninglistCaches($id);
|
|
|
|
}
|
2021-06-12 17:59:17 +02:00
|
|
|
return $id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $list
|
|
|
|
* @param array $current
|
|
|
|
* @param bool $default
|
|
|
|
* @return array|int|string
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
private function __updateList(array $list, array $current, $default = true)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
|
|
|
$list['enabled'] = 0;
|
|
|
|
$warninglist = array();
|
|
|
|
if (!empty($current)) {
|
2021-02-10 21:31:39 +01:00
|
|
|
if ($current['enabled']) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$list['enabled'] = 1;
|
|
|
|
}
|
2021-06-12 17:59:17 +02:00
|
|
|
$warninglist['Warninglist']['id'] = $current['id']; // keep list ID
|
2021-02-10 21:31:39 +01:00
|
|
|
$this->quickDelete($current['id']);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
$fieldsToSave = array('name', 'version', 'description', 'type', 'enabled');
|
|
|
|
foreach ($fieldsToSave as $fieldToSave) {
|
|
|
|
$warninglist['Warninglist'][$fieldToSave] = $list[$fieldToSave];
|
|
|
|
}
|
2021-06-12 17:59:17 +02:00
|
|
|
if (!$default) {
|
|
|
|
$warninglist['Warninglist']['default'] = 0;
|
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->create();
|
2021-06-12 17:59:17 +02:00
|
|
|
if (!$this->save($warninglist)) {
|
|
|
|
return $this->validationErrors;
|
|
|
|
}
|
|
|
|
|
|
|
|
$db = $this->getDataSource();
|
|
|
|
$warninglistId = (int)$this->id;
|
2021-06-30 12:04:32 +02:00
|
|
|
$result = true;
|
2021-06-12 17:59:17 +02:00
|
|
|
|
|
|
|
$keys = array_keys($list['list']);
|
|
|
|
if ($keys === array_keys($keys)) {
|
2021-06-30 12:04:32 +02:00
|
|
|
foreach (array_chunk($list['list'], 500) as $chunk) {
|
|
|
|
$valuesToInsert = [];
|
|
|
|
foreach ($chunk as $value) {
|
|
|
|
if (!empty($value)) {
|
|
|
|
$valuesToInsert[] = ['value' => $value, 'warninglist_id' => $warninglistId];
|
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-06-30 12:04:32 +02:00
|
|
|
$result = $db->insertMulti('warninglist_entries', ['value', 'warninglist_id'], $valuesToInsert);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-06-12 17:59:17 +02:00
|
|
|
} else { // import warninglist with comments
|
2021-06-30 12:04:32 +02:00
|
|
|
foreach (array_chunk($list['list'], 500, true) as $chunk) {
|
|
|
|
$valuesToInsert = [];
|
|
|
|
foreach ($chunk as $value => $comment) {
|
|
|
|
if (!empty($value)) {
|
|
|
|
$valuesToInsert[] = ['value' => $value, 'comment' => $comment, 'warninglist_id' => $warninglistId];
|
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-06-30 12:04:32 +02:00
|
|
|
$result = $db->insertMulti('warninglist_entries', ['value', 'comment', 'warninglist_id'], $valuesToInsert);
|
2021-06-12 17:59:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$result) {
|
|
|
|
return 'Could not insert values.';
|
|
|
|
}
|
2021-06-30 14:52:41 +02:00
|
|
|
|
|
|
|
if (empty($list['matching_attributes'])) {
|
|
|
|
$list['matching_attributes'] = ['ALL'];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2021-06-30 14:52:41 +02:00
|
|
|
$values = array();
|
|
|
|
foreach ($list['matching_attributes'] as $type) {
|
|
|
|
$values[] = array('type' => $type, 'warninglist_id' => $warninglistId);
|
|
|
|
}
|
|
|
|
$this->WarninglistType->saveMany($values);
|
|
|
|
|
2021-06-12 17:59:17 +02:00
|
|
|
return $warninglistId;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-09-07 10:04:18 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function regenerateWarninglistCaches($id = null)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
|
|
|
$redis = $this->setupRedis();
|
|
|
|
if ($redis === false) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-09-07 12:03:04 +02:00
|
|
|
|
2020-11-24 17:23:09 +01:00
|
|
|
// Unlink is non blocking way how to delete keys from Redis, but it must be supported by PHP extension and
|
|
|
|
// Redis itself
|
|
|
|
$unlinkSupported = method_exists($redis, 'unlink') && $redis->unlink(null) !== false;
|
|
|
|
if ($unlinkSupported) {
|
2020-10-03 11:07:33 +02:00
|
|
|
$redis->unlink($redis->keys('misp:wlc:*'));
|
|
|
|
} else {
|
|
|
|
$redis->del($redis->keys('misp:wlc:*'));
|
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
|
2020-09-07 12:03:04 +02:00
|
|
|
if ($id === null) {
|
|
|
|
// delete all cached entries when regenerating whole cache
|
|
|
|
$redis->del($redis->keys('misp:warninglist_entries_cache:*'));
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
$warninglists = $this->find('all', array(
|
|
|
|
'contain' => array('WarninglistType'),
|
|
|
|
'conditions' => array('enabled' => 1),
|
2021-06-01 17:49:45 +02:00
|
|
|
'fields' => ['id', 'name', 'type', 'category'],
|
2020-09-03 15:24:03 +02:00
|
|
|
));
|
2018-07-19 11:48:22 +02:00
|
|
|
$this->cacheWarninglists($warninglists);
|
2020-09-07 10:04:18 +02:00
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
foreach ($warninglists as $warninglist) {
|
|
|
|
if ($id && $warninglist['Warninglist']['id'] != $id) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-23 12:50:19 +01:00
|
|
|
$entries = $this->WarninglistEntry->find('column', array(
|
2020-09-03 15:24:03 +02:00
|
|
|
'conditions' => array('warninglist_id' => $warninglist['Warninglist']['id']),
|
|
|
|
'fields' => array('value')
|
2018-07-19 11:48:22 +02:00
|
|
|
));
|
|
|
|
$this->cacheWarninglistEntries($entries, $warninglist['Warninglist']['id']);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-09-07 12:03:04 +02:00
|
|
|
private function cacheWarninglists(array $warninglists)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
|
|
|
$redis = $this->setupRedis();
|
|
|
|
if ($redis !== false) {
|
|
|
|
$redis->del('misp:warninglist_cache');
|
|
|
|
foreach ($warninglists as $warninglist) {
|
|
|
|
$redis->sAdd('misp:warninglist_cache', json_encode($warninglist));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-07 12:03:04 +02:00
|
|
|
private function cacheWarninglistEntries(array $warninglistEntries, $id)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
|
|
|
$redis = $this->setupRedis();
|
|
|
|
if ($redis !== false) {
|
2019-08-16 19:49:35 +02:00
|
|
|
$key = 'misp:warninglist_entries_cache:' . $id;
|
|
|
|
$redis->del($key);
|
2019-09-16 09:28:43 +02:00
|
|
|
if (method_exists($redis, 'saddArray')) {
|
|
|
|
$redis->sAddArray($key, $warninglistEntries);
|
|
|
|
} else {
|
|
|
|
foreach ($warninglistEntries as $entry) {
|
2020-09-07 12:03:04 +02:00
|
|
|
$redis->sAdd($key, $entry);
|
2019-09-16 09:28:43 +02:00
|
|
|
}
|
2019-09-15 11:15:34 +02:00
|
|
|
}
|
2018-07-19 11:48:22 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getEnabled()
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-09-03 15:24:03 +02:00
|
|
|
if (isset($this->enabledCache)) {
|
|
|
|
return $this->enabledCache;
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
$redis = $this->setupRedis();
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($redis !== false && $redis->exists('misp:warninglist_cache')) {
|
|
|
|
$warninglists = $redis->sMembers('misp:warninglist_cache');
|
|
|
|
foreach ($warninglists as $k => $v) {
|
|
|
|
$warninglists[$k] = json_decode($v, true);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
} else {
|
|
|
|
$warninglists = $this->find('all', array(
|
2021-06-01 17:49:45 +02:00
|
|
|
'contain' => ['WarninglistType'],
|
|
|
|
'conditions' => ['enabled' => 1],
|
|
|
|
'fields' => ['id', 'name', 'type', 'category'],
|
2020-09-03 15:24:03 +02:00
|
|
|
));
|
|
|
|
$this->cacheWarninglists($warninglists);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
|
|
|
|
foreach ($warninglists as &$warninglist) {
|
|
|
|
$warninglist['types'] = [];
|
|
|
|
foreach ($warninglist['WarninglistType'] as $wt) {
|
|
|
|
$warninglist['types'][] = $wt['type'];
|
|
|
|
}
|
|
|
|
unset($warninglist['WarninglistType']);
|
|
|
|
}
|
|
|
|
$this->enabledCache = $warninglists;
|
|
|
|
return $warninglists;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/**
|
|
|
|
* @param int $id
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function getWarninglistEntries($id)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
|
|
|
$redis = $this->setupRedis();
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($redis !== false && $redis->exists('misp:warninglist_entries_cache:' . $id)) {
|
|
|
|
return $redis->sMembers('misp:warninglist_entries_cache:' . $id);
|
2018-07-19 11:48:22 +02:00
|
|
|
} else {
|
2020-12-23 12:50:19 +01:00
|
|
|
$entries = $this->WarninglistEntry->find('column', array(
|
2020-09-03 15:24:03 +02:00
|
|
|
'conditions' => array('warninglist_id' => $id),
|
2020-12-23 12:50:19 +01:00
|
|
|
'fields' => array('WarninglistEntry.value')
|
|
|
|
));
|
2020-09-03 15:24:03 +02:00
|
|
|
$this->cacheWarninglistEntries($entries, $id);
|
|
|
|
return $entries;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 13:46:10 +02:00
|
|
|
/**
|
|
|
|
* For 'hostname', 'string' and 'cidr' warninglist type, values are just in keys to save memory.
|
|
|
|
*
|
|
|
|
* @param array $warninglist
|
|
|
|
* @return array
|
|
|
|
*/
|
2020-09-07 12:03:04 +02:00
|
|
|
public function getFilteredEntries(array $warninglist)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-09-07 13:46:10 +02:00
|
|
|
$id = $warninglist['Warninglist']['id'];
|
|
|
|
if (isset($this->entriesCache[$id])) {
|
|
|
|
return $this->entriesCache[$id];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2019-08-16 17:10:30 +02:00
|
|
|
|
2020-09-07 13:46:10 +02:00
|
|
|
$values = $this->getWarninglistEntries($id);
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($warninglist['Warninglist']['type'] === 'hostname') {
|
|
|
|
$output = [];
|
|
|
|
foreach ($values as $v) {
|
2020-09-07 19:28:59 +02:00
|
|
|
$v = strtolower(trim($v, '.'));
|
2020-09-07 13:46:10 +02:00
|
|
|
$output[$v] = true;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
$values = $output;
|
|
|
|
} else if ($warninglist['Warninglist']['type'] === 'string') {
|
2020-09-07 13:46:10 +02:00
|
|
|
$output = [];
|
|
|
|
foreach ($values as $v) {
|
|
|
|
$output[$v] = true;
|
|
|
|
}
|
|
|
|
$values = $output;
|
2020-09-03 15:24:03 +02:00
|
|
|
} else if ($warninglist['Warninglist']['type'] === 'cidr') {
|
2021-01-28 19:19:34 +01:00
|
|
|
$values = new CidrTool($values);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
|
2020-09-07 13:46:10 +02:00
|
|
|
$this->entriesCache[$id] = $values;
|
2020-09-03 15:24:03 +02:00
|
|
|
|
|
|
|
return $values;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
|
2020-09-04 17:11:45 +02:00
|
|
|
/**
|
|
|
|
* @param array $object
|
|
|
|
* @param array|null $warninglists If null, all enabled warninglists will be used
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function checkForWarning(array $object, $warninglists = null)
|
2019-05-09 17:14:25 +02:00
|
|
|
{
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($warninglists === null) {
|
|
|
|
$warninglists = $this->getEnabled();
|
|
|
|
}
|
|
|
|
|
2020-09-07 10:06:33 +02:00
|
|
|
if ($object['to_ids'] || $this->showForAll) {
|
2020-09-03 15:24:03 +02:00
|
|
|
foreach ($warninglists as $list) {
|
2021-01-10 17:44:37 +01:00
|
|
|
if (in_array('ALL', $list['types'], true) || in_array($object['type'], $list['types'], true)) {
|
2021-08-17 19:00:40 +02:00
|
|
|
$result = $this->checkValue($this->getFilteredEntries($list), $object['value'], $object['type'], $list['Warninglist']['type']);
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($result !== false) {
|
|
|
|
$object['warnings'][] = array(
|
|
|
|
'match' => $result[0],
|
|
|
|
'value' => $result[1],
|
|
|
|
'warninglist_id' => $list['Warninglist']['id'],
|
2021-06-01 17:49:45 +02:00
|
|
|
'warninglist_name' => $list['Warninglist']['name'],
|
|
|
|
'warninglist_category' => $list['Warninglist']['category'],
|
2020-09-03 15:24:03 +02:00
|
|
|
);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $object;
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/**
|
2021-01-28 19:19:34 +01:00
|
|
|
* @param array|CidrTool $listValues
|
2020-09-03 15:24:03 +02:00
|
|
|
* @param string $value
|
|
|
|
* @param string $type
|
|
|
|
* @param string $listType
|
|
|
|
* @return array|false [Matched value, attribute value that matched]
|
|
|
|
*/
|
2021-08-17 19:00:40 +02:00
|
|
|
public function checkValue($listValues, $value, $type, $listType)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2019-08-08 19:11:22 +02:00
|
|
|
if ($type === 'malware-sample' || strpos($type, '|') !== false) {
|
2020-09-07 19:14:17 +02:00
|
|
|
$value = explode('|', $value, 2);
|
2018-07-19 11:48:22 +02:00
|
|
|
} else {
|
|
|
|
$value = array($value);
|
|
|
|
}
|
2020-09-07 19:14:17 +02:00
|
|
|
foreach ($value as $v) {
|
2018-07-19 11:48:22 +02:00
|
|
|
if ($listType === 'cidr') {
|
2021-01-28 19:19:34 +01:00
|
|
|
$result = $listValues->contains($v);
|
2018-07-19 11:48:22 +02:00
|
|
|
} elseif ($listType === 'string') {
|
2020-09-07 19:14:17 +02:00
|
|
|
$result = $this->__evalString($listValues, $v);
|
2018-07-19 11:48:22 +02:00
|
|
|
} elseif ($listType === 'substring') {
|
2020-09-07 19:14:17 +02:00
|
|
|
$result = $this->__evalSubString($listValues, $v);
|
2018-07-19 11:48:22 +02:00
|
|
|
} elseif ($listType === 'hostname') {
|
2020-09-07 19:14:17 +02:00
|
|
|
$result = $this->__evalHostname($listValues, $v);
|
2018-07-19 11:48:22 +02:00
|
|
|
} elseif ($listType === 'regex') {
|
2020-09-07 19:14:17 +02:00
|
|
|
$result = $this->__evalRegex($listValues, $v);
|
2020-09-03 15:24:03 +02:00
|
|
|
} else {
|
|
|
|
$result = false;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($result !== false) {
|
2020-09-07 19:14:17 +02:00
|
|
|
return [$result, $v];
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-07 19:40:06 +02:00
|
|
|
/**
|
|
|
|
* Check for exact match.
|
|
|
|
*
|
|
|
|
* @param array $listValues
|
|
|
|
* @param string $value
|
|
|
|
* @return false
|
|
|
|
*/
|
2018-07-19 11:48:22 +02:00
|
|
|
private function __evalString($listValues, $value)
|
|
|
|
{
|
|
|
|
if (isset($listValues[$value])) {
|
2020-09-07 19:40:06 +02:00
|
|
|
return $value;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __evalSubString($listValues, $value)
|
|
|
|
{
|
|
|
|
foreach ($listValues as $listValue) {
|
|
|
|
if (strpos($value, $listValue) !== false) {
|
2020-09-03 15:24:03 +02:00
|
|
|
return $listValue;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __evalHostname($listValues, $value)
|
|
|
|
{
|
|
|
|
// php's parse_url is dumb, so let's use some hacky workarounds
|
2020-09-07 12:03:04 +02:00
|
|
|
if (strpos($value, '//') === false) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$value = explode('/', $value);
|
|
|
|
$hostname = $value[0];
|
|
|
|
} else {
|
|
|
|
$value = explode('/', $value);
|
|
|
|
$hostname = $value[2];
|
|
|
|
}
|
|
|
|
// If the hostname is not found, just return false
|
|
|
|
if (!isset($hostname)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$hostname = rtrim($hostname, '.');
|
|
|
|
$hostname = explode('.', $hostname);
|
|
|
|
$rebuilt = '';
|
2020-09-07 12:03:04 +02:00
|
|
|
foreach (array_reverse($hostname) as $piece) {
|
2018-07-19 11:48:22 +02:00
|
|
|
if (empty($rebuilt)) {
|
|
|
|
$rebuilt = $piece;
|
|
|
|
} else {
|
|
|
|
$rebuilt = $piece . '.' . $rebuilt;
|
|
|
|
}
|
|
|
|
if (isset($listValues[$rebuilt])) {
|
2020-09-07 19:40:06 +02:00
|
|
|
return $rebuilt;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function __evalRegex($listValues, $value)
|
|
|
|
{
|
|
|
|
foreach ($listValues as $listValue) {
|
|
|
|
if (preg_match($listValue, $value)) {
|
2020-09-03 15:24:03 +02:00
|
|
|
return $listValue;
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-07 10:06:33 +02:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2018-07-19 11:48:22 +02:00
|
|
|
public function fetchTLDLists()
|
|
|
|
{
|
2020-12-23 12:50:19 +01:00
|
|
|
$tldLists = $this->find('column', array(
|
2021-10-04 11:01:58 +02:00
|
|
|
'conditions' => array('Warninglist.name' => self::TLDS),
|
2020-12-23 12:50:19 +01:00
|
|
|
'fields' => array('Warninglist.id')
|
2020-09-03 15:24:03 +02:00
|
|
|
));
|
2018-07-19 11:48:22 +02:00
|
|
|
$tlds = array();
|
|
|
|
if (!empty($tldLists)) {
|
2020-12-23 12:50:19 +01:00
|
|
|
$tlds = $this->WarninglistEntry->find('column', array(
|
2020-09-03 15:24:03 +02:00
|
|
|
'conditions' => array('WarninglistEntry.warninglist_id' => $tldLists),
|
|
|
|
'fields' => array('WarninglistEntry.value')
|
|
|
|
));
|
|
|
|
foreach ($tlds as $key => $value) {
|
|
|
|
$tlds[$key] = strtolower($value);
|
2018-07-19 11:48:22 +02:00
|
|
|
}
|
|
|
|
}
|
2021-01-10 17:44:37 +01:00
|
|
|
if (!in_array('onion', $tlds, true)) {
|
2018-07-19 11:48:22 +02:00
|
|
|
$tlds[] = 'onion';
|
|
|
|
}
|
|
|
|
return $tlds;
|
|
|
|
}
|
|
|
|
|
2020-09-03 15:24:03 +02:00
|
|
|
/**
|
|
|
|
* @param array $attribute
|
|
|
|
* @param array|null $warninglists If null, all enabled warninglists will be used
|
|
|
|
* @return bool
|
|
|
|
*/
|
2020-09-07 16:37:38 +02:00
|
|
|
public function filterWarninglistAttribute(array $attribute, $warninglists = null)
|
2018-07-19 11:48:22 +02:00
|
|
|
{
|
2020-09-03 15:24:03 +02:00
|
|
|
if ($warninglists === null) {
|
|
|
|
$warninglists = $this->getEnabled();
|
|
|
|
}
|
|
|
|
|
2018-07-19 11:48:22 +02:00
|
|
|
foreach ($warninglists as $warninglist) {
|
2021-01-10 17:44:37 +01:00
|
|
|
if (in_array('ALL', $warninglist['types'], true) || in_array($attribute['type'], $warninglist['types'], true)) {
|
2021-08-17 19:00:40 +02:00
|
|
|
$result = $this->checkValue($this->getFilteredEntries($warninglist), $attribute['value'], $attribute['type'], $warninglist['Warninglist']['type']);
|
2018-07-19 11:48:22 +02:00
|
|
|
if ($result !== false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2020-09-07 10:05:56 +02:00
|
|
|
|
|
|
|
public function missingTldLists()
|
|
|
|
{
|
|
|
|
$missingTldLists = array();
|
2021-10-04 11:01:58 +02:00
|
|
|
foreach (self::TLDS as $tldList) {
|
2020-09-07 10:05:56 +02:00
|
|
|
$temp = $this->find('first', array(
|
|
|
|
'recursive' => -1,
|
|
|
|
'conditions' => array('Warninglist.name' => $tldList),
|
|
|
|
'fields' => array('Warninglist.id')
|
|
|
|
));
|
|
|
|
if (empty($temp)) {
|
|
|
|
$missingTldLists[] = $tldList;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $missingTldLists;
|
|
|
|
}
|
2021-06-01 17:49:45 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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'])) {
|
2022-02-26 18:05:02 +01:00
|
|
|
$this->WarninglistEntry->deleteAll(['warninglist_id' => $id], false);
|
2021-06-01 17:49:45 +02:00
|
|
|
$entriesToInsert = [];
|
2022-02-26 17:45:02 +01:00
|
|
|
foreach ($data['WarninglistEntry'] as $entry) {
|
2021-06-01 17:49:45 +02:00
|
|
|
$entriesToInsert[] = [$entry['value'], isset($entry['comment']) ? $entry['comment'] : null, $id];
|
|
|
|
}
|
|
|
|
$db->insertMulti(
|
|
|
|
$this->WarninglistEntry->table,
|
|
|
|
['value', 'comment', 'warninglist_id'],
|
|
|
|
$entriesToInsert
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($data['WarninglistType'])) {
|
2022-02-26 18:05:02 +01:00
|
|
|
$this->WarninglistType->deleteAll(['warninglist_id' => $id], false);
|
2021-06-01 17:49:45 +02:00
|
|
|
foreach ($data['WarninglistType'] as &$entry) {
|
|
|
|
$entry['warninglist_id'] = $id;
|
|
|
|
}
|
|
|
|
$this->WarninglistType->saveMany($data['WarninglistType']);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($transactionBegun) {
|
|
|
|
if ($success) {
|
|
|
|
$db->commit();
|
|
|
|
} else {
|
|
|
|
$db->rollback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
if ($transactionBegun) {
|
|
|
|
$db->rollback();
|
|
|
|
}
|
|
|
|
throw $e;
|
|
|
|
}
|
|
|
|
|
2022-02-26 18:05:02 +01:00
|
|
|
if ($success) {
|
|
|
|
$this->afterFullSave(!isset($data['Warninglist']['id']), $success);
|
|
|
|
}
|
|
|
|
|
2021-06-01 17:49:45 +02:00
|
|
|
return $success;
|
|
|
|
}
|
|
|
|
|
2022-02-26 18:05:02 +01:00
|
|
|
/**
|
|
|
|
* @param bool $created
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
private function afterFullSave($created, array $data)
|
2021-06-02 18:03:13 +02:00
|
|
|
{
|
2022-02-26 18:05:02 +01:00
|
|
|
if (isset($data['Warninglist']['default']) && $data['Warninglist']['default'] == 0) {
|
|
|
|
$this->regenerateWarninglistCaches($data['Warninglist']['id']);
|
2021-06-02 18:03:13 +02:00
|
|
|
}
|
|
|
|
|
2022-02-26 17:45:02 +01:00
|
|
|
if ($this->pubToZmq('warninglist')) {
|
2021-06-02 18:03:13 +02:00
|
|
|
$warninglist = $this->find('first', [
|
2022-02-26 18:05:02 +01:00
|
|
|
'conditions' => ['id' => $data['Warninglist']['id']],
|
2021-06-02 18:03:13 +02:00
|
|
|
'contains' => ['WarninglistEntry', 'WarninglistType'],
|
|
|
|
]);
|
|
|
|
$pubSubTool = $this->getPubSubTool();
|
|
|
|
$pubSubTool->warninglist_save($warninglist, $created ? 'add' : 'edit');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-01 17:49:45 +02:00
|
|
|
/**
|
|
|
|
* @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'),
|
|
|
|
];
|
|
|
|
}
|
2016-05-31 18:06:37 +02:00
|
|
|
}
|