Merge branch '2.4' of https://github.com/MISP/MISP into 2.4

pull/5162/head
chrisr3d 2019-09-12 17:13:19 +02:00
commit c0aec75a09
81 changed files with 6021 additions and 180 deletions

2
.gitignore vendored
View File

@ -43,6 +43,8 @@ tools/mkdocs
!/app/files/misp-galaxy/*
!/app/files/misp-objects
!/app/files/misp-objects/*
!/app/files/misp-decaying-models
!/app/files/misp-decaying-models/*
/app/files/scripts/python-stix/
/app/files/scripts/python-cybox/
/app/files/scripts/mixbox/

5
.gitmodules vendored
View File

@ -42,4 +42,7 @@
url = https://github.com/pear/Crypt_GPG
[submodule "INSTALL/Console_CommandLine"]
path = INSTALL/dependencies/Console_CommandLine
url = https://github.com/pear/Console_CommandLine
url = https://github.com/pear/Console_CommandLine
[submodule "app/files/misp-decaying-models"]
path = app/files/misp-decaying-models
url = https://github.com/MISP/misp-decaying-models.git

View File

@ -943,8 +943,6 @@ alias composer70='composer72'
composer72 () {
cd $PATH_TO_MISP/app
mkdir /var/www/.composer ; chown $WWW_USER:$WWW_USER /var/www/.composer
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
}
@ -961,8 +959,6 @@ composer73 () {
checkFail "composer.phar checksum failed, please investigate manually. " $?
$SUDO_WWW php composer-setup.php
$SUDO_WWW php -r "unlink('composer-setup.php');"
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
}
@ -1329,8 +1325,6 @@ installCake () {
# Make composer cache happy
# /!\ composer on Ubuntu when invoked with sudo -u doesn't set $HOME to /var/www but keeps it /home/misp \!/
sudo mkdir /var/www/.composer ; sudo chown $WWW_USER:$WWW_USER /var/www/.composer
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
# Enable CakeResque with php-redis

View File

@ -1 +1 @@
6f5260ea0b7af730f4b94007e5046f661e3c2585 INSTALL.sh
692b4b07289064b7e0c895a11dc627c39c51f108 INSTALL.sh

View File

@ -1 +1 @@
babd4491825edd02153d7d09624f1668c452ee14279872f367c5729dd51171bc INSTALL.sh
4c3fe46366616bd77f78fcf68f0a33cd90f9c0480dd06b0d14da1fa66f4ed2a8 INSTALL.sh

View File

@ -1 +1 @@
0cf66499a027baaf5b52aba19270a7f6e5fbc7d99df225a9049bf9c35c35f9c4316a59ef92ec544ef2f23eea416897b0 INSTALL.sh
576574cf5390b00b2fd05ef6a2cbe6c85f5d737224776927c8f937a2465c41cbb0bf1a21e564a1715bf8cc104799c59c INSTALL.sh

View File

@ -1 +1 @@
cfc7e4b1749ad8ed2d75fd3e7d984bb48ab253559c4a37318568dfc175fad40612a05bb59d3672dc3de88b651bd18e8b959457c4ae9c72eff2c0a7418e51fce8 INSTALL.sh
3f5e83afab583b63d83d4d71f3d5f395e058b990c1ed68aa6fee4a77a26529a5ebe6110723a4afc7e99aa451ea2fdebcc1931aafb121cadaa09225fa986f3c08 INSTALL.sh

View File

@ -711,6 +711,7 @@ CREATE TABLE IF NOT EXISTS `roles` (
`restricted_to_site_admin` tinyint(1) NOT NULL DEFAULT 0,
`perm_publish_zmq` tinyint(1) NOT NULL DEFAULT 0,
`perm_publish_kafka` tinyint(1) NOT NULL DEFAULT 0,
`perm_decaying` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@ -1262,23 +1263,23 @@ INSERT INTO `feeds` (`id`, `provider`, `name`, `url`, `distribution`, `default`,
-- 7. Read Only - read
--
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (1, 'admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (1, 'admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (2, 'Org Admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (2, 'Org Admin', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (3, 'User', NOW(), NOW(), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (3, 'User', NOW(), NOW(), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (4, 'Publisher', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (4, 'Publisher', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (5, 'Sync user', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (5, 'Sync user', NOW(), NOW(), 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `default_role`)
VALUES (6, 'Read Only', NOW(), NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
INSERT INTO `roles` (`id`, `name`, `created`, `modified`, `perm_add`, `perm_modify`, `perm_modify_org`, `perm_publish`, `perm_publish_zmq`, `perm_publish_kafka`, `perm_sync`, `perm_admin`, `perm_audit`, `perm_full`, `perm_auth`, `perm_regexp_access`, `perm_tagger`, `perm_site_admin`, `perm_template`, `perm_sharing_group`, `perm_tag_editor`, `perm_delegate`, `perm_sighting`, `perm_object_template`, `perm_decaying`, `default_role`)
VALUES (6, 'Read Only', NOW(), NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-- --------------------------------------------------------

View File

@ -309,13 +309,13 @@ COPY public.regexp (id, regexp, replacement, type) FROM stdin;
-- Data for Name: roles; Type: TABLE DATA; Schema: public; Owner: -
--
COPY public.roles (id, name, created, modified, perm_add, perm_modify, perm_modify_org, perm_publish, perm_delegate, perm_sync, perm_admin, perm_audit, perm_full, perm_auth, perm_site_admin, perm_regexp_access, perm_tagger, perm_template, perm_sharing_group, perm_tag_editor, perm_sighting, perm_object_template, default_role, memory_limit, max_execution_time, restricted_to_site_admin, perm_publish_zmq, perm_publish_kafka) FROM stdin;
1 admin 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t t t t t t t t t t t t t t f f t t
2 Org Admin 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t f t t f t f f t t t t t f f f t t
3 User 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t f f f f f f t f f f f f f t f t f f f
4 Publisher 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t f f f f t f f f f f f t f f f t t
5 Sync user 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t t f f f t f f f f t f t f f f t t
6 Read Only 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 f f f f f f f f f t f f f f f f f f f f f f
COPY public.roles (id, name, created, modified, perm_add, perm_modify, perm_modify_org, perm_publish, perm_delegate, perm_sync, perm_admin, perm_audit, perm_full, perm_auth, perm_site_admin, perm_regexp_access, perm_tagger, perm_template, perm_sharing_group, perm_tag_editor, perm_sighting, perm_object_template, default_role, memory_limit, max_execution_time, restricted_to_site_admin, perm_publish_zmq, perm_publish_kafka, perm_decaying) FROM stdin;
1 admin 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t t t t t t t t t t t t t t f f t t t
2 Org Admin 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t f t t f t f f t t t t t f f f t t t
3 User 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t f f f f f f t f f f f f f t f t f f f t
4 Publisher 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t f f f f t f f f f f f t f f f t t t
5 Sync user 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 t t t t t t f f f t f f f f t f t f f f t t t
6 Read Only 2018-11-27 06:22:00+00 2018-11-27 06:22:00+00 f f f f f f f f f t f f f f f f f f f f f f f
\.

View File

@ -1135,7 +1135,8 @@ CREATE TABLE public.roles (
max_execution_time character varying(255) DEFAULT ''::character varying,
restricted_to_site_admin boolean DEFAULT false NOT NULL,
perm_publish_zmq boolean DEFAULT false NOT NULL,
perm_publish_kafka boolean DEFAULT false NOT NULL
perm_publish_kafka boolean DEFAULT false NOT NULL,
perm_decaying boolean DEFAULT false NOT NULL
);

View File

@ -164,20 +164,6 @@
group: root
mode: 0755
- name: Cake-resque installation
composer:
command: "require"
arguments: "kamisama/cake-resque:4.1.2"
working_dir: "/opt/misp-server/misp/app"
register: cakeresque_install
- name: Vendor configure
composer:
command: "config"
arguments: "vendor-dir Vendor"
working_dir: "/opt/misp-server/misp/app"
when: cakeresque_install.changed
- name: PHP composer install
composer:
command: "install"

View File

@ -46,7 +46,7 @@ class AppController extends Controller
public $helpers = array('Utility', 'OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '84';
private $__queryVersion = '85';
public $pyMispVersion = '2.4.114';
public $phpmin = '7.0';
public $phprec = '7.2';
@ -453,6 +453,7 @@ class AppController extends Controller
$this->set('isAclSighting', isset($role['perm_sighting']) ? $role['perm_sighting'] : false);
$this->set('isAclZmq', isset($role['perm_publish_zmq']) ? $role['perm_publish_zmq'] : false);
$this->set('isAclKafka', isset($role['perm_publish_kafka']) ? $role['perm_publish_kafka'] : false);
$this->set('isAclDecaying', isset($role['perm_decaying']) ? $role['perm_decaying'] : false);
$this->userRole = $role;
if (Configure::read('MISP.log_paranoid')) {
$this->Log = ClassRegistry::init('Log');

View File

@ -1891,7 +1891,8 @@ class AttributesController extends AppController
'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
'includeProposals', 'returnFormat', 'published', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'includeSightings', 'includeCorrelations'
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'includeSightings', 'includeCorrelations', 'includeDecayScore',
'decayingModel', 'excludeDecayed', 'modelOverrides', 'includeFullModel', 'score'
);
$filterData = array(
'request' => $this->request,

View File

@ -70,6 +70,28 @@ class ACLComponent extends Component
'view' => array('*'),
'viewPicture' => array('*'),
),
'decayingModel' => array(
"update" => array(),
"export" => array('*'),
"import" => array('*'),
"view" => array('*'),
"index" => array('*'),
"add" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"edit" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"delete" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"enable" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"disable" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"decayingTool" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"getAllDecayingModels" => array('*'),
"decayingToolBasescore" => array('*'),
"decayingToolSimulation" => array('*'),
"decayingToolRestSearch" => array('*'),
"decayingToolComputeSimulation" => array('*')
),
'decayingModelMapping' => array(
"viewAssociatedTypes" => array('*'),
"linkAttributeTypeToModel" => array( 'OR' => array('perm_admin', 'perm_decaying'))
),
'communities' => array(
'index' => array(),
'requestAccess' => array(),
@ -165,7 +187,6 @@ class ACLComponent extends Component
'xml' => array('*')
),
'favouriteTags' => array(
'index' => array('*'),
'toggle' => array('*'),
'getToggleField' => array('*')
),
@ -514,7 +535,6 @@ class ACLComponent extends Component
'edit' => array('*'),
'fetchPGPKey' => array('*'),
'histogram' => array('*'),
'index' => array('*'),
'initiatePasswordReset' => array('perm_admin'),
'login' => array('*'),
'logout' => array('*'),

View File

@ -47,7 +47,7 @@ class RestResponseComponent extends Component
Besides the parameters listed, other, format specific ones can be passed along (for example: requested_attributes and includeContext for the CSV export).
This API allows pagination via the page and limit parameters.",
'mandatory' => array('returnFormat'),
'optional' => array('page', 'limit', 'value' , 'type', 'category', 'org', 'tags', 'date', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'includeEventTags', 'event_timestamp', 'threat_level_id', 'eventinfo', 'includeProposals'),
'optional' => array('page', 'limit', 'value' , 'type', 'category', 'org', 'tags', 'date', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp', 'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'includeEventTags', 'event_timestamp', 'threat_level_id', 'eventinfo', 'includeProposals', 'includeDecayScore', 'includeFullModel', 'decayingModel', 'excludeDecayed', 'score'),
'params' => array()
)
),
@ -443,6 +443,9 @@ class RestResponseComponent extends Component
$type = $format;
}
if (!$raw) {
if (is_string($response)) {
$response = array('message' => $response);
}
$response = json_encode($response, JSON_PRETTY_PRINT);
}
}
@ -725,6 +728,13 @@ class RestResponseComponent extends Component
'autoclose' => true
),
),
'decayingModel' => array(
'input' => 'select',
'type' => 'string',
'operators' => array('equal', 'not_equal'),
'unique' => true,
'help' => 'Specify the decaying model from which the decaying score should be calculated'
),
'default_role' => array(
'input' => 'radio',
'type' => 'integer',
@ -825,6 +835,12 @@ class RestResponseComponent extends Component
'values' => array(1 => 'True', 0 => 'False' ),
'help' => __('The tag is exported when synchronising with other instances')
),
'excludeDecayed' => array(
'input' => 'radio',
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False' ),
'help' => 'Should the decayed elements by excluded'
),
'excludeLocalTags' => array(
'input' => 'radio',
'type' => 'integer',
@ -904,6 +920,12 @@ class RestResponseComponent extends Component
'values' => array(1 => 'True', 0 => 'False' ),
'help' => __('Include matching attributes in the response')
),
'includeDecayScore' => array(
'input' => 'radio',
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False' ),
'help' => 'Include all enabled decaying score'
),
'includeEvent' => array(
'input' => 'radio',
'type' => 'integer',
@ -922,6 +944,12 @@ class RestResponseComponent extends Component
'values' => array(1 => 'True', 0 => 'False' ),
'help' => __('Include tags of matching events in the response')
),
'includeFullModel' => array(
'input' => 'radio',
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False' ),
'help' => 'Include all model information of matching events in the response'
),
'includeProposals' => array(
'input' => 'radio',
'type' => 'integer',
@ -1244,6 +1272,13 @@ class RestResponseComponent extends Component
'operators' => array('equal'),
'validation' => array(0 => 'role1'),
),
'score' => array(
'input' => 'number',
'type' => 'integer',
'operators' => array('equal'),
'validation' => array('min' => 0, 'step' => 1, 'max' => 100),
'help' => 'An alias to override on-the-fly the threshold of the decaying model'
),
'searchall' => array(
'input' => 'radio',
'type' => 'integer',
@ -1546,6 +1581,9 @@ class RestResponseComponent extends Component
case "category":
$this->__overwriteCategory($scope, $fieldsConstraint[$field]);
break;
case "decayingModel":
$this->__overwriteDecayingModel($scope, $fieldsConstraint[$field]);
break;
case "distribution":
$this->__overwriteDistribution($scope, $fieldsConstraint[$field]);
break;
@ -1614,6 +1652,17 @@ class RestResponseComponent extends Component
$field['values'][] = array('label' => $text, 'value' => $d);
}
}
private function __overwriteDecayingModel($scope, &$field) {
$this->{$scope} = ClassRegistry::init("DecayingModel");
$models = $this->{$scope}->find('list', array(
'recursive' => -1,
'fields' => array('name')
));
$field['values'] = array();
foreach($models as $i => $model_name) {
$field['values'][] = array('label' => h($model_name), 'value' => $i);
}
}
private function __overwriteTags($scope, &$field) {
$this->{$scope} = ClassRegistry::init("Tag");
$tags = $this->{$scope}->find('list', array(

View File

@ -0,0 +1,724 @@
<?php
App::uses('AppController', 'Controller');
class DecayingModelController extends AppController
{
public $components = array('Security' ,'RequestHandler');
public $paginate = array(
'limit' => 50,
'order' => array(
'DecayingModel.ID' => 'desc'
)
);
public function update($force=false)
{
if ($this->request->is('post') || $this->request->is('put')) {
$this->DecayingModel->update($force, $this->Auth->user());
$message = __('Default decaying models updated');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('DecayingModel', 'update', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(array('controller' => 'decayingModel', 'action' => 'index'));
}
} else {
throw new MethodNotAllowedException(__("This method is not allowed"));
}
}
public function export($model_id)
{
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $model_id, true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
unset($model['DecayingModel']['id'], $model['DecayingModel']['uuid'], $model['DecayingModel']['org_id'], $model['DecayingModelMapping']);
return $this->RestResponse->viewData($model, $this->response->type());
}
public function import()
{
if ($this->request->is('post') || $this->request->is('put')) {
$data = $this->request->data['DecayingModel'];
if ($data['submittedjson']['name'] != '' && $data['json'] != '') {
throw new MethodNotAllowedException(__('Only one import field can be used'));
}
if ($data['submittedjson']['size'] > 0) {
$filename = basename($data['submittedjson']['name']);
$file_content = file_get_contents($data['submittedjson']['tmp_name']);
if ((isset($data['submittedjson']['error']) && $data['submittedjson']['error'] == 0) ||
(!empty($data['submittedjson']['tmp_name']) && $data['submittedjson']['tmp_name'] != '')
) {
if (!$file_content) {
throw new InternalErrorException(__('PHP says file was not uploaded. Are you attacking me?'));
}
}
$text = $file_content;
} else {
$text = $data['json'];
}
$json = json_decode($text, true);
if ($json === null) {
throw new MethodNotAllowedException(__('Error while decoding JSON'));
}
unset($json['id']);
unset($json['uuid']);
$json['default'] = 0;
$json['org_id'] = $this->Auth->user()['org_id'];
$attribute_types = array();
if (!empty($json['attribute_types'])) {
$attribute_types = $json['attribute_types'];
unset($json['attribute_types']);
}
if ($this->DecayingModel->save($json)) {
$saved_model = array(
'model_id' => $this->DecayingModel->id,
'attribute_types' => $attribute_types
);
if (!empty($saved_model['attribute_types'])) {
$result = $this->DecayingModel->DecayingModelMapping->resetMappingForModel($saved_model, $this->Auth->user());
} else {
$result = true;
}
if (!empty($result)) {
$this->Flash->success(__('The model has been imported.'));
} else {
$this->Flash->error(__('The model has been imported. However importing mapping failed.'));
}
} else {
$this->Flash->error(__('Error while importing model.'));
}
$this->redirect(array('action' => 'index'));
}
}
public function view($id)
{
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$this->set('id', $id);
$this->set('decaying_model', $decaying_model);
$available_formulas = $this->DecayingModel->listAvailableFormulas();
$this->set('available_formulas', $available_formulas);
}
// Sets pagination condition for url parameters
private function __setIndexFilterConditions($passedArgs)
{
$white_list_url_parameters = array('sort', 'direction');
$passedArgsArray = array();
foreach ($passedArgs as $k => $v) {
switch ($k) {
case 'my_models':
$passedArgsArray[$k] = $v;
if ($v) {
$this->paginate['conditions']['AND'] = array('DecayingModel.org_id' => $this->Auth->user('Organisation')['id']);
}
break;
case 'default_models':
$passedArgsArray[$k] = $v;
if ($v) {
$this->paginate['conditions']['AND'] = array('not' => array('DecayingModel.uuid' => null));
}
break;
case 'all_orgs':
$passedArgsArray[$k] = $v;
if ($v) {
$this->paginate['conditions']['AND'] = array('DecayingModel.all_orgs' => $v);
}
break;
default:
if (in_array($k, $white_list_url_parameters)) {
$passedArgsArray[$k] = $v;
}
break;
}
}
return $passedArgsArray;
}
public function index()
{
$conditions = array();
if (!$this->_isSiteAdmin()) {
$conditions['OR'] = array(
'org_id' => $this->Auth->user('Organisation')['id'],
'all_orgs' => 1
);
$this->paginate = Set::merge($this->paginate, array(
'conditions' => $conditions
));
}
$passedArgsArray = $this->__setIndexFilterConditions($this->passedArgs);
$this->set('passedArgsArray', $passedArgsArray);
$this->set('decayingModels', $this->paginate());
$available_formulas = $this->DecayingModel->listAvailableFormulas();
$this->set('available_formulas', $available_formulas);
if ($this->_isRest()) {
return $this->RestResponse->viewData($this->paginate(), $this->response->type());
}
}
public function add()
{
if ($this->request->is('post') || $this->request->is('put')) {
if (!isset($this->request->data['DecayingModel'])) {
$this->request->data = array('DecayingModel' => $this->request->data);
}
$this->request->data['DecayingModel']['org_id'] = $this->Auth->user()['org_id'];
unset($this->request->data['DecayingModel']['id']);
unset($this->request->data['DecayingModel']['uuid']);
$this->request->data['DecayingModel']['default'] = 0;
if (empty($this->request->data['DecayingModel']['name'])) {
throw new MethodNotAllowedException(__("The model must have a name"));
}
$this->request->data = $this->__adjustJSONData($this->request->data);
if ($this->request->data === false) {
return false;
}
$attribute_types = array();
if (!empty($this->request->data['attribute_types'])) {
$attribute_types = $this->request->data['attribute_types'];
unset($this->request->data['attribute_types']);
}
if ($this->DecayingModel->save($this->request->data)) {
$success_message = __('The model has been saved.');
if (!empty($saved_model['attribute_types'])) {
if (!$this->DecayingModel->DecayingModelMapping->resetMappingForModel($saved_model, $this->Auth->user())) {
$success_message = __('The model has been saved. However importing mapping failed.');
}
}
if ($this->request->is('ajax') || $this->_isRest()) {
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id, true, array(), true);
if (empty($saved)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $saved, 'action' => 'add');
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->success($sucess_messaqge);
$this->redirect(array('action' => 'index'));
}
} else {
if ($this->request->is('ajax') || $this->_isRest()) {
$response = array(
'action' => 'add',
'saved' => false,
'errors' => array(__('The model could not be saved. Please try again.'))
);
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->error(__('The model could not be saved. Please try again.' . $this->here));
$this->redirect($this->here);
}
}
} else {
$this->set('action', 'add');
$available_formulas = $this->DecayingModel->listAvailableFormulas();
$formulas = array();
foreach ($available_formulas as $formulaName => $f) {
$formulas[$formulaName] = $formulaName;
}
$this->set('available_formulas', $formulas);
}
}
public function edit($id)
{
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$enforceRestrictedEdition = $decaying_model['DecayingModel']['default'];
if ($this->request->is('post') || $this->request->is('put')) {
$this->request->data['DecayingModel']['id'] = $id;
$fieldListToSave = array('enabled', 'all_orgs');
if (!$enforceRestrictedEdition) {
$fieldListToSave = array_merge($fieldListToSave, array('name', 'description', 'parameters', 'formula'));
$this->request->data = $this->__adjustJSONData($this->request->data);
if ($this->request->data === false) {
return false;
}
}
$save_result = $this->DecayingModel->save($this->request->data, true, $fieldListToSave);
if ($save_result) {
if ($this->request->is('ajax') || $this->_isRest()) {
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id, true, array(), true);
if (empty($saved)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $saved, 'action' => 'edit');
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->success(__('The model has been saved.'));
$this->redirect(array('action' => 'index'));
}
} else {
if ($this->request->is('ajax') || $this->_isRest()) {
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id);
if (empty($saved)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $saved, 'action' => 'edit', 'saved' => false);
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->error(__('The model could not be saved. Please try again.' . $this->here));
$this->redirect($this->here);
}
}
} else {
$this->request->data = $decaying_model;
$this->set('id', $id);
$this->set('decayingModel', $decaying_model);
$this->set('restrictEdition', $enforceRestrictedEdition);
$this->set('action', 'edit');
$available_formulas = $this->DecayingModel->listAvailableFormulas();
$formulas = array();
foreach ($available_formulas as $formulaName => $f) {
$formulas[$formulaName] = $formulaName;
}
$this->set('available_formulas', $formulas);
$this->render('add');
}
}
// Adjust or flash the error to the user
private function __adjustJSONData($json)
{
if (isset($json['DecayingModel']['parameters'])) {
if (isset($json['DecayingModel']['parameters']['settings']) && !is_array($json['DecayingModel']['parameters']['settings'])) {
$settings = json_decode($json['DecayingModel']['parameters']['settings'], true);
if ($settings === null) {
$this->Flash->error(__('Invalid JSON `Settings`.'));
return false;
}
$json['DecayingModel']['parameters']['settings'] = $settings;
}
if (!isset($json['DecayingModel']['parameters']['lifetime'])) {
$this->Flash->error(__('Invalid parameter `lifetime`.'));
return false;
}
if (!isset($json['DecayingModel']['parameters']['decay_speed'])) {
$this->Flash->error(__('Invalid parameter `decay_speed`.'));
return false;
}
if (!isset($json['DecayingModel']['parameters']['threshold'])) {
$this->Flash->error(__('Invalid parameter `threshold`.'));
return false;
}
if (!isset($json['DecayingModel']['parameters']['default_base_score'])) {
$this->Flash->error(__('Invalid parameter `default_base_score`.'));
return false;
}
if (isset($json['DecayingModel']['parameters']['base_score_config']) && $json['DecayingModel']['parameters']['base_score_config'] != '') {
if (!is_array($json['DecayingModel']['parameters']['base_score_config'])) {
$encoded = json_decode($json['DecayingModel']['parameters']['base_score_config'], true);
if ($encoded === null) {
$this->Flash->error(__('Invalid parameter `base_score_config`.'));
return false;
}
$json['DecayingModel']['parameters']['base_score_config'] = $encoded;
}
} else {
$json['DecayingModel']['parameters']['base_score_config'] = new stdClass();
}
} else {
$this->Flash->error(__('Missing JSON key `parameters`.'));
return false;
}
$json['DecayingModel']['parameters'] = json_encode($json['DecayingModel']['parameters']);
return $json;
}
public function delete($id)
{
if ($this->request->is('post') || $this->request->is('put')) {
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if (
!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model) ||
$decaying_model['DecayingModel']['default']
) {
throw new MethodNotAllowedException(__('You are not authorised to delete this model.'));
}
if ($this->DecayingModel->delete($id, true)) {
if ($this->request->is('ajax')) {
$response = array('action' => 'delete', 'saved' => true);
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->success(__('Decaying Model deleted.'));
}
} else {
$error_message = __('The Decaying Model could not be deleted.');
if ($this->request->is('ajax')) {
$response = array('action' => 'delete', 'saved' => false, 'errors' => array($error_message));
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->Flash->error($error_message);
}
}
$this->redirect(array('action' => 'index'));
}
}
public function enable($id)
{
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if (!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model)) {
throw new MethodNotAllowedException(__('You are not authorised to enable this model.'));
}
$decaying_model['DecayingModel']['enabled'] = 1;
if ($this->DecayingModel->save($decaying_model)) {
if ($this->request->is('ajax')) {
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $model, 'action' => 'enable');
return $this->RestResponse->viewData($response, $this->response->type());
}
$this->Flash->success(__('Decaying Model enabled.'));
} else {
if ($this->request->is('ajax')) { // ajax caller expect data to be returned to update the DOM accordingly
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $model, 'action' => 'enable');
return $this->RestResponse->viewData($response, $this->response->type());
} elseif ($this->_isRest()) {
$response = array('errors' => $array(__('Error while enabling decaying model')), 'action' => 'enable');
return $this->RestResponse->viewData($response, $this->response->type());
}
$this->Flash->error(__('Error while enabling decaying model'));
}
$this->redirect($this->referer());
} else {
$this->set('model', $decaying_model['DecayingModel']);
$this->render('ajax/enable_form');
}
}
public function disable($id)
{
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if ($this->request->is('post') || $this->request->is('put')) {
if (!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model)) {
throw new MethodNotAllowedException(__('You are not authorised to disable this model.'));
}
$decaying_model['DecayingModel']['enabled'] = 0;
if ($this->DecayingModel->save($decaying_model)) {
if ($this->request->is('ajax')) {
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $model, 'action' => 'disable');
return $this->RestResponse->viewData($response, $this->response->type());
}
$this->Flash->success(__('Decaying Model disabled.'));
} else {
if ($this->request->is('ajax')) { // ajax caller expect data to be returned to update the DOM accordingly
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$response = array('data' => $model, 'action' => 'disable');
return $this->RestResponse->viewData($response, $this->response->type());
} elseif ($this->_isRest()) {
$response = array('errors' => $array(__('Error while enabling decaying model')), 'action' => 'disable');
return $this->RestResponse->viewData($response, $this->response->type());
}
$this->Flash->error(__('Error while disabling decaying model'));
}
$this->redirect(array('action' => 'index'));
} else {
$this->set('model', $decaying_model['DecayingModel']);
$this->render('ajax/disable_form');
}
}
public function decayingTool()
{
$parameters = array(
'lifetime' => array(
'value' => 30,
'step' => 1,
'max' => 365,
'greek' => '',
'unit' => 'days',
'name' => __('Lifetime'),
'info' => __('Lifetime of the attribute, or time after which the score will be 0')
),
'decay_speed' => array(
'value' => 0.3,
'step' => 0.1,
'max' => 10,
'greek' => '',
'name' => __('Decay speed'),
'info' => __('Decay speed at which an indicator will loose score')
),
'threshold' => array(
'value' => 30,
'step' => 1,
'max' => 100,
'greek' => '',
'name' => __('Cutoff threshold'),
'info' => __('Cutoff value at which an indicator will be marked as decayed instead of 0')
)
);
$types = $this->User->Event->Attribute->typeDefinitions;
$this->loadModel('ObjectTemplateElement');
$objectTypes = $this->ObjectTemplateElement->getAllAvailableTypes();
array_walk($objectTypes, function(&$item, $key) use ($types) {
$item["isObject"] = true;
$isAttribute = isset($types[$key]);
if ($isAttribute) {
$item["isAttribute"] = true;
$item["default_category"] = $types[$key]['default_category'];
$item["to_ids"] = $types[$key]['to_ids'];
} else {
$item["default_category"] = $item["category"];
}
});
$types = array_merge($types, $objectTypes);
ksort($types);
$savedDecayingModels = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user());
$available_formulas = $this->DecayingModel->listAvailableFormulas();
$this->set('available_formulas', $available_formulas);
$this->set('parameters', $parameters);
$this->set('types', $types);
$this->set('savedModels', $savedDecayingModels);
$associated_models = $this->DecayingModel->DecayingModelMapping->getAssociatedModels($this->Auth->user()); // mapping Attribute.type => Models
$this->set('associated_models', $associated_models);
}
public function getAllDecayingModels()
{
$filters = $this->request->query;
$savedDecayingModels = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user(), true, $filters);
return $this->RestResponse->viewData($savedDecayingModels, $this->response->type());
}
public function decayingToolBasescore()
{
$taxonomies = $this->DecayingModel->listTaxonomiesWithNumericalValue();
$this->set('taxonomies', $taxonomies['taxonomies']);
$this->set('taxonomies_not_having_numerical_value', $taxonomies['not_having_numerical_value']);
$this->set('excluded_taxonomies', $taxonomies['excluded_taxonomies']);
}
public function decayingToolSimulation($model_id)
{
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $model_id);
if (empty($decaying_model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if (isset($this->request->params['named']['attribute_id'])) {
$this->set('attribute_id', $this->request->params['named']['attribute_id']);
}
$this->set('user', $this->Auth->user());
$this->set('decaying_model', $decaying_model);
$allowed_models = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user());
$this->set('all_models', $allowed_models);
}
// TODO: Consider using the export tool to perform the post treatement
// as this does not mirror a complete restSearch (not using fetchAttribute)
public function decayingToolRestSearch($continue = false)
{
if ($this->request->is('post') || $this->request->is('put')) {
$body = $this->request->data['decayingToolRestSearch']['filters'];
$decoded_body = json_decode($body, true);
if (is_null($decoded_body)) {
throw new Exception(__("Error Processing Request, can't parse the body"));
}
$this->request->data = $decoded_body;
$paramArray = array(
'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
'includeProposals', 'returnFormat', 'published', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'id', 'includeDecayScore', 'includeFullModel', 'decayingModel', 'excludeDecayed', 'modelOverrides',
'score'
);
$filterData = array(
'request' => $this->request,
'named_params' => $this->params['named'],
'paramArray' => $paramArray,
'ordered_url_params' => compact($paramArray)
);
$exception = false;
$filters = $this->_harvestParameters($filterData, $exception);
if ($filters === false) {
return $exception;
}
$filters['includeEventTags'] = 1;
if (!isset($filters['excludeDecayed'])) {
$filters['excludeDecayed'] = 0;
}
$filters['includeDecayScore'] = 1;
if (isset($filters['id'])) { // allows searching by id
if (Validation::uuid($filters['id'])) {
$filters['uuid'] = $filters['id'];
} else {
$attributes = $this->User->Event->Attribute->fetchAttributes($this->Auth->user(), array(
'conditions' => array('Attribute.id' => $filters['id'])
));
if (!empty($attributes)) {
$filters['uuid'] = $attributes[0]['Attribute']['uuid'];
} else {
$filters['uuid'] = '-1'; // force no result
}
}
unset($filters['id']);
}
unset($filterData);
$this->Session->write('search_attributes_filters', json_encode($filters));
} elseif ($continue === 'results') {
$filters = $this->Session->read('search_attributes_filters');
if (empty($filters)) {
$filters = array();
} else {
$filters = json_decode($filters, true);
}
}
if (isset($filters)) {
$params = $this->User->Event->Attribute->restSearch($this->Auth->user(), 'json', $filters, true);
if (!isset($params['conditions']['Attribute.deleted'])) {
$params['conditions']['Attribute.deleted'] = 0;
}
$this->paginate = $params;
if (empty($this->paginate['limit'])) {
$this->paginate['limit'] = 60;
}
if (empty($this->paginate['page'])) {
$this->paginate['page'] = 1;
}
$this->paginate['recursive'] = -1;
$this->paginate['contain'] = array(
'Event' => array(
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
'Orgc' => array('fields' => array('Orgc.id', 'Orgc.name')),
'Org' => array('fields' => array('Org.id', 'Org.name'))
),
'AttributeTag' => array('Tag'),
'Object' => array(
'fields' => array('Object.id', 'Object.distribution', 'Object.sharing_group_id')
)
);
$attributes = $this->paginate($this->User->Event->Attribute);
// attach sightings and massage tags
$sightingsData = array();
if (!empty($options['overrideLimit'])) {
$overrideLimit = true;
} else {
$overrideLimit = false;
}
$this->loadModel('GalaxyCluster');
$cluster_names = $this->GalaxyCluster->find('list', array('fields' => array('GalaxyCluster.tag_name'), 'group' => array('GalaxyCluster.tag_name', 'GalaxyCluster.id')));
$this->loadModel('Sighting');
$eventTags = array();
foreach ($attributes as $k => $attribute) {
$attributes[$k]['Attribute']['AttributeTag'] = $attributes[$k]['AttributeTag'];
$attributes[$k]['Attribute'] = $this->User->Event->massageTags($attributes[$k]['Attribute'], 'Attribute');
unset($attributes[$k]['AttributeTag']);
foreach ($attributes[$k]['Attribute']['AttributeTag'] as $k2 => $attributeTag) {
if (in_array($attributeTag['Tag']['name'], $cluster_names)) {
unset($attributes[$k]['Attribute']['AttributeTag'][$k2]);
}
}
$sightingsData = array_merge(
$sightingsData,
$this->Sighting->attachToEvent($attribute, $this->Auth->user(), $attributes[$k]['Attribute']['id'], $extraConditions = false)
);
if (!empty($params['includeEventTags'])) {
$tagConditions = array('EventTag.event_id' => $attribute['Event']['id']);
if (empty($params['includeAllTags'])) {
$tagConditions['Tag.exportable'] = 1;
}
$temp = $this->User->Event->EventTag->find('all', array(
'recursive' => -1,
'contain' => array('Tag'),
'conditions' => $tagConditions
));
foreach ($temp as $tag) {
$attributes[$k]['Attribute']['EventTag'][] = $tag;
}
}
if (empty($filters['decayingModel'])) {
$filters['decayingModel'] = false;
}
$model_overrides = isset($filters['modelOverrides']) ? $filters['modelOverrides'] : array();
if (isset($filters['score'])) {
$model_overrides['threshold'] = intval($filters['score']);
}
$attributes[$k]['Attribute'] = $this->DecayingModel->attachScoresToAttribute($this->Auth->user(), $attributes[$k]['Attribute'], $filters['decayingModel'], $model_overrides);
if ($filters['excludeDecayed']) { // filter out decayed attribute
$decayed_flag = true;
foreach ($attributes[$k]['Attribute']['decay_score'] as $decayResult) {
$decayed_flag = $decayed_flag && $decayResult['decayed'];
}
if ($decayed_flag) {
unset($attributes[$k]);
}
}
}
$sightingsData = $this->User->Event->getSightingData(array('Sighting' => $sightingsData));
$this->set('sightingsData', $sightingsData);
$this->set('attributes', $attributes);
$this->set('attrDescriptions', $this->User->Event->Attribute->fieldDescriptions);
$this->set('typeDefinitions', $this->User->Event->Attribute->typeDefinitions);
$this->set('categoryDefinitions', $this->User->Event->Attribute->categoryDefinitions);
$this->set('shortDist', $this->User->Event->Attribute->shortDist);
} else {
$this->render('decayingToolRestSearchForm');
}
}
public function decayingToolComputeSimulation($model_id, $attribute_id)
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__("This method is only accessible via AJAX."));
}
$model_overrides = array();
if (isset($this->params['named']['modelOverride'])) {
$model_overrides = $this->params['named']['modelOverride'];
$model_overrides = json_decode($model_overrides, true);
if ($model_overrides === null) {
$model_overrides = array();
}
}
if (isset($this->params['named']['score'])) {
$model_overrides['threshold'] = intval($this->params['named']['score']);
}
$score_overtime = $this->DecayingModel->getScoreOvertime($this->Auth->user(), $model_id, $attribute_id, $model_overrides);
return $this->RestResponse->viewData($score_overtime, $this->response->type());
}
}

View File

@ -0,0 +1,51 @@
<?php
App::uses('AppController', 'Controller');
class DecayingModelMappingController extends AppController
{
public $components = array('Security' ,'RequestHandler');
public $paginate = array(
'limit' => 50,
'order' => array(
'DecayingModel.name' => 'asc'
)
);
public function viewAssociatedTypes($model_id) {
$associated_types = $this->DecayingModelMapping->getAssociatedTypes($this->Auth->user(), $model_id);
return $this->RestResponse->viewData($associated_types, $this->response->type());
}
public function linkAttributeTypeToModel($model_id) {
$model = $this->DecayingModelMapping->DecayingModel->fetchModel($this->Auth->user(), $model_id);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$this->request->data['DecayingModelMapping']['model_id'] = $model_id;
if (!isset($this->request->data['DecayingModelMapping']['org_id'])) {
$this->request->data['DecayingModelMapping']['org_id'] = $this->Auth->user()['org_id'];
}
if (empty($this->request->data['DecayingModelMapping']['attributetypes'])) {
throw new MethodNotAllowedException(_("The model must link to at least one attribute type"));
} else {
$decoded = json_decode($this->request->data['DecayingModelMapping']['attributetypes'], true);
if ($decoded === null) {
throw new MethodNotAllowedException(_("Invalid JSON: attribute type"));
}
$this->request->data['DecayingModelMapping']['attribute_types'] = $decoded;
unset($this->request->data['DecayingModelMapping']['attributetypes']);
}
$response = $this->DecayingModelMapping->resetMappingForModel($this->request->data['DecayingModelMapping'], $this->Auth->user());
return $this->RestResponse->viewData($response, $this->response->type());
} else {
$this->set('model_id', $model_id);
}
}
}

View File

@ -26,7 +26,7 @@ class EventsController extends AppController
);
private $acceptedFilteringNamedParams = array('sort', 'direction', 'focus', 'extended', 'overrideLimit', 'filterColumnsOverwrite', 'attributeFilter', 'extended', 'page',
'searchFor', 'proposal', 'correlation', 'warning', 'deleted', 'includeRelatedTags', 'distribution', 'taggedAttributes', 'galaxyAttachedAttributes', 'objectType', 'attributeType', 'focus', 'extended', 'overrideLimit', 'filterColumnsOverwrite', 'feed', 'server', 'toIDS', 'sighting'
'searchFor', 'proposal', 'correlation', 'warning', 'deleted', 'includeRelatedTags', 'includeDecayScore', 'distribution', 'taggedAttributes', 'galaxyAttachedAttributes', 'objectType', 'attributeType', 'focus', 'extended', 'overrideLimit', 'filterColumnsOverwrite', 'feed', 'server', 'toIDS', 'sighting'
);
public $defaultFilteringRules = array(
@ -37,6 +37,7 @@ class EventsController extends AppController
'warning' => 0,
'deleted' => 2,
'includeRelatedTags' => 0,
'includeDecayScore' => 0,
'toIDS' => 0,
'feed' => 0,
'server' => 0,
@ -1078,6 +1079,13 @@ class EventsController extends AppController
}
if (isset($filters['deleted'])) {
$conditions['deleted'] = $filters['deleted'] == 2 ? 0 : [0, 1];
if ($filters['deleted'] == 2) { // not-deleted only
$conditions['deleted'] = 0;
} elseif ($filters['deleted'] == 1) { // deleted only
$conditions['deleted'] = 1;
} else { // both
$conditions['deleted'] = [0, 1];
}
}
if (isset($filters['toIDS']) && $filters['toIDS'] != 0) {
$conditions['to_ids'] = $filters['toIDS'] == 2 ? 0 : 1;
@ -1099,6 +1107,12 @@ class EventsController extends AppController
} else {
$this->set('includeRelatedTags', 0);
}
if (!empty($filters['includeDecayScore'])) {
$this->set('includeDecayScore', 1);
$conditions['includeDecayScore'] = 1;
} else {
$this->set('includeDecayScore', 0);
}
$results = $this->Event->fetchEvent($this->Auth->user(), $conditions);
if (empty($results)) {
@ -1213,7 +1227,7 @@ class EventsController extends AppController
}
$deleted = 0;
if (isset($filters['deleted'])) {
$deleted = $filters['deleted'] == 2 ? array(0, 1) : $filters['deleted'];
$deleted = $filters['deleted'] == 2 ? 0 : 1;
}
$this->set('deleted', $deleted);
$this->set('typeGroups', array_keys($this->Event->Attribute->typeGroupings));
@ -1544,6 +1558,9 @@ class EventsController extends AppController
if (isset($this->params['named']['includeRelatedTags']) && $this->params['named']['includeRelatedTags']) {
$conditions['includeRelatedTags'] = 1;
}
if (!empty($this->params['named']['includeDecayScore'])) {
$conditions['includeDecayScore'] = 1;
}
if (isset($this->params['named']['public']) && $this->params['named']['public']) {
$conditions['distribution'] = array(3, 5);
}
@ -1619,6 +1636,7 @@ class EventsController extends AppController
}
$this->set('deleted', isset($this->params['named']['deleted']) ? ($this->params['named']['deleted'] == 2 ? 0 : 1) : 0);
$this->set('includeRelatedTags', (!empty($this->params['named']['includeRelatedTags'])) ? 1 : 0);
$this->set('includeDecayScore', (!empty($this->params['named']['includeDecayScore'])) ? 1 : 0);
if (!$this->_isRest()) {
if ($this->_isSiteAdmin() && $results[0]['Event']['orgc_id'] !== $this->Auth->user('org_id')) {
$this->Flash->info(__('You are currently logged in as a site administrator and editing an event not belonging to your organisation, which goes against the sharing model of MISP. Please only use this as a last resort and use normal user account for day to day work.'));
@ -1762,11 +1780,12 @@ class EventsController extends AppController
$advancedFilteringActive = array_diff_key($filters, array('sort'=>0, 'direction'=>0, 'focus'=>0, 'extended'=>0, 'overrideLimit'=>0, 'filterColumnsOverwrite'=>0, 'attributeFilter'=>0, 'extended' => 0, 'page' => 0));
if (count($advancedFilteringActive) > 0) {
if (count(array_diff_key($advancedFilteringActive, array('deleted', 'includeRelatedTags'))) > 0) {
if (count(array_diff_key($advancedFilteringActive, array('deleted', 'includeRelatedTags', 'includeDecayScore'))) > 0) {
$res = true;
} else if (
(isset($advancedFilteringActive['deleted']) && $advancedFilteringActive['deleted'] == 2)
|| (isset($advancedFilteringActive['includeRelatedTags']) && $advancedFilteringActive['includeRelatedTags'] == 2)
(isset($advancedFilteringActive['deleted']) && $advancedFilteringActive['deleted'] == 2) ||
(isset($advancedFilteringActive['includeRelatedTags']) && $advancedFilteringActive['includeRelatedTags'] == 1) ||
(isset($advancedFilteringActive['includeDecayScore']) && $advancedFilteringActive['includeDecayScore'] == 1)
) {
$res = true;
} else {

View File

@ -416,7 +416,7 @@ class LogsController extends AppController
$this->set('actions', $actions);
// combobox for models
$models = array('Attribute', 'Event', 'EventBlacklist', 'EventTag', 'MispObject', 'Organisation', 'Post', 'Regexp', 'Role', 'Server', 'ShadowAttribute', 'SharingGroup', 'Tag', 'Task', 'Taxonomy', 'Template', 'Thread', 'User', 'Whitelist');
$models = array('Attribute', 'Event', 'EventBlacklist', 'EventTag', 'DecayingModel', 'MispObject', 'Organisation', 'Post', 'Regexp', 'Role', 'Server', 'ShadowAttribute', 'SharingGroup', 'Tag', 'Task', 'Taxonomy', 'Template', 'Thread', 'User', 'Whitelist');
$models = array('' => 'ALL') + $this->_arrayToValuesIndexArray($models);
$this->set('models', $models);
$this->set('actionDefinitions', $this->{$this->defaultModel}->actionDefinitions);

View File

@ -1620,8 +1620,13 @@ class ServersController extends AppController
{
if ($this->request->is('post')) {
$status = $this->Server->getCurrentGitStatus();
$update = $this->Server->update($status);
return new CakeResponse(array('body'=> $update, 'type' => 'txt'));
$raw = array();
$update = $this->Server->update($status, $raw);
if ($this->_isRest()) {
return $this->RestResponse->viewData(array('results' => $raw), $this->response->type());
} else {
return new CakeResponse(array('body'=> $update, 'type' => 'txt'));
}
} else {
$branch = $this->Server->getCurrentBranch();
$this->set('branch', $branch);

View File

@ -314,88 +314,18 @@ class SightingsController extends AppController
public function listSightings($id = false, $context = 'attribute', $org_id = false)
{
$this->loadModel('Event');
$rawId = $id;
$parameters = array('id', 'context', 'org_id');
foreach ($parameters as $parameter) {
if ($this->request->is('post') && isset($this->request->data[$parameter])) {
${$parameter} = $this->request->data[$parameter];
}
}
$rawId = $id;
$id = is_array($id) ? $id : $this->Sighting->explodeIdList($id);
if ($context === 'attribute') {
$object = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array('conditions' => array('Attribute.id' => $id, 'Attribute.deleted' => 0), 'flatten' => 1));
} else {
// let's set the context to event here, since we reuse the variable later on for some additional lookups.
// Passing $context = 'org' could have interesting results otherwise...
$context = 'event';
$object = $this->Event->fetchEvent($this->Auth->user(), $options = array('eventid' => $id, 'metadata' => true));
}
if (empty($object)) {
throw new MethodNotAllowedException('Invalid object.');
}
$conditions = array(
'Sighting.' . $context . '_id' => $id
);
if ($org_id) {
$this->loadModel('Organisation');
$org_id = $this->Toolbox->findIdByUuid($this->Organisation, $org_id);
$conditions[] = array('Sighting.org_id' => $org_id);
}
$sightings = $this->Sighting->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'contain' => array('Organisation.name'),
'order' => array('Sighting.date_sighting DESC')
));
if (!empty($sightings) && empty(Configure::read('Plugin.Sightings_policy')) && !$this->_isSiteAdmin()) {
$eventOwnerOrgIdList = array();
foreach ($sightings as $k => $sighting) {
if (empty($eventOwnerOrgIdList[$sighting['Sighting']['event_id']])) {
$temp_event = $this->Event->find('first', array(
'recursive' => -1,
'conditions' => array('Event.id' => $sighting['Sighting']['event_id']),
'fields' => array('Event.id', 'Event.orgc_id')
));
$eventOwnerOrgIdList[$temp_event['Event']['id']] = $temp_event['Event']['orgc_id'];
}
if (empty($eventOwnerOrgIdList[$sighting['Sighting']['event_id']]) || $eventOwnerOrgIdList[$sighting['Sighting']['event_id']] !== $this->Auth->user('org_id')) {
unset($sightings[$k]);
}
}
$sightings = array_values($sightings);
} else if (!empty($sightings) && Configure::read('Plugin.Sightings_policy') == 1 && !$this->_isSiteAdmin()) {
$eventsWithOwnSightings = array();
foreach ($sightings as $k => $sighting) {
if (empty($eventsWithOwnSightings[$sighting['Sighting']['event_id']])) {
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = false;
$sighting_temp = $this->Sighting->find('first', array(
'recursive' => -1,
'conditions' => array(
'Sighting.event_id' => $sighting['Sighting']['event_id'],
'Sighting.org_id' => $this->Auth->user('org_id')
)
));
if (empty($sighting_temp)) {
$temp_event = $this->Event->find('first', array(
'recursive' => -1,
'conditions' => array(
'Event.id' => $sighting['Sighting']['event_id'],
'Event.orgc_id' => $this->Auth->user('org_id')
),
'fields' => array('Event.id', 'Event.orgc_id')
));
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = !empty($temp_event);
} else {
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = true;
}
}
if (!$eventsWithOwnSightings[$sighting['Sighting']['event_id']]) {
unset($sightings[$k]);
}
}
$sightings = array_values($sightings);
}
$sightings = $this->Sighting->listSightings($this->Auth->user(), $id, $context, $org_id);
$this->set('org_id', $org_id);
$this->set('rawId', $rawId);
$this->set('context', $context);

View File

@ -75,7 +75,7 @@ class AppModel extends Model
13 => false, 14 => false, 15 => false, 18 => false, 19 => false, 20 => false,
21 => false, 22 => false, 23 => false, 24 => false, 25 => false, 26 => false,
27 => false, 28 => false, 29 => false, 30 => false, 31 => false, 32 => false,
33 => false, 34 => false, 35 => false, 36 => false
33 => false, 34 => false, 35 => false, 36 => false, 37 => false
);
public $advanced_updates_description = array(
@ -1193,11 +1193,44 @@ class AppModel extends Model
KEY `org_id` (`org_id`),
KEY `type` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
break;
break;
case 36:
$sqlArray[] = "ALTER TABLE `event_tags` ADD `local` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "ALTER TABLE `attribute_tags` ADD `local` tinyint(1) NOT NULL DEFAULT 0;";
break;
case 37:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS decaying_models (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8_bin DEFAULT NULL,
`name` varchar(255) COLLATE utf8_bin NOT NULL,
`parameters` text,
`attribute_types` text,
`description` text,
`org_id` int(11),
`enabled` tinyint(1) NOT NULL DEFAULT 0,
`all_orgs` tinyint(1) NOT NULL DEFAULT 1,
`ref` text COLLATE utf8_unicode_ci,
`formula` varchar(255) COLLATE utf8_bin NOT NULL,
`version` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '',
`default` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
INDEX `uuid` (`uuid`),
INDEX `name` (`name`),
INDEX `org_id` (`org_id`),
INDEX `enabled` (`enabled`),
INDEX `all_orgs` (`all_orgs`),
INDEX `version` (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
$sqlArray[] = "CREATE TABLE IF NOT EXISTS decaying_model_mappings (
`id` int(11) NOT NULL AUTO_INCREMENT,
`attribute_type` varchar(255) COLLATE utf8_bin NOT NULL,
`model_id` int(11) NOT NULL,
PRIMARY KEY (id),
INDEX `model_id` (`model_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
$sqlArray[] = "ALTER TABLE `roles` ADD `perm_decaying` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "UPDATE `roles` SET `perm_decaying`=1 WHERE `perm_sighting`=1;";
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -2976,7 +2976,8 @@ class Attribute extends AppModel
$params = array(
'conditions' => $this->buildConditions($user),
'fields' => array(),
'recursive' => -1
'recursive' => -1,
'contain' => array()
);
if (isset($options['conditions'])) {
$params['conditions']['AND'][] = $options['conditions'];
@ -2984,10 +2985,14 @@ class Attribute extends AppModel
if (isset($options['fields'])) {
$params['fields'] = $options['fields'];
}
if (isset($options['contain'])) {
$params['contain'] = $options['contain'];
}
$results = $this->find('all', array(
'conditions' => $params['conditions'],
'recursive' => -1,
'fields' => $params['fields'],
'contain' => $params['contain'],
'sort' => false
));
return $results;
@ -3136,6 +3141,23 @@ class Attribute extends AppModel
if (!isset($options['includeWarninglistHits'])) {
$options['includeWarninglistHits'] = false;
}
if (!isset($options['includeDecayScore'])) {
$options['includeDecayScore'] = false;
}
if (!isset($options['decayingModel'])) {
$options['decayingModel'] = false;
}
if (!isset($options['modelOverrides'])) {
$options['modelOverrides'] = array();
}
if (isset($options['score'])) {
$options['modelOverrides']['threshold'] = $options['score'];
}
if (!isset($options['excludeDecayed'])) {
$options['excludeDecayed'] = 0;
} else {
$options['includeDecayScore'] = true;
}
if (!$user['Role']['perm_sync'] || !isset($options['deleted']) || !$options['deleted']) {
$params['conditions']['AND']['(Attribute.deleted + 0)'] = 0;
} else {
@ -3259,6 +3281,20 @@ class Attribute extends AppModel
$results[$key]['Attribute']['data'] = $encodedFile;
}
}
if ($options['includeDecayScore']) {
$this->DecayingModel = ClassRegistry::init('DecayingModel');
$include_full_model = isset($options['includeFullModel']) && $options['includeFullModel'] ? 1 : 0;
$results[$key]['Attribute'] = $this->DecayingModel->attachScoresToAttribute($user, $results[$key]['Attribute'], $options['decayingModel'], $options['modelOverrides'], $include_full_model);
if ($options['excludeDecayed']) { // filter out decayed attribute
$decayed_flag = true;
foreach ($results[$key]['Attribute']['decay_score'] as $decayResult) { // remove attribute if ALL score results in a decay
$decayed_flag = $decayed_flag && $decayResult['decayed'];
}
if ($decayed_flag) {
unset($results[$key]);
}
}
}
if (!empty($results[$key])) {
if (!empty($options['includeGalaxy'])) {
$massaged_attribute = $this->Event->massageTags($results[$key], 'Attribute');
@ -4231,7 +4267,9 @@ class Attribute extends AppModel
'includeWarninglistHits' => !empty($filters['includeWarninglistHits']) ? $filters['includeWarninglistHits'] : 0,
'includeContext' => !empty($filters['includeContext']) ? $filters['includeContext'] : 0,
'includeSightings' => !empty($filters['includeSightings']) ? $filters['includeSightings'] : 0,
'includeCorrelations' => !empty($filters['includeCorrelations']) ? $filters['includeCorrelations'] : 0
'includeCorrelations' => !empty($filters['includeCorrelations']) ? $filters['includeCorrelations'] : 0,
'includeDecayScore' => !empty($filters['includeDecayScore']) ? $filters['includeDecayScore'] : 0,
'includeFullModel' => !empty($filters['includeFullModel']) ? $filters['includeFullModel'] : 0
);
if (!empty($filters['attackGalaxy'])) {
$params['attackGalaxy'] = $filters['attackGalaxy'];
@ -4248,6 +4286,19 @@ class Attribute extends AppModel
if (!empty($filters['deleted'])) {
$params['deleted'] = $filters['deleted'];
}
if (!empty($filters['excludeDecayed'])) {
$params['excludeDecayed'] = $filters['excludeDecayed'];
$params['includeDecayScore'] = 1;
}
if (!empty($filters['decayingModel'])) {
$params['decayingModel'] = $filters['decayingModel'];
}
if (!empty($filters['modelOverrides'])) {
$params['modelOverrides'] = $filters['modelOverrides'];
}
if (!empty($filters['score'])) {
$params['score'] = $filters['score'];
}
if ($paramsOnly) {
return $params;
}

609
app/Model/DecayingModel.php Normal file
View File

@ -0,0 +1,609 @@
<?php
App::uses('AppModel', 'Model');
App::uses('Folder', 'Utility');
App::uses('File', 'Utility');
class DecayingModel extends AppModel
{
public $actsAs = array('Containable');
public $hasMany = array(
'DecayingModelMapping' => array(
'className' => 'DecayingModelMapping',
'foreignKey' => 'model_id',
'dependent' => true
)
);
private $__registered_model_classes = array(); // Proxy for already instantiated classes
public $allowed_overrides = array('threshold' => 1, 'lifetime' => 1, 'decay_speed' => 1);
public function afterFind($results, $primary = false) {
foreach ($results as $k => $v) {
if (!empty($v['DecayingModel']['parameters'])) {
$decoded = json_decode($v['DecayingModel']['parameters'], true);
if ($decoded === null) {
$decoded = array();
}
$results[$k]['DecayingModel']['parameters'] = $decoded;
}
if (!empty($v['DecayingModel']['attribute_types'])) {
$decoded = json_decode($v['DecayingModel']['attribute_types'], true);
if ($decoded === null) {
$decoded = array();
}
$results[$k]['DecayingModel']['attribute_types'] = $decoded;
} else {
$results[$k]['DecayingModel']['attribute_types'] = array();
}
if (!empty($v['DecayingModel']['ref'])) {
$decoded = json_decode($v['DecayingModel']['ref'], true);
if ($decoded === null) {
$decoded = array();
}
$results[$k]['DecayingModel']['ref'] = $decoded;
}
}
return $results;
}
public function beforeValidate($options = array()) {
parent::beforeValidate();
if (!isset($this->data['DecayingModel']['formula'])) { // default to polynomial
$this->data['DecayingModel']['formula'] = 'polynomial';
}
if ($this->data['DecayingModel']['formula'] == 'polynomial') {
if (isset($this->data['DecayingModel']['parameters']['settings'])) { // polynomial doesn't have custom settings
$this->data['DecayingModel']['parameters']['settings'] = '{}';
}
} else if (
isset($this->data['DecayingModel']['parameters']['settings']) &&
$this->data['DecayingModel']['parameters']['settings'] == ''
) {
$this->data['DecayingModel']['parameters']['settings'] = '{}';
}
if (!empty($this->data['DecayingModel']['attribute_types']) && !is_array($this->data['DecayingModel']['attribute_types'])) {
$encoded = json_decode($this->data['DecayingModel']['attribute_types'], true);
if ($encoded === null) {
return false;
}
}
if (
isset($this->data['DecayingModel']['parameters']) &&
!empty($this->data['DecayingModel']['parameters']) &&
!is_array($this->data['DecayingModel']['parameters'])
) {
$encoded = json_decode($this->data['DecayingModel']['parameters'], true);
if ($encoded === null) {
return false;
}
$encoded = $this->__adjustParameters($encoded);
$this->data['DecayingModel']['parameters'] = json_encode($encoded);
return true;
} else {
$this->data['DecayingModel']['parameters'] = $this->__adjustParameters($this->data['DecayingModel']['parameters']);
return $this->data['DecayingModel']['parameters'];
}
}
public function beforeSave($options = array()) {
if (isset($this->data['DecayingModel']['parameters']) && is_array($this->data['DecayingModel']['parameters'])) {
$this->data['DecayingModel']['parameters'] = json_encode($this->data['DecayingModel']['parameters']);
}
if (isset($this->data['DecayingModel']['parameters']['base_score_config']) && is_array($this->data['DecayingModel']['parameters']['base_score_config'])) {
$this->data['DecayingModel']['parameters']['base_score_config'] = json_encode($this->data['DecayingModel']['parameters']['base_score_config']);
}
if (isset($this->data['DecayingModel']['parameters']['settings']) && is_array($this->data['DecayingModel']['parameters']['settings'])) {
$this->data['DecayingModel']['parameters']['settings'] = json_encode($this->data['DecayingModel']['parameters']['settings']);
}
if (isset($this->data['DecayingModel']['attribute_types']) && is_array($this->data['DecayingModel']['attribute_types'])) {
$this->data['DecayingModel']['attribute_types'] = json_encode($this->data['DecayingModel']['attribute_types']);
}
if (isset($this->data['DecayingModel']['ref']) && is_array($this->data['DecayingModel']['ref'])) {
$this->data['DecayingModel']['ref'] = json_encode($this->data['DecayingModel']['ref']);
}
if (!isset($this->data['DecayingModel']['org_id'])) {
$this->data['DecayingModel']['org_id'] = Configure::read('MISP.host_org_id');
}
return true;
}
/*
* May be improved at some point.
* For now, limit the number of digits for the parameters
*/
private function __adjustParameters($parameters)
{
foreach ($parameters as $name => $value) {
if (is_array($value)) {
$this->__adjustParameters($parameters[$name]);
} else if (is_numeric($value)) {
$parameters[$name] = round($value, 4);
} else if (!empty($value)) {
$parameters[$name] = $value;
} else {
$parameters[$name] = 0;
}
}
return $parameters;
}
private function __load_models($force = false)
{
$dir = new Folder(APP . 'files' . DS . 'misp-decaying-models' . DS . 'models');
$files = $dir->find('.*\.json');
$models = array();
foreach ($files as $file) {
$file = new File($dir->pwd() . DS . $file);
$models[] = json_decode($file->read(), true);
$file->close();
}
return $models;
}
public function update($force=false, $user)
{
$new_models = $this->__load_models($force);
if (empty($new_models)) {
throw new NotFoundException(__('Models could not be loaded or default decaying models folder is empty'));
}
$temp = $this->find('all', array(
'recursive' => -1
));
$existing_models = array();
foreach ($temp as $k => $model) { // create UUID proxy
$existing_models[$model['DecayingModel']['uuid']] = $model['DecayingModel'];
}
foreach ($new_models as $k => $new_model) {
if (isset($existing_models[$new_model['uuid']])) {
$existing_model = $existing_models[$new_model['uuid']];
if ($force || $new_model['version'] > $existing_model['version']) {
$new_model['id'] = $existing_model['id'];
$this->save($new_model);
}
} else {
$this->create();
$new_model['default'] = true;
$this->save($new_model);
}
}
}
public function isEditableByCurrentUser($user, $decaying_model)
{
return (
$user['Role']['perm_site_admin'] ||
(
$user['Role']['perm_decaying'] &&
!$decaying_model['DecayingModel']['default'] &&
$decaying_model['DecayingModel']['org_id'] == $user['org_id']
)
);
}
public function attachIsEditableByCurrentUser($user, $decaying_model)
{
$decaying_model['DecayingModel']['isEditable'] = $this->isEditableByCurrentUser($user, $decaying_model);
return $decaying_model;
}
public function fetchAllDefaultModel($user)
{
$default_models = $this->fetchAllAllowedModels($user, false, array(), array('DecayingModel.default' => true));
return $default_models;
}
public function fetchAllAllowedModels($user, $full=true, $filters=array(), $additionnal_conditions=array())
{
$conditions = array();
if (!$user['Role']['perm_site_admin']) {
$conditions['OR'] = array(
'org_id' => $user['Organisation']['id'],
'all_orgs' => 1
);
}
if (!empty($filters)) {
if (isset($filters['my_models']) && $filters['my_models']) {
$conditions[] = array('DecayingModel.org_id' => $user['Organisation']['id']);
} elseif (isset($filters['default_models']) && $filters['default_models']) {
$conditions[] = array('not' => array('DecayingModel.uuid' => null));
}
}
$conditions[] = array('AND' => $additionnal_conditions);
$decayingModels = $this->find('all', array(
'conditions' => $conditions,
'include' => $full ? 'DecayingModelMapping' :''
));
foreach ($decayingModels as $i => $decayingModel) { // includes both model default mapping and user mappings
if ($full) {
$decayingModels[$i]['DecayingModel']['attribute_types'] = $decayingModels[$i]['DecayingModel']['attribute_types'] + Hash::extract($decayingModels[$i]['DecayingModelMapping'], '{n}.attribute_type');
unset($decayingModels[$i]['DecayingModelMapping']);
$decayingModels[$i]['DecayingModel']['attribute_types'] = array_unique($decayingModels[$i]['DecayingModel']['attribute_types']);
}
$decayingModels[$i]['DecayingModel']['isEditable'] = $this->isEditableByCurrentUser($user, $decayingModels[$i]);
}
return $decayingModels;
}
public function fetchModels($user, $ids, $full=true, $conditions=array(), $attach_editable=0)
{
if (is_numeric($ids)) {
$ids = array($ids);
}
$models = array();
foreach ($ids as $id) {
$model = $this->fetchModel($user, $id, $full, $conditions, $attach_editable);
if (!empty($model)) {
$models[] = $model;
}
}
return $models;
}
// Method that fetches decayingModel
// very flexible, it's basically a replacement for find, with the addition that it restricts access based on user
// - full attach Attribute types associated to the requested model
public function fetchModel($user, $id, $full=true, $conditions=array(), $attach_editable=0)
{
$conditions['id'] = $id;
$searchOptions = array(
'conditions' => $conditions,
);
if (!$full) {
$searchOptions['recursive'] = -1;
}
$decayingModel = $this->find('first', $searchOptions);
// if not found throw
if (empty($decayingModel)) {
return array();
}
if (
!$user['Role']['perm_site_admin'] &&
!( // check owner and visibility
$user['Organisation']['id'] == $decayingModel['DecayingModel']['org_id'] ||
$decayingModel['DecayingModel']['all_orgs']
)
) {
return array();
}
if ($full) {
$decayingModel['DecayingModel']['attribute_types'] = $this->DecayingModelMapping->getAssociatedTypes($user, $decayingModel);
}
$decayingModel = $this->attachIsEditableByCurrentUser($user, $decayingModel);
return $decayingModel;
}
// filter out taxonomies and entries not having a numerical value
public function listTaxonomiesWithNumericalValue()
{
$this->Taxonomy = ClassRegistry::init('Taxonomy');
$this->Tag = ClassRegistry::init('Tag');
$taxonomies = $this->Taxonomy->listTaxonomies(array('full' => true, 'enabled' => true));
$excluded_taxonomies = array();
$start_count = count($taxonomies);
foreach ($taxonomies as $namespace => $taxonomy) {
if(!empty($taxonomy['TaxonomyPredicate'])) {
$tags = $this->Tag->getTagsForNamespace($taxonomy['namespace'], false);
foreach($taxonomy['TaxonomyPredicate'] as $p => $predicate) {
if(!empty($predicate['TaxonomyEntry'])) {
foreach ($predicate['TaxonomyEntry'] as $e => $entry) {
if (!is_numeric($entry['numerical_value'])) {
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]);
} else {
$tag_name = sprintf('%s:%s="%s"', $taxonomy['namespace'], $predicate['value'], $entry['value']);
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]['Tag'] = $tags[strtoupper($tag_name)]['Tag'];
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'][$e]['Tag']['numerical_value'] = $entry['numerical_value'];
}
}
if (empty($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'])) {
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]);
} else {
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry'] = array_values($taxonomies[$namespace]['TaxonomyPredicate'][$p]['TaxonomyEntry']);
}
} else { // accept predicates that have a numerical value
if (!is_numeric($predicate['numerical_value'])) {
unset($taxonomies[$namespace]['TaxonomyPredicate'][$p]);
} else {
$tag_name = sprintf('%s:%s', $taxonomy['namespace'], $predicate['value']);
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['Tag'] = $tags[strtoupper($tag_name)]['Tag'];
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['Tag']['numerical_value'] = $predicate['numerical_value'];
$taxonomies[$namespace]['TaxonomyPredicate'][$p]['numerical_predicate'] = true;
}
}
}
if (empty($taxonomies[$namespace]['TaxonomyPredicate'])) {
$excluded_taxonomies[$namespace] = array('taxonomy' => $taxonomies[$namespace], 'reason' => __('No tags nor predicates with `numerical_value`'));
unset($taxonomies[$namespace]);
} else {
$taxonomies[$namespace]['TaxonomyPredicate'] = array_values($taxonomies[$namespace]['TaxonomyPredicate']);
}
} else {
unset($taxonomies[$namespace]);
$excluded_taxonomies[$namespace] = array('taxonomy' => $taxonomies[$namespace], 'reason' => __('No predicate'));
}
}
return array(
'taxonomies' => $taxonomies,
'excluded_taxonomies' => $excluded_taxonomies,
'not_having_numerical_value' => $start_count - count($taxonomies)
);
}
// Include a PHP file and return an instanciation of the formula class
private function __include_formula_file_and_return_instance($filename='Polynomial.php')
{
$formula_files = $this->__listPHPFormulaFiles(); // redundant in some cases but better be safe than sorry
$index = array_search($filename, $formula_files);
if ($index !== false) {
$filename_no_extension = str_replace('.php', '', $formula_files[$index]);
$expected_classname = $filename_no_extension;
$full_path = APP . 'Model/DecayingModelsFormulas/' . $formula_files[$index];
if (is_file($full_path)) {
include_once $full_path;
try {
$model_class = ClassRegistry::init($expected_classname);
if ($model_class->checkLoading() === 'BONFIRE LIT') {
return $model_class;
}
} catch (Exception $e) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'DecayingModel',
'model_id' => 0,
'email' => 'SYSTEM',
'action' => 'include_formula',
'title' => sprintf('Error while trying to include file `%s`: %s', $filename, $e->getMessage()),
'change' => ''
));
return false;
}
}
}
return false;
}
private function __listPHPFormulaFiles()
{
$dir = new Folder(APP . 'Model/DecayingModelsFormulas');
$files = $dir->find('.*\.php', true);
$files = array_diff($files, array('..', '.', 'Base.php'));
return $files;
}
public function listAvailableFormulas()
{
$formula_files = $this->__listPHPFormulaFiles();
$available_formulas = array();
foreach ($formula_files as $formula_file) {
$model_class = $this->__include_formula_file_and_return_instance($formula_file);
if ($model_class === false) {
continue;
}
$available_formulas[get_class($model_class)] = array(
'parent_class' => get_parent_class($model_class) == 'Polynomial' || get_class($model_class) == 'Polynomial' ? 'Polynomial' : get_class($model_class),
'description' => $model_class->description
);
}
return $available_formulas;
}
// Get a instance of the class associated to a model
public function getModelClass($model)
{
$formula_name = $model['DecayingModel']['formula'] === '' ? 'polynomial' : $model['DecayingModel']['formula'];
$expected_filename = Inflector::humanize($formula_name) . '.php';
if (!isset($this->__registered_model_classes[$formula_name])) {
$model_class = $this->__include_formula_file_and_return_instance($expected_filename);
if ($model_class === false) {
throw new NotFoundException(sprintf(__('The class for `%s` was not found or not loaded correctly'), $formula_name));
}
$this->__registered_model_classes[$formula_name] = $model_class;
}
return $this->__registered_model_classes[$formula_name];
}
// returns timestamp set to the rounded hour
public function round_timestamp_to_hour($time, $floor=1)
{
if ($floor) {
return floor((float) $time / 3600) * 3600;
} else {
return round((float) $time / 3600) * 3600;
}
}
// Returns score overtime, sightings, base_score computation and other useful information
public function getScoreOvertime($user, $model_id, $attribute_id, $model_overrides)
{
$this->Attribute = ClassRegistry::init('Attribute');
$attribute = $this->Attribute->fetchAttributes($user, array(
'conditions' => array('Attribute.id' => $attribute_id),
'contain' => array('AttributeTag' => array('Tag')),
'flatten' => 1
));
if (empty($attribute)) {
throw new NotFoundException(__('Attribute not found'));
} else {
$attribute = $attribute[0];
$tagConditions = array('EventTag.event_id' => $attribute['Attribute']['event_id']);
$temp = $this->Attribute->Event->EventTag->find('all', array(
'recursive' => -1,
'contain' => array('Tag'),
'conditions' => $tagConditions
));
foreach ($temp as $tag) {
$tag['EventTag']['Tag'] = $tag['Tag'];
unset($tag['Tag']);
$attribute['Attribute']['EventTag'][] = $tag['EventTag'];
}
$attribute['Attribute']['AttributeTag'] = $attribute['AttributeTag'];
unset($attribute['AttributeTag']);
}
$model = $this->fetchModel($user, $model_id, true);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
if (!empty($model_overrides)) {
$model = $this->overrideModelParameters($model, $model_overrides);
}
$this->Computation = $this->getModelClass($model);
$this->Sighting = ClassRegistry::init('Sighting');
$sightings = $this->Sighting->listSightings($user, $attribute_id, 'attribute', false, 0, false);
if (empty($sightings)) {
$sightings = array(array('Sighting' => array('date_sighting' => $attribute['Attribute']['timestamp']))); // simulate a Sighting nonetheless
}
foreach ($sightings as $i => $sighting) {
$sightings[$i]['Sighting']['rounded_timestamp'] = $this->round_timestamp_to_hour($sighting['Sighting']['date_sighting']);
}
// get start time
$start_time = $attribute['Attribute']['timestamp'];
$start_time = $sightings[0]['Sighting']['date_sighting'] < $start_time ? $sightings[0]['Sighting']['date_sighting'] : $start_time;
$start_time = intval($start_time);
$start_time = $this->round_timestamp_to_hour($start_time);
// get end time
$last_sighting_timestamp = $sightings[count($sightings)-1]['Sighting']['date_sighting'];
if ($attribute['Attribute']['timestamp'] > $last_sighting_timestamp) { // The attribute was modified after the last sighting, simulate a Sighting
$sightings[count($sightings)] = array(
'Sighting' => array(
'date_sighting' => $attribute['Attribute']['timestamp'],
'type' => 0,
'rounded_timestamp' => $this->round_timestamp_to_hour($attribute['Attribute']['timestamp'])
)
);
$last_sighting_timestamp = $attribute['Attribute']['timestamp'];
}
$end_time = $last_sighting_timestamp + $model['DecayingModel']['parameters']['lifetime']*24*60*60;
$end_time = $this->round_timestamp_to_hour($end_time);
$base_score_config = $this->Computation->computeBasescore($model, $attribute['Attribute']);
$base_score = $base_score_config['base_score'];
// generate time range from oldest timestamp to last decay, resolution is hours
$score_overtime = array();
$rounded_sightings = array();
$sighting_index = 0;
for ($t=$start_time; $t < $end_time; $t+=3600) {
// fetch closest sighting to the current time
$sighting_index = $this->getClosestSighting($sightings, $t, $sighting_index);
$last_sighting = $sightings[$sighting_index]['Sighting']['rounded_timestamp'];
$elapsed_time = $t - $last_sighting;
$score_overtime[$t] = $this->Computation->computeScore($model, $attribute['Attribute'], $base_score, $elapsed_time);
}
$csv = 'date,value' . PHP_EOL;
foreach ($score_overtime as $t => $v) {
$csv .= (new DateTime())->setTimestamp($t)->format('Y-m-d H:i:s') . ',' . $v . PHP_EOL;
}
return array(
'csv' => $csv,
'sightings' => $sightings,
'base_score_config' => $base_score_config,
'last_sighting' => $sightings[count($sightings)-1],
'current_score' => $this->Computation->computeCurrentScore($user, $model, $attribute['Attribute'], $base_score, $last_sighting_timestamp),
'Model' => $model['DecayingModel']
);
}
// Get closest the Sighting for a given time
public function getClosestSighting($sightings, $time, $offset)
{
if (count($sightings) <= $offset+1) {
return $offset;
}
$max_time = $time + 3600;
$next_index = $offset+1;
$next_sighting = $sightings[$next_index]['Sighting']['date_sighting'];
while ($next_sighting <= $max_time) {
$next_index++;
if ($next_index >= count($sightings)) {
break;
}
$next_sighting = $sightings[$next_index]['Sighting']['date_sighting'];
}
return $next_index-1;
}
public function overrideModelParameters($model, $model_overrides)
{
foreach ($model_overrides as $parameter => $value) {
if (isset($this->allowed_overrides[$parameter])) {
$model['DecayingModel']['parameters'][$parameter] = $value;
}
}
return $model;
}
public function attachScoresToAttribute($user, $attribute, $model_id=false, $model_overrides=array(), $include_full_model=0)
{
$models = array();
if ($model_id === false) { // fetch all allowed and associated models
$associated_model_ids = $this->DecayingModelMapping->getAssociatedModels($user, $attribute['type'], true);
$associated_model_ids = isset($associated_model_ids[$attribute['type']]) ? array_values($associated_model_ids[$attribute['type']]) : array();
if (!empty($associated_model_ids)) {
$models = $this->fetchModels($user, $associated_model_ids, false, array('enabled' => true));
}
} else {
$models = $this->fetchModels($user, $model_id, false, array());
}
foreach ($models as $model) {
if (!empty($model_overrides)) {
$model = $this->overrideModelParameters($model, $model_overrides);
}
$score = $this->getScore($attribute, $model, $user);
$decayed = $this->isDecayed($attribute, $model, $score);
$to_attach = array(
'score' => $score,
'decayed' => $decayed,
'DecayingModel' => array(
'id' => $model['DecayingModel']['id'],
'name' => $model['DecayingModel']['name']
)
);
if ($include_full_model) {
$to_attach['DecayingModel'] = $model['DecayingModel'];
}
$attribute['decay_score'][] = $to_attach;
}
return $attribute;
}
public function getScore($attribute, $model, $user=false)
{
if (is_numeric($attribute) && $user !== false) {
$this->Attribute = ClassRegistry::init('Attribute');
$attribute = $this->Attribute->fetchAttributes($user, array(
'conditions' => array('Attribute.id' => $attribute),
'contain' => array('AttributeTag' => array('Tag'))
));
}
if (is_numeric($model) && $user !== false) {
$model = $this->fetchModel($user, $model);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
}
$this->Computation = $this->getModelClass($model);
return $this->Computation->computeCurrentScore($user, $model, $attribute);
}
public function isDecayed($attribute, $model, $score=false, $user=false)
{
if ($score === false) {
$score = $this->getScore($attribute, $model, $user);
}
$this->Computation = $this->getModelClass($model);
return $this->Computation->isDecayed($model, $attribute, $score);
}
}

View File

@ -0,0 +1,117 @@
<?php
App::uses('AppModel', 'Model');
class DecayingModelMapping extends AppModel
{
public $actsAs = array('Containable');
public $validate = array(
'attribute_type' => array(
'valueNotEmpty' => array(
'rule' => array('valueNotEmpty'),
),
),
'model_id' => array(
'valueNotEmpty' => array(
'rule' => array('valueNotEmpty'),
),
),
);
public $belongsTo = array(
'DecayingModel' => array(
'className' => 'DecayingModel',
'foreignKey' => 'id'
)
);
public function resetMappingForModel($new_model, $user) {
if (empty($new_model['model_id'])) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
$this->deleteAll(array(
'model_id' => $new_model['model_id']
));
$data = array();
foreach ($new_model['attribute_types'] as $type) {
$to_save = array(
'attribute_type' => $type,
'model_id' => $new_model['model_id']
);
$data[] = $to_save;
}
$result = $this->saveMany($data, array(
'atomic' => true
));
if ($result) {
return $new_model['attribute_types'];
} else {
return array();
}
}
public function getAssociatedTypes($user, $model) {
if (is_numeric($model)) {
$model = $this->DecayingModel->fetchModel($user, $model, false);
if (empty($model)) {
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
}
}
$decaying_model = isset($model['DecayingModel']) ? $model['DecayingModel'] : $model;
if ($decaying_model['default']) {
$associated_types = $decaying_model['attribute_types'];
} else {
$temp = $this->find('list', array(
'conditions' => array(
'model_id' => $decaying_model['id']
),
'recursive' => -1,
'fields' => array('attribute_type')
));
$associated_types = array_values($temp);
}
return $associated_types;
}
public function getAssociatedModels($user, $attribute_type = false) {
$conditions = array(
'OR' => array(
'DecayingModel.org_id' => $user['org_id'],
'DecayingModel.all_orgs' => true
)
);
if ($attribute_type !== false) {
$conditions['attribute_type'] = $attribute_type;
}
$associated_models = $this->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'fields' => array('attribute_type', 'model_id'),
'joins' => array( // joins has to be done to enforce ACL
array(
'table' => 'decaying_models',
'alias' => 'DecayingModel',
'type' => 'INNER',
'conditions' => array(
'DecayingModel.id = DecayingModelMapping.model_id'
)
)
)
));
// Also add default models to selection
$default_models = $this->DecayingModel->fetchAllDefaultModel($user);
$associated_default_models = array();
foreach ($default_models as $i => $model) {
$intersection = array_intersect($model['DecayingModel']['attribute_types'], array($attribute_type));
if (count($intersection) > 0) {
$associated_default_models[$attribute_type][] = $model['DecayingModel']['id'];
}
}
$associated_models = Hash::combine($associated_models, '{n}.DecayingModelMapping.model_id', '{n}.DecayingModelMapping.model_id', '{n}.DecayingModelMapping.attribute_type');
$models = array_merge_recursive($associated_default_models, $associated_models);
return $models;
}
}

View File

@ -0,0 +1,128 @@
<?php
abstract class DecayingModelBase
{
public function checkLoading()
{
return 'BONFIRE LIT';
}
// Get effective taxonomy ratio based on taxonomies attached to the attribute
// Basically, it adapts the ratio defined in the model to fit the actual attached tags
protected function __getRatioScore($model, $tags)
{
$ratioScore = array();
$taxonomy_base_ratio = $model['DecayingModel']['parameters']['base_score_config'];
if (empty($taxonomy_base_ratio)) {
return array();
}
$total_score = 0.0;
foreach ($tags as $tag) {
$namespace_predicate = explode('=', $tag['Tag']['name'])[0];
if (isset($taxonomy_base_ratio[$namespace_predicate]) && is_numeric($tag['Tag']['numerical_value'])) {
$total_score += floatval($taxonomy_base_ratio[$namespace_predicate]);
}
}
foreach ($tags as $i => $tag) {
$namespace_predicate = explode('=', $tag['Tag']['name'])[0];
if (isset($taxonomy_base_ratio[$namespace_predicate]) && is_numeric($tag['Tag']['numerical_value'])) {
$ratioScore[$namespace_predicate] = floatval($taxonomy_base_ratio[$namespace_predicate]) / $total_score;
}
}
return $ratioScore;
}
// return attribute tag with event tag matching the namespace+predicate overridden
protected function __getPrioritisedTag($attribute)
{
$tags = array();
$overridden_tags = array();
$temp_mapping = array();
if (isset($attribute['EventTag'])) {
foreach ($attribute['EventTag'] as $i => $tag) {
$tags[] = $tag;
$namespace_predicate = explode('=', $tag['Tag']['name'])[0];
$temp_mapping[$namespace_predicate][] = $i;
}
}
if (isset($attribute['AttributeTag'])) {
foreach ($attribute['AttributeTag'] as $tag) {
$namespace_predicate = explode('=', $tag['Tag']['name'])[0];
if (!empty($temp_mapping[$namespace_predicate])) { // need to override event tag
foreach ($temp_mapping[$namespace_predicate] as $i => $eventtag_index) {
$overridden_tags[] = array(
'EventTag' => $tags[$eventtag_index],
'AttributeTag' => $tag
);
if ($i === 0) { // override first one
$tags[$eventtag_index] = $tag;
} else { // remove remaining overriden
unset($tags[$eventtag_index]);
}
}
} else {
$tags[] = $tag;
}
}
}
return array('tags' => array_values($tags), 'overridden' => $overridden_tags);
}
public function computeBasescore($model, $attribute)
{
$temp = $this->__getPrioritisedTag($attribute);
$tags = $temp['tags'];
$overridden_tags = $temp['overridden'];
$taxonomy_effective_ratios = $this->__getRatioScore($model, $tags);
$default_base_score = isset($model['DecayingModel']['parameters']['default_base_score']) ? $model['DecayingModel']['parameters']['default_base_score'] : 0 ;
$base_score = 0;
$flag_contain_matching_taxonomy = false;
if (!empty($taxonomy_effective_ratios)) {
foreach ($tags as $k => $tag) {
$taxonomy = explode('=', $tag['Tag']['name'])[0];
if (isset($taxonomy_effective_ratios[$taxonomy])) {
$flag_contain_matching_taxonomy = true;
$base_score += $taxonomy_effective_ratios[$taxonomy] * $tag['Tag']['numerical_value'];
}
}
}
if (!$flag_contain_matching_taxonomy) {
$base_score = $default_base_score;
}
return array(
'base_score' => $base_score,
'overridden' => $overridden_tags,
'tags' => $tags,
'taxonomy_effective_ratios' => $taxonomy_effective_ratios,
'default_base_score' => $default_base_score
);
}
// Compute the current score for the provided attribute according to the last sighting with the provided model
final public function computeCurrentScore($user, $model, $attribute, $base_score = false, $last_sighting_timestamp = false)
{
if ($base_score === false) {
$base_score = $this->computeBasescore($model, $attribute)['base_score'];
}
if ($last_sighting_timestamp === false) {
$this->Sighting = ClassRegistry::init('Sighting');
$all_sightings = $this->Sighting->listSightings($user, $attribute['id'], 'attribute', false, 0, true);
if (!empty($all_sightings)) {
$last_sighting_timestamp = $all_sightings[0]['Sighting']['date_sighting'];
} else {
$last_sighting_timestamp = $attribute['timestamp']; // if no sighting, take the last update time
}
}
if ($attribute['timestamp'] > $last_sighting_timestamp) { // The attribute was modified after the last sighting
$last_sighting_timestamp = $attribute['timestamp'];
}
$timestamp = time();
return $this->computeScore($model, $attribute, $base_score, $timestamp - $last_sighting_timestamp);
}
// Compute the score for the provided attribute according to the elapsed time with the provided model
abstract public function computeScore($model, $attribute, $base_score, $elapsed_time);
// Return a True if the attribute should be marked as decayed
abstract public function isDecayed($model, $attribute, $score);
}
?>

View File

@ -0,0 +1,27 @@
<?php
include_once 'Base.php';
class Polynomial extends DecayingModelBase
{
function __construct() {
$this->description = __('The implementation of the decaying formula from the paper `An indicator scoring method for MISP platforms`.');
}
public function computeScore($model, $attribute, $base_score, $elapsed_time)
{
if ($elapsed_time < 0) {
return 0;
}
$decay_speed = $model['DecayingModel']['parameters']['decay_speed'];
$lifetime = $model['DecayingModel']['parameters']['lifetime']*24*60*60;
$score = $base_score * (1 - pow($elapsed_time / $lifetime, 1 / $decay_speed));
return $score < 0 ? 0 : $score;
}
public function isDecayed($model, $attribute, $score)
{
$threshold = $model['DecayingModel']['parameters']['threshold'];
return $threshold > $score;
}
}
?>

View File

@ -0,0 +1,54 @@
<?php
include_once 'Polynomial.php';
class PolynomialExtended extends Polynomial
{
function __construct() {
$this->description = __('The implementation of the decaying formula from the paper `An indicator scoring method for MISP platforms` with support of the `Retention` taxonomy which overrides the final score.');
// setup `retention` taxonomy
$this->Taxonomy = ClassRegistry::init('Taxonomy');
$retention_taxonomy_id = $this->Taxonomy->find('first', array(
'recursive' => -1,
'conditions' => array('LOWER(Taxonomy.namespace)' => 'retention'),
'fields' => array('id')
));
if (empty($retention_taxonomy_id)) {
throw new Exception(__('`Retention` taxonomy not available'));
} else {
$retention_taxonomy_id = $retention_taxonomy_id['Taxonomy']['id'];
}
$taxonomy = $this->Taxonomy->getTaxonomy($retention_taxonomy_id, array('full' => true));
$this->retention_taxonomy = array();
foreach ($taxonomy['entries'] as $k => $entry) {
$this->retention_taxonomy[$entry['tag']] = $entry['numerical_value'];
}
}
public function computeScore($model, $attribute, $base_score, $elapsed_time)
{
$score = parent::computeScore($model, $attribute, $base_score, $elapsed_time);
// handle `retention` taxonomy tags
$temp = $this->__getPrioritisedTag($attribute);
$tags = $temp['tags'];
foreach ($tags as $tag) {
$tagname = $tag['Tag']['name'];
if (isset($this->retention_taxonomy[$tagname])) {
$timestamp = intval($attribute['timestamp']);
$now = time();
$eol_time = $this->retention_taxonomy[$tagname] * 24 * 60 * 60; // `retention` taxonomy numerical_value are in seconds
if (($now - $timestamp) > $eol_time) {
return 0;
}
}
}
return $score < 0 ? 0 : $score;
}
public function isDecayed($model, $attribute, $score)
{
return parent::isDecayed($model, $attribute, $score);
}
}
?>

View File

@ -1816,7 +1816,8 @@ class Event extends AppModel
'extended',
'excludeGalaxy',
'includeRelatedTags',
'excludeLocalTags'
'excludeLocalTags',
'includeDecayScore'
);
if (!isset($options['excludeLocalTags']) && !empty($user['Role']['perm_sync']) && empty($user['Role']['perm_site_admin'])) {
$options['excludeLocalTags'] = 1;
@ -1824,6 +1825,9 @@ class Event extends AppModel
if (!isset($options['excludeGalaxy']) || !$options['excludeGalaxy']) {
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
}
if (!empty($options['includeDecayScore'])) {
$this->DecayingModel = ClassRegistry::init('DecayingModel');
}
foreach ($possibleOptions as &$opt) {
if (!isset($options[$opt])) {
$options[$opt] = false;
@ -2174,6 +2178,15 @@ class Event extends AppModel
$event['Attribute'][$key]['data'] = $encodedFile;
}
}
if (!empty($options['includeDecayScore'])) {
if (isset($event['EventTag'])) { // include EventTags for score computation
$event['Attribute'][$key]['EventTag'] = $event['EventTag'];
}
$event['Attribute'][$key] = $this->DecayingModel->attachScoresToAttribute($user, $event['Attribute'][$key]);
if (isset($event['EventTag'])) { // remove included EventTags
unset($event['Attribute'][$key]['EventTag']);
}
}
// unset empty attribute tags that got added because the tag wasn't exportable
if (!empty($attribute['AttributeTag'])) {
foreach ($attribute['AttributeTag'] as $atk => $attributeTag) {

View File

@ -35,6 +35,7 @@ class Log extends AppModel
'export',
'file_upload',
'galaxy',
'include_formula',
'login',
'login_fail',
'logout',

View File

@ -41,4 +41,21 @@ class ObjectTemplateElement extends AppModel
}
return true;
}
public function getAllAvailableTypes() {
$temp = $this->find('all', array(
'recursive' => -1,
'fields' => array('object_relation as type', 'description AS desc', 'categories'),
'group' => 'type'
));
$res = array();
foreach ($temp as $type) {
$res[$type['ObjectTemplateElement']['type']] = array(
'desc' => $type['ObjectTemplateElement']['desc'],
'category' => $type['ObjectTemplateElement']['categories']
);
}
return $res;
}
}

View File

@ -134,6 +134,12 @@ class Role extends AppModel
'readonlyenabled' => false,
'title' => 'Create or modify MISP Object templates'
),
'perm_decaying' => array(
'id' => 'RolePermDecaying',
'text' => 'Decaying Model Editor',
'readonlyenabled' => true,
'title' => 'Create or modify MISP Decaying Models'
),
// Urgently needed permission flag to avoid waking up next to a decapitated horse head sent by Enrico
'perm_publish_zmq' => array(
'id' => 'RolePermPublishZmq',

View File

@ -4873,6 +4873,7 @@ class Server extends AppModel
'app/files/misp-objects',
'app/files/noticelists',
'app/files/warninglists',
'app/files/misp-decaying-models',
'cti-python-stix2'
);
return in_array($submodule, $accepted_submodules_names);
@ -5010,7 +5011,7 @@ class Server extends AppModel
return implode('\n', $result);
}
public function update($status)
public function update($status, &$raw = array())
{
$final = '';
$workingDirectoryPrefix = 'cd $(git rev-parse --show-toplevel) && ';
@ -5020,17 +5021,35 @@ class Server extends AppModel
);
foreach ($cleanup_commands as $cleanup_command) {
$final .= $cleanup_command . "\n\n";
exec($cleanup_command, $output);
$status = false;
exec($cleanup_command, $output, $status);
$raw[] = array(
'input' => $cleanup_command,
'output' => $output,
'status' => $status
);
$final .= implode("\n", $output) . "\n\n";
}
$command1 = $workingDirectoryPrefix . 'git pull origin ' . $status['branch'] . ' 2>&1';
$command2 = $workingDirectoryPrefix . 'git submodule update --init --recursive 2>&1';
$final .= $command1 . "\n\n";
exec($command1, $output);
$status = false;
exec($command1, $output, $status);
$raw[] = array(
'input' => $command1,
'output' => $output,
'status' => $status
);
$final .= implode("\n", $output) . "\n\n=================================\n\n";
$output = array();
$final .= $command2 . "\n\n";
exec($command2, $output);
$status = false;
exec($command2, $output, $status);
$raw[] = array(
'input' => $command2,
'output' => $output,
'status' => $status
);
$final .= implode("\n", $output);
return $final;
}

View File

@ -476,6 +476,90 @@ class Sighting extends AppModel
return $sightingsRearranged;
}
public function listSightings($user, $id, $context, $org_id = false, $sightings_type = false, $order_desc = true)
{
$this->Event = ClassRegistry::init('Event');
$id = is_array($id) ? $id : $this->explodeIdList($id);
if ($context === 'attribute') {
$object = $this->Event->Attribute->fetchAttributes($user, array('conditions' => array('Attribute.id' => $id, 'Attribute.deleted' => 0), 'flatten' => 1));
} else {
// let's set the context to event here, since we reuse the variable later on for some additional lookups.
// Passing $context = 'org' could have interesting results otherwise...
$context = 'event';
$object = $this->Event->fetchEvent($user, $options = array('eventid' => $id, 'metadata' => true));
}
if (empty($object)) {
throw new MethodNotAllowedException('Invalid object.');
}
$conditions = array(
'Sighting.' . $context . '_id' => $id
);
if ($org_id) {
$conditions[] = array('Sighting.org_id' => $org_id);
}
if ($sightings_type !== false) {
$conditions[] = array('Sighting.type' => $sightings_type);
}
$sightings = $this->find('all', array(
'conditions' => $conditions,
'recursive' => -1,
'contain' => array('Organisation.name'),
'order' => array(sprintf('Sighting.date_sighting %s', $order_desc ? 'DESC' : ''))
));
if (!empty($sightings) && empty(Configure::read('Plugin.Sightings_policy')) && !$user['Role']['perm_site_admin']) {
$eventOwnerOrgIdList = array();
foreach ($sightings as $k => $sighting) {
if (empty($eventOwnerOrgIdList[$sighting['Sighting']['event_id']])) {
$temp_event = $this->Event->find('first', array(
'recursive' => -1,
'conditions' => array('Event.id' => $sighting['Sighting']['event_id']),
'fields' => array('Event.id', 'Event.orgc_id')
));
$eventOwnerOrgIdList[$temp_event['Event']['id']] = $temp_event['Event']['orgc_id'];
}
if (
empty($eventOwnerOrgIdList[$sighting['Sighting']['event_id']]) ||
($eventOwnerOrgIdList[$sighting['Sighting']['event_id']] !== $user['org_id'] && $sighting['Sighting']['org_id'] !== $user['org_id'])
) {
unset($sightings[$k]);
}
}
$sightings = array_values($sightings);
} else if (!empty($sightings) && Configure::read('Plugin.Sightings_policy') == 1 && !$user['Role']['perm_site_admin']) {
$eventsWithOwnSightings = array();
foreach ($sightings as $k => $sighting) {
if (empty($eventsWithOwnSightings[$sighting['Sighting']['event_id']])) {
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = false;
$sighting_temp = $this->find('first', array(
'recursive' => -1,
'conditions' => array(
'Sighting.event_id' => $sighting['Sighting']['event_id'],
'Sighting.org_id' => $user['org_id']
)
));
if (empty($sighting_temp)) {
$temp_event = $this->Event->find('first', array(
'recursive' => -1,
'conditions' => array(
'Event.id' => $sighting['Sighting']['event_id'],
'Event.orgc_id' => $user['org_id']
),
'fields' => array('Event.id', 'Event.orgc_id')
));
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = !empty($temp_event);
} else {
$eventsWithOwnSightings[$sighting['Sighting']['event_id']] = true;
}
}
if (!$eventsWithOwnSightings[$sighting['Sighting']['event_id']]) {
unset($sightings[$k]);
}
}
$sightings = array_values($sightings);
}
return $sightings;
}
public function restSearch($user, $returnFormat, $filters)
{
if (!isset($this->validFormats[$returnFormat][1])) {

View File

@ -478,6 +478,9 @@ class Taxonomy extends AppModel
));
$taxonomies = array();
foreach ($temp as $t) {
if (isset($options['full']) && $options['full']) {
$t['Taxonomy']['TaxonomyPredicate'] = $t['TaxonomyPredicate'];
}
$taxonomies[$t['Taxonomy']['namespace']] = $t['Taxonomy'];
}
return $taxonomies;

View File

@ -0,0 +1,117 @@
<div class="form">
<?php echo $this->Form->create('DecayingModel');?>
<fieldset>
<legend><?php echo Inflector::humanize($action) . __(' Decaying Model');?></legend>
<?php
if (isset($restrictEdition) && $restrictEdition) {
echo '<div class="alert alert-warning">' . __('You are editing a Default Model, only restricted edition is allowed.') . '</div>';
echo $this->Form->input('all_orgs', array(
'label' => __('Can other organization use this model'),
'type' => 'checkbox',
'checked' => isset($this->request->data['DecayingModel']['all_orgs']) ? $this->request->data['DecayingModel']['all_orgs'] : true
));
echo $this->Form->input('enabled', array(
'type' => 'checkbox'
));
} else {
echo $this->Form->input('name', array(
'type' => 'text'
));
echo $this->Form->input('description', array(
));
echo $this->Form->input('formula', array(
'value' => isset($this->request->data['DecayingModel']['formula']) ? $this->request->data['DecayingModel']['formula'] : 'Polynomial',
'options' => $available_formulas,
'div' => 'clear'
));
echo $this->Form->input('all_orgs', array(
'label' => __('Can other organization use this model'),
'type' => 'checkbox',
'checked' => isset($this->request->data['DecayingModel']['all_orgs']) ? $this->request->data['DecayingModel']['all_orgs'] : true
));
echo $this->Form->input('enabled', array(
'type' => 'checkbox'
));
echo '<div id="ContainerPolynomialSetting">';
echo $this->Form->input('DecayingModel.parameters.lifetime', array(
'label' => sprintf('<b>%s</b> [%s]: %s', __('Lifetime'), __('days'), __('Lifetime of the attribute, or time after which the score will be 0')),
'type' => 'number',
'min' => 0,
'title' => _('The end of life of the indicator'),
'class' => 'form-control span6',
'div' => 'clear',
'value' => isset($this->request->data['DecayingModel']['parameters']['lifetime']) ? $this->request->data['DecayingModel']['parameters']['lifetime'] : ''
));
echo $this->Form->input('DecayingModel.parameters.decay_speed', array(
'label' => sprintf('<b>%s</b> [%s]: %s', __('Decay speed'), __('float'), __('Decay speed at which an indicator will loose score')),
'type' => 'number',
'min' => 0,
'step' => 0.01,
'title' => _('The decay speed of the indicator'),
'class' => 'form-control span6',
'div' => 'clear',
'value' => isset($this->request->data['DecayingModel']['parameters']['decay_speed']) ? $this->request->data['DecayingModel']['parameters']['decay_speed'] : ''
));
echo $this->Form->input('DecayingModel.parameters.threshold', array(
'label' => sprintf('<b>%s</b> [%s]: %s', __('Cutoff threshold'), __('float'), __('Cutoff value at which an indicator will be marked as decayed instead of 0')),
'type' => 'number',
'min' => 0,
'title' => _('The model threshold of the indicator'),
'class' => 'form-control span6',
'div' => 'clear',
'value' => isset($this->request->data['DecayingModel']['parameters']['threshold']) ? $this->request->data['DecayingModel']['parameters']['threshold'] : ''
));
echo $this->Form->input('DecayingModel.parameters.default_base_score', array(
'label' => sprintf('<b>%s</b> [%s]: %s', __('Default base_score'), __('float'), __('Default base_score value if no tags are attached to the indicator')),
'type' => 'number',
'min' => 0,
'max' => 100,
'title' => _('The model default base_score of the indicator'),
'class' => 'form-control span6',
'div' => 'clear',
'value' => isset($this->request->data['DecayingModel']['parameters']['default_base_score']) ? $this->request->data['DecayingModel']['parameters']['default_base_score'] : ''
));
echo '<div class="clear"></div>';
echo '<label for="DecayingModelParametersBaseScoreConfig"><b>' . __('Base Score configuration') . '</b> [json]</label>';
echo $this->Form->textarea('DecayingModel.parameters.base_score_config', array(
'class' => 'form-control span6',
'cols' => '10',
'value' => isset($this->request->data['DecayingModel']['parameters']['base_score_config']) ? json_encode($this->request->data['DecayingModel']['parameters']['base_score_config']) : '{}'
));
echo '</div>';
echo '<div id="ContainerOtherSetting">';
echo '<div class="clear"></div>';
echo '<label for="DecayingModelOtherSettings"><b>' . __('Model Settings') . '</b> [json]</label>';
echo $this->Form->textarea('DecayingModel.parameters.settings', array(
'class' => 'form-control span6',
'cols' => '10',
'value' => isset($this->request->data['DecayingModel']['parameters']['settings']) ? json_encode($this->request->data['DecayingModel']['parameters']['settings']) : '{}'
));
echo '</div>';
}
?>
<div class="clear"></div>
</fieldset>
<?php
echo $this->Form->button(Inflector::humanize($action), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'add'));
?>
<script>
toggleOtherSetting();
$(document).ready(function() {
$('#DecayingModelFormula').on('input', function() {
toggleOtherSetting();
})
});
function toggleOtherSetting() {
if ($('#DecayingModelFormula').val() === 'Polynomial') {
$('#ContainerOtherSetting').hide();
} else {
$('#ContainerOtherSetting').show();
}
}
</script>

View File

@ -0,0 +1,5 @@
<div>
<?php
echo $this->Form->postLink(__('Disable model'), '/DecayingModel/disable/' . h($model['id']))
?>
</div>

View File

@ -0,0 +1,5 @@
<div>
<?php
echo $this->Form->postLink(__('Enable model'), '/DecayingModel/enable/' . h($model['id']))
?>
</div>

View File

@ -0,0 +1,211 @@
<div class="view">
<h2>Decaying Of Indicator Fine Tuning Tool</h2>
<div class="row">
<div class="span9 form-inline" style="border: 1px solid #ddd; border-radius: 4px; margin-bottom: 15px;">
<div style="border-bottom: 1px solid #ddd;">
<label class="checkbox inline">
<input id="table_toggle_all_type" type="checkbox"></input>
<?php echo __('Show All Types'); ?>
</label>
<label class="checkbox inline">
<input id="table_toggle_objects" type="checkbox"></input>
<?php echo __('Show MISP Objects'); ?>
</label>
<input id="table_type_search" class="input" type="text" placeholder="<?php echo __('Search Attribute Type'); ?>"></input>
<button class="btn btn-primary btn-small" onclick="decayingTool.restoreSelection()"><span class="fa fa-history"></span></button>
</div>
<div class="AttributeTypeTableContainer">
<table id="table_attribute_type" class="table table-striped table-bordered">
<thead>
<tr>
<th><input id="checkAll" type="checkbox" title="<?php echo __('Check all'); ?>"></input></th>
<th><?php echo __('Attribute Type'); ?></th>
<th><?php echo __('Category'); ?></th>
<th><?php echo __('Model ID'); ?></th>
</tr>
</thead>
<tbody id="attributeTypeTableBody">
<?php foreach ($types as $type => $info): ?>
<?php
$class = 'hidden ';
if (
isset($info['isObject']) && $info['isObject'] &&
!(isset($info['isAttribute']) && $info['isAttribute'])
) {
$class .= 'isObject';
} else if (isset($info['to_ids']) && $info['to_ids'] != 1) {
$class .= 'isNotToIDS';
} else {
$class = "";
}
?>
<tr class="<?php echo $class; ?>">
<td><input type="checkbox"></input></td>
<td class="useCursorPointer isFilteringField isAttributeTypeField">
<?php if(isset($info['isObject']) && $info['isObject'] && !(isset($info['isAttribute']) && $info['isAttribute'])): ?>
<it class="fa fa-cube" title="<?php echo __('Belong to a MISP Object'); ?>"></it>
<?php endif; ?>
<span title="<?php echo isset($info['desc']) ? h($info['desc']) : ''; ?>"><?php echo h($type); ?></span>
<?php if(isset($info['to_ids']) && $info['to_ids'] == 1): ?>
<it class="fa fa-flag fa-pull-right" title="<?php echo __('To IDS flag set'); ?>"></it>
<?php endif; ?>
</td>
<td class="useCursorPointer isFilteringField"><?php echo is_array($info['default_category']) ? implode('</br>', h($info['default_category'])) : h($info['default_category']); ?></td>
<td class="isFilteringField isModelIdField">
<?php if (isset($associated_models[$type])): ?>
<?php foreach ($associated_models[$type] as $id): ?>
<a href="#" onclick="$('#modelId_<?php echo h($id); ?>').find('.decayingLoadBtn').click();"><?php echo h($id); ?></a>
<?php endforeach; ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="span12">
<div style="margin-bottom: 10px;">
<select id="formulaSelectPicker" style="margin: 0px;">
<?php foreach ($available_formulas as $formula_name => $formula_data): ?>
<option value="<?php echo h($formula_name); ?>" data-extends="<?php echo h($formula_data['parent_class']); ?>" title="<?php echo h($formula_data['description']); ?>"><?php echo h($formula_name); ?></option>
<?php endforeach; ?>
</select>
<i id="formulaSelectPickerHelpText" class="fas fa-question-circle"></i>
</div>
<div id="containerFormulaPolynomialSetting">
<div class="span10" style="border: 1px solid #ddd; border-radius: 4px; margin-bottom: 20px;">
<div id="decayGraph" style="width: 100%;"></div>
</div>
<div class="row">
<div class="span6" style="margin-bottom: 20px;">
<?php foreach ($parameters as $param => $config): ?>
<div class="input-prepend input-append">
<span class="add-on param-name" data-toggle="tooltip" data-placement="left" style="min-width: 100px;" title="<?php echo isset($config['info']) ? h($config['info']) : ''?>">
<?php echo h($config['name']) . (isset($config['greek']) ? ' <strong>' . h($config['greek']).'</strong>' : ''); ?>
</span>
<input id="input_<?php echo h($param); ?>" class="input-mini" type="number" min=0 step=<?php echo h($config['step']); ?> value=<?php echo h($config['value']); ?> max=<?php echo isset($config['max']) ? h($config['max']) : ''; ?> oninput="refreshGraph(this);" ></input>
<span class="add-on"><input id="input_<?php echo h($param); ?>_range" type="range" min=0 <?php echo isset($config['max']) ? 'max=' . h($config['max']) : '' ?> step=<?php echo h($config['step']); ?> value=<?php echo h($config['value']); ?> oninput="$('#input_<?php echo h($param); ?>').val(this.value).trigger('input');"></input></span>
<?php if (isset($config['unit'])): ?>
<span class="add-on"><?php echo h($config['unit']); ?></span>
<?php endif; ?>
</div>
<?php endforeach; ?>
<input id="input_default_base_score" value=0 class="hidden"></input>
<div class="input-append" style="margin-bottom: 0px;">
<input id="input_base_score_config" class="hidden" value="[]"></input>
<button class="btn btn-primary" style="border-radius: 4px 0px 0px 4px;" onclick="decayingTool.toggleBasescoreForm()">
<span class="fa fa-tags"> <?php echo __('Adjust base score'); ?></span>
</button>
<span id="summary_base_score_config" class="add-on param-name">
<span class="far fa-square"></span>
</span>
</div>
<div style="display: inline-block; margin-left: 10px;">
<a id="button-toggle-simulation" target="_blank" class="btn btn-primary" href="" onclick="return !$(this).hasClass('disabled');">
<span class="fa fa-chart-line"> <?php echo __('Simulate this model'); ?></span>
</a>
</div>
</div>
<div class="span6">
<table class="table table-striped table-bordered">
<tbody>
<tr>
<td>Expire after (lifetime)</td>
<td id="infoCellExpired"></td>
</tr>
<tr>
<td>Score halved after (Half-life)</td>
<td id="infoCellHalved"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="containerFormulaOtherSetting" class="hidden">
<textarea id="textarea_other_settings_formulas" style="width: 430px;" rows="5" placeholder="<?php echo(__('Model\'s Settings')); ?>"></textarea>
</div>
<div class="row">
<div class="span12">
<form id="saveForm" class="form-inline">
<input type="text" name="name" class="input" placeholder="Model name" required>
<textarea rows="1" name="description" class="input" placeholder="Description"></textarea>
<span id="save-model-button" class="btn btn-success" data-save-type="add" onclick="decayingTool.saveModel(this)" data-modelid="0" data-isedit="0" data-edittext="<?php echo __("Edit") ?>" data-savetext="<?php echo __("Create") ?>"><i class="fa fa-plus"> <?php echo __("Create") ?></i></span>
</form>
</div>
</div>
<div class="row">
<div class="span12">
<span class="tableRadioFilterOptionsContainer">
<label class="radio inline">
<input type="radio" id="tableRadioFilterAll" name="tableRadioFilterOptions" value="all" checked><?php echo __('All available models');?>
</label>
<label class="radio inline">
<input type="radio" id="tableRadioFilterMy" name="tableRadioFilterOptions" value="my_models"><?php echo __('My models'); ?>
</label>
<label class="radio inline">
<input type="radio" id="tableRadioFilterDefault" name="tableRadioFilterOptions" value="default_models"><?php echo __('Default models'); ?>
</label>
</span>
<table id="table-model" class="table table-striped table-bordered">
<thead id="table-model-head"></thead>
<tbody id="table-model-body"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'decayingTool')); ?>
<?php
echo $this->element('genericElements/assetLoader', array(
'css' => array('decayingTool'),
'js' => array('d3', 'Chart.min', 'decayingTool')
));
?>
<script>
var logged_user_org_id = <?php echo h($me['org_id']); ?>;
$(document).ready(function() {
$('.json-transform').each(function(i) {
var text = $(this).text().trim();
var parsedJson = ''
if (text !== '') {
parsedJson = jsonToNestedTable(text, [], ['table', 'table-condensed', 'table-bordered']);
}
$(this).html(parsedJson);
});
$('#formulaSelectPicker').change(function() {
toggleContainer();
})
$('#formulaSelectPickerHelpText').tooltip({
title: function() {
return $('#formulaSelectPicker > option:selected').attr('title');
},
placement: 'right'
});
});
function toggleContainer() {
var $option = $('#formulaSelectPicker').find('option:selected');
if ($option.data('extends') == 'Polynomial') {
$('#containerFormulaPolynomialSetting').show();
} else {
$('#containerFormulaPolynomialSetting').hide();
}
if ($('#formulaSelectPicker').val() == 'Polynomial') {
$('#containerFormulaOtherSetting').hide();
} else {
$('#containerFormulaOtherSetting').show();
}
}
</script>

View File

@ -0,0 +1,180 @@
<div id="basescore_configurator" class="row">
<div class="span8" class="taxonomyTableContainer">
<input id="table_taxonomy_search" class="input" style="width: 250px; margin: 0px;" type="text" placeholder="<?php echo __('Search Taxonomy'); ?>"></input>
<it class="fa fa-times useCursorPointer" title="<?php echo __('Clear search field'); ?>" onclick="$('#table_taxonomy_search').val('').trigger('input');"></it>
<span style="float: right; margin-top: 6px;" class="badge badge-info"><b><?php echo h($taxonomies_not_having_numerical_value); ?></b><?php echo __(' not having numerical value'); ?></span>
<div class="input-prepend" style="margin: 4px;">
<span class="add-on"><?php echo __('Default basescore') ?></span>
<input id="base_score_default_value" type="number" min=0 max=100 class="input-mini" value="0" placeholder="0"></input>
</div>
<table id="tableTaxonomy" class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th><?php echo __('Taxonomies') ?></th>
<th><?php echo __('Weight') ?></th>
</tr>
</thead>
<tbody id="body_taxonomies">
<?php foreach ($taxonomies as $name => $taxonomy): ?>
<?php if (count($taxonomy['TaxonomyPredicate']) > 1): ?>
<tr class="bold useCursorPointer" data-namespace="<?php echo h($name); ?>" onclick="collapseNamespace(this);">
<td colspan=2 style="background-color: #999; color: white; user-select: none;">
<?php echo h($name); ?>
<i class="caretIconExpand fas fa-caret-down"></i>
</td>
</tr>
<?php endif; ?>
<?php foreach ($taxonomy['TaxonomyPredicate'] as $p => $predicate): ?>
<?php if (!isset($predicate['numerical_predicate']) || !$predicate['numerical_predicate']): ?>
<tr data-namespace="<?php echo h($name); ?>">
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<?php echo h($predicate['value']) ?>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<?php foreach ($predicate['TaxonomyEntry'] as $e => $entry): ?>
<li>
<a style="position: relative; padding: 3px 5px;">
<span class="tagComplete"
style="margin-right: 35px;background-color: <?php echo h($entry['Tag']['colour']); ?>;color:<?php echo h($this->TextColour->getTextColour($entry['Tag']['colour']));?>"
title="<?php echo sprintf('%s: %s', h($entry['expanded']), h($entry['description'])) ?>"><?php echo h($entry['Tag']['name']); ?>
</span>
<span class="label label-inverse numerical-value-label"><?php echo h($entry['numerical_value']) ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</td>
<td>
<input id="slider_<?php echo h($name) ?>" data-taxonomyname="<?php echo sprintf('%s:%s', h($name), h($predicate['value'])); ?>" type="range" min=0 max=100 step=1 value="<?php echo isset($taxonomy['value']) ? h($taxonomy['value']) : 0 ?>" onchange="sliderChanged(this);" oninput="sliderChanged(this);"></input>
<input type="number" min=0 max=100 step=1 value="<?php echo isset($taxonomy['value']) ? h($taxonomy['value']) : 0 ?>" class="taxonomySlider" data-taxonomyname="<?php echo sprintf('%s:%s', h($name), h($predicate['value'])); ?>" onchange="inputChanged(this);" oninput="inputChanged(this);"></input>
</td>
</tr>
<?php else: // numerical_value on predicate ?>
<tr data-namespace="<?php echo h($name); ?>">
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<?php echo h($name) ?>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<?php foreach ($taxonomies[$name]['TaxonomyPredicate'] as $p => $predicate): ?>
<li>
<a style="position: relative; padding: 3px 5px;">
<span class="tagComplete"
style="margin-right: 35px;background-color: <?php echo h($predicate['Tag']['colour']); ?>;color:<?php echo h($this->TextColour->getTextColour($predicate['Tag']['colour']));?>"
title="<?php echo sprintf('%s: %s', h($predicate['expanded']), h($predicate['description'])) ?>"><?php echo h($predicate['Tag']['name']); ?>
</span>
<span class="label label-inverse numerical-value-label"><?php echo h($predicate['numerical_value']) ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
</td>
<td>
<input id="slider_<?php echo h($name) ?>" data-taxonomyname="<?php echo h($name); ?>" type="range" min=0 max=100 step=1 value="<?php echo isset($taxonomy['value']) ? h($taxonomy['value']) : 0 ?>" onchange="sliderChanged(this);" oninput="sliderChanged(this);"></input>
<input type="number" min=0 max=100 step=1 value="<?php echo isset($taxonomy['value']) ? h($taxonomy['value']) : 0 ?>" class="taxonomySlider" data-taxonomyname="<?php echo h($name); ?>" onchange="inputChanged(this);" oninput="inputChanged(this);"></input>
</td>
</tr>
<?php break; ?>
<?php endif; ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php if (count($excluded_taxonomies) > 1): ?>
<tr class="bold useCursorPointer" data-namespace="excluded-taxonomy" onclick="collapseNamespace(this);">
<td colspan=2 style="background-color: #999; color: white; user-select: none;">
<?php echo __('Excluded'); ?>
<i class="caretIconExpand fas fa-caret-up"></i>
</td>
</tr>
<?php endif; ?>
<?php foreach ($excluded_taxonomies as $namespace => $taxonomy): // excluded taxonomies ?>
<tr data-namespace="excluded-taxonomy" style="display: none;">
<td>
<button class="btn" disabled><?php echo h($namespace) ?></button>
</td>
<td style="vertical-align: middle;"><span class='label label-info'><?php echo h($taxonomy['reason']); ?></span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="span8">
<div style="margin-bottom: 5px;">
<div id="treemapGraphTax"></div>
</div>
<div class="org-source-confidence-placeholder">
<?php echo __('Placeholder for `Organisation source confidence`') ?>
</div>
<div>
<h3><?php echo __('Example') ?><it class="fa fa-sync useCursorPointer" style="margin-left: 5px; font-size: small;" onclick="refreshExamples()"></it></h3>
<table id="tableExamples" class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>Attribute</th>
<th>Tags</th>
<th style="min-width: 60px;">Base score</th>
</tr>
</thead>
<tbody>
<tr onclick="genHelpBaseScoreComputation(event, 0)">
<td>
Tag your attribute
</td>
<td id="basescore-example-tag-0">
<div style="width:100%;display:inline-block;" data-original-title="" title="">
<div id="basescore-example-customtag-container" style="float: left;display: flex;flex-flow: wrap;" data-original-title="" title="">
<button id="basescore-example-score-addTagButton" class="btn btn-inverse noPrint" style="line-height:10px; padding: 4px 4px; margin-right: 3px;" title="Add tag" onclick="event.stopPropagation(); addTagWithValue(this);">+</button>
</div>
</div>
</td>
<td id="basescore-example-score-0" class="basescore-example-score">
</td>
</tr>
<tr onclick="genHelpBaseScoreComputation(event, 1)">
<td>Attribute 1</td>
<td id="basescore-example-tag-1"><?php echo __('Pick a Taxonomy'); ?></td>
<td id="basescore-example-score-1" class="basescore-example-score"></td>
</tr>
<tr onclick="genHelpBaseScoreComputation(event, 2)">
<td>Attribute 2</td>
<td id="basescore-example-tag-2"><?php echo __('Pick a Taxonomy'); ?></td>
<td id="basescore-example-score-2" class="basescore-example-score"></td>
</tr>
<tr onclick="genHelpBaseScoreComputation(event, 3)">
<td>Attribute 3</td>
<td id="basescore-example-tag-3"><?php echo __('Pick a Taxonomy'); ?></td>
<td id="basescore-example-score-3" class="basescore-example-score"></td>
</tr>
</tbody>
</table>
<h3><?php echo __('Computation steps') ?></h3>
<?php echo $this->element('DecayingModels/View/basescore_computation_steps'); ?>
</div>
<span class="btn btn-primary" style="margin-top: 5px;" onclick="applyBaseScoreConfig();"><i class="fas fa-wrench"> <?php echo __('Apply base score'); ?></i></span>
</div>
</div>
<?php
echo $this->element('genericElements/assetLoader', array(
'css' => array('treemap'),
'js' => array('decayingToolBasescore')
));
?>
<script>
var taxonomies_with_num_value = <?php echo json_encode($taxonomies); ?>;
function collapseNamespace(clicked) {
var $tr = $(clicked)
var $icon = $tr.find('i.caretIconExpand');
var namespace = $tr.data('namespace');
$tr.parent().find('[data-namespace="' + namespace + '"]').not($tr).toggle();
$icon.toggleClass('fa-caret-down').toggleClass('fa-caret-up');
}
</script>

View File

@ -0,0 +1,91 @@
<div id="attribute_div">
<div class="pagination" style="margin: 0px;">
<ul>
<?php
$this->Paginator->options(array(
'update' => '#attribute_div',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<?php
$headers = array(
'ID',
$this->Paginator->sort('event_id'),
$this->Paginator->sort('date', __('Date')),
$this->Paginator->sort('Event.orgc_id', __('Org')),
$this->Paginator->sort('category', __('Category')),
$this->Paginator->sort('type', __('Type')),
$this->Paginator->sort('value', __('Value')),
__('Tags'),
__('Event Tags'),
__('Galaxies'),
$this->Paginator->sort('comment', __('Comment')),
sprintf('<span title="%s">%s', $attrDescriptions['signature']['desc'], $this->Paginator->sort('IDS')),
__('Sightings'),
$this->Paginator->sort('decay_score.score', __('Score')),
);
foreach ($headers as $k => &$header) {
$header = sprintf('<th>%s</th>', $header);
}
$header = sprintf('<thead><tr>%s</tr></thead>', implode('', $headers));
$rows = array();
foreach ($attributes as $k => $attribute) {
$event = array(
'Event' => $attribute['Event'],
'Orgc' => $attribute['Event']['Orgc'],
);
$rows[] = $this->element('DecayingModels/View/row_attribute_simulation', array(
'object' => $attribute['Attribute'],
'event' => $event
));
}
echo sprintf('<table class="table table-striped table-hover table-condensed">%s %s</table>', $header, implode('', $rows));
?>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<script>
$(document).ready(function() {
$('#attribute_div .pagination a, #attribute_div thead th > a').click(function(e) {
var url = this.href;
$.ajax({
beforeSend:function() {
$('#attributeTableContainer').html('<div style="height:100%; display:flex; align-items:center; justify-content:center;"><span class="fa fa-spinner fa-spin" style="font-size: xx-large;"></span></div>');
},
success:function (data, textStatus) {
$('#attributeTableContainer').html(data);
},
error:function() {
showMessage('fail', '<?php echo __('Failed to perform RestSearch') ?>');
},
type:'get',
url: url,
});
return false;
});
});
</script>

View File

@ -0,0 +1,8 @@
<div class="hidden">
<?php echo $this->Form->create('decayingToolRestSearch', array('url' => array('controller' => 'decayingModel', 'action' => 'decayingToolRestSearch', 'results')));?>
<legend><?php echo __('Decaying Model RestSearch');?></legend>
<fieldset>
<?php echo $this->Form->input('filters'); ?>
</fieldset>
<?php echo $this->Form->end(); ?>
</div>

View File

@ -0,0 +1,291 @@
<div class="view">
<div id="simulationContainer">
<div class="simulationSubContainer">
<div style="height: 40%; display: flex">
<div style="width: 20%; display: flex; flex-direction: column;">
<div class="panel-container" style="display: flex; flex-direction: column; flex-grow: 1">
<div style="display: flex; flex-wrap: wrap;">
<select id="select_model_to_simulate" onchange="modelChangeHandler(this)" style="flex-grow: 1;">
<?php foreach ($all_models as $model): ?>
<option value="<?php echo h($model['DecayingModel']['id']) ?>" <?php echo $decaying_model['DecayingModel']['id'] == $model['DecayingModel']['id'] ? 'selected' : '' ?>><?php echo h($model['DecayingModel']['name']); ?></option>
<?php endforeach; ?>
</select>
<span id="select_model_to_simulate_infobox" class="btn"><span class="fa fa-question-circle"></span></span>
</div>
<ul class="nav nav-tabs" style="margin-right: -5px; margin-bottom: 0px;" id="simulation-tabs">
<li class="<?php echo isset($attribute_id) ? '' : 'active'; ?>"><a href="#restsearch" data-toggle="tab">RestSearch</a></li>
<li class="<?php echo !isset($attribute_id) ? '' : 'active'; ?>"><a href="#specificid" data-toggle="tab">Specific ID</a></li>
</ul>
<div class="tab-content" style="padding: 5px; height: 100%;">
<div class="tab-pane <?php echo isset($attribute_id) ? '' : 'active'; ?>" id="restsearch" style="height: 100%;">
<div style="display: flex; flex-direction: column; height: 100%;">
<h3 style="">Attribute RestSearch<span style="vertical-align: top; font-size: x-small;" class="fa fa-question-circle" title="Enforced fields: [returnFormat, includeEventTags]"></span></h3>
<?php
$registered_taxonomies = array_keys($decaying_model['DecayingModel']['parameters']['base_score_config']);
foreach ($registered_taxonomies as $i => &$taxonomy_name) {
$taxonomy_name = $taxonomy_name . '%' ;
}
?>
<textarea id="restSearchTextarea">
{
"includeDecayScore": 1,
"includeFullModel": 0,
"score": <?php echo h($decaying_model['DecayingModel']['parameters']['threshold']); ?>,
"excludeDecayed": 0,
"decayingModel": [<?php echo h($decaying_model['DecayingModel']['id']); ?>],
"to_ids": 1,
"tags": <?php echo json_encode($registered_taxonomies); ?>,
"modelOverrides": {
}
}</textarea>
</br>
<span class="btn btn-primary" style="width: fit-content;" role="button" onclick="doRestSearch(this)"><?php echo __('Search'); ?></span>
</div>
</div>
<div class="tab-pane <?php echo !isset($attribute_id) ? '' : 'active'; ?>" id="specificid">
<h3 style=""><?php echo __('Specific Attribute'); ?></h3>
<div style="display: flex; flex-wrap: wrap;">
<div style="margin-left: 4px; margin-bottom: 0px;" class="input-prepend">
<span class="add-on">ID</span>
<input type="text" value="<?php echo isset($attribute_id) ? h($attribute_id) : ''; ?>" placeholder="<?php echo __('Attribute ID or UUID') ?>" onkeypress="handle_input_key(event)" style="width: auto;">
</div>
<span id="performRestSearchButton" class="btn btn-primary" style="width: fit-content; margin-left: 4px;" role="button" onclick="doSpecificSearch(this)"><?php echo __('Simulate'); ?></span>
</div>
</div>
</div>
</div>
</div>
<div style="width: 80%; display: flex;">
<div class="panel-container" style="flex-grow: 1; display: flex; width: 100%">
<div id="basescore-simulation-container" style="width: 30%; min-width: 300px; height: 100%; margin-right: 10px;">
<div>
<h5 style="display: inline-block;"><?php echo __('Base score') ?> <i id="basescore_enlarge_icon" class="fas fa-expand useCursorPointer"></i></h5>
<div id="alert-basescore-not-set" class="alert alert-warning" style="display: inline-block; margin-bottom: auto; margin-left: 5px; padding: 4px 8px;">
<strong><?php echo __('Base score configuration'); ?></strong> <?php echo __('not set. But default value sets.') ?>
</div>
<div id="alert-basescore-not-set" class="alert alert-error" style="display: inline-block; margin-bottom: auto; margin-left: 5px; padding: 4px 8px;">
<strong><?php echo __('Base score configuration'); ?></strong> <?php echo __('not set') ?>
</div>
</div>
<div style="position: relative; max-height: calc(100% - 75px); overflow: auto; margin-bottom: 5px;">
<?php echo $this->element('DecayingModels/View/basescore_computation_steps'); ?>
</div>
<div style="margin-bottom: 5px; white-space: nowrap;">
<div style="margin-left: 4px; margin-bottom: 0px;" class="input-prepend input-append">
<span class="add-on"><?php echo __('Sighting'); ?></span>
<span id="simulation-sighting" class="add-on"></span>
</div>
<div style="margin-left: 4px; margin-bottom: 0px;" class="input-prepend input-append">
<span class="add-on"><?php echo __('Current score'); ?></span>
<span id="simulation-current-score" class="add-on"></span>
</div>
</div>
</div>
<div id="chart-decay-simulation-container" style="width: 70%; height: 100%; position: relative; overflow: hidden;">
<div id="simulation_chart" class="svg-container"></div>
</div>
</div>
</div>
</div>
<div style="height: 60%; overflow-y: auto; background-color: #ffffff; min-width: 1600px; " class="panel-container">
<div style="height: 100%;" id="attributeTableContainer"></div>
</div>
</div>
</div>
</div>
<?php echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => '')); ?>
<?php
echo $this->element('genericElements/assetLoader', array(
'css' => array('treemap', 'decayingTool'),
'js' => array('d3', 'decayingModelSimulation')
));
?>
<script>
var model_list = <?php echo json_encode($all_models); ?>;
var models = {};
$('#alert-basescore-not-set').hide();
$('#alert-basescore-not-set.alert-error').hide();
$(document).ready(function() {
model_list.forEach(function(m) {
models[m.DecayingModel.id] = m.DecayingModel;
});
$('#select_model_to_simulate_infobox').popover({
title: function() {
return $('<div>').text($('#select_model_to_simulate option:selected').text()).html();
},
content: function() {
return '<div>' + syntaxHighlightJson(models[$('#select_model_to_simulate').val()]) + '</div>';
},
html: true,
placement: 'bottom'
});
$('#basescore_enlarge_icon').click(function() {
var $table = $('#computation_help_container > table');
var css_container = 'width: 1000px; left: calc(50% - 500px); margin-left: 0;';
var css_body = 'max-height: 80%;';
openModal('<?php echo __('Basescore computation steps'); ?>', $table[0].outerHTML, '', {}, css_container, css_body);
});
$('body').on('click', function (e) {
if (
$(e.target).attr('id') !== 'select_model_to_simulate'
&& $(e.target).attr('id') !== 'select_model_to_simulate_infobox'
&& $(e.target).parents('#select_model_to_simulate_infobox').length === 0
&& $(e.target).parents('.popover.in').length === 0) {
$('#select_model_to_simulate_infobox').popover('hide');
}
});
<?php echo isset($attribute_id) ? '$("#performRestSearchButton").click();' : ''; ?>
});
function doRestSearch(clicked, query) {
var data = query === undefined ? $(clicked).parent().find('textarea').val() : query;
var json;
try {
json = JSON.parse(data);
} catch (SyntaxError) {
showMessage('fail', 'Invalid JSON syntax');
return;
}
fetchFormDataAjax('/decayingModel/decayingToolRestSearch/', function(formData) {
var $formData = $(formData);
url = $formData.find('form').attr('action');
$('#simulationContainer').append($formData);
$formData.find('#decayingToolRestSearchFilters').val(data);
$.ajax({
data: $formData.find('form').serialize(),
beforeSend:function() {
$('#attributeTableContainer').html('<div class="loading-spinner-container"><span class="fa fa-spinner fa-spin" style="font-size: xx-large;"></span></div>');
},
success:function (data, textStatus) {
$('#attributeTableContainer').html(data);
var $trs = $('#attributeTableContainer tbody > tr');
if ($trs.length == 1) {
$trs.click();
}
// pass potential model overrides
if (json.modelOverrides !== undefined) {
$trs.data('modelOverride', JSON.stringify(json.modelOverrides));
}
if (json.score !== undefined) {
$trs.data('score', json.score);
}
},
error:function(jqXHR, textStatus, errorThrown) {
$('#attributeTableContainer').text(textStatus + ': ' + errorThrown);
showMessage('fail', '<?php echo __('Failed to perform RestSearch') ?>');
},
type:'post',
cache: false,
url: url,
});
});
}
function doSpecificSearch(clicked) {
var body = {
id: $(clicked).parent().find('input').val(),
decayingModel: $('#select_model_to_simulate').val()
}
doRestSearch(clicked, JSON.stringify(body));
}
function handle_input_key(e) {
if(e.keyCode === 13){
e.preventDefault();
$('#performRestSearchButton').click();
}
}
function doSimulation(clicked, attribute_id) {
$('#attribute_div tr').removeClass('success');
$(clicked).addClass('success');
var model_id = $('#select_model_to_simulate').val();
var simulation_chart = $('#simulation_chart').data('DecayingSimulation');
var simulation_table = $('#basescore-simulation-container #computation_help_container_body').data('BasescoreComputationTable');
if (simulation_chart === undefined) {
simulation_chart = $('#simulation_chart').decayingSimulation({});
}
if (simulation_table === undefined) {
simulation_table = $('#basescore-simulation-container #computation_help_container_body').basescoreComputationTable({});
}
var url = '/decayingModel/decayingToolComputeSimulation/' + model_id + '/' + attribute_id;
var model_override = $(clicked).data('modelOverride');
if (model_override !== undefined) {
url += '/modelOverride:' + model_override;
}
var score = $(clicked).data('score');
if (score !== undefined) {
url += '/score:' + score;
}
$.ajax({
beforeSend:function() {
simulation_chart.toggleLoading(true);
simulation_table.toggleLoading(true);
},
success:function (data, textStatus) {
simulation_chart.update(data, data.Model);
simulation_table.update(data, data.Model);
if (Object.keys(data.base_score_config.taxonomy_effective_ratios).length > 0) { // show alert base_score not set
$('#alert-basescore-not-set').hide();
$('#alert-basescore-not-set.alert-error').hide();
$('#basescore-simulation-container #computation_help_container_body tr').removeClass('warning').removeClass('error');
} else {
if (data.base_score_config.default_base_score == 0) { // show alert base_score not set
$('#alert-basescore-not-set.alert-error').show('fade', {}, 250);
$('#alert-basescore-not-set').hide();
$('#basescore-simulation-container #computation_help_container_body tr').removeClass('warning').addClass('error');
} else {
$('#alert-basescore-not-set').show('fade', {}, 250);
$('#alert-basescore-not-set.alert-error').hide();
$('#basescore-simulation-container #computation_help_container_body tr').removeClass('error').addClass('warning');
}
}
$('#simulation-sighting')
.text(
d3.time.format("%c")(new Date(parseInt(data.last_sighting.Sighting.date_sighting)*1000))
);
$('#simulation-sighting').parent().tooltip({
title: 'From ' + (data.last_sighting.Organisation !== undefined ? data.last_sighting.Organisation.name : '?'),
});
$('#simulation-current-score')
.text(data.current_score.toFixed(2))
.removeClass(data.current_score > models[$('#select_model_to_simulate').val()].parameters.threshold ? 'alert-error' : 'alert-success')
.addClass(data.current_score > models[$('#select_model_to_simulate').val()].parameters.threshold ? 'alert-success' : 'alert-error');
},
error:function() {
showMessage('fail', '<?php echo __('Failed to perform the simulation') ?>');
},
complete:function() {
simulation_chart.toggleLoading(false);
simulation_table.toggleLoading(false);
},
type:'get',
cache: false,
dataType: 'json',
url: url,
});
}
function refreshSimulation() {
var $row = $('#attribute_div tr.success');
var attribute_id = $row.find('td:first').text();
if (attribute_id !== '') {
doSimulation($row, attribute_id);
}
}
function modelChangeHandler(clicked) {
$('#select_model_to_simulate_infobox').popover('show');
refreshSimulation();
}
</script>

View File

@ -0,0 +1,30 @@
<div class="form">
<?php echo $this->Form->create('DecayingModel', array('enctype' => 'multipart/form-data'));?>
<fieldset>
<legend><?php echo __('Import model data');?></legend>
<p><?php echo __('Paste a MISP model JSON or provide a JSON file below to add models.');?></p>
<div>
<?php
echo $this->Form->input('json', array(
'div' => 'input clear',
'label' => __('JSON'),
'placeholder' => __('Model JSON'),
'class' => 'form-control span6',
'type' => 'textarea',
'rows' => 18
));
echo $this->Form->input('submittedjson', array(
'div' => 'input clear',
'label' => __('JSON file'),
'type' => 'file'
));
?>
</div>
</fieldset>
<?php
echo $this->Form->button(__('Add'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'import'));

View File

@ -0,0 +1,191 @@
<div class="templates index">
<h2><?php echo __('Decaying Models');?></h2>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'update' => '.span12',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<?php
$temp = $passedArgsArray;
unset($temp['sort']);
unset($temp['direction']);
$filter_active = count(array_keys($temp)) > 0;
$data = array(
'children' => array(
array(
'children' => array(
array(
'title' => __('All Models'),
'text' => __('All Models'),
'url' => sprintf('%s/%s%s',
$baseurl . '/decayingModel/index',
isset($passedArgsArray['sort']) ? 'sort:' . $passedArgsArray['sort'] . '/' : '',
isset($passedArgsArray['direction']) ? 'direction:' . $passedArgsArray['direction'] . '/' : ''
),
'class' => 'searchFilterButton',
'active' => !$filter_active
),
array(
'title' => __('My models only'),
'text' => __('My Models'),
'url' => sprintf('%s/%s%s%s',
$baseurl . '/decayingModel/index',
isset($passedArgsArray['sort']) ? 'sort:' . $passedArgsArray['sort'] . '/' : '',
isset($passedArgsArray['direction']) ? 'direction:' . $passedArgsArray['direction'] . '/' : '',
'my_models:' . (!isset($passedArgsArray['my_models']) || !$passedArgsArray['my_models'] ? '1' : '0')
),
'class' => 'searchFilterButton',
'active' => isset($passedArgsArray['my_models']) && $passedArgsArray['my_models']
),
array(
'title' => __('Models available to everyone'),
'text' => __('Shared Models'),
'url' => sprintf('%s/%s%s%s',
$baseurl . '/decayingModel/index',
isset($passedArgsArray['sort']) ? 'sort:' . $passedArgsArray['sort'] . '/' : '',
isset($passedArgsArray['direction']) ? 'direction:' . $passedArgsArray['direction'] . '/' : '',
'all_orgs:' . (!isset($passedArgsArray['all_orgs']) || !$passedArgsArray['all_orgs'] ? '1' : '0')
),
'class' => 'searchFilterButton',
'active' => isset($passedArgsArray['all_orgs']) && $passedArgsArray['all_orgs']
),
array(
'title' => __('Default models only'),
'text' => __('Default Models'),
'url' => sprintf('%s/%s%s%s',
$baseurl . '/decayingModel/index',
isset($passedArgsArray['sort']) ? 'sort:' . $passedArgsArray['sort'] . '/' : '',
isset($passedArgsArray['direction']) ? 'direction:' . $passedArgsArray['direction'] . '/' : '',
'default_models:' . (!isset($passedArgsArray['default_models']) || !$passedArgsArray['default_models'] ? '1' : '0')
),
'class' => 'searchFilterButton',
'active' => isset($passedArgsArray['default_models']) && $passedArgsArray['default_models']
),
)
)
)
);
echo $this->element('/genericElements/ListTopBar/scaffold', array('data' => $data));
?>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('ID');?></th>
<th><?php echo $this->Paginator->sort('org', __('Organization'));?></th>
<th><?php echo $this->Paginator->sort('all_orgs', __('Usable to everyone'));?></th>
<th><?php echo $this->Paginator->sort('name', __('Name'));?></th>
<th><?php echo $this->Paginator->sort('description', __('Description'));?></th>
<th>
<?php echo __('Parameters'); ?>
<a class="useCursorPointer" title="<?php echo __('Pretty print') ?>"><b style="font-size: larger;" onclick="prettyPrintJson();">{ }</b></a>
</th>
<th><?php echo $this->Paginator->sort('formula', __('Formula'));?></th>
<th><?php echo __('# Assigned Types') ?></th>
<th><?php echo $this->Paginator->sort('version', __('Version'));?></th>
<th><?php echo $this->Paginator->sort('enabled', __('Enabled'));?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr><?php
foreach ($decayingModels as $item): ?>
<tr>
<td class="short"><a href="<?php echo $baseurl."/decayingModel/view/" . h($item['DecayingModel']['id']); ?>"><?php echo h($item['DecayingModel']['id']); ?>&nbsp;</a></td>
<td class="short">
<?php
echo $this->OrgImg->getOrgImg(array('name' => $item['DecayingModel']['org_id'], 'size' => 24));
?>
&nbsp;
</td>
<td><i class="fas fa-<?php echo $item['DecayingModel']['all_orgs'] ? 'check' : 'times';?>"></i></td>
<td>
<a href="<?php echo $baseurl."/decayingModel/view/" . h($item['DecayingModel']['id']); ?>"><?php echo h($item['DecayingModel']['name']); ?>&nbsp;</a>
<?php if ($item['DecayingModel']['default']): ?>
<img src="<?php echo $baseurl;?>/img/orgs/MISP.png" width="24" height="24" style="padding-bottom:3px;" title="<?php echo __('Default Model from MISP Project'); ?>" />
<?php endif; ?>
</td>
<td><?php echo h($item['DecayingModel']['description']); ?>&nbsp;</td>
<?php
if (isset($item['DecayingModel']['parameters']['base_score_config']) && empty($item['DecayingModel']['parameters']['base_score_config'])) {
$item['DecayingModel']['parameters']['base_score_config'] = new stdClass(); // force output to be {} instead of []
}
?>
<td data-toggle="json" ondblclick="document.location.href ='<?php echo $baseurl . '/decayingModel/view/' . h($item['DecayingModel']['id']); ?>'"><?php echo h(json_encode($item['DecayingModel']['parameters'])); ?>&nbsp;</td>
<td>
<?php echo h($item['DecayingModel']['formula']); ?>
<?php if (isset($available_formulas[$item['DecayingModel']['formula']]['description'])): ?>
<i class="fas fa-question-circle" data-toggle="tooltip" title="<?php echo h($available_formulas[$item['DecayingModel']['formula']]['description']); ?>"></i>
<?php else: ?>
&nbsp
<?php endif; ?>
</td>
<td><?php echo count($item['DecayingModel']['attribute_types']); ?>&nbsp;</td>
<td><?php echo h($item['DecayingModel']['version']); ?>&nbsp;</td>
<td><i class="fas fa-<?php echo $item['DecayingModel']['enabled'] ? 'check' : 'times';?>"></i></td>
<td class="short action-links">
<?php echo $this->Html->link('', array('action' => 'view', $item['DecayingModel']['id']), array('class' => 'icon-list-alt', 'title' => 'View'));?>
<?php echo $this->Html->link('', array('action' => 'export', $item['DecayingModel']['id'] . '.json'), array('download' => true, 'class' => 'fa fa-cloud-download-alt', 'title' => __('Download model')));?>
<?php if ($me['Role']['perm_admin']): ?>
<?php if ($me['Role']['perm_site_admin'] || $item['DecayingModel']['org_id'] == $me['org_id']): ?>
<?php
if (!$item['DecayingModel']['default']) {
echo $this->Form->postLink('', array('action' => 'delete', $item['DecayingModel']['id']), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete DecayingModel #' . h($item['DecayingModel']['id']) . '?'));
}
?>
<?php echo $this->Html->link('', array('action' => 'edit', $item['DecayingModel']['id']), array('class' => 'icon-edit', 'title' => 'Edit'));?>
<?php
if ($item['DecayingModel']['enabled']):
echo $this->Form->postLink('', array('action' => 'disable', $item['DecayingModel']['id']), array('class' => 'fa fa-pause', 'title' => 'Disable model'), __('Are you sure you want to disable DecayingModel #' . h($item['DecayingModel']['id']) . '?'));
else:
echo $this->Form->postLink('', array('action' => 'enable', $item['DecayingModel']['id']), array('class' => 'fa fa-play', 'title' => 'Enable model'), __('Are you sure you want to enable DecayingModel #' . h($item['DecayingModel']['id']) . '?'));
endif;
?>
<?php endif; ?>
<?php endif; ?>
</td>
</tr><?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<script>
$(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip();
});
function prettyPrintJson() {
$('[data-toggle=\"json\"]').each(function() {
$(this).attr('data-toggle', '')
.html(syntaxHighlightJson($(this).text().trim()));
});
}
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'index'));
?>

View File

@ -0,0 +1,63 @@
<?php
$table_data = array();
$table_data[] = array('key' => __('Model ID'), 'value' => $decaying_model['DecayingModel']['id']);
$table_data[] = array(
'key' => __('Creator org'),
'html' => sprintf(
'<a href="%s/organisations/view/%s">%s</a>',
$baseurl,
h($decaying_model['DecayingModel']['org_id']),
h($decaying_model['DecayingModel']['org_id'])
)
);
$table_data[] = array(
'key' => __('Name'),
'html' => sprintf(
'%s %s',
h($decaying_model['DecayingModel']['name']),
$decaying_model['DecayingModel']['default'] ? '<img src="' . $baseurl . '/img/orgs/MISP.png" width="24" height="24" style="padding-bottom:3px;" title="' . __('Default Model from MISP Project') . '" />' : ''
)
);
$table_data[] = array('key' => __('Description'), 'value' => $decaying_model['DecayingModel']['description']);
if (isset($decaying_model['DecayingModel']['parameters']['base_score_config']) && empty($decaying_model['DecayingModel']['parameters']['base_score_config'])) {
$decaying_model['DecayingModel']['parameters']['base_score_config'] = new stdClass(); // force output to be {} instead of []
}
$table_data[] = array('key' => __('Version'), 'value' => $decaying_model['DecayingModel']['version']);
$table_data[] = array(
'key' => __('All orgs'),
'html' => '<i class="fas fa-' . ($decaying_model['DecayingModel']['all_orgs'] ? 'check' : 'times') . '"></i>'
);
$table_data[] = array(
'key' => __('Enabled'),
'html' => '<i class="fas fa-' . ($decaying_model['DecayingModel']['enabled'] ? 'check' : 'times') . '"></i>'
);
$table_data[] = array(
'key' => __('Formula'),
'html' => h($decaying_model['DecayingModel']['formula']) . (
isset($available_formulas[$decaying_model['DecayingModel']['formula']]['description']) ? sprintf(' <i class="fas fa-question-circle" data-toggle="tooltip" title="%s"></i>', h($available_formulas[$decaying_model['DecayingModel']['formula']]['description'])) : ''
)
);
$table_data[] = array('key' => __('Parameters'), 'value' => json_encode($decaying_model['DecayingModel']['parameters']), 'class' => 'json-transform');
$table_data[] = array('key' => __('Reference(s)'), 'html' => implode('<br/>', (empty($decaying_model['DecayingModel']['ref']) ? array() : h($decaying_model['DecayingModel']['ref']))));
$table_data[] = array('key' => __('Associated types'), 'value' => json_encode($decaying_model['DecayingModel']['attribute_types']), 'class' => 'json-transform');
?>
<div class='view'>
<div class="row-fluid">
<div class="span8">
<?php echo $this->element('genericElements/viewMetaTable', array('table_data' => $table_data)); ?>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('td.meta_table_value.json-transform').each(function(i) {
var parsedJson = syntaxHighlightJson($(this).text().trim());
$(this).html(parsedJson);
});
$('[data-toggle="tooltip"]').tooltip({placement: 'right'});
});
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'view'));
?>

View File

@ -0,0 +1,21 @@
<div class="form">
<?php echo $this->Form->create('DecayingModelMapping');?>
<fieldset>
<legend><?php echo __('Add DecayingModelMapping');?></legend>
<?php
echo $this->Form->input('model_id', array(
'hidden' => true,
'value' => $model_id
));
echo $this->Form->input('attributetypes', array());
?>
<div class="clear"></div>
</fieldset>
<?php
echo $this->Form->button(__('Add'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'decayingModel', 'menuItem' => 'add'));
?>

View File

@ -0,0 +1,18 @@
<div>
<?php if (isset($object['decay_score'])): ?>
<?php foreach ($object['decay_score'] as $dc): ?>
<?php $class_score = $dc['decayed'] ? 'alert-error' : 'alert-success'; ?>
<?php echo(isset($uselink) && $uselink ? '<a' : '<div') ?>
target="_blank"
href="/decayingModel/decayingToolSimulation/<?php echo h($dc['DecayingModel']['id']); ?>/attribute_id:<?php echo h($object['id']); ?>"
style="display: block; margin-bottom: 3px;"
class="input-prepend input-append"
>
<span class="add-on ellipsis-overflow" style="max-width: 12em;" title="<?php echo h($dc['DecayingModel']['name']); ?>"><?php echo h($dc['DecayingModel']['name']); ?></span>
<span id="simulation-current-score" class="add-on <?php echo $class_score ?>"><?php echo round($dc['score'], 2) ?></span>
<?php echo(isset($uselink) && $uselink ? '</a>' : '</div>') ?>
<?php endforeach; ?>
<?php else: ?>
&nbsp;
<?php endif; ?>
</div>

View File

@ -0,0 +1,20 @@
<div id="computation_help_container" style="margin-bottom: 0px; border: 1px solid #dddddd; border-radius: 4px; text-align: center; background-color: white; overflow: auto; ">
<table class="table histogram-legendH4">
<thead>
<tr>
<th rowspan="2" style="vertical-align: middle;"><?php echo __('Tag') ?></th>
<th colspan="3"><?php echo __('Computation') ?></th>
<th rowspan="2" style="vertical-align: middle;"><?php echo __('Result') ?></th>
</tr>
<tr>
<th style="padding: 0px; width: 50px;" title="<?php echo __('Taxonomy effective ratio') ?>"><?php echo __('Eff. Ratio') ?></th>
<th></th>
<th style="padding: 0px; width: 50px;" title="<?php echo __('Tag numerical value') ?>"><?php echo __('Value') ?></th>
</tr>
</thead>
<tbody id="computation_help_container_body">
<tr><td></td><td></td><td></td><td></td><td></td></tr>
</tbody>
</table>
<b id="pick_notice"><?php echo __('Pick an Attribute') ?></b>
</div>

View File

@ -0,0 +1,90 @@
<?php
$tr_class = array('useCursorPointer');
if (!empty($object['object_relation'])) {
$tr_class[] = '';
}
$tr_class = implode(' ', $tr_class);
?>
<tr id = "Attribute_<?php echo h($object['id']); ?>_tr" class="<?php echo $tr_class; ?>" tabindex="0" onclick="doSimulation(this, <?php echo h($object['id']); ?>)">
<td class="short">
<?php echo h($object['id']); ?>
</td>
<td class="short">
<a href="<?php echo $baseurl . '/events/view/' . h($object['event_id']) ?>" target="_blank"><?php echo h($object['event_id']); ?></a>
</td>
<td class="short">
<?php echo date('Y-m-d', $object['timestamp']); ?>
</td>
<td class="short">
<?php echo $this->OrgImg->getOrgImg(array('name' => $event['Orgc']['name'], 'id' => $event['Orgc']['id'], 'size' => 24)); ?>
&nbsp;
</td>
<td class="short">
<div id = "Attribute_<?php echo $object['id']; ?>_category_solid" class="inline-field-solid">
<?php echo h($object['category']); ?>
</div>
</td>
<td class="short">
<?php if (!empty($object['object_relation'])): ?>
<div class="bold"><?php echo h($object['object_relation']); ?>:</div>
<?php endif; ?>
<div id = "Attribute_<?php echo $object['id']; ?>_type_solid" class="inline-field-solid">
<?php echo h($object['type']); ?>
</div>
</td>
<td id="Attribute_<?php echo h($object['id']); ?>_container" class="showspaces limitedWidth shortish">
<div id = "Attribute_<?php echo $object['id']; ?>_value_solid" class="inline-field-solid" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<span style="white-space: nowrap;" title="<?php echo $this->element('/Events/View/value_field', array('object' => $object, 'linkClass' => 'blue')) ?>"><?php echo $this->element('/Events/View/value_field', array('object' => $object, 'linkClass' => 'blue')) ?></span>
</div>
</td>
<td class="shortish">
<div class="attributeTagContainer" id="#Attribute_<?php echo h($object['id']);?>_tr .attributeTagContainer">
<?php echo $this->element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['AttributeTag'], 'tagAccess' => false)); ?>
</div>
</td>
<?php
$element = '';
if (!empty($object['EventTag'])) {
$element = $this->element('ajaxAttributeTags', array('attributeId' => $object['id'], 'attributeTags' => $object['EventTag'], 'tagAccess' => false));
}
echo sprintf(
'<td class="shortish"><div %s>%s</div></td>',
'class="attributeRelatedTagContainer" id="#Attribute_' . h($object['id']) . 'Related_tr .attributeTagContainer"',
$element
);
?>
<td class="short" id="attribute_<?php echo h($object['id']); ?>_galaxy">
<?php
echo $this->element('galaxyQuickViewMini', array(
'mayModify' => false,
'isSiteAdmin' => false, // prevent add button
'isAclTagger' => false,
'data' => (!empty($object['Galaxy']) ? $object['Galaxy'] : array()),
'target_id' => $object['id'],
'target_type' => 'attribute'
));
?>
</td>
<td class="showspaces bitwider">
<div id = "Attribute_<?php echo $object['id']; ?>_comment_solid" class="inline-field-solid">
<?php echo nl2br(h($object['comment'])); ?>&nbsp;
</div>
</td>
<td class="short">
<div id = "Attribute_<?php echo $object['id']; ?>_to_ids_solid" class="inline-field-solid">
<span class="fa fa-<?php echo $object['to_ids'] ? 'check' : 'times' ; ?>"></span>
</div>
</td>
<td class="short">
<?php
if (!empty($sightingsData['csv'][$object['id']])) {
echo $this->element('sparkline', array('scope' => 'object', 'id' => $object['id'], 'csv' => $sightingsData['csv'][$object['id']]));
}
?>
</td>
<td class="short">
<div id = "Attribute_<?php echo $object['id']; ?>_score_solid" class="inline-field-solid">
<?php echo $this->element('DecayingModels/View/attribute_decay_score', array('scope' => 'object', 'object' => $object, 'uselink' => false)); ?>
</div>
</td>
</tr>

View File

@ -120,6 +120,20 @@ function triggerEventFilteringTool(hide) {
1: "Yes"
}
},
{
"input": "radio",
"type": "integer",
"operators": [
"equal",
],
"unique": true,
"id": "includeDecayScore",
"label": "Decay Score",
"values": {
0: "No",
1: "Yes"
}
},
{
"input": "radio",
"type": "integer",
@ -310,6 +324,13 @@ function triggerEventFilteringTool(hide) {
value: <?php echo isset($filters['includeRelatedTags']) ? h($filters['includeRelatedTags']) : 0; ?>
},
<?php endif; ?>
<?php if (count($advancedFilteringActiveRules) == 0 || isset($advancedFilteringActiveRules['includeDecayScore'])): ?>
{
field: 'includeDecayScore',
id: 'includeDecayScore',
value: <?php echo isset($filters['includeDecayScore']) ? h($filters['includeDecayScore']) : 0; ?>
},
<?php endif; ?>
<?php if (count($advancedFilteringActiveRules) == 0 || isset($advancedFilteringActiveRules['toIDS'])): ?>
{
field: 'toIDS',

View File

@ -348,6 +348,13 @@
'page' => $page
));
?>
<?php if (!empty($includeDecayScore)): ?>
<td class="decayingScoreField">
<div id = "Attribute_<?php echo h($object['id']); ?>_score_solid" class="inline-field-solid">
<?php echo $this->element('DecayingModels/View/attribute_decay_score', array('scope' => 'object', 'object' => $object, 'uselink' => true)); ?>
</div>
</td>
<?php endif; ?>
<td class="short action-links">
<?php
if ($object['deleted']):

View File

@ -112,6 +112,9 @@
</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<?php if (!empty($includeDecayScore)): ?>
<td class="decayingScoreField">&nbsp;</td>
<?php endif; ?>
<td class="short action-links">
<?php
if ($mayModify && empty($object['deleted'])) {

View File

@ -174,6 +174,9 @@
<td class="shortish">&nbsp;</td>
<td class="shortish">&nbsp;</td>
<td class="short">&nbsp;</td>
<?php if (!empty($includeDecayScore)): ?>
<td class="decayingScoreField">&nbsp;</td>
<?php endif; ?>
<td class="short action-links">
<?php
if (($event['Orgc']['id'] == $me['org_id'] && $mayModify) || $isSiteAdmin) {

View File

@ -163,6 +163,10 @@
<th title="<?php echo $attrDescriptions['distribution']['desc'];?>"><?php echo $this->Paginator->sort('distribution');?></th>
<th><?php echo __('Sightings');?></th>
<th><?php echo __('Activity');?></th>
<?php if ($includeDecayScore): ?>
<th class="decayingScoreField" title="<?php echo __('Decaying Score');?>"><?php echo __('Score');?></th>
<?php $fieldCount += 1; ?>
<?php endif; ?>
<th class="actions"><?php echo __('Actions');?></th>
</tr>
<?php
@ -182,7 +186,8 @@
'mayChangeCorrelation' => $mayChangeCorrelation,
'page' => $page,
'fieldCount' => $fieldCount,
'includeRelatedTags' => !empty($includeRelatedTags) ? 1 : 0
'includeRelatedTags' => !empty($includeRelatedTags) ? 1 : 0,
'includeDecayingScore' => !empty($includeDecayingScore) ? 1 : 0
));
if (!empty($focus) && ($object['objectType'] == 'object' || $object['objectType'] == 'attribute') && $object['uuid'] == $focus) {
$focusedRow = $k;

View File

@ -168,6 +168,15 @@
'onClickParams' => array($urlHere, 'deleted'),
'requirement' => ($me['Role']['perm_sync'] || $event['Orgc']['id'] == $me['org_id'])
),
array(
'id' => 'show_attribute_decaying_score',
'title' => __('Show attribute decaying score'),
'fa-icon' => 'chart-line',
'text' => __('Decay score'),
'active' => $includeDecayScore,
'onClick' => 'toggleBoolFilter',
'onClickParams' => array($urlHere, 'includeDecayScore')
),
array(
'id' => 'show_attribute_context',
'title' => __('Show attribute context fields'),

View File

@ -914,6 +914,55 @@
}
break;
case 'decayingModel':
if ($isAdmin) {
if ($isSiteAdmin && ($menuItem === 'view' || $menuItem === 'index')) {
echo $this->element('/genericElements/SideMenu/side_menu_post_link', array(
'event_id' => 'update',
'url' => '/decayingModel/update',
'text' => __('Update Default Models')
));
echo $this->element('/genericElements/SideMenu/side_menu_post_link', array(
'event_id' => 'update',
'url' => '/decayingModel/update/true',
'text' => __('Force Update Default Models')
));
}
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/decayingModel/import',
'text' => __('Import Decaying Model')
));
echo $this->element('/genericElements/SideMenu/side_menu_divider');
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/decayingModel/add',
'text' => __('Add Decaying Model')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/decayingModel/decayingTool',
'text' => __('Decaying Tool')
));
echo $this->element('/genericElements/SideMenu/side_menu_divider');
}
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/decayingModel/index',
'text' => __('List Decaying Models')
));
if (($menuItem === 'view' || $menuItem === 'edit')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'view',
'url' => '/decayingModel/view/' . h($id),
'text' => __('View Decaying Model')
));
if ($isSiteAdmin) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'edit',
'url' => '/decayingModel/edit/' . h($id),
'text' => __('Edit Decaying Model')
));
}
}
break;
case 'feeds':
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => '/feeds/index',

View File

@ -182,6 +182,18 @@
array(
'type' => 'separator'
),
array(
'text' => __('Decaying Models Tool'),
'url' => '/decayingModel/decayingTool',
'requirement' => $isAdmin
),
array(
'text' => __('Decaying Models'),
'url' => '/decayingModel/index',
),
array(
'type' => 'separator'
),
array(
'text' => __('User Guide'),
'url' => 'https://www.circl.lu/doc/misp/'
@ -291,7 +303,6 @@
),
array(
'type' => 'separator',
'requirement' => $isSiteAdmin
),
array(
'text' => __('Server Settings & Maintenance'),

View File

@ -40,6 +40,7 @@
<body>
<div id="popover_form" class="ajax_popover_form"></div>
<div id="popover_form_large" class="ajax_popover_form ajax_popover_form_large"></div>
<div id="popover_form_x_large" class="ajax_popover_form ajax_popover_form_x_large"></div>
<div id="popover_matrix" class="ajax_popover_form ajax_popover_matrix"></div>
<div id="popover_box" class="popover_box"></div>
<div id="screenshot_box" class="screenshot_box"></div>

@ -0,0 +1 @@
Subproject commit f38d1604f1fbd602d59d5a88fd906a2d7c21dfb3

View File

@ -0,0 +1,181 @@
/* decaying tool */
#table_type_search {
width: 250px;
margin-left: 5px;
}
.AttributeTypeTableContainer {
height: calc(100vh - 175px - 25px);
overflow-y: scroll;
resize: vertical;
}
.decayingGraphLine {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.decayingGraphLineThres {
fill: none;
stroke: firebrick;
stroke-width: 2px;
}
.decayingGraphArea {
fill: lightsteelblue;
}
.decayingGraphAreaThres {
fill: firebrick;
}
.decayingGraphDot{
stroke: #ffffff;
fill: #da4f49;
stroke-width: 2px;
}
.decayingGraphHandleDot{
stroke: #ffffff;
fill: steelblue;
stroke-width: 2px;
}
.decayingGraphAxis path {
stroke-width: 2px;
stroke: #000;
fill: none;
}
.decayingGraphAxis line {
stroke: #000;
}
.decayingGraphAxis text {
user-select: none;
}
.decayingGraphAxis.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.decayingGraphAxis.grid path {
stroke-width: 0;
}
/* decaying basescore */
#basescore_configurator {
overflow: auto;
padding: 15px;
max-height: 90vh;
}
#basescore_configurator > div.taxonomyTableContainer {
height: calc(90vh);
overflow-y: scroll;
border: 1px solid #ddd;
}
#basescore_configurator #body_taxonomies .taxonomySlider {
display: inline-block;
margin-left: 5px;
margin: 0px;
width: 40px;
}
#basescore_configurator #body_taxonomies span.numerical-value-label {
position: absolute;
right: 5px;
top: 50%;
margin-top: -9px;
}
#treemapGraphTax {
border: 1px solid #dddddd;
border-radius: 4px;
text-align: center;
}
#basescore_configurator div.org-source-confidence-placeholder {
margin-bottom: 5px;
border: 1px solid #dddddd;
border-radius: 4px;
text-align: center;
background-color: white;
}
#basescore_configurator #tableExamples td.basescore-example-score {
position: relative;
text-align: center;
vertical-align: middle;
}
.decayingExampleTags {
margin-bottom: 3px;
}
/* decaying simulation */
div.simulationSubContainer{
padding: 15px;
height: 90vh;
min-width: 1600px;
display: flex;
flex-direction: column;
}
#select_model_to_simulate_infobox {
padding: 4px;
height: fit-content;
margin-left: 5px;
}
#simulationContainer #restSearchTextarea {
margin-bottom: 0px;
margin-left: 4px;
flex-grow: 3;
width: auto;
}
div.loading-spinner-container {
height:100%;
display:flex;
align-items:center;
justify-content:center;
}
.svg-container {
display: inline-block;
position: relative;
width: 100%;
min-width: 500px;
height: 100%;
padding-bottom: 100%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
left: 0;
}
svg text.axis-label {
font: 10px sans-serif;
fill: #999;
}
.tableRadioFilterOptionsContainer {
display: inline-block;
padding: 0 5px;
margin-left: 10px;
border-top: 1px solid #ddd;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}

View File

@ -891,6 +891,13 @@ a.proposal_link_red:hover {
.ajax_popover_form_large {
width: 1400px;
left: calc(50% - 700px);
z-index: 30;
}
.ajax_popover_form_x_large {
width: 1700px;
left: calc(50% - 850px);
z-index: 30;
}
.ajax_popover_matrix {
@ -1124,6 +1131,10 @@ li .generic-picker-item-element-check {
z-index:-111;
}
span.success {
background-color: #d0e9c6 !important;
}
.ajaxMessage {
display:none;
margin-left:10px;
@ -1139,6 +1150,14 @@ li .generic-picker-item-element-check {
border-radius: 3px;
}
.panel-container {
border: 1px solid #dddddd;
border-radius: 4px;
background-color: #fafafa;
padding: 5px;
margin: 5px;
}
.inline-input {
width:100% !important;
margin-bottom:0px !important;
@ -1260,6 +1279,10 @@ li .generic-picker-item-element-check {
border-top: 1px solid #ddd;
}
.cellHeavyTopBorder {
border-top: 2px solid #000;
}
.tabMenuFilterFieldHeader {
border-radius: 10px 0px 0 0 !important;
height:14px !important;
@ -1545,6 +1568,13 @@ li .generic-picker-item-element-check {
border-top:0px !important;
}
.fa.helptext-in-cell, .fas.helptext-in-cell {
position: absolute;
right: 5px;
top: 50%;
margin-top: -6px;
}
.templateTableHeader {
display:inline-block;
}

View File

@ -0,0 +1,664 @@
(function(factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (window.jQuery && !window.jQuery.fn.DecayingSimulation) {
factory(window.jQuery);
}
}
(function($) {
'use strict';
var DecayingSimulation = function(container, options, data) {
this.container_id = '#' + container.id;
this.$container = $(container);
this._validateOptions(options);
var default_options = {
tick_num: 300,
margin: {top: 10, right: 10, bottom: 35, left: 35},
animation_duration: 250,
animation_short_duration: 100,
redraw_timeout: 200,
time_format: '%Y-%m-%d %H:%M:%S'
};
this.options = $.extend(true, {}, default_options, options);
this.chart_data = [];
this.sightings = [];
this._init();
this._init_canvas();
if (data !== undefined) {
this.update(data)
}
};
DecayingSimulation.prototype = {
constructor: DecayingSimulation,
_validateOptions: function(options) {
},
_init: function() {
var that = this;
this.$loadingContainer = $('<div id="loadingSimulationContainer" style="background: #ffffff9f"><span class="fa fa-spinner fa-spin" style="font-size: xx-large;"></span></div>').css({
position: 'absolute',
left: '0',
right: '0',
top: '0',
bottom: '0',
display: 'flex',
'align-items': 'center',
'justify-content': 'center'
}).hide();
this.tooltip_container = d3.select('body').append('div')
.classed('tooltip', true)
.style('opacity', 0)
.style('padding', '3px')
.style('background-color', '#000')
.style('color', 'white')
.style('border-radius', '5px')
.style('display', 'none');
this.$container.append(this.$loadingContainer);
this.timeFormatter = d3.time.format(this.options.time_format).parse;
},
_init_canvas: function() {
var that = this;
this.$container.empty();
this.svg_width = this.$container.width();
this.svg_height = this.$container.height();
this.width = this.svg_width - this.options.margin.left - this.options.margin.right;
this.height = this.svg_height - this.options.margin.top - this.options.margin.bottom;
this.x = d3.time.scale()
.domain(d3.extent(this.chart_data, function(d) { return d.date; }))
.range([ 0, this.width ]);
this.y = d3.scale.linear()
.domain([0, 100])
.range([ this.height, 0 ]);
this.yGrid = d3.svg.axis().scale(this.x).orient("bottom")
.tickSize(-this.height)
.tickFormat("");
this.xGrid = d3.svg.axis().scale(this.y).orient("left")
.ticks(3)
.tickSize(-this.width)
.tickFormat("");
this.value_line = d3.svg.line()
.x(function(d) { return that.x(d.date); })
.y(function(d) { return that.y(d.value); });
this.svg = d3.select(this.container_id)
.append("svg")
.classed('svg-content-responsive', true)
.attr("width", this.svg_width)
.attr("height", this.svg_height)
.append("g")
.attr("transform", "translate(" + this.options.margin.left + "," + this.options.margin.top + ")");
this.svg.append("g")
.attr('class', 'decayingGraphAxis axis-x')
.attr("transform", "translate(0," + this.height + ")")
this.svg.append("g")
.attr('class', 'decayingGraphAxis axis-y')
this.svg.append("g")
.attr("class", "decayingGraphAxis grid grid-x");
this.svg.append("g")
.attr("class", "decayingGraphAxis grid grid-y")
.attr("transform", "translate(0," + this.height + ")");
this.svg.append("text")
.classed('axis-label', true)
.attr("text-anchor", "end")
.attr("x", this.width / 2)
.attr("y", this.height)
.attr("dy", '30px')
.text("Date");
this.svg.append("text")
.classed('axis-label', true)
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90 0 " + this.height / 2 + ")")
.attr("x", 0)
.attr("dy", '-25px')
.attr("y", this.height / 2)
.text("Score");
this.svg.append('g')
.classed('line-group', true);
this.svg
.append("g")
.classed("d3-line-guides-group", true);
this.svg.insert('g')
.classed('circles', true);
window.addEventListener("resize", function() {
if (that.resize_timeout !== undefined) {
clearTimeout(that.resize_timeout);
}
that.resize_timeout = setTimeout(function() { that.redraw_timeout_handler(that) }, that.options.redraw_timeout);
});
},
redraw_timeout_handler: function(inst) {
clearTimeout(inst.resize_timeout);
inst._init_canvas();
inst._draw();
},
update: function(data, model) {
this.raw_data = data;
this.chart_data = data.csv;
this.sightings = data.sightings;
this.current_score = data.current_score;
this.model = model;
this._parseDataset();
this._draw();
},
_draw: function() {
var that = this;
this.x.domain(d3.extent(this.chart_data, function(d) { return d.date; }))
this.xAxis = this.svg.select('.axis-x')
.call(d3.svg.axis().scale(this.x).orient('bottom'));
this.yAxis = this.svg.select('.axis-y')
.call(d3.svg.axis().scale(this.y).orient("left"));
this.svg.select('.grid-x')
.call(this.xGrid);
this.svg.select('.grid-y')
.call(this.yGrid);
this.line = this.svg.select('.line-group')
.selectAll('.line')
.data([this.chart_data]);
this.line
.enter()
.append('path')
.attr("class","line")
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2.5)
this.line
.transition()
.duration(this.options.animation_duration)
.attr("d", this.value_line);
this.line.exit().remove();
this.line_guides = this.svg
.select('.d3-line-guides-group')
.selectAll('.d3-line-guides')
.data(this.sightings_data);
this.line_guides
.enter()
.append('line')
.attr('class', 'd3-line-guides')
this.line_guides // Update
.attr('x1', function(d) { return that.x(d.date); })
.attr('y1', function(d) { return that.height; })
.attr('x2', function(d) { return that.x(d.date); })
.attr('y2', function(d) { return that.height; })
.style("stroke", "rgba(70, 130, 180, 0.5)")
.style("stroke-dasharray", "4,2")
.style("shape-rendering", "crispEdges")
.transition()
.duration(this.options.animation_duration)
.attr('y2', function(d) { return that.y(d.value); });
this.line_guides.exit().remove();
// current time
this.line_guide_now = this.svg
.selectAll('.d3-line-now')
.data([new Date()]);
this.line_guide_now
.enter()
.append('line')
.attr('class', 'd3-line-now')
this.line_guide_now
.attr('x1', function(d) { return that.x(d); })
.attr('y1', function(d) { return that.y(0); })
.attr('x2', function(d) { return that.x(d); })
.attr('y2', function(d) { return that.y(101); })
.style("stroke", "#000")
.attr("stroke-width", 2)
.on('mouseover', function(d) {
that.tooltipText(true, this, d3.time.format("%e %B @ %H:%M")(new Date()));
})
.on('mouseout', function() {
that.tooltipText(false);
});
this.line_guide_now.exit().remove();
this.carret_line_guide_now = this.svg
.selectAll('.carret-time-now')
.data([new Date()]);
this.carret_line_guide_now
.enter()
.append('text')
.attr('class', 'carret-time-now')
this.carret_line_guide_now
.attr('x', that.x(new Date())-5.5)
.attr('y', that.y(99))
.attr('font-family', 'FontAwesome')
.attr('font-size', '20px')
.text(function(d) { return '\uf0d7' });
this.carret_line_guide_now.exit().remove();
this.svg.append('rect')
.attr('class', 'decayingGraphAreaThres')
.style('opacity', 0.6)
.attr('x', 0)
.attr('y', this.height)
.attr('width', this.width)
.attr('height', 0)
.on('mouseover', function(d) {
d3.select(this).transition()
.duration(that.options.animation_duration)
.style('opacity', 0.9)
that.tooltipText(true, this, 'Cutoff threshold: <b>' + that.model.parameters.threshold + '</b>');
})
.on('mouseout', function() {
d3.select(this).transition()
.duration(that.options.animation_duration)
.style('opacity', 0.6)
that.tooltipText(false);
});
this.svg.select('.decayingGraphAreaThres')
.transition()
.duration(this.options.animation_duration)
.attr('height', this.height-this.y(this.model.parameters.threshold))
.attr('y', this.y(this.model.parameters.threshold));
this.points = this.svg
.selectAll('.d3-line-circle')
.data(this.sightings_data);
this.points
.enter()
.append('circle')
.attr('class', 'decayingGraphHandleDot useCursorPointer d3-line-circle');
this.points // Update
.attr('cx', function (d) { return that.x(d.date); })
.attr('cy', function (d) { return that.y(d.value); })
.attr('r', 5)
.on('mouseover', function(d) {
d3.select(this).transition()
.duration(that.options.animation_duration)
.attr('r', 7);
that.tooltipDate(true, this, d);
})
.on('mouseout', function() {
d3.select(this).transition()
.duration(that.options.animation_duration)
.attr('r', 5);
that.tooltipDate(false);
})
.style('opacity', 0)
.transition()
.duration(this.options.animation_duration)
.delay(this.options.animation_duration)
.ease('linear')
.style('opacity', 1);
this.points.exit().remove();
// current score
this.current_score_target = this.svg
.selectAll('.current-score-target')
.data([new Date()]);
this.current_score_target
.enter()
.append('circle')
.classed('useCursorPointer current-score-target', true);
this.current_score_target
.attr('cx', that.x(new Date()))
.attr('cy', that.y(this.current_score))
.attr('fill', 'white')
.attr('stroke', 'black')
.attr('stroke-width', 2)
.attr('r', 6);
this.current_score_target = this.svg
.selectAll('.current-score-target-center')
.data([new Date()]);
this.current_score_target
.enter()
.append('circle')
.classed('useCursorPointer current-score-target-center', true);
this.current_score_target
.attr('cx', that.x(new Date()))
.attr('cy', that.y(this.current_score))
.attr('stroke', 'black')
.attr('r', 2);
d3.selectAll('.current-score-target, .current-score-target-center')
.on('click', function(d) {
$('#simulation-current-score').parent().children().effect('highlight');
});
},
toggleLoading: function(state) {
if (state === undefined) {
this.$loadingContainer.toggle();
} else if(state) {
this.$loadingContainer.show();
} else {
this.$loadingContainer.hide();
}
this.$container;
},
tooltipDate: function(show, d3Element, datum) {
var that = this;
var tooltip = this._toggleTooltip(show, d3Element);
if (show) {
tooltip.html(this._generate_tooltip(datum));
}
},
tooltipText: function(show, d3Element, html) {
var that = this;
var tooltip = this._toggleTooltip(show, d3Element);
if (show) {
tooltip.html(html);
}
},
_toggleTooltip: function(show, d3Element) {
var that = this;
if (show) {
var bb_rect = d3.select(d3Element)[0][0].getBoundingClientRect();
var cx = bb_rect.left;
var cy = bb_rect.top;
this.tooltip_container
.style('display', 'block')
.style('left', (cx + 17) + 'px')
.style('top', (cy - 6) + 'px')
.transition()
.duration(that.options.animation_short_duration)
.delay(that.options.animation_short_duration/2)
.style('opacity', '0.7');
} else {
this.tooltip_container.transition()
.duration(this.options.animation_short_duration)
.style('opacity', 0)
.delay(this.options.animation_short_duration)
.style('display', 'none');
}
return this.tooltip_container;
},
_generate_tooltip: function(datum) {
var formated_date = d3.time.format("%e %B @ %H:%M")(datum.date);
var html = 'Sighting on ' + formated_date + ' by ' + datum.org;
return html;
},
_parseDataset: function() {
var that = this;
if (typeof this.chart_data === 'string') {
this.chart_data = d3.csv.parse(this.chart_data, function(d){
var parsed_date = that.timeFormatter(d.date);
return { timestamp: Math.floor(parsed_date.getTime() / 1000), date: parsed_date, value : parseFloat(d.value) }
});
} else if (Array.isArray(this.chart_data)){
this.chart_data.forEach(function(entry, i) {
that.chart_data[i].date = that.timeFormatter(entry.date);
})
}
this.sightings_data = this.sightings.map(function(d) {
var sighting = d.Sighting;
var res = { timestamp: sighting.rounded_timestamp, date: new Date(sighting.rounded_timestamp*1000), value : that.raw_data.base_score_config.base_score };
res['org'] = d.Organisation !== undefined ? d.Organisation.name : '?';
return res;
});
}
}
$.DecayingSimulation = DecayingSimulation;
$.fn.decayingSimulation = function(options) {
var pickedArgs = arguments;
var $elements = this.each(function() {
var $this = $(this),
inst = $this.data('DecayingSimulation');
options = ((typeof options === 'object') ? options : {});
if ((!inst) && (typeof options !== 'string')) {
$this.data('DecayingSimulation', new DecayingSimulation(this, options));
} else {
if (typeof options === 'string') {
inst[options].apply(inst, Array.prototype.slice.call(pickerArgs, 1));
}
}
});
return $elements.length == 1 ? $elements.data('DecayingSimulation') : $elements;
};
$.fn.decayingSimulation.constructor = DecayingSimulation;
})
);
(function(factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (window.jQuery && !window.jQuery.fn.BasescoreComputationTable) {
factory(window.jQuery);
}
}
(function($) {
'use strict';
var BasescoreComputationTable = function(container, options, data) {
this.container_id = '#' + container.id;
this.$container = $(container);
this._validateOptions(options);
var default_options = {
animation_duration: 250,
};
this.options = $.extend(true, {}, default_options, options);
this._init();
if (data !== undefined) {
this.update(data)
}
};
BasescoreComputationTable.prototype = {
constructor: BasescoreComputationTable,
_validateOptions: function(options) {
},
_init: function() {
var that = this;
this.$loadingContainer = $('<div id="loadingSimulationContainer" style="background: #ffffff9f"><span class="fa fa-spinner fa-spin" style="font-size: xx-large;"></span></div>').css({
position: 'absolute',
left: '0',
right: '0',
top: '0',
bottom: '0',
display: 'flex',
'align-items': 'center',
'justify-content': 'center'
}).hide();
this.tooltip_container = d3.select('body').append('div')
.classed('tooltip', true)
.style('opacity', 0)
.style('padding', '3px')
.style('background-color', '#000')
.style('color', 'white')
.style('border-radius', '5px')
.style('display', 'none');
this.$container.append(this.$loadingContainer);
$('#basescore-simulation-container #pick_notice').remove();
},
update: function(data, model) {
this.base_score_config = data.base_score_config;
this.base_score = data.base_score_config.base_score;
this.tags = data.base_score_config.tags;
this.overriddenTags = data.base_score_config.overridden;
this._draw();
},
_create_all_tag_html: function(tag) {
var that = this;
if (tag !== false) {
var html_tag = this._create_tag_html(tag);
var overridden_html = '';
var namespace_predicate = tag.Tag.name.split('=')[0];
this.overriddenTags.forEach(function(entry) {
var cur_namespace_predicate = entry.AttributeTag.Tag.name.split('=')[0];
if (namespace_predicate == cur_namespace_predicate) {
overridden_html += '<div class="overriden_tag_wrapper" style="filter: grayscale(80%);">' + that._create_tag_html(entry.EventTag) + '</div>';
}
});
if (overridden_html !== '') {
return '<div style="position:relative;" class="useCursorPointer overridden_tags_container">'
+ overridden_html
+ '<div class="attribute_tag_wrapper" style="top:-12px;margin-bottom:-12px; left:4px;margin-right:-4px; float: left; position: relative;">' + html_tag + '</div>'
+ '</div>';
} else {
return html_tag;
}
} else { // last row
return '<span style="border-radius: 4px; border: 1px solid #ccc; background-color: #eeeeee; padding: 4px 5px;">base_score</span>';
}
},
_create_tag_html: function(tag) {
if (tag !== false) {
var $span = $('<span></span>');
$span.addClass('tag')
.css({
'white-space': 'nowrap',
'background-color': tag.Tag.colour,
'color': getTextColour(tag.Tag.colour)
})
.text(tag.Tag.name);
return $span[0].outerHTML;
} else { // last row
return '<span style="border-radius: 4px; border: 1px solid #ccc; background-color: #eeeeee; padding: 4px 5px;">base_score</span>';
}
},
_get_computation_step: function(tag) {
if (tag === false) {
return ['', '', '', this.base_score.toFixed(2)];
}
var namespace = tag.Tag.name.split('=')[0];
if (this.base_score_config.taxonomy_effective_ratios[namespace] !== undefined) {
var html1 = this.base_score_config.taxonomy_effective_ratios[namespace].toFixed(2);
var html4 = (parseFloat(tag.Tag.numerical_value) * this.base_score_config.taxonomy_effective_ratios[namespace]).toFixed(2);
} else {
var html1 = '0';
var html4 = '0';
}
var html2 = '<it class="fa fa-times" style=""></it>';
var html3 = parseFloat(tag.Tag.numerical_value).toFixed(2);
return [html1, html2, html3, html4];
},
_draw: function() {
var that = this;
$('#basescore-simulation-container #computation_help_container_body').empty();
var tbody = d3.select('#basescore-simulation-container #computation_help_container_body');
// create a row for each object in the data
var rows = tbody.selectAll('tr')
.data(this.tags.concat(false))
.enter()
.append('tr')
.attr('class', function(e, row_i) {
if (that.tags.length == row_i) {
return 'cellHeavyTopBorder bold';
}
});
// create a cell in each row for each column
var cells = rows.selectAll('td')
.data(function (tag, row_i) {
var html_computation = that._get_computation_step(tag);
return [
that._create_all_tag_html(tag),
html_computation[0], html_computation[1], html_computation[2], html_computation[3]
]
});
cells.enter()
.append('td')
.html(function (e) { return e; })
.style('opacity', 0.0)
.style('padding', function(e, col_i) {
if (col_i == 2) {
return '8px 2px 8px 8px';
}
return '';
})
.transition()
.duration(this.options.animation_duration)
.style('opacity', 1.0)
.each("end", function(td_content, col_i){
var $div = $(td_content);
if (col_i == 0 && $div.hasClass('overridden_tags_container')) {
$('.overridden_tags_container').popover({
title: 'Event tag overridden by Attribute tag',
content: that._generateOverridenExplanationPopoverHTML($div),
html: true,
trigger: 'hover',
placement: 'left',
container: 'body'
});
}
});
},
_generateOverridenExplanationPopoverHTML: function($div) {
var $tags_event = $div.find('.overriden_tag_wrapper .tag');
var $tag_attribute = $div.find('.attribute_tag_wrapper .tag');
var html = '<div style="text-align: center;">';
$tags_event.each(function() {
html += '<div>' + $(this)[0].outerHTML + '</div>'
});
html += '<div><i class="fa fa-arrow-down"></i></div>'
html += '<div>' + $tag_attribute[0].outerHTML + '</div>'
html += '</div>';
return html;
},
toggleLoading: function(state) {
if (state === undefined) {
this.$loadingContainer.toggle();
} else if(state) {
this.$loadingContainer.show();
} else {
this.$loadingContainer.hide();
}
this.$container;
},
}
$.BasescoreComputationTable = BasescoreComputationTable;
$.fn.basescoreComputationTable = function(options) {
var pickedArgs = arguments;
var $elements = this.each(function() {
var $this = $(this),
inst = $this.data('BasescoreComputationTable');
options = ((typeof options === 'object') ? options : {});
if ((!inst) && (typeof options !== 'string')) {
$this.data('BasescoreComputationTable', new BasescoreComputationTable(this, options));
} else {
if (typeof options === 'string') {
inst[options].apply(inst, Array.prototype.slice.call(pickerArgs, 1));
}
}
});
return $elements.length == 1 ? $elements.data('BasescoreComputationTable') : $elements;
};
$.fn.basescoreComputationTable.constructor = BasescoreComputationTable;
})
);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,435 @@
var mapping_tag_name_to_tag = {};
var base_score_computation = [];
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
function addTagWithValue(clicked) {
var $select = regenerateValidTags();
var html = '<div>' + $select[0].outerHTML + '<button class="btn btn-primary" style="margin-left: 5px;" onclick="addPickedTags(this)">Tag</button> </div>';
openPopover(clicked, html, false, 'right', function($popover) {
$popover.find('select').chosen({
width: '300px',
});
});
}
function addPickedTags(clicked) {
var numerical_values = [];
var $select = $('#basescore-example-tag-picker');
var $previous_tags = $('#basescore-example-customtag-container span.decayingExampleTags');
$previous_tags.each(function() {
numerical_values.push({name: getPrefixTagName($(this).text()), value: parseInt($(this).data('numerical_value'))});
});
$select.val().forEach(function(tag_id) {
var tag = mapping_tag_name_to_tag[tag_id];
tag.numerical_value = parseInt(tag.numerical_value);
var $outer_tag = $('<div></div>').css({display: 'inline-block'})
.attr('title', 'numerical_value=' + tag.numerical_value);
var $inner_tag_1 = $('<span></span>').addClass('tagComplete decayingExampleTags')
.css({'background-color': tag.colour, color: getTextColour(tag.colour)})
.data('numerical_value', tag.numerical_value)
.text(tag.name);
var $inner_tag_2 = $('<span></span>').addClass('tagSecondHalf useCursorPointer')
.css({'margin-right': '4px'})
.attr('onclick', 'removeCustomTag(this);')
.text('×');
$outer_tag.append($inner_tag_1).append($inner_tag_2);
$('#basescore-example-customtag-container').append($outer_tag);
var tag_name = getPrefixTagName(tag.name);
numerical_values.push({name: tag_name, value: tag['numerical_value']});
});
base_score_computation[0] = compute_base_score(numerical_values);
var base_score = base_score_computation[0].score.toFixed(1);
$('#basescore-example-score-0').empty()
.text(base_score)
.append('<i class="fas fa-question-circle helptext-in-cell useCursorPointer" onclick="genHelpBaseScoreComputation(event, 0)"></i>');
$('#basescore-example-score-addTagButton').popover('destroy');
$('#basescore-example-customtag-container').find('span.tagFirstHalf').parent().tooltip({placement: 'right', container: 'body'});
}
function removeCustomTag(clicked) {
$(clicked).parent().tooltip('destroy');
$(clicked).add($(clicked).prev()).remove();
var numerical_values = [];
var $previous_tags = $('#basescore-example-customtag-container span.decayingExampleTags');
$previous_tags.each(function() {
numerical_values.push({name: getPrefixTagName($(this).text()), value: parseInt($(this).data('numerical_value'))});
});
base_score_computation[0] = compute_base_score(numerical_values);
var base_score = base_score_computation[0].score.toFixed(1);
$('#basescore-example-score-0').empty()
.text(base_score)
.append('<i class="fas fa-question-circle helptext-in-cell useCursorPointer" onclick="genHelpBaseScoreComputation(event, 0)"></i>');
}
function regenerateValidTags() {
var $select = $('<select id="basescore-example-tag-picker" multiple="multiple"/>');
Object.keys(taxonomies_with_num_value).forEach(function(taxonomy_name) {
var taxonomy = taxonomies_with_num_value[taxonomy_name];
var $optgroup = $('<optgroup></optgroup>').attr('label', taxonomy_name);
taxonomy['TaxonomyPredicate'].forEach(function(predicate) {
if (predicate['numerical_predicate'] !== undefined && predicate['numerical_predicate']) {
mapping_tag_name_to_tag[predicate['Tag']['id']] = predicate['Tag'];
var $option = $('<option></option>')
.val(predicate['Tag']['id'])
.text(predicate['Tag']['name'] + ' [' + predicate['Tag']['numerical_value'] + ']');
$optgroup.append($option);
} else {
predicate['TaxonomyEntry'].forEach(function(entry) {
mapping_tag_name_to_tag[entry['Tag']['id']] = entry['Tag'];
var $option = $('<option></option>')
.val(entry['Tag']['id'])
.text(entry['Tag']['name'] + ' [' + entry['Tag']['numerical_value'] + ']');
$optgroup.append($option);
});
}
});
$select.append($optgroup);
});
return $select;
}
function applyBaseScoreConfig() {
var base_score_default_value = $('#base_score_default_value').val() >= 0 ? $('#base_score_default_value').val() : 0;
decayingTool.applyBaseScore(getRatioScore(), base_score_default_value);
$('#popover_form_large').fadeOut();
$('#gray_out').fadeOut();
}
function fetchTaxonomyConfig() {
var matching_inputs = $('#body_taxonomies > tr')
.find('input[type="number"]')
.filter(function() {
return parseInt($(this).val()) > 0;
});
var taxonomy_config = {};
matching_inputs.each(function() {
taxonomy_config[$(this).data('taxonomyname')] = parseInt($(this).val());
});
return taxonomy_config;
}
function getRatioScore(taxonomies_name) {
var config = fetchTaxonomyConfig();
if (taxonomies_name === undefined) {
taxonomies_name = Object.keys(config);
}
var ratioScore = {};
total_score = taxonomies_name.reduce(function(acc, name) {
var inc = 0;
if (config[name] !== undefined) {
inc = config[name];
}
return acc + inc;
}, 0)
taxonomies_name.forEach(function(name) {
if (config[name] !== undefined) {
ratioScore[name] = config[name] / total_score;
}
});
return ratioScore;
}
// return namespace:predicate for 3-components tagnames and namespace for 2-components tagnames
function getPrefixTagName(tag_name) {
var temp_tag_name = tag_name.split('='); // split on value
var prefix = temp_tag_name.length == 1 ? temp_tag_name[0].split(':')[0] : temp_tag_name[0];
return prefix;
}
function pickRandomTags() {
var taxonomies_name = Object.keys(fetchTaxonomyConfig());
var tags = [];
if (taxonomies_name.length == 0) {
return [];
}
var temp_taxonomies_name = taxonomies_name.slice();
var max_tag_num = taxonomies_name.length > 3 ? 3 : taxonomies_name.length;
var picked_tag_number = getRandomInt(max_tag_num) + 1;
for (var i = 0; i < picked_tag_number; i++) { // number of tags
// pick a taxonomy
var picked_number_taxonomy = getRandomInt(temp_taxonomies_name.length);
var picked_taxonomy_name_full = temp_taxonomies_name[picked_number_taxonomy];
var temp_split = picked_taxonomy_name_full.split(':');
var picked_taxonomy_namespace = temp_split[0];
var picked_taxonomy_predicate = temp_split[1]
var picked_taxonomy = taxonomies_with_num_value[picked_taxonomy_namespace];
// pick the predicate
var picked_predicate, picked_entry;
if (picked_taxonomy['TaxonomyPredicate'][0]['numerical_predicate'] !== undefined && picked_taxonomy['TaxonomyPredicate'][0]['numerical_predicate']) { // predicate is an entry
var picked_number_predicate = getRandomInt(picked_taxonomy['TaxonomyPredicate'].length);
picked_entry = picked_taxonomy['TaxonomyPredicate'][picked_number_predicate];
} else {
for (var j=0; j<picked_taxonomy['TaxonomyPredicate'].length; j++) {
if (picked_taxonomy['TaxonomyPredicate'][j]['value'] == picked_taxonomy_predicate) {
picked_predicate = picked_taxonomy['TaxonomyPredicate'][j];
break;
}
}
// pick a random entry -> tag
var picked_number_entry = getRandomInt(picked_predicate['TaxonomyEntry'].length);
picked_entry = picked_predicate['TaxonomyEntry'][picked_number_entry];
}
picked_entry['Tag']['numerical_value'] = parseInt(picked_entry['Tag']['numerical_value']);
tags.push(picked_entry['Tag']);
temp_taxonomies_name.splice(picked_number_taxonomy, 1);
}
return tags;
}
function html_computation_step(steps, i) {
var step = steps.steps.computation[i];
if (step === undefined) {
return ['', '', ''];
}
var html1 = step.ratio.toFixed(2);
var html2 = '*';
var html3 = step.tag_value.toFixed(2);
return [html1, html2, html3];
}
function computation_step(steps, i) {
var step = steps.steps.computation[i];
if (step === undefined) { // last row, just sum everything up
return (steps.score).toFixed(2);
}
return (step.ratio * step.tag_value).toFixed(2);
}
function genHelpBaseScoreComputation(e, index) {
e.preventDefault();
$('#tableExamples > tbody > tr').removeClass('success').css('font-weight', 'inherit');
$('#tableExamples > tbody > tr:nth-child(' + (index+1) + ')').addClass('success').css('font-weight', 'bold');
var steps = base_score_computation[index];
if (steps === undefined) {
steps = {
score: 0,
steps: {
computation: [],
init: { config: [] }
}
};
}
var $tags = $('#basescore-example-tag-'+index + ' span.decayingExampleTags');
var last_tag_index = $tags.length;
$tags.push($(''));
$('#computation_help_container_body').empty();
var tbody = d3.select('#computation_help_container_body');
// create a row for each object in the data
var rows = tbody.selectAll('tr')
.data($tags)
.enter()
.append('tr')
.attr('class', function(e, row_i) {
if (last_tag_index == row_i) {
return 'cellHeavyTopBorder bold';
}
});
// create a cell in each row for each column
var cells = rows.selectAll('td')
.data(function (tag, row_i) {
var html_computation = html_computation_step(steps, row_i);
return [
tag.outerHTML,
html_computation[0], html_computation[1], html_computation[2],
computation_step(steps, row_i),
]
});
cells.enter()
.append('td')
.html(function (e) { return e; })
.style('opacity', 0.0)
.transition().duration(500)
.style('opacity', 1.0);
$('#pick_notice').remove();
}
function refreshExamples() {
for (var i = 1; i <= 3; i++) {
var numerical_values = [];
var tags = pickRandomTags();
var $tag_container = $('<div></div>').css({'display': 'flex', 'flex-flow': 'wrap' });
tags.forEach(function(tag) {
var tag_name = getPrefixTagName(tag.name);
numerical_values.push({name: tag_name, value: tag['numerical_value']});
var text_color = getTextColour(tag.colour);
var $tag = $('<span></span>').addClass('tagComplete decayingExampleTags')
.css({'background-color': tag.colour, color: text_color, 'margin-right': '4px'})
.attr('title', 'numerical_value=' + tag['numerical_value'])
.data('placement', 'right')
.text(tag.name);
$tag_container.append($tag);
});
base_score_computation[i] = compute_base_score(numerical_values);
var base_score = base_score_computation[i].score.toFixed(1);
var base_score_computation_steps = base_score_computation[i].steps;
$('#basescore-example-tag-'+i).empty().append($tag_container);
$('#basescore-example-score-'+i).empty()
.text(base_score)
.append('<i class="fas fa-question-circle helptext-in-cell useCursorPointer" onclick="genHelpBaseScoreComputation(event, ' + i + ')"></i>');
$('span.decayingExampleTags').tooltip({ container: 'body' });
}
}
function compute_base_score(numerical_values) {
var base_score = 0;
var config = getRatioScore(numerical_values.map(x => x.name));
var steps = {
init: {
config
},
computation: []
};
numerical_values.forEach(function(tag) {
var coefficient = 0;
if (!isNaN(config[tag.name])) {
coefficient = config[tag.name];
base_score += coefficient * tag.value;
}
steps.computation.push({ ratio: coefficient, tag_value: tag.value });
});
return { score: base_score, steps: steps };
}
function filterTableTaxonomy(searchString) {
var $table = $('#tableTaxonomy');
var $body = $table.find('tbody');
if (searchString === '') {
$body.find('tr').forceClass('hidden', false);
} else {
$body.find('tr').forceClass('hidden', true);
// show only matching elements
var $cells = $body.find('tr > td:nth-child(1)');
$cells.each(function() {
if ($(this).text().trim().toUpperCase().indexOf(searchString.toUpperCase()) != -1) {
$(this).parent().forceClass('hidden', false);
}
});
}
}
$('#table_taxonomy_search').on('input', function() {
filterTableTaxonomy(this.value);
});
var refreshExamplesTimeout = null;
function refreshExamplesThrottle() {
if (refreshExamplesTimeout !== null) {
clearTimeout(refreshExamplesTimeout);
}
refreshExamplesTimeout = setTimeout(function() { refreshExamples(); }, 200);
}
function sliderChanged(changed) {
$(changed).parent().find('input[type="number"]').val(changed.value);
var new_data = genTreeData();
updateTree(new_data);
refreshExamplesThrottle();
}
function inputChanged(changed) {
$(changed).parent().find('input[type="range"]').val(changed.value);
var new_data = genTreeData();
updateTree(new_data);
refreshExamplesThrottle();
}
function updateTree(new_data) {
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
.value(function(d) { return d.size; });
var nodes = div.datum(new_data).selectAll(".node")
.data(treemap.nodes);
nodes.enter()
.append("div")
.attr("class", "node useCursorPointer")
.style("background", function(d) {
if (d.depth == 0) {
return 'white';
} else if (!d.children) {
return color(d.name);
} else {
return null;
}
})
.attr("id", function(d) { return d.name + '-node'})
.on('click', function() { $('#table_taxonomy_search').val(d3.select(this).data()[0].name).trigger('input');})
nodes.transition().duration(100)
.call(position)
.attr("title", function(d) { return d.name + ': ' + d.size})
.text(function(d) {
if (d.children) {
return '';
} else if (d.name !== '' && !isNaN(d.ratio) ) {
return d.name + ' ('+parseInt(d.ratio*100)+'%)';
} else {
return '';
}
});
nodes.exit()
.remove();
}
function genTreeData() {
var root = {
name: '',
children: []
};
var sum = 0;
var $sliders = $('#body_taxonomies').find('input[type="range"]');
$sliders.each(function(){
sum += parseInt($(this).val());
});
$sliders.each(function(){
var val = parseInt($(this).val());
if (val > 0) {
var tmp = {
name: $(this).data('taxonomyname'),
size: val,
ratio: val/sum
};
root.children.push(tmp);
}
});
return root;
}
var root = genTreeData();
var margin = {top: 0, right: 0, bottom: 0, left: 0},
width = 620 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.category20c();
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
.value(function(d) { return d.size; });
var div = d3.select("#treemapGraphTax").append("div").text('No taxonomy')
.style("position", "relative")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", (height + margin.top + margin.bottom) + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
updateTree(root);
refreshExamples();
function position() {
this.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
.style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; });
}

View File

@ -1492,7 +1492,7 @@ function templateElementFileCategoryChange(category) {
}
}
function openPopup(id, adjust_layout) {
function openPopup(id, adjust_layout, callback) {
adjust_layout = adjust_layout === undefined ? true : adjust_layout;
if (adjust_layout) {
var window_height = $(window).height();
@ -1511,10 +1511,14 @@ function openPopup(id, adjust_layout) {
}
}
$("#gray_out").fadeIn();
$(id).fadeIn();
$(id).fadeIn(400, function() {
if (callback !== undefined) {
callback();
}
});
}
function openPopover(clicked, data, hover, placement) {
function openPopover(clicked, data, hover, placement, callback) {
hover = hover === undefined ? false : hover;
placement = placement === undefined ? 'right' : placement;
/* popup handling */
@ -1549,6 +1553,9 @@ function openPopover(clicked, data, hover, placement) {
}
var popoverTitle = popover.find('h3.popover-title');
popoverTitle.html(title + closeButtonHtml);
if (callback !== undefined) {
callback(popover);
}
})
.on('keydown.volatilePopover', function(e) {
if(e.keyCode == 27) { // ESC
@ -1743,6 +1750,26 @@ function choicePopup(legend, list) {
openPopup("#popover_form");
}
function openModal(heading, body, footer, modal_option, css_container, css_body) {
var modal_id = 'dynamic_modal_' + new Date().getTime();
var modal_html = '<div id="' + modal_id + '" class="modal hide fade" style="' + (css_container !== undefined ? css_container : '') + '" tabindex="-1" role="dialog" aria-hidden="true">';
if (heading !== undefined && heading !== '') {
modal_html += '<div class="modal-header">'
+ '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>'
+ '<h3 id="myModalLabel">' + heading + '</h3>'
+ '</div>';
}
if (body !== undefined && body !== '') {
modal_html += '<div class="modal-body" style="' + (css_body !== undefined ? css_body : '') + '">' + body + '</div>';
}
if (footer !== undefined && footer !== '') {
modal_html += '<div class="modal-footer">' + footer + '</div>';
}
modal_html += '</div>';
$('body').append($(modal_html));
$('#'+modal_id).modal(modal_option !== undefined ? modal_option : {});
}
function resizePopoverBody() {
var bodyheight = $(window).height();
bodyheight = 3 * bodyheight / 4 - 150;
@ -3049,6 +3076,19 @@ function testConnection(id) {
})
}
function getTextColour(hex) {
hex = hex.slice(1);
var r = parseInt(hex.substring(0,2), 16);
var g = parseInt(hex.substring(2,4), 16);
var b = parseInt(hex.substring(4,6), 16);
var avg = ((2 * r) + b + (3 * g))/6;
if (avg < 128) {
return 'white';
} else {
return 'black';
}
}
function pgpChoiceSelect(uri) {
$("#popover_form").fadeOut();
$("#gray_out").fadeOut();
@ -3307,11 +3347,18 @@ function toggleBoolFilter(url, param) {
url = url.replace(re, '');
}
});
if (res[param] !== undefined) { // allow toggle for `deleted`.
res[param] = res[param] == '0' ? '2' : '0';
if (res[param] !== undefined) {
if (param == 'deleted') {
res[param] = res[param] == 0 ? 2 : 0;
} else {
res[param] = res[param] == 0 ? 1 : 0;
}
} else {
res[param] = '0';
if (param == 'deleted') {
res[param] = 0;
} else {
res[param] = 1;
}
}
url += buildFilterURL(res);
@ -4259,6 +4306,42 @@ function syntaxHighlightJson(json, indent) {
});
}
function jsonToNestedTable(json, header, table_classes) {
if (typeof json == 'string') {
json = JSON.parse(json);
}
if (Object.keys(json).length == 0) {
return '';
}
header = header === undefined ? [] : header;
table_classes = table_classes === undefined ? [] : table_classes;
var $table = $('<table></table>');
table_classes.forEach(function(classname) {
$table.addClass(classname);
});
if (header.length > 0) {
var $header = $('<thead><tr></tr></thead>');
header.forEach(function(col) {
$header.child().append($('<td></td>').text(col));
});
$table.append($header);
}
var $body = $('<tbody></tbody>');
Object.keys(json).forEach(function(k) {
var value = json[k];
if (typeof value === 'object') {
value = JSON.stringify(value);
}
$body.append(
$('<tr></tr>')
.append($('<td></td>').text(k))
.append($('<td></td>').text(value))
);
});
$table.append($body);
return $table[0].outerHTML;
}
function liveFilter() {
var lookupString = $('#liveFilterField').val();
if (lookupString == '') {

View File

@ -23,7 +23,6 @@ Install the AWS PHP SDK
```bash
cd /var/www/MISP/app
sudo -u www-data php composer.phar config vendor-dir Vendor
sudo -u www-data php composer.phar require aws/aws-sdk-php
```

View File

@ -327,8 +327,6 @@ installCake_RHEL ()
#$SUDO_WWW $RUN_PHP -- php -r "if (hash_file('SHA384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
#$SUDO_WWW $RUN_PHP "php composer-setup.php"
#$SUDO_WWW $RUN_PHP -- php -r "unlink('composer-setup.php');"
$SUDO_WWW $RUN_PHP "php composer.phar require kamisama/cake-resque:4.1.2"
$SUDO_WWW $RUN_PHP "php composer.phar config vendor-dir Vendor"
$SUDO_WWW $RUN_PHP "php composer.phar install"
## sudo yum install php-redis -y

View File

@ -304,8 +304,6 @@ installCake_RHEL ()
$SUDO_WWW php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$SUDO_WWW php composer-setup.php
$SUDO_WWW php -r "unlink('composer-setup.php');"
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
## sudo yum install php-redis -y

View File

@ -206,8 +206,6 @@ installCake () {
# Make composer cache happy
# /!\ composer on Ubuntu when invoked with sudo -u doesn't set $HOME to /var/www but keeps it /home/misp \!/
sudo mkdir /var/www/.composer ; sudo chown $WWW_USER:$WWW_USER /var/www/.composer
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
# Enable CakeResque with php-redis

View File

@ -160,8 +160,6 @@ cd /var/www/MISP/app
# Make composer cache happy
# /!\ composer on Ubuntu when invoked with sudo -u doesn't set $HOME to /var/www but keeps it /home/misp \!/
sudo mkdir /var/www/.composer ; sudo chown www-data:www-data /var/www/.composer
sudo -H -u www-data php composer.phar require kamisama/cake-resque:4.1.2
sudo -H -u www-data php composer.phar config vendor-dir Vendor
sudo -H -u www-data php composer.phar install
# Enable CakeResque with php-redis

View File

@ -251,8 +251,6 @@ sudo -H -u www ${PATH_TO_MISP}/venv/bin/pip install .
```bash
# Install CakeResque along with its dependencies if you intend to use the built in background jobs:
cd ${PATH_TO_MISP}/app
sudo -u www php composer.phar require kamisama/cake-resque:4.1.2
sudo -u www php composer.phar config vendor-dir Vendor
sudo -u www php composer.phar install
# To use the scheduler worker for scheduled tasks, do the following:

View File

@ -788,8 +788,6 @@ alias composer70='composer72'
composer72 () {
cd $PATH_TO_MISP/app
mkdir /var/www/.composer ; chown $WWW_USER:$WWW_USER /var/www/.composer
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
}
@ -806,8 +804,6 @@ composer73 () {
checkFail "composer.phar checksum failed, please investigate manually. " $?
$SUDO_WWW php composer-setup.php
$SUDO_WWW php -r "unlink('composer-setup.php');"
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
}

View File

@ -238,9 +238,6 @@ sudo -u www php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe
('composer-setup.php'); } echo PHP_EOL;"
sudo -u www env HOME=/tmp php composer-setup.php
sudo -u www php -r "unlink('composer-setup.php');"
sudo -u www HOME=/tmp php composer.phar require kamisama/cake-resque:4.1.2
sudo -u www HOME=/tmp php composer.phar config vendor-dir Vendor
sudo -u www HOME=/tmp php composer.phar install
# To use the scheduler worker for scheduled tasks, do the following:

View File

@ -413,8 +413,6 @@ doas -u www php -r "copy('https://getcomposer.org/installer', 'composer-setup.ph
doas -u www php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
doas -u www env HOME=/var/www php composer-setup.php
doas -u www php -r "unlink('composer-setup.php');"
doas -u www env HOME=/var/www php composer.phar require kamisama/cake-resque:4.1.2
doas -u www env HOME=/var/www php composer.phar config vendor-dir Vendor
doas -u www env HOME=/var/www php composer.phar install
# To use the scheduler worker for scheduled tasks, do the following:

View File

@ -244,8 +244,6 @@ cd $PATH_TO_MISP/app
#$SUDO_WWW $RUN_PHP -- php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
#$SUDO_WWW $RUN_PHP "php composer-setup.php"
#$SUDO_WWW $RUN_PHP -- php -r "unlink('composer-setup.php');"
$SUDO_WWW $RUN_PHP "php composer.phar require kamisama/cake-resque:4.1.2"
$SUDO_WWW $RUN_PHP "php composer.phar config vendor-dir Vendor"
$SUDO_WWW $RUN_PHP "php composer.phar install"
sudo yum install php-redis -y

View File

@ -231,8 +231,6 @@ cd $PATH_TO_MISP/app
#$SUDO_WWW $RUN_PHP -- php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
#$SUDO_WWW $RUN_PHP "php composer-setup.php"
#$SUDO_WWW $RUN_PHP -- php -r "unlink('composer-setup.php');"
$SUDO_WWW $RUN_PHP "php composer.phar require kamisama/cake-resque:4.1.2"
$SUDO_WWW $RUN_PHP "php composer.phar config vendor-dir Vendor"
$SUDO_WWW $RUN_PHP "php composer.phar install"
sudo yum install php-redis -y

View File

@ -201,8 +201,6 @@ $SUDO_WWW php -r "copy('https://getcomposer.org/installer', 'composer-setup.php'
$SUDO_WWW php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$SUDO_WWW php composer-setup.php
$SUDO_WWW php -r "unlink('composer-setup.php');"
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
# Enable CakeResque with php-redis

View File

@ -221,8 +221,6 @@ sudo mkdir /var/www/.composer ; sudo chown $WWW_USER:$WWW_USER /var/www/.compose
# $SUDO_WWW php -r "if (hash_file('SHA384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
# $SUDO_WWW php composer-setup.php
# $SUDO_WWW php -r "unlink('composer-setup.php');"
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
# Enable CakeResque with php-redis

View File

@ -228,8 +228,6 @@ function installMISPonTsurugi() {
cd $PATH_TO_MISP/app
mkdir /var/www/.composer ; chown www-data:www-data /var/www/.composer
$SUDO_WWW php composer.phar require kamisama/cake-resque:4.1.2
$SUDO_WWW php composer.phar config vendor-dir Vendor
$SUDO_WWW php composer.phar install
$SUDO_WWW cp -fa $PATH_TO_MISP/INSTALL/setup/config.php $PATH_TO_MISP/app/Plugin/CakeResque/Config/config.php

View File

@ -202,8 +202,6 @@ ${PATH_TO_MISP}/venv/bin/pip install .
# Install CakeResque along with its dependencies if you intend to use the built in background jobs:
cd ${PATH_TO_MISP}/app
php composer.phar require kamisama/cake-resque:4.1.2
php composer.phar config vendor-dir Vendor
php composer.phar install
# Enable CakeResque with php-redis