Compare commits

...

579 Commits

Author SHA1 Message Date
iglocska 92a07b01a4
Merge branch '2.4' of github.com:MISP/MISP into 2.4 2024-05-16 11:33:27 +02:00
iglocska 1c2e08ca23
new: [fatal error] logging added
- helps administrators to easily see what went wrong in terms of timeouts / oom issues
2024-05-16 11:32:26 +02:00
Alexandre Dulaunoy d1f113de3a
Merge pull request #9741 from schatzistogias/2.4
Updated git link
2024-05-15 18:11:34 +02:00
Stelios Chatzistogias a3e3b0e587 Updated git link 2024-05-15 16:48:42 +03:00
iglocska 694da4e641
fix: [server correlation UI] fixed link to index preview 2024-05-10 08:45:47 +02:00
iglocska 7b45a9e831
fix: [password reset] ACL fix 2024-05-08 08:53:19 +02:00
iglocska a5fa8f14bc
fix: [ACL] fixed pre-auth dynamic function calls 2024-05-08 08:49:56 +02:00
iglocska 504aae680c
Merge branch '2.4' of github.com:MISP/MISP into 2.4 2024-05-07 12:21:08 +02:00
iglocska 1286f61e5a
fix: [server/feed] correlation bug
- too many correlating events makes MISP barf
2024-05-07 12:18:48 +02:00
Alexandre Dulaunoy 7c8afc84ec
Merge pull request #9720 from schatzistogias/patch-1
Add Infoblox feed to defaults.json
2024-05-07 05:54:35 +02:00
Sami Mokaddem d682d92973
chg: [component:CRUD] Added support of afterFind in the delete function 2024-05-03 15:28:23 +02:00
iglocska b6769c5f58
chg: [schema] fix 2024-05-03 15:01:28 +02:00
iglocska d3324b6172
fix: [redirect loops] fixed for users that haven't done multiple mandatory tasks during login yet
- such as email OTP, change PW, read the news, etc.
2024-05-03 13:45:36 +02:00
iglocska f4f378159e
fix: [news UI] fixed notice error 2024-05-03 13:41:07 +02:00
iglocska 64f2fd9c31
fix: [security tests] removed otp_disabled check for email otp endpoint
- the two are distinct features
2024-05-03 12:54:41 +02:00
iglocska bf909d5fff
fix: [OTP] restored 2024-05-03 12:08:43 +02:00
iglocska 9f3735c5c2
fix: [Email OTP] invalid ACL check reverted, allowing the feature to function again 2024-05-03 08:16:28 +02:00
iglocska 6f2e162fd8
fix: [evnet view] excluding correlations should also exclude over_correlated attributes, fixes #9366 2024-05-02 21:35:23 +02:00
iglocska 4f2638b687
Merge branch 'develop' into 2.4 2024-05-02 15:33:26 +02:00
iglocska 7490bd19e7
chg: [VERSION] bump 2024-05-02 15:33:10 +02:00
Sami Mokaddem 18b0d3c22d
chg: [analyst-data:view] Removed the redundant UUID popover button from the UUID field 2024-05-02 15:14:50 +02:00
Sami Mokaddem 3ae6351509
chg: [analyst-data:beforeSave] Make sure to set distribution to default value if not provided 2024-05-02 15:14:18 +02:00
Sami Mokaddem 70c01ae049
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-05-02 15:13:48 +02:00
Sami Mokaddem b5ce3e99a4
fix: [workflow:workflow-shell] Make sure a user is set when using non-blocking workflow
- Fix #9722
- Thanks to @microblag for the proposed fix
2024-05-02 15:12:12 +02:00
iglocska cdfc12008c
fix: [external auth] fixed auth logging generating notices, fixes #9445 2024-05-02 13:47:40 +02:00
iglocska ecc4087b08
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-05-02 12:11:36 +02:00
iglocska 8dbe02d115
fix: [analystdata] don't include the parent via the viewAnalystData endpoints 2024-05-02 12:08:23 +02:00
Sami Mokaddem a87ca3b4d7
chg: [analyst-data:UI] Removed dep libraries 2024-05-02 11:49:54 +02:00
iglocska d6d4c8e08a
fix: [UI] added missing views 2024-05-02 11:41:23 +02:00
iglocska 2aa4b95de6
fix: [UI] removed dumb check 2024-05-02 11:40:14 +02:00
iglocska 2b1d2cb344
fix: [analystdata] ui fixes 2024-05-02 11:37:54 +02:00
iglocska 523fd1e121
new: [analyst data] missing views added 2024-05-02 11:30:12 +02:00
iglocska 304581e2b6
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-05-02 11:27:20 +02:00
iglocska 4795d9c183
fix: [analyst data] UI changes to make the loading on demand in the event view 2024-05-02 10:17:44 +02:00
schatzistogias 078c43f406
Add Infoblox feed to defaults.json 2024-04-30 13:19:46 +03:00
Sami Mokaddem 9a0f13c244
Merge branch 'feature/analyst-data-api' into develop 2024-04-29 14:13:34 +02:00
Sami Mokaddem 51c00f434d
Merge branch 'develop' of github.com:MISP/MISP into feature/analyst-data-api 2024-04-29 14:12:07 +02:00
Sami Mokaddem 021ae24e3f
fix: [logs] Fixed bug in paginating logs 2024-04-29 14:11:44 +02:00
Sami Mokaddem 002749d5d9
chg: [analyst-data] Added support of capturing analyst-data nested in attributes, events, eventreports and objects 2024-04-29 14:11:04 +02:00
Alexandre Dulaunoy 8f56d8cef8
chg: [warning-lists] updated to the latest version 2024-04-26 16:46:18 +02:00
Alexandre Dulaunoy 724a361bd3
chg: [misp-galaxy] updated to the latest version 2024-04-26 16:45:43 +02:00
Alexandre Dulaunoy a4a4b8c1dc
chg: [misp-objects] updated to the latest version 2024-04-26 16:45:20 +02:00
Jakub Onderka 902c99ac82
Merge pull request #9690 from JakubOnderka/opt_disabled
new: [security] Make possible to disable (T/H)OTP
2024-04-26 13:40:56 +02:00
Jakub Onderka bbb5ee96ab
Merge pull request #9700 from JakubOnderka/oidc-issuer-fix
fix: [oidc] Fix issuer if not set
2024-04-26 13:40:38 +02:00
iglocska 3d3a207d4d
chg: [UI] clicking on your user name should bring up the user profile, fixes #9708 2024-04-26 10:41:03 +02:00
iglocska 947dbe1085
fix: [event add] default value of threat level ID correctly injected into the form, fixes #9714 2024-04-26 10:15:52 +02:00
iglocska 66532a095c
Merge branch '2.4' into develop 2024-04-26 08:47:04 +02:00
iglocska 14106b811a
fix: [freetext] ip-src/ip-dst meta-type didn't have a valid category list 2024-04-26 08:43:00 +02:00
iglocska ee196c1349
fix: [user registration] pgp key not saved from the registration 2024-04-26 08:32:39 +02:00
Sami Mokaddem 7416a9dd97
fix: [logs:index] Fixed UI pagination in application logs 2024-04-25 14:46:22 +02:00
iglocska 89a6cbdfe6
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-23 15:12:30 +02:00
iglocska b6a8d43bbd
Merge branch 'browscap_default' into develop 2024-04-23 15:12:17 +02:00
Andras Iklody d629922a7f
Merge pull request #9697 from Wachizungu/add-orgc-filter-for-galaxy-clusters-index
fix: [galaxy_clusters] Add orgc filter option for index, set it as de…
2024-04-23 15:10:21 +02:00
iglocska 91e1c27746
Merge branch '2.4' into develop 2024-04-23 15:08:50 +02:00
iglocska cd25980da9
fix: [sql logs] captured when benchmarking is enabled but debug level is < 2 2024-04-23 15:03:22 +02:00
iglocska ed790e2ab7
Merge branch '2.4' into develop 2024-04-23 14:54:47 +02:00
iglocska 597977694d
fix: [security] stored XSS in the correlation top list
- if an attribute with an XSS payload as its value ends up being in the top list of correlations, then an administrator viewing the top correlations would execute the XSS

- as reported by Grzegorz Misiun
2024-04-23 14:51:58 +02:00
iglocska 4c75abbb70
new: [fast api auth] added
- added a new optional functionality to temporarily store hashed API keys in redis
  - The duration of the temporary storage is controllable by a setting (defaults to 3 minutes)
  - the hashing function used is an hmac sha-512 function, with the key being stored in a generated file on the instance
  - this cuts the query times of extremely fast endpoints down drastically on heavy repeated use (such as warninglists/checkValue)
2024-04-23 13:23:31 +02:00
iglocska b46d5a433e
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-23 13:23:04 +02:00
iglocska 7c5d052105
new: [fast api auth] added
- added a new optional functionality to temporarily store hashed API keys in redis
  - The duration of the temporary storage is controllable by a setting (defaults to 3 minutes)
  - the hashing function used is an hmac sha-512 function, with the key being stored in a generated file on the instance
  - this cuts the query times of extremely fast endpoints down drastically on heavy repeated use (such as warninglists/checkValue)
2024-04-23 13:20:45 +02:00
Sami Mokaddem a12f21ff61
fix: [workflow:ui] Make sure to use full available width 2024-04-23 07:47:13 +02:00
iglocska eb9f1011e1
Merge branch '2.4' into develop 2024-04-23 07:15:31 +02:00
iglocska fa9ff6f88e
fix: [benchmarking] speculative fix for using db settings and benchmarking, fixes #9702
- causes issues for some users, couldn't reproduce it, but addressed the potential issues
2024-04-23 07:14:31 +02:00
Alexandre Dulaunoy f5862203be
Merge branch '2.4' into develop 2024-04-22 22:19:31 +02:00
Jakub Onderka 34c85cfe7e fix: [oidc] Fix issuer if not set 2024-04-22 16:57:06 +02:00
iglocska 85062915a6
chg: [version bump] 2024-04-22 15:52:15 +02:00
Alexandre Dulaunoy 2b3a0d73ed
Merge branch '2.4' into develop 2024-04-22 09:51:10 +02:00
Jakub Onderka 536bbb9d92
Merge pull request #9695 from christianmg99/allow-oidc-roles-string
chg: [config] Allow Oidc roles as string
2024-04-22 09:43:06 +02:00
Sami Mokaddem 68c68febda
chg: [behavior:analystDataParent] Prevent double nesting analyst data when bulk fetching 2024-04-22 09:42:28 +02:00
Sami Mokaddem 051153b0c6
Merge branch '2.4' into develop 2024-04-22 08:55:57 +02:00
Sami Mokaddem 745d2407cf
fix: [analyst-data:fetchAnalystDataBulk] Make sure to include all analyst-data type 2024-04-22 08:55:44 +02:00
Sami Mokaddem ed6280f82a
fix: [analyst-data:thread] Make sure to link the add_analyst_* buttons to the correct element 2024-04-22 08:55:12 +02:00
Sami Mokaddem 5a202af3e8
fix: [events:index] Fixed `tags` index filtering parameter to correctly support list 2024-04-22 08:39:52 +02:00
Sami Mokaddem dd02d86e9d
Revert "Revert "new: [event:index] Added support of ANDed tag filtering in the backend""
This reverts commit 7cf9bcc94c.
2024-04-22 08:39:02 +02:00
Sami Mokaddem 84ac9b0733
Merge remote-tracking branch 'origin/2.4' into develop 2024-04-22 08:37:22 +02:00
Sami Mokaddem 1b7f086c16
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-22 08:37:16 +02:00
Sami Mokaddem 7cf9bcc94c
Revert "new: [event:index] Added support of ANDed tag filtering in the backend"
This reverts commit fc92291092.
2024-04-22 08:36:54 +02:00
christianmg99 ce7ab72190 chg: [config] Allow Oidc roles as string 2024-04-22 00:23:25 +02:00
Jeroen Pinoy 2b3cd11142
fix: [galaxy_clusters] Add orgc filter option for index, set it as default for galaxy view 'My Clusters' 2024-04-22 00:03:58 +02:00
Jakub Onderka 0adf16175a
Merge pull request #9696 from JakubOnderka/json-update
chg: [CLI] Simplify updating JSON structures
2024-04-21 10:56:12 +02:00
Jakub Onderka 2dd74ed79b chg: [CLI] Simplify updating JSON structures 2024-04-21 10:37:08 +02:00
Jakub Onderka f5f838f3f3
Merge pull request #9694 from christianmg99/set-oidc-issuer
chg: [config] Set Oidc issuer
2024-04-20 22:06:36 +02:00
christianmg99 ddd0a0cd46 chg: [config] Allow Oidc roles as string 2024-04-20 16:21:50 +02:00
Jakub Onderka 8ecb50a492
Merge pull request #8673 from JakubOnderka/menu-ui
chg: [UI] Make menu little bit nicer
2024-04-20 14:31:00 +02:00
Jakub Onderka 9ea64750bc new: [test] Security test for OTP disabled 2024-04-20 14:27:37 +02:00
Jakub Onderka 97e6224755 new: [test] Security test for forget password 2024-04-20 14:27:37 +02:00
Jakub Onderka b5100dcedd chg: [test] Avoid sleep for 6 seconds 2024-04-20 14:27:37 +02:00
Jakub Onderka 0ca6a47ef8 chg: [acl] Move site admin check as last check 2024-04-20 14:27:37 +02:00
Jakub Onderka d5ba5af530 chg: [security] Disable resetting password when password change is disabled 2024-04-20 14:27:37 +02:00
Jakub Onderka 79f6124bd2 new: [security] Make possible to disable (T/H)OTP
This is useful if MISP is connected to identity provider that already provides strong authentication
2024-04-20 14:27:35 +02:00
Jakub Onderka 722bcabed4
Merge pull request #8464 from JakubOnderka/restsearch-key-fetch
chg: [internal] Remove old way for putting API key to rest search
2024-04-20 14:26:41 +02:00
Jakub Onderka 2234a85adf chg: [internal] Remove outdated code from beforeFilter 2024-04-20 14:15:47 +02:00
Jakub Onderka fa02aed60c chg: [internal] Remove old way for putting API key to rest search 2024-04-20 14:15:47 +02:00
Jakub Onderka c0572af7dc
Merge pull request #9686 from JakubOnderka/sentry-breadcrumb
new: [internal] Send more logs to sentry as breadcrumbs
2024-04-20 13:38:02 +02:00
Jakub Onderka 43c234f345
Merge pull request #9693 from JakubOnderka/image-helper-fix-vol2
fix: [internal] Normalize extension for image helper
2024-04-20 13:05:45 +02:00
Christian Morales Guerrero 1933d30a7f
chg: [config] Set Oidc issuer 2024-04-20 01:36:27 +02:00
Jakub Onderka b64e0bc61d fix: [internal] Normalize extension for image helper
Fixes #9692
2024-04-19 23:39:35 +02:00
iglocska 471840ce33
Merge branch 'develop' into 2.4 2024-04-18 15:05:04 +02:00
Raphaël Vinot 9f3e6ce20e chg: [PyMISP] Bump 2024-04-18 14:57:57 +02:00
Alexandre Dulaunoy 2bb12095d5
chg: [warninglists] updated to the latest version 2024-04-18 14:53:52 +02:00
Alexandre Dulaunoy 89fd016e46
chg: [taxonomy] updated to the latest version 2024-04-18 14:53:06 +02:00
Alexandre Dulaunoy 1819cece53
chg: [misp-galaxy] updated to the latest version 2024-04-18 14:52:35 +02:00
Alexandre Dulaunoy 4f6e4360e4
chg: [misp-objects] updated 2024-04-18 14:52:13 +02:00
iglocska c78641ef85
chg: [version] bump 2024-04-18 14:48:16 +02:00
iglocska 182148d5fa
Merge branch '2.4' into develop 2024-04-18 14:34:17 +02:00
Andras Iklody d2b18b0e8e
Merge pull request #9529 from obert01/fix-hover-enrich-accessibility
Accessibility: Hover enrichment icon
2024-04-18 14:33:18 +02:00
Sami Mokaddem 62392fe540
fix: [analyst-data:fetchAnalystDataBulk] Make sure to include all analyst-data type 2024-04-18 14:32:54 +02:00
iglocska 222bd2d698
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-18 13:36:43 +02:00
iglocska 3c163d0c12
Merge branch 'feed_tag_collections' into develop 2024-04-18 13:34:45 +02:00
Raphaël Vinot 35fe93fc02 chg: Bump PyMISP 2024-04-18 13:03:10 +02:00
iglocska be9ad95905
chg: [syslog] output slightly changed
- always have a consistent number of fields conveyed, include delimited ( -- ) fields even if no data is passed to a field
- Avoid linebreaks in content
2024-04-18 12:46:11 +02:00
Sami Mokaddem 7f3db27667
chg: [db_schema] Bumped version 2024-04-18 09:25:08 +02:00
Sami Mokaddem 00991bda27
chg: [feed] Added support of tag_collection_id when dealing with feeds 2024-04-17 15:59:10 +02:00
Sami Mokaddem a2ea6ae0c0
fix: [feed] Added tag_collection_id as column 2024-04-17 15:17:53 +02:00
iglocska a55a19cd09
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-17 15:10:30 +02:00
iglocska 4544ef2516
new: [benchmarking suite] added
- collect metrics about the usage of MISP
  - stored in redis
  - per endpoint / user / user-agent collection
  - collection of execution time, php memory use, sql execution time, sql query count
  - the collection happens on a daily basis
- Searchable / filterable interface for the collected data
- Dashboard widget for the collected data
2024-04-17 15:08:38 +02:00
Sami Mokaddem 254b6d7646
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-17 14:48:45 +02:00
Sami Mokaddem 7ba2b39fe1
chg: [workflow:editor] Show 100 entry max in picker 2024-04-17 14:48:01 +02:00
iglocska 4dd5d369b4
chg: [attribute search] by uuid updated
- pre-checks if the passed UUID is actually an event UUID before going with the slow query against both tables
2024-04-17 12:00:53 +02:00
Alexandre Dulaunoy 4a0b8e6b90
Merge branch '2.4' into develop 2024-04-17 11:47:47 +02:00
Sami Mokaddem b5a60b5bfb
fix: [analyst-data:thread] Only render the HTML when opening the popover 2024-04-17 11:33:32 +02:00
Jakub Onderka 3b4e9675dd new: [internal] Send more logs to sentry as breadcrumbs 2024-04-15 21:56:27 +02:00
iglocska 8934982ff2
fix: [eventreport] import from url api fixed 2024-04-15 07:23:03 +02:00
Jakub Onderka 88ab8196da
Merge pull request #9639 from JakubOnderka/http-json-content-type
chg: [internal] Log content type when JSON could not be parsed
2024-04-14 15:41:11 +02:00
Jakub Onderka 731b96984a
Merge pull request #9659 from JakubOnderka/curl-timeout-5-mins
chg: [sync] Reduce default timeout for remote HTTP request to 300 sec…
2024-04-14 15:39:24 +02:00
Jakub Onderka df7ff3d4cd
Merge pull request #9651 from JakubOnderka/server-sync-debug
Server sync debug
2024-04-14 15:38:57 +02:00
Alexandre Dulaunoy be724e26af
chg: [README] add the CLA FREE logo
Ref: https://ossbase.org/initiatives/cla-free/
2024-04-13 18:20:12 +02:00
Jakub Onderka 47d35dae0b chg: [sync] Change way how event index is cached in Redis to save memory 2024-04-13 12:42:54 +02:00
Jakub Onderka d2176ab8bd chg: [sync] Try to reduce memory usage when fetching event index from Redis 2024-04-13 12:02:06 +02:00
Alexandre Dulaunoy f8f49a8a8d
Merge branch '2.4' into develop 2024-04-13 11:16:08 +02:00
Andras Iklody c591f06fea
Merge pull request #9678 from TheDr1ver/patch-1
Define $relationshipsInbound before call
2024-04-13 11:12:52 +02:00
Alexandre Dulaunoy 5f7fab1564
Merge branch '2.4' into develop 2024-04-12 17:00:19 +02:00
Alexandre Dulaunoy e968ee982a
chg: [openapi] STIX export is also supported at attribute level 2024-04-12 16:59:36 +02:00
Nick Driver a4c230e4e4
Define $relationshipsInbound before call
Debug.log was showing the following error otherwise:

```
2024-04-12 14:11:52 Notice: Notice (8): Undefined variable: relationshipsInbound in [/var/www/MISP/app/View/Elements/Events/View/row_object.ctp, line 40]
Trace:
ErrorHandler::handleError() - APP/Lib/cakephp/lib/Cake/Error/ErrorHandler.php, line 230
include - APP/View/Elements/Events/View/row_object.ctp, line 40
View::_evaluate() - APP/Lib/cakephp/lib/Cake/View/View.php, line 971
View::_render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 933
View::_renderElement() - APP/Lib/cakephp/lib/Cake/View/View.php, line 1224
View::element() - APP/Lib/cakephp/lib/Cake/View/View.php, line 418
include - APP/View/Elements/eventattribute.ctp, line 148
View::_evaluate() - APP/Lib/cakephp/lib/Cake/View/View.php, line 971
View::_render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 933
View::_renderElement() - APP/Lib/cakephp/lib/Cake/View/View.php, line 1224
View::element() - APP/Lib/cakephp/lib/Cake/View/View.php, line 418
include - APP/View/Elements/Events/View/event_contents.ctp, line 64
View::_evaluate() - APP/Lib/cakephp/lib/Cake/View/View.php, line 971
View::_render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 933
View::_renderElement() - APP/Lib/cakephp/lib/Cake/View/View.php, line 1224
View::element() - APP/Lib/cakephp/lib/Cake/View/View.php, line 418
include - APP/View/Elements/genericElements/SingleViews/single_view.ctp, line 113
View::_evaluate() - APP/Lib/cakephp/lib/Cake/View/View.php, line 971
View::_render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 933
View::_renderElement() - APP/Lib/cakephp/lib/Cake/View/View.php, line 1224
View::element() - APP/Lib/cakephp/lib/Cake/View/View.php, line 418
include - APP/View/Events/view.ctp, line 296
View::_evaluate() - APP/Lib/cakephp/lib/Cake/View/View.php, line 971
View::_render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 933
View::render() - APP/Lib/cakephp/lib/Cake/View/View.php, line 473
Controller::render() - APP/Lib/cakephp/lib/Cake/Controller/Controller.php, line 968
Dispatcher::_invoke() - APP/Lib/cakephp/lib/Cake/Routing/Dispatcher.php, line 200
Dispatcher::dispatch() - APP/Lib/cakephp/lib/Cake/Routing/Dispatcher.php, line 167
[main] - APP/webroot/index.php, line 101

```
2024-04-12 10:19:56 -04:00
Sami Mokaddem 038c411366
new: [feed:pullEvents] Added support of tag collection in feed configuration
This allow to specify a tag collection for which all the tags will be applied on the pulled Events
2024-04-12 15:58:19 +02:00
Sami Mokaddem 9060c21adf
chg: [workflowModules:distribution-if] Allow choosing `sharing-group` and keeping the selected sharing-group list empty
This enables users to simply check that the sharing-group distribution was used
2024-04-12 10:35:47 +02:00
Sami Mokaddem a9be1561e1
new: [workflowMouldes:stop-execution] Added message paramter to allow user to provide a reason why the execution was stopped 2024-04-12 10:34:58 +02:00
Sami Mokaddem a0b92e4c7b
fix: [workflow:evaluateConfition] Fixed bug in `in_and` operator to make it order independant 2024-04-12 10:31:22 +02:00
Sami Mokaddem b5b0412022
chg: [ui:galaxy_matrix] Resize matrix header on load 2024-04-11 16:35:58 +02:00
Sami Mokaddem 353e8c5195
fix: [users:statistics] Division by 0 when no events or no orgs 2024-04-11 11:20:04 +02:00
Sami Mokaddem 0808a6a23d
fix [event:view] Missing variable definition in row_object 2024-04-11 10:04:53 +02:00
Sami Mokaddem ea490063c0
fix: [analystData:editableField] Made getEditableFields inheritance aware 2024-04-11 10:03:32 +02:00
Sami Mokaddem 77a114673a
chg: [analystData:API] Automatically encapsulate request's data into the analystType 2024-04-11 10:01:52 +02:00
Sami Mokaddem 309242f358
chg: [eventReports:extractAllFromReport] Expose functionality to API 2024-04-11 09:41:20 +02:00
Sami Mokaddem 6e9d748f08
fix: [eventreports:transformFreeTextIntoSuggestion] Add to_ids fallback value 2024-04-11 09:40:18 +02:00
Sami Mokaddem c2d614f878
fix: [tagCollection:removeTag] Fixed incorrect permission check 2024-04-10 15:36:09 +02:00
Sami Mokaddem e7fa969487
Merge branch '2.4' into develop 2024-04-10 12:17:58 +02:00
Sami Mokaddem 004b18e1d9
fix: [component:restSearch] Restored behavior of searching for org and cluster metadata 2024-04-10 12:16:49 +02:00
iglocska 04100d13d3
chg: [statistics] (R)etrieval (o)f (m)ember (m)etrics (e)valuation (l)ist (f)or (s)tatistics changed
- will include soft deleted attributes too
2024-04-09 13:44:07 +02:00
iglocska 45176f7dcd
chg: [statistics] (R)etrieval (o)f (m)ember (m)etrics (e)valuation (l)ist (f)or (s)tatistics changed
- will include soft deleted attributes too
2024-04-09 13:41:56 +02:00
Jakub Onderka e2dbc690ac chg: [sync] Enable garbage collector when pulling events from remote server 2024-04-08 19:45:30 +02:00
Sami Mokaddem 05be803393
fix: [dashboard:updating] Prevent sending multiple time the same save request[1;5D 2024-04-08 16:41:46 +02:00
Sami Mokaddem 5235b9729c
fix: [widget:EventEvolutionWidget] Fixed filtering on organisation not working as expected 2024-04-08 16:37:57 +02:00
Sami Mokaddem fc92291092
new: [event:index] Added support of ANDed tag filtering in the backend
In addition of the OR filtering using searchtag:1|2, /events/index now supports AND filtering with searchtag:1&2.
The UI has not been updated yet.
2024-04-08 15:38:29 +02:00
Sami Mokaddem c4c395af31
new: [feed] Added unpublish_event setting to ensure pulled events are in the unpublished state 2024-04-08 14:48:04 +02:00
Sami Mokaddem b54eec95c1
fix: [dashboard:widgetAdd] Improved error handling for invalid JSON config 2024-04-08 11:36:51 +02:00
iglocska 5495dccb31
Merge branch '2.4' into develop 2024-04-08 10:34:06 +02:00
iglocska ef17beb59d
fix: [status widget] ignore index hint for deleted field 2024-04-08 10:33:15 +02:00
iglocska a7bdb225d8
Merge branch '2.4' into develop 2024-04-08 10:18:58 +02:00
iglocska 2c8c0fe508
fix: [index] Don't load analyst data by default 2024-04-08 10:18:30 +02:00
iglocska 13d33a3acb
chg: [comment] added to the previous fix to make it clear what it does 2024-04-08 10:12:22 +02:00
Jakub Onderka 8a42cf460d chg: [sync] Reduce default timeout for remote HTTP request to 300 seconds (5 mins) 2024-04-08 09:47:36 +02:00
Jakub Onderka a322217cbd chg: [sync] Try to save memory when fetching sightings 2024-04-08 09:45:33 +02:00
Jakub Onderka 8cd3cb0ef2 chg: [internal] Ltrim response in HttpSocketHttpException 2024-04-08 09:45:33 +02:00
Jakub Onderka e2b5e6edc3 chg: [CI] Split logs in CI 2024-04-08 09:45:33 +02:00
Jakub Onderka 2b38de942b chg: [internal] Server sync debug messages 2024-04-08 09:45:33 +02:00
Jakub Onderka d861ff2b2d
Merge pull request #9665 from JakubOnderka/sightings-fetching-cleanup
chg: [sync] Move blocklist fetching out of ServerSyncTool
2024-04-08 09:33:49 +02:00
Jakub Onderka 2e32d22d2c chg: [sync] Move blocklist fetching out of ServerSyncTool and reduce sightings fetched in one fetch 2024-04-06 14:05:44 +02:00
iglocska 5817075607
Merge branch 'develop' into 2.4 2024-04-05 14:42:18 +02:00
iglocska a54a1254cb
chg: [version] bump 2024-04-05 14:41:39 +02:00
Alexandre Dulaunoy 2b6519248f
chg: [GeoOpen] updated to the latest version 2024-04-05 14:36:30 +02:00
Alexandre Dulaunoy d0c7acfb10
chg: [misp-objects] updated to the latest version 2024-04-05 14:35:12 +02:00
Alexandre Dulaunoy d3ee1c0c46
chg: [taxonomies] updated 2024-04-05 14:34:49 +02:00
Alexandre Dulaunoy bc65739adc
chg: [warninglists] updated 2024-04-05 14:34:26 +02:00
Alexandre Dulaunoy 0f2cc3061e
chg: [misp-galaxy] updated 2024-04-05 14:34:08 +02:00
iglocska b1639bdb25
chg: [schema] updated 2024-04-05 14:21:08 +02:00
iglocska e1bc2052ae
chg: [ACL] entries added 2024-04-04 12:40:12 +02:00
iglocska 914ae20dd4
fix: [junk] removed 2024-04-04 12:14:03 +02:00
iglocska 480d3ac16d
chg: [setting] added for the sighting blocklisting 2024-04-04 12:13:04 +02:00
iglocska ef39b8959e
new: [sighting sync] blocklisting added
- block organisations' sightings from being created / pulled
- Added a new option to the restsearch of sightings too which this feature uses if available
  - if it isn't, the system will block the insertion on the beforeValidate() level

- Outcome of the JTAN hackathon on 04.04.2024 in Luxembourg
2024-04-04 12:08:22 +02:00
iglocska 31a2507fb4
chg: [sighting restsearch] added org negations
- the org_id filter now allows for the use of a prepended '!' character for negations
2024-04-04 09:42:14 +02:00
iglocska 540f86b361
Merge branch '2.4' into develop 2024-04-04 08:17:46 +02:00
Andras Iklody b8ef22754f
Merge pull request #9553 from jloehel/fix-9552
fix [INSTALL/MySQL]: Create table `user_login_profiles` only if it not exists
2024-04-04 08:17:05 +02:00
Jakub Onderka bb0c294c76
Merge pull request #9662 from JakubOnderka/build-test-json-valid
chg: [test] Check if MISP and STIX2 are valid in build-test.sh
2024-04-03 17:17:55 +02:00
Jakub Onderka 076f2beb3b chg: [test] Check if MISP and STIX2 are valid in build-test.sh 2024-04-03 17:06:48 +02:00
Sami Mokaddem 7dcca1ae2a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-03 16:43:30 +02:00
Sami Mokaddem 3d8fe9d90e
fix: [analyst-data:attachData] Make sure to also load child notes and opinions
Changed the old behavior: Before we were loading 3 children. Now, we only load 1 by default.
2024-04-03 16:42:18 +02:00
Jakub Onderka c68031edd8
Merge pull request #9658 from JakubOnderka/stix-exception-logging
chg: [internal] Log exception when importing stix file
2024-04-03 16:33:31 +02:00
Jakub Onderka 5159a72d11
Merge pull request #9660 from JakubOnderka/duplicate-sighting-uuid
fix: [sync] Avoid problem with duplicate sightings UUID
2024-04-03 16:32:56 +02:00
Jakub Onderka 728cb1584c
Merge pull request #9661 from JakubOnderka/misp-stix-update
chg: [internal] Update misp-stix
2024-04-03 16:32:36 +02:00
Jakub Onderka 6f9767df56 chg: [internal] Update misp-stix 2024-04-03 16:17:12 +02:00
Sami Mokaddem 94dd4fa093
fix: [analyst-data:UI] Added missing entries for view elements 2024-04-03 15:39:20 +02:00
Sami Mokaddem 87c71ecfc9
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-03 15:19:31 +02:00
iglocska e9f9781d51
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-03 15:14:03 +02:00
iglocska 644a457d8a
fix: [analystdata] added to events as the previous commits purged it 2024-04-03 15:13:34 +02:00
iglocska 946c012e62
fix: [analyst data chunk size] increased 2024-04-03 15:12:53 +02:00
Sami Mokaddem 1624c2a8d1
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-03 14:44:47 +02:00
Sami Mokaddem 16439afde5
new: [analyst-data] Added Inbound Relationship to all views. 2024-04-03 14:44:08 +02:00
Jakub Onderka 2f72afd59f fix: [sync] Avoid problem with duplicate sightings UUID 2024-04-03 13:42:23 +02:00
Alexandre Dulaunoy d720a9b42d
chg: [PyMISP] updated 2024-04-03 13:28:49 +02:00
Jakub Onderka 67e2478845
Merge pull request #8760 from JakubOnderka/sightings-conditions-simplify
chg: [internal] Speedup sighting rest search
2024-04-03 13:09:16 +02:00
Jakub Onderka 16c9c18b8f fix: [internal] Try to fix STIX import 2024-04-03 12:34:30 +02:00
Jakub Onderka e8d3d76fd9 chg: [internal] Log exception when importing stix file 2024-04-03 12:18:33 +02:00
Alexandre Dulaunoy e60fe35e0a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-04-03 12:09:09 +02:00
iglocska ebef5a388c
chg: [UI] event view now only load analyst data for objects/attributes actually shown via pagination 2024-04-03 12:06:17 +02:00
Alexandre Dulaunoy 02bf0ebd54
new: [attribute] new attribute type added `integer`
Initially, we utilised a counter type across numerous objects.

However, the semantic significance of this type became unclear when establishing relationships with integers in various objects.

Signed-off-by: Alexandre Dulaunoy <a@foo.be>
2024-04-03 12:04:46 +02:00
iglocska 51782c1d03
chg: [curl client] added option for timeout 2024-04-03 09:50:57 +02:00
Jakub Onderka 09eaacaf38
Merge pull request #9657 from JakubOnderka/remove-php-ends
chg: [internal] Remove possible empty lines from output
2024-04-02 19:54:54 +02:00
Jakub Onderka 1f3f018bf7 fix: [internal] Attribute.php code style fix 2024-04-02 19:40:14 +02:00
Jakub Onderka 486e74cff0 chg: [internal] Remove possible empty lines from output 2024-04-02 19:36:53 +02:00
Jakub Onderka 240e793e82
Merge pull request #9652 from JakubOnderka/curl-zstd-drop
fix: [sync] Drop support for zstd from CurlClient
2024-03-31 11:43:39 +02:00
Jakub Onderka 52e7c218fe fix: [sync] Drop support for zstd from CurlClient 2024-03-31 11:11:05 +02:00
Jakub Onderka 10ee756dd3
Merge pull request #9649 from JakubOnderka/oidc-is-user-valid-fix
fix: [oidc] Use the same handling of org also for Oidc::isUserValid
2024-03-29 10:54:27 +01:00
Jakub Onderka 55a2054448 fix: [oidc] Use the same handling of org also for Oidc::isUserValid 2024-03-29 09:04:08 +01:00
Alexandre Dulaunoy 0d3a42eff7
Merge pull request #9641 from Wachizungu/chg-background-jobs-migration-guide-add-rhel
chg: [docs:new-background-workers] add rhel specific steps to migrati…
2024-03-25 23:22:48 +01:00
Jakub Onderka 7d3cbb1abf
Merge pull request #9642 from JakubOnderka/attibute-search-500
chg: [test] Check attribute search
2024-03-25 18:12:50 +01:00
Jakub Onderka f182cbcec5 fix: [search] Attribute search error 500 because of force index search 2024-03-25 17:54:38 +01:00
Jakub Onderka 95de5d982c chg: [test] Check attribute search 2024-03-25 17:44:31 +01:00
Jakub Onderka 95e5faa911
Merge pull request #9640 from JakubOnderka/event-log-correlation-graph
fix: [UI] Showing event logo in correlation graph
2024-03-25 15:23:37 +01:00
Jeroen Pinoy 02cca29523
chg: [docs:new-background-workers] add rhel specific steps to migration guide 2024-03-25 15:10:53 +01:00
Jakub Onderka 90a2e3a53d fix: [UI] Showing event logo in correlation graph 2024-03-25 14:59:35 +01:00
Jakub Onderka 5b11e6b212 chg: [internal] Log content type when JSON could not be parsed 2024-03-24 18:46:02 +01:00
Jakub Onderka c946d7c451
Merge pull request #9637 from JakubOnderka/undefined-index-fixes
Undefined index fixes
2024-03-24 13:48:50 +01:00
Jakub Onderka 5247b9cd6d fix: [internal] Check if values is not empty for MysqlExtended 2024-03-24 13:35:00 +01:00
Jakub Onderka aaa8301ab2 fix: [internal] Undefined index in error message during sync 2024-03-24 13:31:11 +01:00
Alexandre Dulaunoy 3e4738adeb
Merge pull request #9636 from Wachizungu/fix-rhel-httpd-listen-config
fix: [doc:rhel-installer] Correct conditional addition of httpd Liste…
2024-03-24 12:19:27 +01:00
Jeroen Pinoy b61a39ff94
fix: [doc:rhel-installer] Correct conditional addition of httpd Listen 443 line 2024-03-23 17:26:37 +01:00
Jakub Onderka 0a77e3c3b8
Merge pull request #9635 from JakubOnderka/error-handling-sighting
chg: [internal] Better error handling when fetching sightings
2024-03-23 11:51:51 +01:00
Jakub Onderka 646c58095f chg: [internal] Better error handling when fetching sightings 2024-03-23 11:30:44 +01:00
Jakub Onderka fbaff5da96
Merge pull request #9634 from JakubOnderka/response-etag
fix: [API] Cleanup compression marks added by Apache from Etag
2024-03-23 08:30:41 +01:00
Jakub Onderka 0763b826cf fix: [API] Cleanup compression marks added by Apache from Etag 2024-03-23 08:17:04 +01:00
iglocska 8ac96cc104
Merge branch 'develop' into 2.4 2024-03-22 16:00:38 +01:00
Raphaël Vinot d39d9b4714 chg: [PyMISP] Bump, again 2024-03-22 15:48:50 +01:00
Raphaël Vinot 0a385e4b0f chg: [PyMISP] Bump 2024-03-22 15:36:41 +01:00
iglocska dbe2660f25
chg: [version] bump 2024-03-22 15:35:07 +01:00
iglocska 74579bb1fe
fix: [attribute search] enforce unpublishedprivate directive 2024-03-22 15:24:05 +01:00
iglocska 035b80239a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-22 15:06:09 +01:00
iglocska fed7149e93
fix: [curlclient] HEAD failing
- added CURLOPT_NOBODY for HEAD requests, as described in https://www.php.net/manual/en/function.curl-setopt.php
2024-03-22 15:04:57 +01:00
Christian Studer 317fd056b4
chg, fix: [misp-stix] Bumped latest version
- Fixing an issue where the custom Galaxy Clusters
  generated with the conversion from STIX 2.x were
  not correctly built to generate the Galaxy
  elements after the validation of the content
2024-03-21 16:51:55 +01:00
Christian Studer a21e931c0d
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-21 16:42:24 +01:00
Jakub Onderka 9fb1939b70
Merge pull request #9631 from JakubOnderka/attachment-scan-error
fix: [internal] Error handling for error message in AttachmentScan
2024-03-21 15:12:48 +01:00
Jakub Onderka 7894b9e7e7 fix: [internal] Error handling for error message in AttachmentScan 2024-03-21 14:34:17 +01:00
iglocska 544a450fea
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-21 14:31:26 +01:00
iglocska 7bbae462ad
fix: [publish] don't pop the list of failed servers before generating the error array 2024-03-21 14:31:14 +01:00
iglocska 7f0b4cd9ab
fix: [sync] if push rules don't have the type_attributes set, don't throw an error 2024-03-21 14:30:49 +01:00
Jakub Onderka de6c920589
Merge pull request #9630 from JakubOnderka/oidc-default-org-handling
fix: [OIDC] Default organisation handling if not provided by OIDC
2024-03-21 12:48:50 +01:00
Jakub Onderka e95b333096 fix: [CLI] Fix redisReady for dragonfly 2024-03-21 12:25:37 +01:00
Jakub Onderka 5bbdeb0ee6 fix: [ECS] Change type from Exception to Throwable 2024-03-21 12:12:01 +01:00
Jakub Onderka 8f6c6b9ef3 chg: [CI] Mark BadRequestException as fail log 2024-03-21 10:45:05 +01:00
Jakub Onderka f4b540b48c chg: [internal] Better error handling 2024-03-21 10:39:16 +01:00
Jakub Onderka 2380b4466b fix: [OIDC] Default organisation handling if not provided by OIDC 2024-03-21 10:19:57 +01:00
iglocska ec0b0721be
chg: [tests] trying to fix the failing test 2024-03-20 14:51:51 +01:00
iglocska 0bbc10929b
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-20 14:42:07 +01:00
iglocska c44e5050a6
fix: [attempt] fix for the etag test 2024-03-20 14:41:37 +01:00
Raphaël Vinot 5b5584596c chg: [PyMISP] Bump 2024-03-20 14:15:17 +01:00
iglocska 6e1811a8e0
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-20 14:11:17 +01:00
Alexandre Dulaunoy 2b0721cca1
chg: [misp-galaxy] updated 2024-03-20 14:10:27 +01:00
Alexandre Dulaunoy c73ab62b4a
chg: [misp-object] updated 2024-03-20 14:09:49 +01:00
iglocska 394d680a7b
chg: [version] bump 2024-03-20 14:09:22 +01:00
Alexandre Dulaunoy 4ce0ea4fcb
chg: [warning-lists] updated 2024-03-20 14:09:06 +01:00
iglocska 94d7537eec
chg: [attribute search] rework
- Massive performance improvement when using MysqlExtended or MysqlObserverExtended data sources
- event level lookup moved to subqueries, allowing for simpler, much faster indexed queries
- Ignoring the deleted index as it slows things down
2024-03-20 13:07:10 +01:00
iglocska 7072451d0f
new: [datasource] improvements
- Some datasources updated with the ignoreIndexHint parameter
  - mysqlExtended
  - mysqlObserverExtended

- Also fixed forceIndexHint
2024-03-20 13:04:36 +01:00
Sami Mokaddem 448b5dbdf0
Merge branch 'pr-9589' into develop 2024-03-19 14:22:57 +01:00
Sami Mokaddem 1be477c457
Merge remote-tracking branch 'origin/develop' into pr-9589 2024-03-19 14:22:32 +01:00
Sami Mokaddem 5b86e5b51f
chg: [openapi:analyst_data] Added content for analyst-data 2024-03-19 11:50:41 +01:00
Sami Mokaddem 6c35c5e11e
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-19 10:54:36 +01:00
Sami Mokaddem 88cf4919b0
chg: [openapi:event_report] Added content for event-reports 2024-03-19 10:53:52 +01:00
iglocska a129f2e58b
Merge branch '2.4' into develop 2024-03-18 16:27:38 +01:00
iglocska 0fb58cff44
fix: [performance] load analyst data in bulk
speeds up event loading dramatically
2024-03-18 16:07:55 +01:00
Vincenzo Caputo 752638528b Fix key error on shadow attribute's id 2024-03-16 16:27:57 +00:00
Vincenzo Caputo 044923ee3a Change trigger's icon 2024-03-16 15:33:45 +00:00
Vincenzo Caputo ee3508182d Change scope to 'shadow-attribute' 2024-03-16 15:32:42 +00:00
iglocska 3022d51a06
fix: [performance] load analyst data in bulk
speeds up event loading dramatically
2024-03-15 08:41:55 +01:00
iglocska 945f875e10
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-15 07:48:18 +01:00
iglocska 6b408a6be5
chg: [attribute fetch] slightly refactored
- simplify conditions
- don't load acl conditions twice
2024-03-15 07:43:58 +01:00
Sami Mokaddem c23363ac87
chg: [ls22shell] Improvement for LS24 adding support of analyst-data & detection/mitigation rules + some tweaks 2024-03-14 16:31:22 +01:00
Alexandre Dulaunoy 60fccf0723
chg: [misp-galaxy] updated 2024-03-14 16:25:24 +01:00
Alexandre Dulaunoy fa0fa036b5
Merge branch '2.4' into develop 2024-03-14 16:24:52 +01:00
Alexandre Dulaunoy 0723035c02
Merge pull request #9615 from vincenzocaputo/fix-accept-delegation-attachments
fix: Attachments deletion when accepting a delegation request
2024-03-14 16:20:38 +01:00
Alexandre Dulaunoy 7ce57dd24b
Merge branch '2.4' into develop 2024-03-14 15:57:41 +01:00
Alexandre Dulaunoy 00ade9cc91
Merge pull request #9616 from cudeso/2.4
Add ICS-CSIRT.io community
2024-03-14 15:57:03 +01:00
Koen Van Impe 9dd238c90d Add ICS-CSIRT.io community 2024-03-14 14:16:18 +01:00
Alexandre Dulaunoy 4834fa96a4
Merge branch '2.4' into develop 2024-03-13 11:18:19 +01:00
Vincenzo Caputo f0e1dcb3da
Add include attachments option when fetching event in EventDelegation.php 2024-03-13 10:57:39 +01:00
Sami Mokaddem c797865c7c
chg: [sightings:getLastSighting] Added support of sighting policy
Fix #8660
2024-03-12 14:41:22 +01:00
Sami Mokaddem 7d8b1b0260
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-12 11:33:54 +01:00
Sami Mokaddem ec769c3f27
chg: [attribute:restSearch] Improved performance of `includeDecayScore` by a factor of 5 2024-03-12 11:32:10 +01:00
Jakub Onderka 0f32956aa4
Merge pull request #9613 from JakubOnderka/alert-email-title
chg: [internal] Add title to alert template
2024-03-12 10:17:06 +01:00
Jakub Onderka df27db5644 fix: [UI] Add missing `MISP.email_reply_to` to server config 2024-03-12 10:06:48 +01:00
Jakub Onderka 031afce5d2 chg: [internal] Add title to alert template 2024-03-12 09:33:44 +01:00
iglocska 3c79ebbc06
new: [settings] added setting to (temporarily) disable the loading of sightings via the API
- affected endpoints: restsearch and /events/view
- temporarily skips the loading of sightings

- helps alleviate absolutely massive sighting data sets from killing server performance
- temporary measure, doesn't prevent the creation of sightings / viewing of sightings via the UI
2024-03-12 08:24:13 +01:00
iglocska 661b238b3f
Merge branch 'develop' into 2.4 2024-03-07 15:05:10 +01:00
iglocska 59732c4b53
chg: [Version] bump 2024-03-07 15:04:13 +01:00
iglocska 30f6e07a8a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-07 15:03:53 +01:00
Raphaël Vinot 08367489c9 chg: [PyMISP] Update 2024-03-07 14:51:35 +01:00
iglocska 3aa1ddbe03
new: [cli] added org list to the shell commands
- and some fixes to the roles
2024-03-07 14:49:24 +01:00
Alexandre Dulaunoy 834b873e03
chg: [misp-galaxy] updated to the latest version 2024-03-07 14:41:33 +01:00
Alexandre Dulaunoy 095afcd666
chg: [misp-warninglists] updated to the latest version 2024-03-07 14:40:33 +01:00
Alexandre Dulaunoy 0218bf86a4
chg: [misp-objects] updated to the latest version 2024-03-07 14:40:01 +01:00
Alexandre Dulaunoy a8bcacfcb0
chg: [taxonomies] 2.4.187 2024-03-07 14:39:23 +01:00
iglocska 31d20f094f
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-07 13:56:42 +01:00
iglocska f1102decf6
fix: [CLI] added some new functionalities
- list roles
- create user
2024-03-07 13:56:03 +01:00
Sami Mokaddem aaf3633cb0
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-07 10:54:44 +01:00
Sami Mokaddem 3dcf54aad5
fix: [events:restsearch] Correctly unset variable by reference after looping
- This avoid attributes being overridden others when using `includeAnalystData` parameter
2024-03-07 10:52:54 +01:00
iglocska b6d7755e9e
fix: [sync] pulls should continue after an event save failure
- fixes #9558
2024-03-06 13:28:11 +01:00
iglocska 826c60b62c
Merge branch '2.4' into develop 2024-03-06 11:01:47 +01:00
Andras Iklody 11865f6755
Merge pull request #9602 from karenyousefi/2.4
Update Event.php
2024-03-06 11:00:39 +01:00
iglocska aac29ad6af
fix: [db update] added IF NOT EXISTS clauses to create table calls 2024-03-06 10:57:30 +01:00
iglocska 6979fef446
fix: [API consistency]
- represent the local field for tags as a boolean rather than an int
2024-03-06 10:47:28 +01:00
iglocska 30e8aa454a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-03-06 10:40:41 +01:00
iglocska dc0cb15675
fix: [logging] fixed using removeTagFromObject()
- no longer creates erroneous log entries when unpublishing the event
2024-03-06 10:39:55 +01:00
Andras Iklody e42802bcfb
fix: [database update] fix
- for older mysql versions
2024-03-06 10:24:54 +01:00
Andras Iklody bdc0637e3d
Update AppModel.php
fix: [analyst data] update script

- remove default current_timestamp() on older versions of v121 of the db updates
- avoids chicken and egg problem on ancient mysql versions
2024-03-06 10:18:09 +01:00
Jakub Onderka e79fc41ce2
Merge pull request #9605 from JakubOnderka/fix-pull-analyst
fix: [pull] Fix pulling from remote server when analyst data is not s…
2024-03-05 16:44:45 +01:00
iglocska 6a2986be6a
fix: [security] properly check for valid logo upload
- as kindly reported by Rémi Matasse and Raphael Lob from Synacktiv (https://www.synacktiv.com)
2024-03-05 14:48:57 +01:00
iglocska 238010bfd0
fix: [security] properly check for valid file upload
- as kindly reported by Rémi Matasse and Raphael Lob from Synacktiv (https://www.synacktiv.com)
2024-03-05 13:54:28 +01:00
Jakub Onderka 14f8a7120e
Merge pull request #9606 from JakubOnderka/cli-role-change
new: [CLI] New command to change user role
2024-03-04 18:35:33 +01:00
Jakub Onderka 7d719639e2
Merge pull request #9607 from JakubOnderka/oidc-fix-update-role
fix: [oidc] Setting checking if variable is false
2024-03-04 18:35:03 +01:00
Jakub Onderka 258b521870 fix: [oidc] Setting checking if variable is false 2024-03-04 18:23:48 +01:00
Jakub Onderka 6140f8a14a new: [CLI] New command to change user role 2024-03-04 18:18:47 +01:00
Jakub Onderka 37cfd37cdb
Merge pull request #9604 from JakubOnderka/ext-zstd-suggested
chg: [internal] Add ext-zstd to suggested PHP extension
2024-03-04 15:56:26 +01:00
Jakub Onderka 5acf0a922c fix: [pull] Fix pulling from remote server when analyst data is not supported 2024-03-04 15:36:34 +01:00
Sami Mokaddem 1c7121b881
chg: [analyst-data:add] Fixed non-focusable relationship dropdown search field 2024-03-04 15:28:57 +01:00
Jakub Onderka 84ea097995 chg: [internal] Add ext-zstd to suggested PHP extension 2024-03-04 15:27:07 +01:00
Sami Mokaddem 242cfb192a
Merget branch 'develop' of github.com:MISP/MISP into develop 2024-03-04 08:18:34 +01:00
Sami Mokaddem 974e58c121
fix: [Galaxies:toggle] Display correct message when disabling a galaxy 2024-03-04 08:18:00 +01:00
Karen Yousefi 939764d274
Update Event.php
fix error Undefined offset: 0 in [/var/www/MISP/app/Model/Event.php, line 3682]
2024-03-01 22:03:58 +03:30
Jakub Onderka 745098c9dd
Merge pull request #9600 from JakubOnderka/oidc-update-user-role
new: [oidc] New option OidcAuth.update_user_role to disable role chan…
2024-03-01 10:15:08 +01:00
iglocska 708d18174a
Merge branch 'develop' into 2.4 2024-02-29 15:49:21 +01:00
iglocska 8f85bda6bb
fix: [schema] fixed 2024-02-29 15:33:47 +01:00
Sami Mokaddem 5c21896d96
Merge branch 'develop' into 2.4 2024-02-29 14:53:20 +01:00
Sami Mokaddem a4f0a6681b
fix: [event:_mergeExtension] Include analyst data on extension if originally requested in the request 2024-02-29 14:52:52 +01:00
iglocska 9c0ea04bb2
Merge branch 'develop' into 2.4 2024-02-29 14:36:47 +01:00
iglocska 970e4b6916
chg: [schema] dumped 2024-02-29 14:35:41 +01:00
Sami Mokaddem 6d7ba5ecfa
Merge remote-tracking branch 'origin/develop' into 2.4 2024-02-29 14:27:26 +01:00
Sami Mokaddem e6dd70bd64
fix: [analyst-data:hasMoreNotesOrOpinions] Use correct model to fetch additional opinions 2024-02-29 14:24:45 +01:00
Jakub Onderka 7ebb7a5107 new: [oidc] New option OidcAuth.update_user_role to disable role changes from OIDC 2024-02-29 13:00:41 +01:00
iglocska c6710443e0
Merge branch 'develop' into 2.4 2024-02-29 11:19:51 +01:00
iglocska 334d3caac3
chg: [version] bump 2024-02-29 11:19:05 +01:00
iglocska b870728f6b
fix: [analystdata] push and pull fixes
- push: check sharing group data correctly
- pull: Don't throw errors if not all 3 types of notes exist on the remote
2024-02-29 11:18:06 +01:00
Bradley Logan ee986fc2fc
chg: Set BrowscapPHP logging from default DEBUG to INFO 2024-02-28 15:22:14 -08:00
Jakub Onderka 8854fa58b2
Merge pull request #9508 from JakubOnderka/redis-info
new: [UI] Show dragonfly version in diagnostics
2024-02-28 13:53:59 +01:00
Jakub Onderka 23c6ad9091
Merge pull request #9594 from Wachizungu/fix-object-templates-misp-logo-display
fix: [UI] Fix MISP logo display on object templates index
2024-02-28 13:53:34 +01:00
Jeroen Pinoy c09d5861c6
fix: [UI] Fix MISP logo display on object templates index 2024-02-28 11:20:39 +01:00
Raphaël Vinot 7cd28317de chg: [PyMISP] Bump 2024-02-28 00:25:04 +01:00
Christian Studer d262767ab7
fix: [stix2 import] Making the organisation uuid argument specific to external STIX 2 import 2024-02-27 22:09:12 +01:00
Christian Studer 3d2e563c33
chg: [misp-stix] Bumped latest version 2024-02-27 17:46:29 +01:00
Christian Studer fdfd783f0f
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-27 17:39:43 +01:00
Alexandre Dulaunoy 7c66aa699c
chg: [warning-lists] updated to the latest version 2024-02-27 17:39:22 +01:00
Alexandre Dulaunoy d66f6d90d5
chg: [misp-galaxy] updated to the latest version 2024-02-27 17:38:58 +01:00
iglocska cf0910dc04
fix: [analystdata] removed invalid field from the change before the last 2024-02-27 09:06:15 +01:00
iglocska 4d8e04fd4c
fix: [analyst data blocklist] removed unused edit button 2024-02-27 09:04:55 +01:00
iglocska 060cf4f45d
fix: [analystdata] restrict what to display in associated models 2024-02-27 09:03:17 +01:00
iglocska 38c6ffd7a0
fix: [analystdata] fixed editing of context specific editable fields 2024-02-27 08:44:34 +01:00
iglocska 9f859892c2
fix: [analyst data] zero out sharing group ID when other distribution setting is selected 2024-02-27 08:24:24 +01:00
iglocska 27885e19ca
fix: [analystdata] clarified hover text 2024-02-27 07:24:48 +01:00
iglocska ba08a8219b
fix: [analystdata ui] oversanitisation of relationships fixed 2024-02-27 07:21:15 +01:00
Vincenzo Caputo 84eed089c2 Remove newline in overhead message 2024-02-25 16:00:01 +00:00
Vincenzo Caputo 74c7133be8 Add overhead message 2024-02-25 15:59:14 +00:00
Vincenzo Caputo eca3cd9cbf Add call to trigger before saving shadow attribute 2024-02-25 15:54:42 +00:00
Vincenzo Caputo 02de43a49e Add shadow attribute before save trigger 2024-02-25 15:51:01 +00:00
Alexandre Dulaunoy d82387b376
chg: [misp-galaxy] updated 2024-02-24 18:17:34 +01:00
Alexandre Dulaunoy 8d1a74b40b
Merge branch '2.4' into develop 2024-02-24 13:46:09 +01:00
Alexandre Dulaunoy e93beba4d3
Merge pull request #9588 from vincenzocaputo/fix-workflow-tag-replacement-module-description
Fix Tag replacement workflow module description
2024-02-24 13:43:12 +01:00
Vincenzo Caputo 626fafc40f Fix Tag replacement workflow module description 2024-02-24 11:31:17 +00:00
Christian Studer 7b5e75a1b5
fix: [stix2 import] Added missing `organisation_uuid` argument 2024-02-23 23:44:08 +01:00
Christian Studer 354da05e19
fix: [upload_stix] Fixed naive copy paste failing after an arbitrary variable name change 2024-02-23 22:44:38 +01:00
Christian Studer 71d1d5fc4a
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-23 22:14:10 +01:00
Christian Studer 54c15476c6
Merge pull request #9440 from chrisr3d/develop
Handling clusters distribution and sharing group for content imported from STIX 2.x
2024-02-23 22:13:36 +01:00
Christian Studer 5f6c1327ff
chg: [misp-stix] Bumped latest version with the changes on the organisation uuid argument 2024-02-23 21:42:38 +01:00
Christian Studer 41b20f96d3
add: [stix2 import] Added organisation UUID parameter to be used when generating custom Galaxy Clusters UUID 2024-02-23 21:41:08 +01:00
Christian Studer 1163539038
fix: [upload_stix] Fixed undefined index `cluster_sharing_group_id` when uploading stix file 2024-02-23 16:03:00 +01:00
Christian Studer 9221682157
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-23 15:49:03 +01:00
Sami Mokaddem 105a6c39b0
Merge remote-tracking branch 'origin/2.4' into develop 2024-02-23 12:19:28 +01:00
Jakub Onderka c07ee0066c
fix: [UI] Catch exception when custom file is not readable 2024-02-23 12:19:00 +01:00
Sami Mokaddem 78399abd03
Merge remote-tracking branch 'origin/2.4' into develop 2024-02-23 12:16:47 +01:00
Sami Mokaddem f09fdad92d
Merge branch 'fix/custom-image-rendering' into 2.4 2024-02-23 12:10:39 +01:00
Sami Mokaddem f9174e9a4d
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-23 12:07:48 +01:00
Sami Mokaddem abcbc575c1
fix: [users:login401] Usage of Image->base64 to follow what users:login does 2024-02-23 12:07:45 +01:00
Sami Mokaddem 7ad892a028
fix: [users:login401] Usage of Image->base64 to follow what users:login does 2024-02-23 12:06:49 +01:00
Alexandre Dulaunoy b5d0f2407c
Merge branch '2.4' into develop 2024-02-23 12:06:09 +01:00
Alexandre Dulaunoy 5646474130
Merge pull request #9582 from cudeso/2.4
Minor documentation changes; add example to create users via REST API
2024-02-23 12:05:31 +01:00
Sami Mokaddem af1ba18319
fix: [user:login] Make sure welcome_logos exists before trying to render them 2024-02-23 12:03:35 +01:00
Sami Mokaddem 6e06cf433c
fix: [user:login] Make sure welcome_logos exists before trying to render them 2024-02-23 12:00:26 +01:00
iglocska f7c76e965f
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-23 11:03:01 +01:00
iglocska 0561953c12
fix: [UI] correct encoding for the notes 2024-02-23 11:02:36 +01:00
Sami Mokaddem 0fce6c7784
fix: [eventReports:view/analystData] Load assets before trying to render notes 2024-02-23 11:02:23 +01:00
Jakub Onderka a92b2c5111
Merge pull request #9583 from JakubOnderka/image-helper-fix
fix: [UI] Catch exception when custom file is not readable
2024-02-23 10:45:07 +01:00
Sami Mokaddem a7c47f9b24
fix: [users:login] Check file existence in the correct location 2024-02-23 10:34:23 +01:00
Jakub Onderka f8a92524ee
fix: [UI] Custom logos 2024-02-23 10:31:42 +01:00
Jakub Onderka 52ff88d5c8 fix: [internal] exif_imagetype is not standard part of PHP 2024-02-23 10:29:18 +01:00
iglocska e6ec7871e3
fix: [notes] changed timestamp output to not include timezone
- doesn't work on all versions of mariadb/mysql
2024-02-23 10:18:29 +01:00
Jakub Onderka 7e7dcec240 fix: [UI] Catch exception when custom file is not readable 2024-02-23 10:01:18 +01:00
Koen Van Impe 105e7fc267
Minor documentation changes; add example to create users via REST API 2024-02-23 09:43:53 +01:00
iglocska 6dfaa6d97c
Merge branch '2.4' of github.com:MISP/MISP into 2.4 2024-02-22 17:05:50 +01:00
iglocska 45e23c8509
fix: [processtool] make old versions happy
- proc_open only started accepting $command as an array in 7.4
2024-02-22 17:03:48 +01:00
Sami Mokaddem 8d9eef79fb
Revert "fix: [users:login] Check file existence in the correct location"
This reverts commit a1bba71204.
2024-02-22 15:49:54 +01:00
Sami Mokaddem a1bba71204
fix: [users:login] Check file existence in the correct location 2024-02-22 15:38:26 +01:00
Sami Mokaddem 6655697dbc
fix: [users:login] Check file existence in the correct location 2024-02-22 10:30:15 +01:00
Sami Mokaddem b8c2c7be64
fix: [login:UI] Reverted change that swapped `main_logo` with `home_logo` 2024-02-22 10:20:11 +01:00
Sami Mokaddem a2497f5763
fix: [db_schema] Bumped db_version 2024-02-22 08:56:12 +01:00
Sami Mokaddem 48a7addb04
fix: [galaxyCluster:view/analystData] Load assets before trying to render notes 2024-02-22 08:40:58 +01:00
Sami Mokaddem 224415c3b4
chg: [analyst-data:edit] Added support of editable fields 2024-02-22 08:32:47 +01:00
Sami Mokaddem 76e61d3e26
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-22 08:32:12 +01:00
Sami Mokaddem 7b661f740a
chg: [analyst-data:UI] Added highlight on note opener button
- As request by gallypette
2024-02-22 08:32:05 +01:00
Sami Mokaddem b7242f7dae
chg: [analyst-data:UI] Added highlight on note opener button
- As request by gallypette
2024-02-21 16:26:06 +01:00
Sami Mokaddem 396837675e
chg: [analyst-data:thread] Gracefully catch cases where the related object is not found when generating link URL 2024-02-21 16:20:26 +01:00
Sami Mokaddem 720336f65d
chg: [analyst-data:datetimes] Moved datetime manamgent of created and modified field from the DB to the app.
- This change is to enforce the usage of UTC time as using MySQL's CURRENT_TIMESTAMP uses the TZ of the server
2024-02-21 16:20:07 +01:00
Christian Studer 9573c308e0
fix: [stix2 import] Setting the `single_event` argument to avoid skipping content in case of multiple reports or groupings 2024-02-21 11:46:21 +01:00
Christian Studer e29924b55d
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-21 11:45:49 +01:00
Jakub Onderka 43bfbbe6dc
Merge pull request #9575 from JakubOnderka/fix-custom-logos
fix: [UI] Custom logos
2024-02-20 16:29:47 +01:00
Jakub Onderka 2c43d5c277 fix: [UI] Custom logos 2024-02-20 16:17:43 +01:00
Sami Mokaddem d8bf22b422
Merge remote-tracking branch 'origin/develop' into notes 2024-02-20 11:16:01 +01:00
Sami Mokaddem 752807ef37
fix: [workflowModules:attributeEditionOperation] Make sure to call Attribute->editAttribute on data to be saved 2024-02-19 15:02:27 +01:00
Jeroen Pinoy 31cd3f2023
fix: [workflow] fix attribute edit module actions 2024-02-18 18:05:08 +01:00
iglocska b2cb4faedc
Merge branch 'develop' into 2.4 2024-02-16 16:51:21 +01:00
Christian Studer e703307f14
chg: [misp-stix] Bumped latest version 2024-02-16 16:29:04 +01:00
Christian Studer 1a9f2836c8
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-16 16:28:30 +01:00
iglocska 5ac9c995aa
chg: [VERSION] bump 2024-02-16 16:27:57 +01:00
iglocska fd7548243b
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-16 16:27:43 +01:00
iglocska a2c9740c0f
fix: [missing images] re-added 2024-02-16 16:27:17 +01:00
Alexandre Dulaunoy b1649cca55
chg: [taxonomies] updated to the latest version 2024-02-16 16:23:47 +01:00
Alexandre Dulaunoy e84ca24ff5
chg: [misp-objects] updated to the latest version 2024-02-16 16:23:09 +01:00
Alexandre Dulaunoy 1b3fd41a64
chg: [warning-lists] updated to the latest version 2024-02-16 16:22:42 +01:00
Alexandre Dulaunoy ecfa6224a9
chg: [misp-galaxy] updated to the latest version 2024-02-16 16:22:17 +01:00
Sami Mokaddem 6f99b148f0
Merge remote-tracking branch 'origin/develop' into notes 2024-02-16 15:29:03 +01:00
Sami Mokaddem 8530d6344b
fix: [analyst-data:relationship] Make sure to rearrange data only when the referrenced element exists 2024-02-16 15:26:11 +01:00
Raphaël Vinot e4e6f1625a chg: [PyMISP] Bump version 2024-02-16 14:50:47 +01:00
iglocska 0f7b55a1df
fix: [db_schema] bump 2024-02-16 14:41:19 +01:00
iglocska c47f1987dc
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-16 14:24:44 +01:00
iglocska c1638e0a9c
fix: [sighting sync] speculative fix for critical sync issue
- pulls from an instance with extremely high numbers of sightings (~300M+) can lead to the pulled instance becoming unusable
- This fix addresses multiple issues:
  - The use of last:0 as a sighting pull filter parameter lead to a search using an unindexed field
  - Internally searching for sighting IDs across 500 events in one shot can lead to massive data-sets
  - Internally searching for sighting IDs by Event.uuid on a joined table is extremely slow compared to searching on the sighting table alone
2024-02-16 14:21:10 +01:00
Sami Mokaddem 159f5278ef
fix: [analyst-data:view] Fixed analyst-data/view/all endpoint 2024-02-16 10:30:26 +01:00
Sami Mokaddem 87f4ef1bed
fix: [db_schema] Bumped db_version 2024-02-16 09:09:02 +01:00
Sami Mokaddem 9d66ff0815
fix: [app] Fixed error while merging in db_change number 2024-02-16 08:38:45 +01:00
Sami Mokaddem 66cd091ac4
Merge remote-tracking branch 'origin/develop' into notes 2024-02-16 08:30:04 +01:00
Andras Iklody aa67046917
Merge pull request #9564 from Wachizungu/fix-objects-restsearch-first-seen
fix: fix objects restsearch first_seen filter
2024-02-15 17:26:43 +01:00
iglocska 7a22d7c413
Merge branch 'develop' of github.com:MISP/MISP into develop 2024-02-15 15:37:32 +01:00
iglocska 9c244eb115
fix: [security] Org image upload moved out of webroot
- images will no longer be accessible directly, only via inclusion via file-read/b64 encoding
- The new store for org images is MISP/app/files/img/orgs

- As reported by Yusuke Nakajima
2024-02-15 15:35:12 +01:00
Sami Mokaddem 502682ee51
fix: [analyst-data:pull] Return early if there is nothing to pull 2024-02-15 15:21:00 +01:00
Sami Mokaddem c33a8774a8
chg: [tests:testlive_sync] Trying to understand why it fails 2024-02-15 15:09:43 +01:00
Sami Mokaddem 3db65a5548
fix: [test:testlive_sync] Adapted message to adhere to server change 2024-02-15 14:39:43 +01:00
Sami Mokaddem 25869b189f
fix: [object:editObject] Call function from the correct model 2024-02-15 14:12:55 +01:00
Sami Mokaddem 147c9b1af4
fix: [object:editObject] Avoid un-nesting object when not applicable 2024-02-15 13:31:53 +01:00
Sami Mokaddem 5827170008
fix: [aclComponent] Make queryACL not complaining 2024-02-15 13:30:16 +01:00
Sami Mokaddem df95b4ba7f
fix: [eventReport:editReport] Call function from the correct model 2024-02-15 13:27:54 +01:00
Sami Mokaddem 92f1f61dc9
chg: [db_schema] Updated to latest 2024-02-15 13:09:02 +01:00
Sami Mokaddem 66926d1b60
fix: [attribute:editAttributePostProcessing] Call function from the correct model 2024-02-15 12:09:36 +01:00
Sami Mokaddem 0e47d79340
Merge remote-tracking branch 'origin/develop' into notes 2024-02-14 15:54:28 +01:00
Jeroen Pinoy bf6a148bc8
fix: fix objects restsearch first_seen filter 2024-02-14 15:40:06 +01:00
Andras Iklody cc10d2a741
Merge pull request #9563 from Wachizungu/fix-object-restsearch-filters
fix: fix object_name, object_template_uuid and object_template_versio…
2024-02-14 13:54:42 +01:00
Sami Mokaddem 54b3c566d4
new: [Event:_edit] Added support of recursive update of analyst data 2024-02-14 11:22:43 +01:00
Sami Mokaddem 006c900c8e
new: [Event:_add] Added support of recursive capture of analyst data 2024-02-14 09:48:11 +01:00
Jeroen Pinoy e99b89433a
fix: fix object_name, object_template_uuid and object_template_version object restsearch filters 2024-02-14 09:41:08 +01:00
Sami Mokaddem 714cb9ea78
chg: [event:restSearch] Added support of `includeAnalystData` options
Also export analyst data using the event `Download as` function by default
2024-02-14 08:36:28 +01:00
Jeroen Pinoy 236759217e
chg: [doc/openapi] clarify 'deleted' restsearch filter (#9485) 2024-02-13 17:40:30 +01:00
Sami Mokaddem 9c64255e50
chg: [analyst-data:UI] Reduced number of inline asset ressources inclusion 2024-02-13 16:02:12 +01:00
Sami Mokaddem 8c59b9897d
fix: [analyst-data:add] Allow not providing a language when creating a note 2024-02-13 15:41:57 +01:00
Sami Mokaddem 1afd609581
chg: [analyst-data:thread-view] Added possibility to fetch data having a deeper depth 2024-02-13 15:38:23 +01:00
Sami Mokaddem e9d01c5f5f
chg: [analyst-data] Added many improvements for UI and fixed infite loop due to recursion 2024-02-13 14:37:00 +01:00
Sami Mokaddem 31ed2113fb
fix: [analyst-data:recursive-fetch] Second tentative to prevent recursion in relationship 2024-02-13 11:13:39 +01:00
Sami Mokaddem 9e97ae868c
fix: [analystData:fetchChildNotesAndOpinions] Added support of depth 2024-02-13 10:23:09 +01:00
Raphaël Vinot 2af975494d chg: [PyMISP] Bump version 2024-02-12 12:18:19 +01:00
Raphaël Vinot badca75620 chg: [PyMISP] fix a few regressions. 2024-02-12 11:52:23 +01:00
Jürgen Löhel 3c05037674
fix [INSTALL/MySQL]: Create table `user_login_profiles` only if it not exists
fixes: #9552

Signed-off-by: Jürgen Löhel <juergen.loehel@inlyse.com>
2024-02-07 13:36:10 -06:00
Sami Mokaddem 9e19438a16
Merge remote-tracking branch 'origin/develop' into notes 2024-02-07 17:17:29 +01:00
Sami Mokaddem 9887843358
chg: [servers:getVersion] Include the remote instance UUID if user has perm_sync permission 2024-02-07 17:15:58 +01:00
Sami Mokaddem 509708a2a2
chg: [app:queryVersion] Bumped version 2024-02-07 15:21:58 +01:00
Sami Mokaddem bb6b105bef
chg: [analyst-data:edit] Fetch referenced element for analyst-data relationships by default 2024-02-07 15:20:13 +01:00
Sami Mokaddem 7653b0d450
fix: [analyst-data:add] Added missing field `related_object_type` in form 2024-02-07 15:17:56 +01:00
Sami Mokaddem a1e215c097
chg: [analyst-data:add] Added support of author field and prefill it with current user's email address 2024-02-07 15:07:13 +01:00
Sami Mokaddem 2fa33ef129
chg: [analyst-data:global_menu] Added entry in the global menu 2024-02-07 14:59:56 +01:00
Sami Mokaddem 4fca835c39
chg: [analyst-data:crud] Do not recursively fetch child analyst-data in REST context 2024-02-07 14:53:37 +01:00
Sami Mokaddem ceb04b2662
chg: [analyst-data:beforeValidate] Do not override authors field if already set 2024-02-07 11:29:35 +01:00
Sami Mokaddem 4ed433a0eb
Merge remote-tracking branch 'origin/develop' into notes 2024-02-07 11:12:10 +01:00
Sami Mokaddem 0eb23bbf2f
fix: [console:serverShell] Set `CurrentUserId` to the ID of the user being used 2024-02-07 11:10:35 +01:00
Alexandre Dulaunoy da7a21a333
chg: [develop] merge back the Curl option issue 2024-02-07 10:51:54 +01:00
Alexandre Dulaunoy 90126dc1dd
Merge pull request #9551 from Benni0/2.4
fix: CurlClient doesn't use correct Proxy settings
2024-02-07 10:51:26 +01:00
Sami Mokaddem d67506e9a6
chg: [analyst-data:CRUD] Allow viewing, editing and deleting any analyst-data by their UUIDs 2024-02-07 10:34:09 +01:00
Benni0 45f264de49 fix: CurlClient doesn't use correct Proxy settings 2024-02-07 09:59:26 +01:00
Sami Mokaddem ea7e48b2a7
Merge remote-tracking branch 'origin/develop' into notes 2024-02-06 15:04:09 +01:00
Sami Mokaddem 6e41c956fa
chg: [collection-elements:addElementToCollection] Redirect to collection creation if there no collections 2024-02-06 14:25:54 +01:00
Sami Mokaddem 3944d75f44
fix: [collection] Enforce cascade on delete 2024-02-06 14:20:47 +01:00
Sami Mokaddem ff42823f2f
fix: [analyst-data:pull] Make sure to correctly decode returned data 2024-02-06 11:34:37 +01:00
Sami Mokaddem 18dde0a73b
fix: [analyst-data:indexMinimal] Use the organisation name instead of UUID
- This is because PULL sync filter rules relies on organisation names of the remote
- This change is to avoid rewriting the regular sync path that relies on the org name
2024-02-06 11:30:11 +01:00
Sami Mokaddem ca7b7dfb18
chg: [analyst-data:pull] Refactored condition building function for PULL sync rules 2024-02-06 11:15:05 +01:00
Sami Mokaddem a8607c54dd
ichg: [analyst-note:pull] Continuation of adding support of PULL sync filtering rule - WiP 2024-02-06 10:58:58 +01:00
Sami Mokaddem b928e8241b
chg: [analyst-note:pull] Started adding support of PULL sync filtering rule - WiP 2024-02-06 10:23:19 +01:00
Sami Mokaddem e5d000143a
chg: [analyst-data:push] Added support of sync-filtering rules 2024-02-06 10:13:16 +01:00
Sami Mokaddem 9d18007b2e
fix: [analyst-data] Various fixes regarding ACL and recursive fetching 2024-02-06 10:04:20 +01:00
Sami Mokaddem 744a1124fd
chg: [analyst-data:identifyForPush] Removed commented code 2024-02-06 09:14:55 +01:00
Sami Mokaddem 5d112ced18
chg: [analyst-data] Allow fetching analyst-data by UUID 2024-02-06 09:09:30 +01:00
Sami Mokaddem 80f97ad79f
chg: [analyst-data] Added missing ACL entries and improved pre-filtering before negotiation starts 2024-02-06 08:50:21 +01:00
Sami Mokaddem 3a8fe00df8
chg: [analyst-data:push] Simplified filtering logic during negotiation 2024-02-05 15:45:38 +01:00
Sami Mokaddem a82fde10b4
chg: [analyst-data] Renamed bunch of synchronisation functions 2024-02-05 15:18:07 +01:00
Alexandre Dulaunoy 88f83ea295
Merge pull request #9544 from pswapneel/2.4
Added Shreshta Newly registered domain names 1-week and 1-month community policy feeds
2024-02-05 14:55:21 +01:00
Sami Mokaddem 207c55e1e4
fix: [analyst-data:push] Correctly adjust locked flag for push 2024-02-05 11:59:13 +01:00
Sami Mokaddem 7fee219b45
fix: [analyst-data:pull] Correctly adjust distribution level and locked flag when pulling 2024-02-05 11:58:40 +01:00
Sami Mokaddem f71b50d3d7
fix: [analyst-data:edit] Bump `modified` field before updating 2024-02-05 11:24:15 +01:00
Sami Mokaddem 1444523dfa
chg: [component:CRUD] Added support of parameter as a list 2024-02-05 11:03:07 +01:00
Sami Mokaddem f649814afb
chg: [analyst-data:pull] Change in pull strategy + few improvements 2024-02-05 10:59:20 +01:00
Sami Mokaddem ea88d5c7bb
chg: [analyst-data:pull] Continuation implementation of pull - WiP 2024-02-05 09:27:13 +01:00
Swapneel Patnekar a8fb77c848 Added Shreshta NRD 1 week and 1 month community feeds 2024-02-03 21:53:48 +05:30
Jakub Onderka 9ebf18e82b new: [UI] Show dragonfly version in diagnostics 2024-02-01 17:34:53 +01:00
Sami Mokaddem 8e6758e6f6
chg: [analyst-data:pull] Started implementation of pull - WiP 2024-02-01 16:27:54 +01:00
Sami Mokaddem 727ca98f93
chg: [analyst-data:delete] Make deletion `hard` by default 2024-02-01 15:03:58 +01:00
Sami Mokaddem c4fc994857
fix: [analyst-data:pushAnalystData] Typo in success reporting log line 2024-02-01 14:53:36 +01:00
Sami Mokaddem dc6b6cc3b3
fix: [analyst-data:CRUD] Make sure to return the data in the afterFind function 2024-02-01 14:51:26 +01:00
Sami Mokaddem 2c2c297b32
fix: [analyst-data:db-migration] Fixed typo in create table instruction 2024-02-01 14:43:33 +01:00
Sami Mokaddem eaf8a2b98a
chg: [analyst-data] Added `locked` flag, support of orgc/org, analyst-data-blocklist and most implementation of push synchronisation - WiP 2024-02-01 14:24:41 +01:00
Sami Mokaddem 8cef82f1ea
chg: [server:sync/analyst-data] Started integration of server synchronisation - WiP 2024-01-31 15:10:08 +01:00
Sami Mokaddem ceb423ae76
chg: [permission:analyst-data] Added new permission `perm_analyst_data` 2024-01-31 15:05:49 +01:00
Sami Mokaddem 8ecdf70da0
chg: [galaxyClusters:view] Added analystData support in /galaxyClusters/view 2024-01-30 15:17:40 +01:00
Sami Mokaddem b6c6ee60e4
chg: [eventReport:view] Added analystData support in /eventReports/view 2024-01-30 15:16:38 +01:00
Sami Mokaddem e060aed9ee
fix: [events:view/analyst-data] Added missing relationship_path 2024-01-30 15:16:29 +01:00
Sami Mokaddem b2f3602265
chg: [analyst-data:ACL] Enforced ACL and reflected the change in the UI 2024-01-30 15:15:26 +01:00
Sami Mokaddem b9f1a0ad89
chg: [analyst-data:index] Improved UI for related element 2024-01-30 11:06:21 +01:00
Sami Mokaddem d702535a76
chg: [analyst-data:UI] Improved UI, better support of opinions in CRUD views and added single/index fields for opinion scale 2024-01-30 10:51:31 +01:00
Sami Mokaddem 9feed62a5d
fix: [analyst-data:ui-generic] Make sure to always show analyst-data 2024-01-30 10:14:27 +01:00
Sami Mokaddem caf55c3eec
chg: [analyst-data:event-report] Added support of analyst-data to event reports 2024-01-30 10:13:58 +01:00
Sami Mokaddem 5664a735e2
chg: [analyst-data:ACL] Added ACL rules and fixed side-menu to support ACL 2024-01-30 09:45:51 +01:00
Sami Mokaddem e3b09cd5a5
fix: [analyst-data:afterFind] Only rearrange key sharing-group key if they distribution exists 2024-01-30 09:36:45 +01:00
Sami Mokaddem 90ae8739da
chg: [analyst-data:view] Display fields based on note model and slightly improved UI 2024-01-30 09:29:36 +01:00
Sami Mokaddem 9de54fa208
fix: [analyst-data:view] Use correct model to access element property 2024-01-30 09:12:49 +01:00
Sami Mokaddem 1975e38d8c
chg: [galaxyCluster] Added support of analyst-note in the UI 2024-01-30 09:03:16 +01:00
Sami Mokaddem f534b22582
chg: [analyst-data:sideMenu] Added support of analyst-data in the side menu 2024-01-30 09:01:06 +01:00
Sami Mokaddem 6909e5feaf
new: [singleView:sidePanels] Added new `html` side panel template to feed any HTML into the view 2024-01-30 09:00:06 +01:00
Sami Mokaddem b0c45124f3
chg: [analyst-data:UI] Separated notes&opinions threads into their own file 2024-01-30 08:58:47 +01:00
Sami Mokaddem f15429e444
fix: [analyst-data] Fixed sharing group associations 2024-01-30 08:57:14 +01:00
Olivier BERT 13d43ab377 Accessibility: Added the possibility to focus the hover enrichment icon on attributes. 2024-01-29 15:19:05 +01:00
Sami Mokaddem 0bbd5bf05e
fix: [analyst-data-behavior:afterFind] Restored behavior that fetched child notes and opinions in the analyst-data afterFind method
Might be reverted later on
2024-01-29 12:00:13 +01:00
Sami Mokaddem 990b574867
chg: [analyst-data:add] Added support of picker for relationship type and improved UI for sharing-group 2024-01-29 11:01:02 +01:00
Sami Mokaddem ffdb21d58f
chg: [analyst-data:add] toggle sharing group input depending on the distribution setting 2024-01-29 10:09:41 +01:00
Sami Mokaddem 0c53d96d5d
Merge branch 'feature/analyst-data' into notes 2024-01-29 10:06:25 +01:00
Sami Mokaddem d443ccfe2a
chg: [analyst-data:UI-generic] Removed debugging string 2024-01-29 09:47:50 +01:00
iglocska 68722c8827
fix: [analystdata] added behavior to objects 2024-01-28 18:12:05 +01:00
iglocska 839047d8e1
chg: [collections] added db changes 2024-01-28 18:06:33 +01:00
iglocska 846c130fa3
new: [collections] feature added. Still missing sync integration - WiP 2024-01-28 18:05:29 +01:00
iglocska 553e328f1d
fix: [CRUD] more accurate results in save functions (show the state after the save) 2024-01-28 17:22:56 +01:00
Sami Mokaddem 80b50c5a8d
chg: [analyst-note] Added support of opinion on relationships 2024-01-26 16:25:09 +01:00
Sami Mokaddem 4f33648290
chg: [analyst-data:UI] Added support of relation for object + refactoring + fixes 2024-01-26 16:05:35 +01:00
iglocska 12bb7e5363
Merge branch 'notes' of github.com:MISP/MISP into notes 2024-01-26 15:41:58 +01:00
Sami Mokaddem ccb784268a
fix: [analyst-data:ui-generic] Fixed template overriding the $seed leading to weird behaviors with bootstrap tabs 2024-01-26 15:16:36 +01:00
Sami Mokaddem a391846d2e
chg: [analyst-data:ui-generic] Removed debugging string 2024-01-26 15:11:11 +01:00
Sami Mokaddem 7d8aa33996
fix: [analyst-data:ui-generic] Small refacto + fixed style not being generated for first-level opinions 2024-01-26 14:58:13 +01:00
Sami Mokaddem 0f97c07ab7
chg: [analyst-data:index] Added missing fields in the indexes 2024-01-26 14:57:28 +01:00
Sami Mokaddem 6742f9ed42
chg: [analyst-data] Added support of fetching & displaying of related object + refacto + fixes - WiP 2024-01-25 20:01:04 +01:00
Sami Mokaddem dca913c969
chg: [analyst-data] Linked CRUD and UI together - WiP
- Added dynamic association binding
- Recursive notes and opinions injection
- few improvements
- fixes

-> Still need to link CRUD for relationships and UI
-> Still need to refactor for performance notes/opinions loading
2024-01-24 21:48:53 +01:00
iglocska eb03f8fcc0
chg: [uuid field] update 2024-01-19 17:54:54 +01:00
iglocska ceda8c3788
chf: [notes] wip 2024-01-19 17:54:06 +01:00
Sami Mokaddem a3af8b402f
chg: [analyst-notes:ui] Added support of relationship and bootstrap tabs 2024-01-11 08:48:27 +01:00
iglocska 2ab819f3cb
chg: [analystdata wip] 2024-01-04 19:56:11 +01:00
iglocska 22c413059f
fix: [UI] uuid length for the display fixed 2024-01-04 12:23:44 +01:00
Sami Mokaddem 160d7442ff
Merge branch 'feature/analyst-notes' into notes 2024-01-04 11:20:04 +01:00
Sami Mokaddem 0d61abd4e8
chg: [analyst-notes:ui] Few improvements 2024-01-04 11:15:43 +01:00
Sami Mokaddem 959ffa5196
chg: [analyst-notes:ui] Removed unused code 2024-01-04 11:10:39 +01:00
Sami Mokaddem 67156760dc
chg: [analyst-notes:ui] Started integration in events/view 2024-01-04 10:48:13 +01:00
iglocska e04c810ae3
new: [analystdata] wip 2024-01-04 10:12:47 +01:00
Sami Mokaddem baf6ca3cab
chg: [analyst-notes:ui] Add fallback for passing data
- To be removed later on
2024-01-04 09:54:12 +01:00
Sami Mokaddem 3cf306bee5
fix: [events:getThreads] Removed fake unused function 2024-01-04 09:50:56 +01:00
Sami Mokaddem f6abd75732
Merge remote-tracking branch 'mokaddem/feature/analyst-note-ui' into feature/analyst-notes 2024-01-04 09:49:05 +01:00
Sami Mokaddem eda21a41ea
chg: [analyst-notes:ui] Move the popover position a bit less
- To be fixed later on
2024-01-04 09:47:35 +01:00
Sami Mokaddem 554a37e203
chg: [analyst-notes:ui] Removed leftover code when opinions were using stars 2024-01-04 09:46:54 +01:00
Sami Mokaddem 2b4565f720
fix: [analyst-notes:ui] Small fix on the vbar for opinion's comment 2024-01-04 09:45:35 +01:00
Sami Mokaddem 459a706bfc
chg: [analyst-notes:ui] Improved UI of opinion notes
- Based on the valuable feedback from @adulau
2023-12-21 15:42:07 +01:00
Sami Mokaddem c1c44fa644
chg: [analyst-notes:ui] Added support of permissions, callbacks and improved UI - WiP 2023-12-21 09:28:43 +01:00
Sami Mokaddem 8015f76c69
new: [analyst-notes:UI] Started UI for analyst notes - WiP 2023-12-20 14:36:45 +01:00
Christian Studer 167d6d646e Merge branch 'develop' of github.com:MISP/MISP into develop 2023-12-14 14:31:07 +01:00
Christian Studer 49ef966823 Merge branch 'develop' of github.com:MISP/MISP into develop 2023-12-14 14:21:42 +01:00
Christian Studer 6a0f3f1b73 Merge branch 'develop' of github.com:MISP/MISP into develop 2023-12-14 14:11:43 +01:00
Christian Studer d70150d237
fix: [upload_stix] Avoiding issues with sharing group arguments being null 2023-12-14 14:10:58 +01:00
iglocska e73d1001a0
new: [db] tables added for notes 2023-12-14 12:28:13 +01:00
Christian Studer 472cfab3c7 Merge branch 'develop' of github.com:MISP/MISP into develop 2023-12-14 11:42:43 +01:00
Christian Studer debae13bc2
chg: [upload_stix] Casting distributions and sharing group IDs type 2023-12-14 11:41:47 +01:00
Christian Studer c5baab3328
fix: [stix2 import] Fixed STIX2 parser name 2023-12-12 11:34:05 +01:00
Christian Studer 6cdfa7b5f7 Merge branch 'develop' of github.com:MISP/MISP into develop 2023-12-12 11:25:52 +01:00
Christian Studer 5e8b122c2e
chg: [misp-stix] Bumped latest version 2023-12-12 11:24:30 +01:00
Christian Studer bdcfe06cf3
add: [upload_stix] Handling cluster distribution and sharing group for content imported from STIX 2.x 2023-12-12 10:57:57 +01:00
Jakub Onderka 8f3f7bc866 chg: [internal] Speedup sighting rest search 2022-12-22 12:57:54 +01:00
Jakub Onderka a4f72a7ddf chg: [UI] Make menu little bit nicer 2022-10-17 18:37:16 +02:00
245 changed files with 12791 additions and 2398 deletions

View File

@ -184,20 +184,8 @@ jobs:
app/Console/cake Admin setSetting "Plugin.ZeroMQ_enable" 1
app/Console/cake Admin setSetting "Plugin.ZeroMQ_audit_notifications_enable" 1
- name: Update Galaxies
run: app/Console/cake Admin updateGalaxies
- name: Update Taxonomies
run: app/Console/cake Admin updateTaxonomies
- name: Update Warninglists
run: app/Console/cake Admin updateWarningLists --verbose
- name: Update Noticelists
run: app/Console/cake Admin updateNoticeLists
- name: Update Object Templates
run: app/Console/cake Admin updateObjectTemplates 1
- name: Update JSON
run: app/Console/cake Admin updateJSON
- name: Turn MISP live
run: app/Console/cake Admin live 1
@ -269,13 +257,16 @@ jobs:
- name: Check requirements.txt
run: python tests/check_requirements.py
- name: Logs
- name: System logs
if: ${{ always() }}
# update logs_test.sh when adding more logsources here
run: |
tail -n +1 `pwd`/app/tmp/logs/*
tail -n +1 /var/log/apache2/*.log
- name: Application logs
if: ${{ always() }}
run: |
app/Console/cake Log export /tmp/logs.json.gz --without-changes
zcat /tmp/logs.json.gz

10
.gitignore vendored
View File

@ -82,10 +82,20 @@ app/Lib/EventWarning/Custom/*
!/app/webroot/img/orgs/MISP.png
!/app/webroot/img/orgs/NATO.png
!/app/webroot/img/orgs/NCIRC.png
/app/files/img/custom/*
!/app/files/img/custom/empty
!/app/files/img/orgs
/app/files/img/orgs/*
!/app/files/img/orgs/ADMIN.png
!/app/files/img/orgs/MIL.be.png
!/app/files/img/orgs/MISP.png
!/app/files/img/orgs/NATO.png
!/app/files/img/orgs/NCIRC.png
/app/Config/bootstrap.php
/app/Config/database.php
/app/Config/core.php
/app/Config/config.php
/app/Config/hmac_key.php
/app/Console/Command/training.json
/app/Lib/cakephp
/app/webroot/gpg.asc

View File

@ -1428,7 +1428,7 @@ CREATE TABLE IF NOT EXISTS `users` (
-- Table structure for table `user_login_profiles`
--
CREATE TABLE `user_login_profiles` (
CREATE TABLE IF NOT EXISTS `user_login_profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`user_id` int(11) NOT NULL,
@ -1698,4 +1698,4 @@ INSERT IGNORE INTO `org_blocklists` (`org_uuid`, `created`, `org_name`, `comment
INSERT IGNORE INTO `admin_settings` (`setting`, `value`) VALUES
('fix_login', NOW()),
('default_role', 3);
('default_role', 3);

2
PyMISP

@ -1 +1 @@
Subproject commit c0077c19cf3843a65d5b26076dd26deca0405033
Subproject commit 8b4f98ac4c2e6c8cc1dba064f937dac816b67d0f

View File

@ -46,6 +46,8 @@ The objective of MISP is to foster the sharing of structured information within
</tr>
</table>
[![CLA FREE initiative](https://raw.githubusercontent.com/ossbase-org/ossbase.org/main/logos/cla-free-small.png)](https://ossbase.org/initiatives/cla-free/)
Core functions
------------------
- An **efficient IOC and indicators** database, allowing to store technical and non-technical information about malware samples, incidents, attackers and intelligence.

View File

@ -1 +1 @@
{"major":2, "minor":4, "hotfix":184}
{"major":2, "minor":4, "hotfix":192}

View File

@ -180,3 +180,7 @@ CakeLog::config('error', array(
CakePlugin::loadAll(array(
'CakeResque' => array('bootstrap' => true)
));
// Enable the additional exception logging for certain failures (timeouts, out of memory, etc)
Configure::write('Exception.renderer', 'AppExceptionRenderer');

View File

@ -302,16 +302,22 @@ class AdminShell extends AppShell
public function updateJSON()
{
$this->out('Updating all JSON structures.');
$results = $this->Server->updateJSON();
foreach ($results as $type => $result) {
$overallSuccess = true;
foreach ($this->Server->updateJSON() as $type => $result) {
$type = Inflector::pluralize(Inflector::humanize($type));
if ($result !== false) {
$this->out(__('%s updated.', $type));
if ($result['success']) {
$this->out(__('%s updated in %.2f seconds.', $type, $result['duration']));
} else {
$this->out(__('Could not update %s.', $type));
$this->out($result['result']);
$overallSuccess = false;
}
}
$this->out('All JSON structures updated. Thank you and have a very safe and productive day.');
if ($overallSuccess) {
$this->out('All JSON structures updated. Thank you and have a very safe and productive day.');
} else {
$this->error('Some structure could no be updated');
}
}
public function updateGalaxies()
@ -616,9 +622,9 @@ class AdminShell extends AppShell
try {
$redis = RedisTool::init();
for ($i = 0; $i < 10; $i++) {
$persistence = $redis->info('persistence');
if (isset($persistence['loading']) && $persistence['loading']) {
$this->out('Redis is still loading...');
$pong = $redis->ping();
if ($pong !== true) {
$this->out('Redis is still loading... ' . $pong);
sleep(1);
} else {
break;

View File

@ -18,6 +18,7 @@
App::uses('AppModel', 'Model');
App::uses('BackgroundJobsTool', 'Tools');
App::uses('BenchmarkTool', 'Tools');
require_once dirname(__DIR__) . '/../Model/Attribute.php'; // FIXME workaround bug where Vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php is loaded instead
@ -38,7 +39,18 @@ abstract class AppShell extends Shell
{
$configLoad = $this->Tasks->load('ConfigLoad');
$configLoad->execute();
if (Configure::read('Plugin.Benchmarking_enable')) {
$Benchmark = new BenchmarkTool(ClassRegistry::init('User'));
$start_time = $Benchmark->startBenchmark();
register_shutdown_function(function () use ($start_time, $Benchmark) {
$Benchmark->stopBenchmark([
'user' => 0,
'controller' => 'Shell::' . $this->modelClass,
'action' => $this->command,
'start_time' => $start_time
]);
});
}
parent::initialize();
}

View File

@ -20,7 +20,8 @@ class Ls22Shell extends AppShell
$this->__servers[] = [
'Server' => [
'url' => trim($fields[0]),
'authkey' => trim($fields[2])
'authkey' => trim($fields[2]),
'self_signed' => true,
]
];
}
@ -30,6 +31,7 @@ class Ls22Shell extends AppShell
public function getOptionParser()
{
$this->stdout->styles('green', array('text' => 'green'));
$this->stdout->styles('black', array('text' => 'black'));
$parser = parent::getOptionParser();
$parser->addSubcommand('enableTaxonomy', [
@ -414,6 +416,13 @@ class Ls22Shell extends AppShell
public function scores()
{
$MITIGATION_DETECTION_OBJECT_UUIDs = [
'b5acf82e-ecca-4868-82fe-9dbdf4d808c3', # yara
'3c177337-fb80-405a-a6c1-1b2ddea8684a', # suricata
'aa21a3cd-ab2c-442a-9999-a5e6626591ec', # sigma
'6bce7d01-dbec-4054-b3c2-3655a19382e2', # script
'35b4dd03-4fa9-4e0e-97d8-a2867b11c956', # yabin
];
$results = [];
$this->__getInstances($this->param('instances'));
$server = null;
@ -464,6 +473,7 @@ class Ls22Shell extends AppShell
$params = [
'org' => $org_id,
'includeWarninglistHits' => true,
// 'includeAnalystData' => true,
];
if (!empty($time_range)) {
$params['publish_timestamp'] = $time_range;
@ -486,6 +496,8 @@ class Ls22Shell extends AppShell
'warnings' => 0,
'events_extended' => 0,
'extending_events' => 0,
'mitigation_detection_rules_count' => 0,
'analyst_data_count' => 0,
];
foreach ($events['response'] as $event) {
$event_uuid_per_org[$event['Event']['uuid']] = $event['Event']['Orgc']['name'];
@ -511,6 +523,11 @@ class Ls22Shell extends AppShell
}
}
}
# ['Note' => 0, 'Opinion' => 0, 'Relationship' => 0,]
$analystDataCount = $this->countAnalystData($event['Event'], $org_name);
$results[$org_name]['analyst_data_count'] = $analystDataCount['Note'] + $analystDataCount['Opinion'] + $analystDataCount['Relationship'];
foreach ($event['Event']['Attribute'] as $attribute) {
if (!empty($attribute['referenced_by'])) {
$results[$org_name]['connected_elements'] +=1;
@ -533,6 +550,9 @@ class Ls22Shell extends AppShell
foreach ($event['Event']['Object'] as $object) {
$results[$org_name]['attribute_count'] += count($object['Attribute']);
$results[$org_name]['object_count'] += 1;
if (in_array($object['template_uuid'], $MITIGATION_DETECTION_OBJECT_UUIDs)) {
$results[$org_name]['mitigation_detection_rules_count'] += 1;
}
if (!empty($object['ObjectReference'])) {
$results[$org_name]['connected_elements'] += 1;
}
@ -547,6 +567,9 @@ class Ls22Shell extends AppShell
}
}
}
if (!empty($attribute['warnings'])) {
$results[$org_name]['warnings'] += 1;
}
}
}
@ -579,13 +602,24 @@ class Ls22Shell extends AppShell
} else {
$results[$k]['metrics']['warnings'] = 0;
}
$results[$k]['metrics']['mitigation_detection_rules'] = 100 * ($result['mitigation_detection_rules_count'] / ($result['event_count']));
$results[$k]['metrics']['connectedness'] = 100 * ($result['connected_elements'] / ($result['attribute_count'] + $result['object_count']));
$results[$k]['metrics']['attack_weight'] = 100 * (2*($result['attack']) + $result['attribute_attack']) / ($result['attribute_count'] + $result['object_count']);
$results[$k]['metrics']['other_weight'] = 100 * (2*($result['other']) + $result['attribute_other']) / ($result['attribute_count'] + $result['object_count']);
$results[$k]['metrics']['collaboration'] = 100 * ((2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
$results[$k]['metrics']['collaboration'] = 100 * (2*(2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
// $results[$k]['metrics']['collaboration'] = 100 * ((2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
// $results[$k]['metrics']['collaboration'] = 100 * (2*(2*$result['events_extended'] + $result['extending_events']) / $result['event_count']);
$results[$k]['metrics']['collaboration'] = 100 * (($result['events_extended'] + $result['extending_events']));
# Math magic to give lots of points of you extend or have your events extended. You quickly get point, but it slows down very quick
if (($result['events_extended'] + $result['extending_events']) == 0) {
$results[$k]['metrics']['collaboration'] = 0;
} else {
$results[$k]['metrics']['collaboration'] = min(5*log(($result['events_extended'] + $result['extending_events']), 1.17), 100);
}
$results[$k]['metrics']['collaboration_analyst'] = $result['analyst_data_count'] > 0 ? 100 : 0;
}
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings', 'collaboration'] as $metric) {
foreach (['connectedness', 'mitigation_detection_rules', 'attack_weight', 'other_weight', 'warnings', 'collaboration', 'collaboration_analyst'] as $metric) {
if (empty($results[$k]['metrics'][$metric])) {
$results[$k]['metrics'][$metric] = 0;
}
@ -594,18 +628,21 @@ class Ls22Shell extends AppShell
}
}
$results[$k]['score'] = round(
20 * $results[$k]['metrics']['warnings'] +
20 * $results[$k]['metrics']['connectedness'] +
15 * $results[$k]['metrics']['warnings'] +
15 * $results[$k]['metrics']['mitigation_detection_rules'] +
10 * $results[$k]['metrics']['connectedness'] +
40 * $results[$k]['metrics']['attack_weight'] +
10 * $results[$k]['metrics']['other_weight'] +
// 7 * $results[$k]['metrics']['collaboration'] + 3 * $results[$k]['metrics']['collaboration_analyst']
10 * $results[$k]['metrics']['collaboration']
) / 100;
$scores[$k]['total'] = $results[$k]['score'];
$scores[$k]['warnings'] = round(20 * $results[$k]['metrics']['warnings']);
$scores[$k]['connectedness'] = round(20 * $results[$k]['metrics']['connectedness']);
$scores[$k]['warnings'] = round(15 * $results[$k]['metrics']['warnings']);
$scores[$k]['mitigation_detection_rules'] = round(15 * $results[$k]['metrics']['mitigation_detection_rules']);
$scores[$k]['connectedness'] = round(10 * $results[$k]['metrics']['connectedness']);
$scores[$k]['attack_weight'] = round(40 * $results[$k]['metrics']['attack_weight']);
$scores[$k]['other_weight'] = round(10 * $results[$k]['metrics']['other_weight']);
$scores[$k]['collaboration'] = round(10 * $results[$k]['metrics']['collaboration']);
$scores[$k]['collaboration'] = round(7 * $results[$k]['metrics']['collaboration']) + round(3 * $results[$k]['metrics']['collaboration_analyst']);
}
arsort($scores, SORT_DESC);
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
@ -618,20 +655,22 @@ class Ls22Shell extends AppShell
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
foreach ($scores as $org => $score) {
$score_string[0] = str_repeat('█', round($score['warnings']/100));
$score_string[1] = str_repeat('█', round($score['connectedness']/100));
$score_string[2] = str_repeat('█', round($score['attack_weight']/100));
$score_string[3] = str_repeat('█', round($score['other_weight']/100));
$score_string[4] = str_repeat('█', round($score['collaboration']/100));
$score_string[1] = str_repeat('█', round($score['mitigation_detection_rules']/100));
$score_string[2] = str_repeat('█', round($score['connectedness']/100));
$score_string[3] = str_repeat('█', round($score['attack_weight']/100));
$score_string[4] = str_repeat('█', round($score['other_weight']/100));
$score_string[5] = str_repeat('█', round($score['collaboration']/100));
$this->out(sprintf(
'| %s | %s | %s |',
str_pad($org, 10, ' ', STR_PAD_RIGHT),
sprintf(
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info><green>%s</green>%s',
'<error>%s</error><black>%s</black><warning>%s</warning><question>%s</question><info>%s</info><green>%s</green>%s',
$score_string[0],
$score_string[1],
$score_string[2],
$score_string[3],
$score_string[4],
$score_string[5],
str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
),
str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
@ -639,8 +678,9 @@ class Ls22Shell extends AppShell
}
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
$this->out(sprintf(
'| Legend: %s %s %s %s %s |',
'| Legend: %s %s %s %s %s %s |',
'<error>█: Warnings</error>',
'<black>█: Detection/Mitigation Rules</black>',
'<warning>█: Connectedness</warning>',
'<question>█: ATT&CK context</question>',
'<info>█: Other Context</info>',
@ -650,4 +690,30 @@ class Ls22Shell extends AppShell
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
file_put_contents(APP . 'tmp/report.json', json_encode($results, JSON_PRETTY_PRINT));
}
private function countAnalystData($data, $orgc_name): array {
$analystTypes = ['Note', 'Opinion', 'Relationship'];
$counts = [
'Note' => 0,
'Opinion' => 0,
'Relationship' => 0,
];
foreach ($analystTypes as $type) {
if (!empty($data[$type])) {
foreach ($data[$type] as $entry) {
if ($entry['Orgc']['name'] == $orgc_name) {
$counts[$type] += 1;
}
}
foreach ($data[$type] as $child) {
$nestedCounts = $this->countAnalystData($child, $orgc_name);
$counts['Note'] += $nestedCounts['Note'];
$counts['Opinion'] += $nestedCounts['Opinion'];
$counts['Relationship'] += $nestedCounts['Relationship'];
}
}
}
return $counts;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* @property User $User
* @property Log $Log
* @property UserLoginProfile $UserLoginProfile
*/
class OrganisationShell extends AppShell
{
public $uses = ['Organisation'];
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->addSubcommand('list', [
'help' => __('Get list of organisations.'),
'parser' => [
'arguments' => [
'filter' => ['help' => __('Filter the list by name.'), 'required' => false],
'local' => ['help' => __('Filter the list by local/known organisations.'), 'required' => false],
],
'options' => [
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
],
]
]);
return $parser;
}
public function list()
{
$filter = $this->args[0] ?? null;
$local = isset($this->args[1]) ? $this->args[1] : null;
$conditions = [];
if ($filter) {
$conditions = ['OR' => [
'Organisation.name LIKE' => "%$filter%",
'Organisation.uuid LIKE' => "%$filter%"
]];
}
if ($local !== null) {
$conditions['OR'][] = [
'Organisation.local' => $local
];
}
$organisations = $this->Organisation->find('all', [
'recursive' => -1,
'conditions' => $conditions
]);
if ($this->params['json']) {
$this->out($this->json($organisations));
} else {
foreach ($organisations as $organisation) {
$this->out(sprintf(
'%d. [%s] %s',
$organisation['Organisation']['id'],
$organisation['Organisation']['uuid'],
$organisation['Organisation']['name']
));
}
$this->out(count($organisations) . ' hits.');
}
}
}

View File

@ -0,0 +1,56 @@
<?php
/**
* @property User $User
* @property Log $Log
* @property UserLoginProfile $UserLoginProfile
*/
class RoleShell extends AppShell
{
public $uses = ['Role'];
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->addSubcommand('list', [
'help' => __('Get list of the roles.'),
'parser' => [
'arguments' => [
'filter' => ['help' => __('Filter list by name.'), 'required' => false],
],
'options' => [
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
],
]
]);
return $parser;
}
public function list()
{
$filter = $this->args[0] ?? null;
if ($filter) {
$conditions = ['OR' => [
'Role.name LIKE' => "%$filter%"
]];
} else {
$conditions = [];
}
$roles = $this->Role->find('all', [
'recursive' => -1,
'conditions' => $conditions
]);
if ($this->params['json']) {
$this->out($this->json($roles));
} else {
foreach ($roles as $role) {
$this->out(sprintf(
'%d. %s',
$role['Role']['id'],
$role['Role']['name']
));
}
}
$this->out(count($roles) . ' hits.');
}
}

View File

@ -85,6 +85,7 @@ class ServerShell extends AppShell
$userId = $this->args[0];
$user = $this->getUser($userId);
Configure::write('CurrentUserId', $userId);
if (!empty($this->args[1])) {
$technique = $this->args[1];
@ -124,11 +125,11 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Pull'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$serverId = $this->args[1];
$server = $this->getServer($serverId);
Configure::write('CurrentUserId', $userId);
if (!empty($this->args[2])) {
$technique = $this->args[2];
} else {
@ -143,10 +144,14 @@ class ServerShell extends AppShell
if (!empty($this->args[4]) && $this->args[4] === 'force') {
$force = true;
}
// Try to enable garbage collector as pulling events can use a lot of memory
gc_enable();
try {
$result = $this->Server->pull($user, $technique, $server, $jobId, $force);
if (is_array($result)) {
$message = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
$message = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled, %s analyst data pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4], $result[5]);
$this->Job->saveStatus($jobId, true, $message);
} else {
$message = __('ERROR: %s', $result);
@ -164,11 +169,12 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Push'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$serverId = $this->args[1];
$server = $this->getServer($serverId);
Configure::write('CurrentUserId', $userId);
$technique = empty($this->args[2]) ? 'full' : $this->args[2];
if (!empty($this->args[3])) {
$jobId = $this->args[3];
@ -203,6 +209,7 @@ class ServerShell extends AppShell
$userId = $this->args[0];
$user = $this->getUser($userId);
Configure::write('CurrentUserId', $userId);
$technique = isset($this->args[1]) ? $this->args[1] : 'full';
$servers = $this->Server->find('list', array(
@ -366,7 +373,7 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Fetch feeds as local data'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$feedId = $this->args[1];
@ -422,7 +429,7 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Cache server'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$scope = $this->args[1];
@ -485,7 +492,7 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Cache feeds for quick lookups'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$scope = $this->args[1];
@ -731,6 +738,7 @@ class ServerShell extends AppShell
public function sendPeriodicSummaryToUsers()
{
$periods = $this->__getPeriodsForToday();
$start_time = time();
echo __n('Started periodic summary generation for the %s period', 'Started periodic summary generation for periods: %s', count($periods), implode(', ', $periods)) . PHP_EOL;
@ -796,7 +804,7 @@ class ServerShell extends AppShell
if (empty($this->args[0]) || empty($this->args[1])) {
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Push Taxii'] . PHP_EOL);
}
$userId = $this->args[0];
$user = $this->getUser($userId);
$serverId = $this->args[1];

View File

@ -303,10 +303,10 @@ class StatisticsShell extends AppShell {
$this->out(json_encode([
'events' => $this->Event->find('count'),
'attributes' => $this->Event->Attribute->find('count',
['conditions' => ['Attribute.deleted' => 0], 'recursive' => -1]
['recursive' => -1]
),
'objects' => $this->Event->Object->find('count',
['conditions' => ['Object.deleted' => 0], 'recursive' => -1]
['recursive' => -1]
),
'correlations' => $this->Correlation->find('count') / 2,
'users' => $this->User->find('count',

View File

@ -16,7 +16,21 @@ class UserShell extends AppShell
'help' => __('Get list of user accounts.'),
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
'userId' => ['help' => __('User ID or e-mail address to filter.'), 'required' => false],
],
'options' => [
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
],
]
]);
$parser->addSubcommand('create', [
'help' => __('Create a new user account.'),
'parser' => [
'arguments' => [
'email' => ['help' => __('E-mail address (also used as the username.'), 'required' => true],
'role_id' => ['help' => __('Role ID of the user. For a list of available roles, use `cake Roles list`.'), 'required' => true],
'org_id' => ['help' => __('Organisation under which the user should be created'), 'required' => true],
'password' => ['help' => __('Enter a password to assign to the user (optional) - if none is set, the user will receive a temporary password.')]
],
'options' => [
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
@ -82,6 +96,15 @@ class UserShell extends AppShell
],
],
]);
$parser->addSubcommand('change_role', [
'help' => __('Change user role.'),
'parser' => [
'arguments' => [
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
'new_role' => ['help' => __('Role ID or Role name.'), 'required' => true],
]
],
]);
$parser->addSubcommand('change_authkey', [
'help' => __('Change authkey. When advanced authkeys are enabled, old authkeys will be disabled.'),
'parser' => [
@ -180,6 +203,48 @@ class UserShell extends AppShell
}
}
public function create()
{
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
$this->err('Invalid input. Usage: `User create [email] [role_id] [org_id] [password:optional]`');
}
$user = [
'email' => $this->args[0],
'role_id' => $this->args[1],
'org_id' => $this->args[2],
'change_pw' => true
];
if (!empty($this->args[3])) {
$user['password'] = $this->args[3];
$user['confirm_password'] = $this->args[3];
$user['change_pw'] = true;
}
$this->User->create();
$result = $this->User->save($user);
// do not fetch sensitive or big values
$schema = $this->User->schema();
unset($schema['authkey']);
unset($schema['password']);
unset($schema['gpgkey']);
unset($schema['certif_public']);
$fields = array_keys($schema);
$fields[] = 'Role.*';
$fields[] = 'Organisation.*';
$user = $this->User->find('first', [
'recursive' => -1,
'fields' => $fields,
'conditions' => ['User.id' => $this->User->id],
'contain' => ['Organisation', 'Role', 'UserSetting'],
]);
if ($this->params['json']) {
$this->out($this->json($user));
} else {
$this->out('User created.');
}
}
public function init()
{
if (!Configure::read('Security.salt')) {
@ -443,6 +508,35 @@ class UserShell extends AppShell
}
}
public function change_role()
{
list($userId, $newRole) = $this->args;
$user = $this->getUser($userId);
if (is_numeric($newRole)) {
$conditions = ['Role.id' => $newRole];
} else {
$conditions = ['Role.name' => $newRole];
}
$newRoleFromDb = $this->User->Role->find('first', [
'conditions' => $conditions,
'fields' => ['Role.id'],
]);
if (empty($newRoleFromDb)) {
$this->error("Role `$newRole` not found.");
}
if ($newRoleFromDb['Role']['id'] == $user['role_id']) {
$this->error("Role `$newRole` is already assigned to {$user['email']}.");
}
$this->User->updateField($user, 'role_id', $newRoleFromDb['Role']['id']);
$this->out("Role changed from `{$user['role_id']}` to `{$newRoleFromDb['Role']['id']}`.");
}
public function user_ips()
{
list($userId) = $this->args;
@ -575,7 +669,7 @@ class UserShell extends AppShell
}
/**
* @param string|int $userId
* @param string|int $userId User ID or User e-mail
* @return array
*/
private function getUser($userId)

View File

@ -17,6 +17,9 @@ class WorkflowShell extends AppShell {
$data = JsonTool::decode($this->args[1]);
$logging = JsonTool::decode($this->args[2]);
$jobId = $this->args[3];
if (!empty($this->args[4])) {
Configure::write('CurrentUserId', JsonTool::decode($this->args[4]));
}
$blockingErrors = [];
$executionSuccess = $this->Workflow->executeWorkflowForTrigger($trigger_id, $data, $blockingErrors);

View File

@ -0,0 +1,103 @@
<?php
App::uses('AppController', 'Controller');
class AnalystDataBlocklistsController extends AppController
{
public $components = array('Session', 'RequestHandler', 'BlockList');
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 entries <- no we won't, this is the max a user van view/page.
'order' => array(
'AnalystDataBlocklist.created' => 'DESC'
),
);
public function index()
{
$passedArgsArray = array();
$passedArgs = $this->passedArgs;
$params = array();
$validParams = array('analyst_data_uuid', 'comment', 'analyst_data_info', 'analyst_data_orgc');
foreach ($validParams as $validParam) {
if (!empty($this->params['named'][$validParam])) {
$params[$validParam] = $this->params['named'][$validParam];
}
}
if (!empty($this->params['named']['searchall'])) {
$params['AND']['OR'] = array(
'analyst_data_uuid' => $this->params['named']['searchall'],
'comment' => $this->params['named']['searchall'],
'analyst_data_info' => $this->params['named']['searchall'],
'analyst_data_orgc' => $this->params['named']['searchall']
);
}
$this->set('passedArgs', json_encode($passedArgs));
$this->set('passedArgsArray', $passedArgsArray);
return $this->BlockList->index($this->_isRest(), $params);
}
public function add()
{
$this->set('action', 'add');
return $this->BlockList->add($this->_isRest());
}
public function edit($id)
{
$this->set('action', 'edit');
return $this->BlockList->edit($this->_isRest(), $id);
}
public function delete($id)
{
if (Validation::uuid($id)) {
$entry = $this->AnalystDataBlocklist->find('first', array(
'conditions' => array('analyst_data_uuid' => $id)
));
if (empty($entry)) {
throw new NotFoundException(__('Invalid blocklist entry'));
}
$id = $entry['AnalystDataBlocklist']['id'];
}
return $this->BlockList->delete($this->_isRest(), $id);
}
public function massDelete()
{
if ($this->request->is('post') || $this->request->is('put')) {
if (!isset($this->request->data['AnalystDataBlocklist'])) {
$this->request->data = array('AnalystDataBlocklist' => $this->request->data);
}
$ids = $this->request->data['AnalystDataBlocklist']['ids'];
$analyst_data_ids = json_decode($ids, true);
if (empty($analyst_data_ids)) {
throw new NotFoundException(__('Invalid Analyst Data IDs.'));
}
$result = $this->AnalystDataBlocklist->deleteAll(array('AnalystDataBlocklist.id' => $analyst_data_ids));
if ($result) {
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('AnalystDataBlocklist', 'Deleted', $ids, $this->response->type());
} else {
$this->Flash->success('Blocklist entry removed');
$this->redirect(array('controller' => 'AnalystDataBlocklist', 'action' => 'index'));
}
} else {
$error = __('Failed to delete Analyst Data from AnalystDataBlocklist. Error: ') . PHP_EOL . h($result);
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('AnalystDataBlocklist', 'Deleted', false, $error, $this->response->type());
} else {
$this->Flash->error($error);
$this->redirect(array('controller' => 'AnalystDataBlocklists', 'action' => 'index'));
}
}
} else {
$ids = json_decode($this->request->query('ids'), true);
if (empty($ids)) {
throw new NotFoundException(__('Invalid analyst data IDs.'));
}
$this->set('analyst_data_ids', $ids);
}
}
}

View File

@ -0,0 +1,333 @@
<?php
App::uses('AppController', 'Controller');
class AnalystDataController extends AppController
{
public $components = ['Session', 'RequestHandler'];
public $paginate = [
'limit' => 60,
'order' => []
];
public $uses = [
'Opinion',
'Note',
'Relationship'
];
private $__valid_types = [
'Opinion',
'Note',
'Relationship'
];
// public $modelSelection = 'Note';
private function _setViewElements()
{
$dropdownData = [];
$this->loadModel('Event');
$dropdownData['distributionLevels'] = $this->Event->distributionLevels;
$this->set('initialDistribution', Configure::read('MISP.default_event_distribution'));
$dropdownData['sgs'] = $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
$dropdownData['valid_targets'] = array_combine($this->AnalystData->valid_targets, $this->AnalystData->valid_targets);
$this->set(compact('dropdownData'));
$this->set('modelSelection', $this->modelSelection);
$this->set('distributionLevels', $this->Event->distributionLevels);
App::uses('LanguageRFC5646Tool', 'Tools');
$this->set('languageRFC5646', ['' => __('- No language -'), LanguageRFC5646Tool::getLanguages()]);
}
public function add($type = 'Note', $object_uuid = null, $object_type = null)
{
$this->__typeSelector($type);
if (!empty($object_uuid)) {
$this->request->data[$this->modelSelection]['object_uuid'] = $object_uuid;
}
if (!empty($object_type)) {
$this->request->data[$this->modelSelection]['object_type'] = $object_type;
}
if (empty($this->request->data[$this->modelSelection]['object_type']) && !empty($this->request->data[$this->modelSelection]['object_uuid'])) {
$this->request->data[$this->modelSelection]['object_type'] = $this->AnalystData->deduceType($object_uuid);
}
$params = [];
$this->CRUD->add($params);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
$this->_setViewElements();
if ($type == 'Relationship') {
$this->set('existingRelations', $this->AnalystData->getExistingRelationships());
}
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'add_' . strtolower($type)));
$this->render('add');
}
public function edit($type = 'Note', $id)
{
if ($type === 'all' && Validation::uuid($id)) {
$this->loadModel('AnalystData');
$type = $this->AnalystData->deduceType($id);
}
$this->__typeSelector($type);
if (!is_numeric($id) && Validation::uuid($id)) {
$id = $this->AnalystData->getIDFromUUID($type, $id);
}
$this->set('id', $id);
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$params = [
'fields' => $this->AnalystData->getEditableFields(),
'conditions' => $conditions,
'afterFind' => function(array $analystData): array {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$canEdit) {
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
}
return $analystData;
},
'beforeSave' => function(array $analystData): array {
$analystData[$this->modelSelection]['modified'] = date('Y-m-d H:i:s');
return $analystData;
}
];
$this->CRUD->edit($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->_setViewElements();
if ($type == 'Relationship') {
$this->set('existingRelations', $this->AnalystData->getExistingRelationships());
}
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'edit'));
$this->render('add');
}
public function delete($type = 'Note', $id, $hard=true)
{
if ($type === 'all' && Validation::uuid($id)) {
$this->loadModel('AnalystData');
$type = $this->AnalystData->deduceType($id);
}
$this->__typeSelector($type);
if (!is_numeric($id) && Validation::uuid($id)) {
$id = $this->AnalystData->getIDFromUUID($type, $id);
}
$params = [
'afterFind' => function(array $analystData) {
$canEdit = $this->ACL->canEditAnalystData($this->Auth->user(), $analystData, $this->modelSelection);
if (!$canEdit) {
throw new MethodNotAllowedException(__('You are not authorised to do that.'));
}
return $analystData;
},
'afterDelete' => function($deletedAnalystData) use ($hard) {
if (empty($hard)) {
return;
}
$type = $this->AnalystData->deduceAnalystDataType($deletedAnalystData);
$info = '- Unsupported analyst type -';
if ($type === 'Note') {
$info = $deletedAnalystData[$type]['note'];
} else if ($type === 'Opinion') {
$info = sprintf('%s/100 :: %s', $deletedAnalystData[$type]['opinion'], $deletedAnalystData[$type]['comment']);
} else if ($type === 'Relationship') {
$info = sprintf('-- %s --> %s :: %s', $deletedAnalystData[$type]['relationship_type'] ?? '[undefined]', $deletedAnalystData[$type]['related_object_type'], $deletedAnalystData[$type]['related_object_uuid']);
}
$this->AnalystDataBlocklist = ClassRegistry::init('AnalystDataBlocklist');
$this->AnalystDataBlocklist->create();
if (!empty($deletedAnalystData[$type]['orgc_uuid'])) {
if (!empty($deletedAnalystData[$type]['Orgc'])) {
$orgc = $deletedAnalystData[$type];
} else {
$orgc = $this->Orgc->find('first', array(
'conditions' => ['Orgc.uuid' => $deletedAnalystData[$type]['orgc_uuid']],
'recursive' => -1,
'fields' => ['Orgc.name'],
));
}
} else {
$orgc = ['Orgc' => ['name' => 'MISP']];
}
$this->AnalystDataBlocklist->save(['analyst_data_uuid' => $deletedAnalystData[$type]['uuid'], 'analyst_data_info' => $info, 'analyst_data_orgc' => $orgc['Orgc']['name']]);
}
];
$this->CRUD->delete($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function view($type = 'Note', $id)
{
if ($type === 'all' && Validation::uuid($id)) {
$this->loadModel('AnalystData');
$type = $this->AnalystData->getAnalystDataTypeFromUUID($id);
}
$this->__typeSelector($type);
if (!is_numeric($id) && Validation::uuid($id)) {
$id = $this->AnalystData->getIDFromUUID($type, $id);
}
if (!$this->IndexFilter->isRest()) {
$this->AnalystData->fetchRecursive = true;
}
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$this->CRUD->view($id, [
'conditions' => $conditions,
'contain' => ['Org', 'Orgc'],
'afterFind' => function(array $analystData) {
if (!$this->request->is('ajax')) {
unset($analystData[$this->modelSelection]['_canEdit']);
}
return $analystData;
}
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('id', $id);
$this->loadModel('Event');
$this->_setViewElements();
$this->set('distributionLevels', $this->Event->distributionLevels);
$this->set('shortDist', $this->Event->shortDist);
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'view'));
$this->render('view');
}
public function index($type = 'Note')
{
$this->__typeSelector($type);
$conditions = $this->AnalystData->buildConditions($this->Auth->user());
$params = [
'filters' => ['uuid', 'target_object'],
'quickFilters' => ['name'],
'conditions' => $conditions,
'afterFind' => function(array $data) {
foreach ($data as $i => $analystData) {
if (!$this->request->is('ajax')) {
unset($analystData[$this->modelSelection]['_canEdit']);
}
}
return $data;
}
];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->_setViewElements();
$this->set('menuData', array('menuList' => 'analyst_data', 'menuItem' => 'index'));
}
public function getRelatedElement($type, $uuid)
{
$this->__typeSelector('Relationship');
$data = $this->AnalystData->getRelatedElement($this->Auth->user(), $type, $uuid);
return $this->RestResponse->viewData($data, 'json');
}
public function getChildren($type = 'Note', $uuid, $depth=2)
{
$this->__typeSelector($type);
$data = $this->AnalystData->getChildren($this->Auth->user(), $uuid, $depth);
return $this->RestResponse->viewData($data, 'json');
}
public function filterAnalystDataForPush()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This function is only accessible via POST requests.'));
}
$this->loadModel('AnalystData');
$allIncomingAnalystData = $this->request->data;
$allData = $this->AnalystData->filterAnalystDataForPush($allIncomingAnalystData);
return $this->RestResponse->viewData($allData, $this->response->type());
}
public function indexMinimal()
{
$this->loadModel('AnalystData');
$filters = [];
if ($this->request->is('post')) {
$filters = $this->request->data;
}
$options = [];
if (!empty($filters['orgc_name'])) {
$orgcNames = $filters['orgc_name'];
if (!is_array($orgcNames)) {
$orgcName = [$orgcNames];
}
$filterName = 'orgc_uuid';
foreach ($orgcNames as $orgcName) {
if ($orgcName[0] === '!') {
$orgc = $this->AnalystData->Orgc->fetchOrg(substr($orgcName, 1));
if ($orgc === false) {
continue;
}
$options[]['AND'][] = ["{$filterName} !=" => $orgc['uuid']];
} else {
$orgc = $this->AnalystData->Orgc->fetchOrg($orgcName);
if ($orgc === false) {
continue;
}
$options['OR'][] = [$filterName => $orgc['uuid']];
}
}
}
$allData = $this->AnalystData->indexMinimal($this->Auth->user(), $options);
return $this->RestResponse->viewData($allData, $this->response->type());
}
public function pushAnalystData()
{
if (!$this->Auth->user()['Role']['perm_sync'] || !$this->Auth->user()['Role']['perm_analyst_data']) {
throw new MethodNotAllowedException(__('You do not have the permission to do that.'));
}
if (!$this->_isRest()) {
throw new MethodNotAllowedException(__('This action is only accessible via a REST request.'));
}
if ($this->request->is('post')) {
$this->loadModel('AnalystData');
$analystData = $this->request->data;
$saveResult = $this->AnalystData->captureAnalystData($this->Auth->user(), $analystData);
$messageInfo = __('%s imported, %s ignored, %s failed. %s', $saveResult['imported'], $saveResult['ignored'], $saveResult['failed'], !empty($saveResult['errors']) ? implode(', ', $saveResult['errors']) : '');
if ($saveResult['success']) {
$message = __('Analyst Data imported. ') . $messageInfo;
return $this->RestResponse->saveSuccessResponse('AnalystData', 'pushAnalystData', false, $this->response->type(), $message);
} else {
$message = __('Could not import analyst data. ') . $messageInfo;
return $this->RestResponse->saveFailResponse('AnalystData', 'pushAnalystData', false, $message);
}
}
}
private function __typeSelector($type) {
foreach ($this->__valid_types as $vt) {
if ($type === $vt) {
$this->modelSelection = $vt;
$this->loadModel($vt);
$this->AnalystData = $this->{$vt};
$this->modelClass = $vt;
$this->{$vt}->current_user = $this->Auth->user();
if (!empty($this->request->data)) {
if (!isset($this->request->data[$type])) {
$this->request->data = [$type => $this->request->data];
}
}
return $vt;
}
}
throw new MethodNotAllowedException(__('Invalid type.'));
}
}

View File

@ -33,13 +33,19 @@ class AppController extends Controller
public $helpers = array('OrgImg', 'FontAwesome', 'UserName');
private $__queryVersion = '158';
public $pyMispVersion = '2.4.184';
private $__queryVersion = '162';
public $pyMispVersion = '2.4.190';
public $phpmin = '7.2';
public $phprec = '7.4';
public $phptoonew = '8.0';
private $isApiAuthed = false;
/** @var redis */
private $redis = null;
/** @var benchmark_results */
private $benchmark_results = null;
public $baseurl = '';
public $restResponsePayload = null;
@ -57,9 +63,14 @@ class AppController extends Controller
/** @var ACLComponent */
public $ACL;
/** @var BenchmarkComponent */
public $Benchmark;
/** @var RestResponseComponent */
public $RestResponse;
public $start_time;
public function __construct($request = null, $response = null)
{
parent::__construct($request, $response);
@ -97,13 +108,19 @@ class AppController extends Controller
public function beforeFilter()
{
$controller = $this->request->params['controller'];
$action = $this->request->params['action'];
if (Configure::read('MISP.system_setting_db')) {
App::uses('SystemSetting', 'Model');
SystemSetting::setGlobalSetting();
}
$this->User = ClassRegistry::init('User');
if (Configure::read('Plugin.Benchmarking_enable')) {
App::uses('BenchmarkTool', 'Tools');
$this->Benchmark = new BenchmarkTool($this->User);
$this->start_time = $this->Benchmark->startBenchmark();
}
$controller = $this->request->params['controller'];
$action = $this->request->params['action'];
$this->_setupBaseurl();
$this->Auth->loginRedirect = $this->baseurl . '/users/routeafterlogin';
@ -147,8 +164,6 @@ class AppController extends Controller
Configure::write('Config.language', 'eng');
}
$this->User = ClassRegistry::init('User');
if (!empty($this->request->params['named']['disable_background_processing'])) {
Configure::write('MISP.background_jobs', 0);
}
@ -226,6 +241,7 @@ class AppController extends Controller
) {
// REST authentication
if ($this->_isRest() || $this->_isAutomation()) {
// disable CSRF for REST access
$this->Security->csrfCheck = false;
$loginByAuthKeyResult = $this->__loginByAuthKey();
@ -306,6 +322,7 @@ class AppController extends Controller
$this->set('isAclTagger', $role['perm_tagger']);
$this->set('isAclGalaxyEditor', !empty($role['perm_galaxy_editor']));
$this->set('isAclSighting', $role['perm_sighting'] ?? false);
$this->set('isAclAnalystDataCreator', $role['perm_analyst_data'] ?? false);
$this->set('aclComponent', $this->ACL);
$this->userRole = $role;
@ -632,21 +649,21 @@ class AppController extends Controller
}
// Check if user accepted terms and conditions
if (!$user['termsaccepted'] && !empty(Configure::read('MISP.terms_file')) && !$this->_isControllerAction(['users' => ['terms', 'logout', 'login', 'downloadTerms']])) {
if (!$user['termsaccepted'] && !empty(Configure::read('MISP.terms_file')) && !$this->_isControllerAction(['users' => ['terms', 'logout', 'login', 'downloadTerms', 'totp_new', 'email_otp']])) {
//if ($this->_isRest()) throw new MethodNotAllowedException('You have not accepted the terms of use yet, please log in via the web interface and accept them.');
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
return false;
}
// Check if user must change password
if ($user['change_pw'] && !$this->_isControllerAction(['users' => ['terms', 'change_pw', 'logout', 'login']])) {
if ($user['change_pw'] && !$this->_isControllerAction(['users' => ['terms', 'change_pw', 'logout', 'login', 'totp_new', 'email_otp']])) {
//if ($this->_isRest()) throw new MethodNotAllowedException('Your user account is expecting a password change, please log in via the web interface and change it before proceeding.');
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
return false;
}
// Check if user must read news
if (!$this->_isControllerAction(['news' => ['index'], 'users' => ['terms', 'change_pw', 'login', 'logout']])) {
if (!$this->_isControllerAction(['news' => ['index'], 'users' => ['terms', 'change_pw', 'login', 'logout', 'totp_new', 'email_otp']])) {
$this->loadModel('News');
$latestNewsCreated = $this->News->latestNewsTimestamp();
if ($latestNewsCreated && $user['newsread'] < $latestNewsCreated) {
@ -862,6 +879,21 @@ class AppController extends Controller
public function afterFilter()
{
// benchmarking
if (Configure::read('Plugin.Benchmarking_enable') && isset($this->Benchmark)) {
$this->Benchmark->stopBenchmark([
'user' => $this->Auth->user('id'),
'controller' => $this->request->params['controller'],
'action' => $this->request->params['action'],
'start_time' => $this->start_time
]);
//if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $key)) {
//$redis->setex('misp:auth_fail_throttling:' . $key, 3600, 1);
//return true;
//}
}
if ($this->isApiAuthed && $this->_isRest() && !Configure::read('Security.authkey_keep_session')) {
$this->Session->destroy();
}
@ -960,31 +992,6 @@ class AppController extends Controller
return $this->userRole['perm_site_admin'];
}
protected function _getApiAuthUser($key, &$exception)
{
if (strlen($key) === 40) {
// check if the key is valid -> search for users based on key
$user = $this->_checkAuthUser($key);
if (!$user) {
$exception = $this->RestResponse->throwException(
401,
__('This authentication key is not authorized to be used for exports. Contact your administrator.')
);
return false;
}
} else {
$user = $this->Auth->user();
if (!$user) {
$exception = $this->RestResponse->throwException(
401,
__('You have to be logged in to do that.')
);
return false;
}
}
return $user;
}
private function __captureParam($data, $param, $value)
{
if ($this->modelClass->checkParam($param)) {
@ -1032,7 +1039,19 @@ class AppController extends Controller
$data = array_merge($data, $temp);
} else {
foreach ($options['paramArray'] as $param) {
if (isset($temp[$param])) {
if (substr($param, -1) == '*') {
$root = substr($param, 0, strlen($param)-1);
foreach ($temp as $existingParamKey => $v) {
$leftover = substr($existingParamKey, strlen($param)-1);
if (
$root == substr($existingParamKey, 0, strlen($root)) &&
preg_match('/^[\w_-. ]+$/', $leftover) == 1
) {
$data[$existingParamKey] = $temp[$existingParamKey];
break;
}
}
} else if (isset($temp[$param])) {
$data[$param] = $temp[$param];
}
}
@ -1100,6 +1119,23 @@ class AppController extends Controller
protected function _checkAuthUser($authkey)
{
if (Configure::read('Security.api_key_quick_lookup')) {
$redis = RedisTool::init();
if (file_exists(APP . 'Config/hmac_key.php')) {
include(APP . 'Config/hmac_key.php');
$hashed_authkey = hash_hmac('sha512', $authkey, $hmac_key);
if ($redis && $redis->exists('misp:fast_authkey_lookup:' . $hashed_authkey)) {
$user = RedisTool::deserialize($redis->get('misp:fast_authkey_lookup:' . $hashed_authkey));
if ($user) {
return $user;
}
}
} else {
App::uses('RandomTool', 'Tools');
$hmac_key = RandomTool::random_str(true, 40);
file_put_contents(APP . 'Config/hmac_key.php', sprintf('<?php%s$hmac_key = \'%s\';', PHP_EOL, $hmac_key));
}
}
if (Configure::read('Security.advanced_authkeys')) {
$user = $this->User->AuthKey->getAuthUserByAuthKey($authkey);
} else {
@ -1113,6 +1149,13 @@ class AppController extends Controller
return false;
}
$user['logged_by_authkey'] = true;
if (Configure::read('Security.api_key_quick_lookup') && !empty($hmac_key) && $redis) {
$expiration = Configure::read('Security.api_key_quick_lookup_expiration') ? Configure::read('Security.api_key_quick_lookup_expiration') : 180;
if ($redis) {
$hashed_authkey = hash_hmac('sha512', $authkey, $hmac_key);
$redis->setex('misp:fast_authkey_lookup:' . $hashed_authkey, $expiration, RedisTool::serialize($user));
}
}
return $user;
}
@ -1161,12 +1204,11 @@ class AppController extends Controller
null);
$this->__preAuthException($authName . ' authentication failed. Contact your MISP support for additional information at: ' . Configure::read('MISP.contact'));
}
$temp = $this->_checkExternalAuthUser($server[$headerNamespace . $header]);
$user['User'] = $temp;
if ($user['User']) {
$this->User->updateLoginTimes($user['User']);
$user = $this->_checkExternalAuthUser($server[$headerNamespace . $header]);
if ($user) {
$this->User->updateLoginTimes($user);
//$this->Session->renew();
$this->Session->write(AuthComponent::$sessionKey, $user['User']);
$this->Session->write(AuthComponent::$sessionKey, $user);
if (Configure::read('MISP.log_auth')) {
$this->Log = ClassRegistry::init('Log');
$change = $this->User->UserLoginProfile->_getUserProfile();
@ -1176,7 +1218,7 @@ class AppController extends Controller
$user,
'auth',
'User',
$user['User']['id'],
$user['id'],
'Successful authentication using ' . $authName . ' key',
json_encode($change));
}
@ -1326,13 +1368,8 @@ class AppController extends Controller
if ($filters === false) {
return $exception;
}
$key = empty($filters['key']) ? $filters['returnFormat'] : $filters['key'];
$user = $this->_getApiAuthUser($key, $exception);
if ($user === false) {
return $exception;
}
session_write_close(); // Rest search can be longer, so close session to allow concurrent requests
$user = $this->_closeSession();
if (isset($filters['returnFormat'])) {
$returnFormat = $filters['returnFormat'];
@ -1519,10 +1556,20 @@ class AppController extends Controller
* Close session without writing changes to them and return current user.
* @return array
*/
protected function _closeSession()
protected function _closeSession($saveSession = false)
{
$user = $this->Auth->user();
session_abort();
// Hack to store user info in static AuthComponent::$_user variable to avoid starting session again by calling
// $this->Auth->user()
AuthComponent::$sessionKey = null;
$this->Auth->login($user);
if ($saveSession) {
@session_write_close();
} else {
session_abort();
}
return $user;
}

View File

@ -33,38 +33,16 @@ class AttributesController extends AppController
{
parent::beforeFilter();
$this->Auth->allow('restSearch');
$this->Auth->allow('returnAttributes');
$this->Auth->allow('downloadAttachment');
$this->Auth->allow('text');
$this->Auth->allow('rpz');
$this->Auth->allow('bro');
// permit reuse of CSRF tokens on the search page.
if ('search' === $this->request->params['action']) {
$this->Security->csrfCheck = false;
}
$this->Security->unlockedActions[] = 'getMassEditForm';
$this->Security->unlockedActions[] = 'search';
if ($this->request->action === 'add_attachment') {
$this->Security->unlockedFields = array('values');
}
// convert uuid to id if present in the url and overwrite id field
if (isset($this->request->params->query['uuid'])) {
$params = array(
'conditions' => array('Attribute.uuid' => $this->request->params->query['uuid']),
'recursive' => 0,
'fields' => 'Attribute.id'
);
$result = $this->Attribute->find('first', $params);
if (isset($result['Attribute']) && isset($result['Attribute']['id'])) {
$id = $result['Attribute']['id'];
$this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now
}
}
if ($this->request->action === 'viewPicture') {
} elseif ($this->request->action === 'viewPicture') {
$this->Security->doNotGenerateToken = true;
}
}
@ -793,12 +771,14 @@ class AttributesController extends AppController
$result = $this->Attribute->save($this->request->data, array('fieldList' => Attribute::EDITABLE_FIELDS));
if ($result) {
$this->Attribute->AttributeTag->handleAttributeTags($this->Auth->user(), $this->request->data['Attribute'], $attribute['Event']['id'], $capture=true);
$this->Attribute->Event->captureAnalystData($this->Auth->user(), $this->request->data['Attribute'], 'Attribute', $existingAttribute['Attribute']['uuid']);
}
$this->Attribute->Object->updateTimestamp($existingAttribute['Attribute']['object_id']);
} else {
$result = $this->Attribute->save($this->request->data, array('fieldList' => Attribute::EDITABLE_FIELDS));
if ($result) {
$this->Attribute->AttributeTag->handleAttributeTags($this->Auth->user(), $this->request->data['Attribute'], $attribute['Event']['id'], $capture=true);
$this->Attribute->Event->captureAnalystData($this->Auth->user(), $this->request->data['Attribute'], 'Attribute', $existingAttribute['Attribute']['uuid']);
}
if ($this->request->is('ajax')) {
$this->autoRender = false;
@ -1576,10 +1556,11 @@ class AttributesController extends AppController
// Force index for performance reasons see #3321
if (isset($filters['value'])) {
$this->paginate['forceIndexHint'] = '(value1, value2)';
$this->paginate['forceIndexHint'] = 'value1, value2';
}
$this->paginate['conditions'] = $params['conditions'];
$this->paginate['ignoreIndexHint'] = 'deleted';
$attributes = $this->paginate();
$this->Attribute->attachTagsToAttributes($attributes, ['includeAllTags' => true]);
@ -3038,4 +3019,35 @@ class AttributesController extends AppController
];
}
}
public function viewAnalystData($id, $seed = null)
{
$this->Attribute->includeAnalystDataRecursive = true;
$attribute = $this->Attribute->fetchAttributes(
$this->Auth->user(),
[
'conditions' => $this->__idToConditions($id),
'flatten' => true
]
);
if(empty($attribute)) {
throw new NotFoundException(__('Invalid Attribute.'));
} else {
$attribute[0]['Attribute'] = array_merge_recursive($attribute[0]['Attribute'], $this->Attribute->attachAnalystData($attribute[0]['Attribute']));
}
if ($this->_isRest()) {
$validFields = ['Note', 'Opinion', 'Relationship'];
$results = [];
foreach ($validFields as $field) {
if (!empty($attribute[0]['Attribute'][$field])) {
$results[$field] = $attribute[0]['Attribute'][$field];
}
}
return $this->RestResponse->viewData($results, $this->response->type());
}
$this->layout = null;
$this->set('shortDist', $this->Attribute->shortDist);
$this->set('object', $attribute[0]['Attribute']);
$this->set('seed', $seed);
}
}

View File

@ -0,0 +1,123 @@
<?php
App::uses('AppController', 'Controller');
class BenchmarksController extends AppController
{
public $components = array('Session', 'RequestHandler');
public $paginate = [
'limit' => 60,
'maxLimit' => 9999,
];
public function beforeFilter()
{
parent::beforeFilter();
}
public function index()
{
$this->set('menuData', ['menuList' => 'admin', 'menuItem' => 'index']);
$this->loadModel('User');
App::uses('BenchmarkTool', 'Tools');
$this->Benchmark = new BenchmarkTool($this->User);
$passedArgs = $this->passedArgs;
$this->paginate['order'] = 'value';
$defaults = [
'days' => null,
'average' => false,
'aggregate' => false,
'scope' => null,
'field' => null,
'key' => null,
'quickFilter' => null
];
$filters = $this->IndexFilter->harvestParameters(array_keys($defaults));
foreach ($defaults as $key => $value) {
if (!isset($filters[$key])) {
$filters[$key] = $defaults[$key];
}
}
$temp = $this->Benchmark->getAllTopLists(
$filters['days'] ?? null,
$filters['limit'] ?? 100,
$filters['average'] ?? null,
$filters['aggregate'] ?? null
);
$settings = $this->Benchmark->getSettings();
$units = $this->Benchmark->getUnits();
$this->set('settings', $settings);
$data = [];
$userLookup = [];
foreach ($temp as $scope => $t) {
if (!empty($filters['scope']) && $filters['scope'] !== 'all' && $scope !== $filters['scope']) {
continue;
}
foreach ($t as $field => $t2) {
if (!empty($filters['field']) && $filters['field'] !== 'all' && $field !== $filters['field']) {
continue;
}
foreach ($t2 as $date => $t3) {
foreach ($t3 as $key => $value) {
if ($scope == 'user') {
if ($key === 'SYSTEM') {
$text = 'SYSTEM';
} else if (isset($userLookup[$key])) {
$text = $userLookup[$key];
} else {
$user = $this->User->find('first', [
'fields' => ['User.id', 'User.email'],
'recursive' => -1,
'conditions' => ['User.id' => $key]
]);
if (empty($user)) {
$text = '(' . $key . ') ' . __('Invalid user');
} else {
$text = '(' . $key . ') ' . $user['User']['email'];
}
$userLookup[$key] = $text;
}
} else {
$text = $key;
}
if (!empty($filters['quickFilter'])) {
$q = strtolower($filters['quickFilter']);
if (
strpos(strtolower($scope), $q) === false &&
strpos(strtolower($field), $q) === false &&
strpos(strtolower($key), $q) === false &&
strpos(strtolower($value), $q) === false &&
strpos(strtolower($date), $q) === false &&
strpos(strtolower($text), $q) === false
) {
continue;
}
}
if (empty($filters['key']) || $key == $filters['key']) {
$data[] = [
'scope' => $scope,
'field' => $field,
'date' => $date,
'key' => $key,
'text' => $text,
'value' => $value,
'unit' => $units[$field]
];
}
}
}
}
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($data, $this->response->type());
}
App::uses('CustomPaginationTool', 'Tools');
$customPagination = new CustomPaginationTool();
$customPagination->truncateAndPaginate($data, $this->params, $this->modelClass, true);
$this->set('data', $data);
$this->set('passedArgs', json_encode($passedArgs));
$this->set('filters', $filters);
}
}

View File

@ -0,0 +1,146 @@
<?php
use PHPUnit\Framework\MockObject\InvalidMethodNameException;
App::uses('AppController', 'Controller');
class CollectionElementsController extends AppController
{
public $components = ['Session', 'RequestHandler'];
public $paginate = [
'limit' => 60,
'order' => []
];
public $uses = [
];
public function add($collection_id)
{
$this->CollectionElement->Collection->current_user = $this->Auth->user();
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), intval($collection_id))) {
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
}
$this->CRUD->add([
'beforeSave' => function (array $collectionElement) use ($collection_id) {
$collectionElement['CollectionElement']['collection_id'] = intval($collection_id);
return $collectionElement;
}
]);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
$dropdownData = [
'types' => array_combine($this->CollectionElement->valid_types, $this->CollectionElement->valid_types)
];
$this->set(compact('dropdownData'));
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'add_element'));
}
public function delete($element_id)
{
$collectionElement = $this->CollectionElement->find('first', [
'recursive' => -1,
'conditions' => [
'CollectionElement.id' => $element_id
]
]);
$collection_id = $collectionElement['CollectionElement']['collection_id'];
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), $collection_id)) {
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
}
$this->CRUD->delete($element_id);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
}
public function index($collection_id)
{
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'index'));
if (!$this->CollectionElement->Collection->mayView($this->Auth->user('id'), intval($collection_id))) {
throw new NotFoundException(__('Invalid collection or no access.'));
}
$params = [
'filters' => ['uuid', 'type', 'name'],
'quickFilters' => ['name'],
'conditions' => ['collection_id' => $collection_id]
];
$this->loadModel('Event');
$this->set('distributionLevels', $this->Event->distributionLevels);
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function addElementToCollection($element_type, $element_uuid)
{
if ($this->request->is('get')) {
$validCollections = $this->CollectionElement->Collection->find('list', [
'recursive' => -1,
'fields' => ['Collection.id', 'Collection.name'],
'conditions' => ['Collection.orgc_id' => $this->Auth->user('org_id')]
]);
if (empty($validCollections)) {
if ($this->request->is('ajax')) {
return $this->redirect(['controller' => 'collections', 'action' => 'add']);
}
throw new NotFoundException(__('You don\'t have any collections yet. Make sure you create one first before you can start adding elements.'));
}
$dropdownData = [
'collections' => $validCollections
];
$this->set(compact('dropdownData'));
} else if ($this->request->is('post')) {
if (!isset($this->request->data['CollectionElement'])) {
$this->request->data = ['CollectionElement' => $this->request->data];
}
if (!isset($this->request->data['CollectionElement']['collection_id'])) {
throw new NotFoundException(__('No collection_id specified.'));
}
$collection_id = intval($this->request->data['CollectionElement']['collection_id']);
if (!$this->CollectionElement->Collection->mayModify($this->Auth->user('id'), $collection_id)) {
throw new NotFoundException(__('Invalid collection or not authorized.'));
}
$description = empty($this->request->data['CollectionElement']['description']) ? '' : $this->request->data['CollectionElement']['description'];
$dataToSave = [
'CollectionElement' => [
'element_uuid' => $element_uuid,
'element_type' => $element_type,
'description' => $description,
'collection_id' => $collection_id
]
];
$this->CollectionElement->create();
$error = '';
try {
$result = $this->CollectionElement->save($dataToSave);
} catch (PDOException $e) {
if ($e->errorInfo[0] == 23000) {
$error = __(' Element already in Collection.');
}
}
if ($result) {
$message = __('Element added to the Collection.');
if ($this->IndexFilter->isRest()) {
return $this->RestResponse->saveSuccessResponse('CollectionElements', 'addElementToCollection', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect(Router::url($this->referer(), true));
}
} else {
$message = __('Element could not be added to the Collection.%s', $error);
if ($this->IndexFilter->isRest()) {
return $this->RestResponse->saveFailResponse('CollectionElements', 'addElementToCollection', false, $message, $this->response->type());
} else {
$this->Flash->error($message);
$this->redirect(Router::url($this->referer(), true));
}
}
}
}
}

View File

@ -0,0 +1,171 @@
<?php
App::uses('AppController', 'Controller');
class CollectionsController extends AppController
{
public $components = ['Session', 'RequestHandler'];
public $paginate = [
'limit' => 60,
'order' => []
];
public $uses = [
];
private $valid_types = [
'campaign',
'intrusion_set',
'named_threat',
'other',
'research'
];
public function add()
{
$this->Collection->current_user = $this->Auth->user();
$params = [];
if ($this->request->is('post')) {
$data = $this->request->data;
$params = [
'afterSave' => function (array $collection) use ($data) {
$this->Collection->CollectionElement->captureElements($collection);
return $collection;
}
];
}
$this->CRUD->add($params);
if ($this->restResponsePayload) {
return $this->restResponsePayload;
}
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'add'));
$this->loadModel('Event');
$dropdownData = [
'types' => array_combine($this->valid_types, $this->valid_types),
'distributionLevels' => $this->Event->distributionLevels,
'sgs' => $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1)
];
$this->set('initialDistribution', Configure::read('MISP.default_event_distribution'));
$this->set(compact('dropdownData'));
$this->render('add');
}
public function edit($id)
{
$this->Collection->current_user = $this->Auth->user();
if (!$this->Collection->mayModify($this->Auth->user('id'), $id)) {
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
}
$params = [];
if ($this->request->is('post') || $this->request->is('put')) {
$oldCollection = $this->Collection->find('first', [
'recursive' => -1,
'conditions' => ['Collection.id' => intval($id)]
]);
if (empty($oldCollection)) {
throw new NotFoundException(__('Invalid collection.'));
}
if (empty($this->request->data['Collection'])) {
$this->request->data = ['Collection' => $this->request->data];
}
$data = $this->request->data;
if (
isset($data['Collection']['modified']) &&
$data['Collection']['modified'] <= $oldCollection['Collection']['modified']
) {
throw new ForbiddenException(__('Collection received older or same as local version.'));
}
$params = [
'afterSave' => function (array &$collection) use ($data) {
$collection = $this->Collection->CollectionElement->captureElements($collection);
return $collection;
}
];
}
$this->set('id', $id);
$this->CRUD->edit($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'edit'));
$this->loadModel('Event');
$dropdownData = [
'types' => $this->valid_types,
'distributionLevels' => $this->Event->distributionLevels,
'sgs' => $this->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1)
];
$this->set(compact('dropdownData'));
$this->render('add');
}
public function delete($id)
{
if (!$this->Collection->mayModify($this->Auth->user('id'), $id)) {
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
}
$this->CRUD->delete($id);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function view($id)
{
$this->set('mayModify', $this->Collection->mayModify($this->Auth->user('id'), $id));
if (!$this->Collection->mayView($this->Auth->user('id'), $id)) {
throw new MethodNotAllowedException(__('Invalid Collection or insuficient privileges'));
}
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'view'));
$params = [
'contain' => [
'Orgc',
'Org',
'User',
'CollectionElement'
],
'afterFind' => function (array $collection){
return $this->Collection->rearrangeCollection($collection);
}
];
$this->CRUD->view($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('id', $id);
$this->loadModel('Event');
$this->set('distributionLevels', $this->Event->distributionLevels);
$this->render('view');
}
public function index($filter = null)
{
$this->set('menuData', array('menuList' => 'collections', 'menuItem' => 'index'));
$params = [
'filters' => ['Collection.uuid', 'Collection.type', 'Collection.name'],
'quickFilters' => ['Collection.name'],
'contain' => ['Orgc'],
'afterFind' => function($collections) {
foreach ($collections as $k => $collection) {
$collections[$k]['Collection']['element_count'] = $this->Collection->CollectionElement->find('count', [
'recursive' => -1,
'conditions' => ['CollectionElement.collection_id' => $collection['Collection']['id']]
]);
}
return $collections;
}
];
if ($filter === 'my_collections') {
$params['conditions']['Collection.user_id'] = $this->Auth->user('id');
}
if ($filter === 'org_collections') {
$params['conditions']['Collection.orgc_id'] = $this->Auth->user('org_id');
}
$this->loadModel('Event');
$this->set('distributionLevels', $this->Event->distributionLevels);
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ class CRUDComponent extends Component
}
$options['filters'][] = 'quickFilter';
}
$this->Controller->{$this->Controller->modelClass}->includeAnalystData = true;
$params = $this->Controller->IndexFilter->harvestParameters(empty($options['filters']) ? [] : $options['filters']);
$query = [];
$query = $this->setFilters($params, $query);
@ -39,6 +40,7 @@ class CRUDComponent extends Component
if (!empty($this->Controller->paginate['fields'])) {
$query['fields'] = $this->Controller->paginate['fields'];
}
$query['includeAnalystData'] = true;
$data = $this->Controller->{$this->Controller->modelClass}->find('all', $query);
if (isset($options['afterFind'])) {
if (is_callable($options['afterFind'])) {
@ -49,6 +51,7 @@ class CRUDComponent extends Component
}
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
} else {
$query['includeAnalystData'] = true;
$this->Controller->paginate = $query;
$data = $this->Controller->paginate();
if (isset($options['afterFind'])) {
@ -93,7 +96,7 @@ class CRUDComponent extends Component
$savedData = $model->save($data);
if ($savedData) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
$params['afterSave']($savedData);
}
$data = $model->find('first', [
'recursive' => -1,
@ -200,7 +203,7 @@ class CRUDComponent extends Component
if (isset($params['beforeSave'])) {
$data = $params['beforeSave']($data);
}
if ($model->save($data)) {
if ($data = $model->save($data)) {
if (isset($params['afterSave'])) {
$params['afterSave']($data);
}
@ -231,6 +234,8 @@ class CRUDComponent extends Component
if (empty($id)) {
throw new NotFoundException(__('Invalid %s.', $modelName));
}
$this->Controller->{$modelName}->includeAnalystData = true;
$this->Controller->{$modelName}->includeAnalystDataRecursive = true;
$query = [
'recursive' => -1,
'conditions' => [$modelName . '.id' => $id],
@ -284,6 +289,9 @@ class CRUDComponent extends Component
}
}
}
if (isset($params['afterFind'])) {
$data = $params['afterFind']($data);
}
if (isset($params['beforeDelete'])) {
$data = $params['beforeDelete']($data);
if (empty($data)) {
@ -297,6 +305,9 @@ class CRUDComponent extends Component
$result = $this->Controller->{$modelName}->delete($id);
}
if ($result) {
if (isset($params['afterDelete']) && is_callable($params['afterDelete'])) {
$params['afterDelete']($data);
}
$message = __('%s deleted.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveSuccessResponse($modelName, 'delete', $id, 'json', $message);
@ -336,7 +347,9 @@ class CRUDComponent extends Component
if ($filter === 'quickFilter') {
continue;
}
if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
if (is_array($filterValue)) {
$query['conditions']['AND'][] = [$filter => $filterValue];
} else if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
$query['conditions']['AND'][] = [$filter => $filterValue];
} else {
$query['conditions']['AND'][] = [$filter . ' LIKE' => $filterValue];

View File

@ -37,14 +37,17 @@ class CompressedRequestHandlerComponent extends Component
private function decodeGzipEncodedContent(Controller $controller)
{
if (function_exists('gzdecode')) {
$decoded = gzdecode($controller->request->input());
$input = $controller->request->input();
if (empty($input)) {
throw new BadRequestException('Request data should be gzip encoded, but request is empty.');
}
$decoded = gzdecode($input);
if ($decoded === false) {
throw new BadRequestException('Invalid compressed data.');
}
return $decoded;
} else {
throw new BadRequestException("This server doesn't support GZIP compressed requests.");
}
throw new BadRequestException("This server doesn't support GZIP compressed requests.");
}
/**

View File

@ -213,6 +213,7 @@ class RestResponseComponent extends Component
'perm_tag_editor',
'default_role',
'perm_sighting',
'perm_analyst_data',
'permission'
)
),
@ -234,6 +235,7 @@ class RestResponseComponent extends Component
'perm_tag_editor',
'default_role',
'perm_sighting',
'perm_analyst_data',
'permission'
)
)
@ -669,9 +671,10 @@ class RestResponseComponent extends Component
}
if ($response instanceof TmpFileTool) {
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$requestEtag = $this->requestEtag();
if ($requestEtag !== null) {
$etag = '"' . $response->hash('sha1') . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
if ($requestEtag === $etag) {
return new CakeResponse(['status' => 304]);
}
$headers['ETag'] = $etag;
@ -687,9 +690,10 @@ class RestResponseComponent extends Component
}
} else {
// Check if resource was changed when `If-None-Match` header is send and return 304 Not Modified
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$requestEtag = $this->requestEtag();
if ($requestEtag !== null) {
$etag = '"' . sha1($response) . '"';
if ($_SERVER['HTTP_IF_NONE_MATCH'] === $etag) {
if ($requestEtag === $etag) {
return new CakeResponse(['status' => 304]);
}
// Generate etag just when HTTP_IF_NONE_MATCH is set
@ -722,6 +726,25 @@ class RestResponseComponent extends Component
return $cakeResponse;
}
/**
* Return etag from If-None-Match HTTP request header without compression marks added by Apache
* @return string|null
*/
private function requestEtag()
{
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
// Remove compression marks that adds Apache for compressed content
$requestEtag = $_SERVER['HTTP_IF_NONE_MATCH'];
$etagWithoutQuotes = trim($requestEtag, '"');
$dashPos = strrpos($etagWithoutQuotes, '-');
if ($dashPos && in_array(substr($etagWithoutQuotes, $dashPos + 1), ['br', 'gzip'], true)) {
return '"' . substr($etagWithoutQuotes, 0, $dashPos) . '"';
}
return $requestEtag;
}
return null;
}
/**
* @param string $response
* @return string Signature as base64 encoded string
@ -1052,7 +1075,7 @@ class RestResponseComponent extends Component
'input' => 'radio',
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False' ),
'help' => __('Include deleted elements')
'help' => __('Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned')
),
'delta_merge' => array(
'input' => 'radio',
@ -1557,6 +1580,11 @@ class RestResponseComponent extends Component
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False')
),
'perm_analyst_data' => array(
'input' => 'radio',
'type' => 'integer',
'values' => array(1 => 'True', 0 => 'False')
),
'permission' => array(
'input' => 'select',
'type' => 'string',

View File

@ -124,6 +124,7 @@ class RestSearchComponent extends Component
'extended',
'extensionList',
'excludeGalaxy',
'includeAnalystData',
'includeRelatedTags',
'includeDecayScore',
'includeScoresOnEvent',
@ -143,7 +144,11 @@ class RestSearchComponent extends Component
'retry',
'expiry',
'minimum_ttl',
'ttl'
'ttl',
'org.sector',
'org.local',
'org.nationality',
'galaxy.*',
],
'Object' => [
'returnFormat',
@ -180,7 +185,10 @@ class RestSearchComponent extends Component
'attackGalaxy',
'object_relation',
'metadata',
'includeAllTags'
'includeAllTags',
'object_name',
'object_template_uuid',
'object_template_version'
],
'Sighting' => [
'context',

View File

@ -61,6 +61,8 @@ class EventReportsController extends AppController
public function view($reportId, $ajax=false)
{
$this->EventReport->includeAnalystData = true;
$this->EventReport->includeAnalystDataRecursive = true;
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
if ($this->_isRest()) {
return $this->RestResponse->viewData($report, $this->response->type());
@ -175,6 +177,7 @@ class EventReportsController extends AppController
$filters = $this->IndexFilter->harvestParameters(['event_id', 'value', 'context', 'index_for_event', 'extended_event']);
$filters['embedded_view'] = $this->request->is('ajax');
$compiledConditions = $this->__generateIndexConditions($filters);
$this->EventReport->includeAnalystData = true;
if ($this->_isRest()) {
$reports = $this->EventReport->find('all', [
'recursive' => -1,
@ -210,10 +213,13 @@ class EventReportsController extends AppController
public function extractAllFromReport($reportId)
{
if (!$this->request->is('ajax')) {
if (!$this->request->is('ajax') && !$this->_isRest()) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
}
if ($this->request->is('post')) {
if (!isset($this->data['EventReport'])) {
$this->data = ['EventReport' => $this->data];
}
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $reportId, 'edit', $throwErrors=true, $full=false);
$results = $this->EventReport->getComplexTypeToolResultWithReplacements($this->Auth->user(), $report);
$report['EventReport']['content'] = $results['replacementResult']['contentWithReplacements'];
@ -296,13 +302,16 @@ class EventReportsController extends AppController
public function importReportFromUrl($event_id)
{
if (!$this->request->is('ajax')) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
if (!$this->request->is('ajax') && !$this->_isRest()) {
throw new MethodNotAllowedException(__('This function can only be reached via AJAX and via the API.'));
}
$fetcherModule = $this->EventReport->isFetchURLModuleEnabled();
if ($this->request->is('post')) {
if (empty($this->data['EventReport'])) {
$this->data = ['EventReport' => $this->data];
}
if (empty($this->data['EventReport']['url'])) {
throw new MethodNotAllowedException(__('An URL must be provided'));
throw new MethodNotAllowedException(__('A URL must be provided'));
}
$url = $this->data['EventReport']['url'];
$format = 'html';
@ -313,7 +322,6 @@ class EventReportsController extends AppController
$format = $parsed_format;
}
}
$content = $this->EventReport->downloadMarkdownFromURL($event_id, $url, $format);
$errors = [];
@ -515,6 +523,7 @@ class EventReportsController extends AppController
{
$distributionLevels = $this->EventReport->Event->Attribute->distributionLevels;
$this->set('distributionLevels', $distributionLevels);
$this->set('shortDist', $this->EventReport->Event->Attribute->shortDist);
$this->set('initialDistribution', $this->EventReport->Event->Attribute->defaultDistribution());
}
@ -559,6 +568,12 @@ class EventReportsController extends AppController
$savedReport['EventReport'][$field] = $newReport['EventReport'][$field];
}
}
$this->loadModel('AnalystData');
foreach ($this->AnalystData::ANALYST_DATA_TYPES as $type) {
if (!empty($newReport['EventReport'][$type])) {
$savedReport['EventReport'][$type] = $newReport['EventReport'][$type];
}
}
return $savedReport;
}
}

View File

@ -14,16 +14,16 @@ class EventsController extends AppController
);
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
'order' => array(
'Event.timestamp' => 'DESC'
),
'contain' => array(
'Org' => array('fields' => array('id', 'name', 'uuid')),
'Orgc' => array('fields' => array('id', 'name', 'uuid')),
'SharingGroup' => array('fields' => array('id', 'name', 'uuid'))
)
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
'order' => array(
'Event.timestamp' => 'DESC'
),
'contain' => array(
'Org' => array('fields' => array('id', 'name', 'uuid')),
'Orgc' => array('fields' => array('id', 'name', 'uuid')),
'SharingGroup' => array('fields' => array('id', 'name', 'uuid'))
)
);
// private
@ -61,46 +61,14 @@ class EventsController extends AppController
'publish_timestamp'
];
public $paginationFunctions = array('index', 'proposalEventIndex');
public function beforeFilter()
{
parent::beforeFilter();
// what pages are allowed for non-logged-in users
$this->Auth->allow('xml');
$this->Auth->allow('csv');
$this->Auth->allow('nids');
$this->Auth->allow('hids_md5');
$this->Auth->allow('hids_sha1');
$this->Auth->allow('text');
$this->Auth->allow('restSearch');
$this->Auth->allow('stix');
$this->Auth->allow('stix2');
$this->Security->unlockedActions[] = 'viewEventAttributes';
// TODO Audit, activate logable in a Controller
if (count($this->uses) && $this->{$this->modelClass}->Behaviors->attached('SysLogLogable')) {
$this->{$this->modelClass}->setUserData($this->activeUser);
}
// convert uuid to id if present in the url, and overwrite id field
if (isset($this->request->params->query['uuid'])) {
$params = array(
'conditions' => array('Event.uuid' => $this->params->query['uuid']),
'recursive' => 0,
'fields' => 'Event.id'
);
$result = $this->Event->find('first', $params);
if (isset($result['Event']) && isset($result['Event']['id'])) {
$id = $result['Event']['id'];
$this->params->addParams(array('pass' => array($id))); // FIXME find better way to change id variable if uuid is found. params->url and params->here is not modified accordingly now
}
}
// if not admin or own org, check private as well..
if (!$this->_isSiteAdmin() && in_array($this->request->action, $this->paginationFunctions, true)) {
if (!$this->_isSiteAdmin() && in_array($this->request->action, ['index', 'proposalEventIndex'], true)) {
$conditions = $this->Event->createEventConditions($this->Auth->user());
if ($this->userRole['perm_sync'] && $this->Auth->user('Server')['push_rules']) {
$conditions['AND'][] = $this->Event->filterRulesToConditions($this->Auth->user('Server')['push_rules']);
@ -497,6 +465,11 @@ class EventsController extends AppController
continue 2;
}
$pieces = is_array($v) ? $v : explode('|', $v);
$isANDed = false;
if (count($pieces) == 1 && strpos($pieces[0], '&') !== false) {
$pieces = explode('&', $pieces[0]);
$isANDed = count($pieces) > 1;
}
$filterString = "";
$expectOR = false;
$tagRules = [];
@ -563,10 +536,19 @@ class EventsController extends AppController
}
if (!empty($tagRules['include'])) {
$include = $this->Event->EventTag->find('column', array(
'conditions' => array('EventTag.tag_id' => $tagRules['include']),
'fields' => ['EventTag.event_id'],
));
if ($isANDed) {
$include = $this->Event->EventTag->find('column', array(
'conditions' => ['EventTag.tag_id' => $tagRules['include']],
'fields' => ['EventTag.event_id'],
'group' => ['EventTag.event_id'],
'having' => ['COUNT(*) =' => count($tagRules['include'])],
));
} else {
$include = $this->Event->EventTag->find('column', array(
'conditions' => array('EventTag.tag_id' => $tagRules['include']),
'fields' => ['EventTag.event_id'],
));
}
if (!empty($include)) {
$this->paginate['conditions']['AND'][] = 'Event.id IN (' . implode(",", $include) . ')';
} else {
@ -755,7 +737,8 @@ class EventsController extends AppController
if ($nothing) {
$this->paginate['conditions']['AND'][] = ['Event.id' => -1]; // do not fetch any event
}
$this->Event->includeAnalystData = isset($passedArgs['includeAnalystData']) ? $passedArgs['includeAnalystData'] : false;
$this->paginate['includeAnalystData'] = isset($passedArgs['includeAnalystData']) ? $passedArgs['includeAnalystData'] : false;
$events = $this->paginate();
if (count($events) === 1 && isset($this->passedArgs['searchall'])) {
@ -811,6 +794,7 @@ class EventsController extends AppController
$rules = [
'contain' => ['EventTag'],
'fields' => array_keys($fieldNames),
'includeAnalystData' => isset($passedArgs['includeAnalystData']) ? $passedArgs['includeAnalystData'] : false,
];
}
if (isset($passedArgs['sort']) && isset($fieldNames[$passedArgs['sort']])) {
@ -1227,6 +1211,9 @@ class EventsController extends AppController
}
}
$this->Event->Attribute->includeAnalystData = true;
$this->Event->Attribute->includeAnalystDataRecursive = true;
if (isset($filters['focus'])) {
$this->set('focus', $filters['focus']);
}
@ -1411,7 +1398,8 @@ class EventsController extends AppController
$exception = false;
$warningTagConflicts = array();
$filters = $this->_harvestParameters($filterData, $exception);
$analystData = $this->Event->attachAnalystData($event['Event']);
$event['Event'] = array_merge($event['Event'], $analystData);
$emptyEvent = (empty($event['Object']) && empty($event['Attribute']));
$this->set('emptyEvent', $emptyEvent);
@ -1485,7 +1473,6 @@ class EventsController extends AppController
$containsProposals = true;
}
}
foreach ($event['Object'] as $k => $object) {
$modDate = date("Y-m-d", $object['timestamp']);
$modificationMap[$modDate] = !isset($modificationMap[$modDate])? 1 : $modificationMap[$modDate] + 1;
@ -1517,7 +1504,6 @@ class EventsController extends AppController
}
}
}
if ($containsProposals && $this->__canPublishEvent($event, $user)) {
$mess = $this->Session->read('Message');
if (empty($mess)) {
@ -1691,8 +1677,8 @@ class EventsController extends AppController
}
$namedParams = $this->request->params['named'];
if ($this->_isRest()) {
$conditions['includeAnalystData'] = true;
$conditions['includeAttachments'] = isset($namedParams['includeAttachments']) ? $namedParams['includeAttachments'] : true;
} else {
$conditions['includeAllTags'] = true;
@ -1786,7 +1772,6 @@ class EventsController extends AppController
} else {
$user = $this->Auth->user();
}
$results = $this->Event->fetchEvent($user, $conditions);
if (empty($results)) {
throw new NotFoundException(__('Invalid event'));
@ -2365,7 +2350,13 @@ class EventsController extends AppController
}
$isXml = $ext === 'xml';
$data = FileAccessTool::readFromFile($file['tmp_name'], $file['size']);
$matches = null;
$tmp_name = $file['tmp_name'];
if (preg_match_all('/[\w\/\-\.]*/', $tmp_name, $matches) && file_exists($file['tmp_name'])) {
$data = FileAccessTool::readFromFile($matches[0][0], $file['size']);
} else {
throw new NotFoundException(__('Invalid file.'));
}
} else {
throw new MethodNotAllowedException(__('No file uploaded.'));
}
@ -2374,7 +2365,6 @@ class EventsController extends AppController
&& (isset($this->request->data['Event']['takeownership']) && $this->request->data['Event']['takeownership'] == 1);
$publish = $this->request->data['Event']['publish'] ?? false;
try {
$results = $this->Event->addMISPExportFile($this->Auth->user(), $data, $isXml, $takeOwnership, $publish);
} catch (Exception $e) {
@ -2404,24 +2394,41 @@ class EventsController extends AppController
}
if (isset($this->params['named']['distribution'])) {
$distribution = intval($this->params['named']['distribution']);
if (array_key_exists($distribution, $distributionLevels)) {
$initialDistribution = $distribution;
} else {
throw new MethodNotAllowedException(__('Wrong distribution level'));
if (!array_key_exists($distribution, $distributionLevels)) {
throw new BadRequestException(__('Wrong distribution level'));
}
} else {
$distribution = $initialDistribution;
}
$sharingGroupId = null;
if ($initialDistribution == 4) {
if ($distribution == 4) {
if (!isset($this->params['named']['sharing_group_id'])) {
throw new MethodNotAllowedException(__('The sharing group id is needed when the distribution is set to 4 ("Sharing group").'));
throw new BadRequestException(__('The sharing group id is needed when the distribution is set to 4 ("Sharing group").'));
}
$sharingGroupId = intval($this->params['named']['sharing_group_id']);
if (!array_key_exists($sharingGroupId, $sgs)) {
throw new MethodNotAllowedException(__('Please select a valid sharing group id.'));
throw new BadRequestException(__('Please select a valid sharing group id.'));
}
}
$clusterDistribution = $initialDistribution;
$clusterSharingGroupId = null;
if (isset($this->params['named']['galaxies_as_tags'])) {
$galaxies_as_tags = $this->params['named']['galaxies_as_tags'];
if (isset($this->params['name']['cluster_distribution'])) {
$clusterDistribution = intval($this->params['named']['cluster_distribution']);
if (!array_key_exists($clusterDistribution, $distributionLevels)) {
throw new BadRequestException(__('Wrong cluster distribution level'));
}
if ($clusterDistribution == 4) {
if (!isset($this->params['named']['cluster_sharing_group_id'])) {
throw new BadRequestException(__('The cluster sharing group id is needed when the cluster distribution is set to 4 ("Sharing group").'));
}
$clusterSharingGroupId = intval($this->params['named']['cluster_sharing_group_id']);
if (!array_key_exists($clusterSharingGroupId, $sgs)) {
throw new BadRequestException(__('Please select a valid cluster sharing group id.'));
}
}
}
}
if (isset($this->params['named']['debugging'])) {
$debug = $this->params['named']['debugging'];
@ -2433,9 +2440,11 @@ class EventsController extends AppController
$stix_version,
'uploaded_stix_file.' . ($stix_version == '1' ? 'xml' : 'json'),
$publish,
$initialDistribution,
$distribution,
$sharingGroupId,
$galaxies_as_tags,
$clusterDistribution,
$clusterSharingGroupId,
$debug
);
if (is_numeric($result)) {
@ -2448,8 +2457,8 @@ class EventsController extends AppController
} else {
return $this->RestResponse->saveFailResponse('Events', 'upload_stix', false, $result, $this->response->type());
}
} else {
$original_file = !empty($this->data['Event']['original_file']) ? $this->data['Event']['stix']['name'] : '';
} else { // not REST request
$originalFile = !empty($this->data['Event']['original_file']) ? $this->data['Event']['stix']['name'] : '';
if (isset($this->data['Event']['stix']) && $this->data['Event']['stix']['size'] > 0 && is_uploaded_file($this->data['Event']['stix']['tmp_name'])) {
$filePath = FileAccessTool::createTempFile();
if (!move_uploaded_file($this->data['Event']['stix']['tmp_name'], $filePath)) {
@ -2462,11 +2471,13 @@ class EventsController extends AppController
$this->Auth->user(),
$filePath,
$stix_version,
$original_file,
$originalFile,
$this->data['Event']['publish'],
$this->data['Event']['distribution'],
$this->data['Event']['sharing_group_id'] ?? null,
$this->data['Event']['galaxies_handling'],
$this->data['Event']['galaxies_handling'] ?? false,
$this->data['Event']['cluster_distribution'] ?? 0,
$this->data['Event']['cluster_sharing_group_id'] ?? null,
$debug
);
if (is_numeric($result)) {
@ -2692,7 +2703,7 @@ class EventsController extends AppController
$this->request->data = $this->request->data['Event'];
}
$eventToSave = $event;
$capturedObjects = ['Attribute', 'Object', 'Tag', 'Galaxy', 'EventReport'];
$capturedObjects = ['Attribute', 'Object', 'Tag', 'Galaxy', 'EventReport', 'Note', 'Opinion', 'Relationship',];
foreach ($capturedObjects as $objectType) {
if (!empty($this->request->data[$objectType])) {
if (!empty($regenerateUUIDs)) {
@ -3082,9 +3093,9 @@ class EventsController extends AppController
$errors['Module'] = 'Module failure.';
}
} else {
$errors['failed_servers'] = $result;
$lastResult = array_pop($result);
$resultString = (count($result) > 0) ? implode(', ', $result) . ' and ' . $lastResult : $lastResult;
$errors['failed_servers'] = $result;
$message = __('Event published but not pushed to %s, re-try later. If the issue persists, make sure that the correct sync user credentials are used for the server link and that the sync user on the remote server has authentication privileges.', $resultString);
}
} else {
@ -4131,7 +4142,13 @@ class EventsController extends AppController
}
}
$this->Event->Attribute->fetchRelated($this->Auth->user(), $resultArray);
$typeCategoryMapping = array();
$typeCategoryMapping = [
'ip-src/ip-dst' => [
'Network activity' => 'Network activity',
'Payload delivery' => 'Payload delivery',
'External analysis' => 'External analysis'
],
];
foreach ($this->Event->Attribute->categoryDefinitions as $k => $cat) {
foreach ($cat['types'] as $type) {
$typeCategoryMapping[$type][$k] = $k;
@ -4373,12 +4390,12 @@ class EventsController extends AppController
$id = $event['Event']['id'];
$exports = array(
'json' => array(
'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json',
'url' => $this->baseurl . '/events/restSearch/json/includeAnalystData:1/eventid:' . $id . '.json',
'text' => __('MISP JSON (metadata + all attributes)'),
'requiresPublished' => false,
'checkbox' => true,
'checkbox_text' => __('Encode Attachments'),
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json',
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/includeAnalystData:1/eventid:' . $id . '.json',
'checkbox_default' => true,
),
'xml' => array(
@ -4832,16 +4849,18 @@ class EventsController extends AppController
public function updateGraph($id, $type = 'event')
{
$user = $this->_closeSession();
$validTools = array('event', 'galaxy', 'tag');
if (!in_array($type, $validTools, true)) {
throw new MethodNotAllowedException(__('Invalid type.'));
}
$this->loadModel('Taxonomy');
$this->loadModel('GalaxyCluster');
App::uses('CorrelationGraphTool', 'Tools');
$grapher = new CorrelationGraphTool();
$data = $this->request->is('post') ? $this->request->data : array();
$grapher->construct($this->Event, $this->Taxonomy, $this->GalaxyCluster, $user, $data);
$grapher = new CorrelationGraphTool($this->Event, $this->Taxonomy, $this->GalaxyCluster, $user, $data);
$json = $grapher->buildGraphJson($id, $type);
array_walk_recursive($json, function (&$item, $key) {
if (!mb_detect_encoding($item, 'utf-8', true)) {

View File

@ -74,6 +74,8 @@ class FeedsController extends AppController
);
}
}
$loggedUser = $this->Auth->user();
$this->loadModel('TagCollection');
$this->CRUD->index([
'filters' => [
@ -92,7 +94,7 @@ class FeedsController extends AppController
'source_format'
],
'conditions' => $conditions,
'afterFind' => function (array $feeds) {
'afterFind' => function (array $feeds) use ($loggedUser) {
if ($this->_isSiteAdmin()) {
$feeds = $this->Feed->attachFeedCacheTimestamps($feeds);
}
@ -106,6 +108,19 @@ class FeedsController extends AppController
}
}
foreach ($feeds as &$feed) {
if (!empty($feed['Feed']['tag_collection_id'])) {
$tagCollection = $this->TagCollection->fetchTagCollection($loggedUser, [
'conditions' => [
'TagCollection.id' => $feed['Feed']['tag_collection_id'],
]
]);
if (!empty($tagCollection)) {
$feed['TagCollection'] = $tagCollection;
}
}
}
return $feeds;
}
]);
@ -294,6 +309,10 @@ class FeedsController extends AppController
}
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->loadModel('TagCollection');
$tagCollections = $this->TagCollection->fetchTagCollection($this->Auth->user());
$tagCollections = Hash::combine($tagCollections, '{n}.TagCollection.id', '{n}.TagCollection.name');
$tagCollections[0] = 'None';
$this->loadModel('Server');
$allTypes = $this->Server->getAllTypes();
@ -304,6 +323,7 @@ class FeedsController extends AppController
'order' => 'LOWER(name)'
)),
'tags' => $tags,
'tag_collections' => $tagCollections,
'feedTypes' => $this->Feed->getFeedTypesOptions(),
'sharingGroups' => $sharingGroups,
'distributionLevels' => $distributionLevels,
@ -340,6 +360,7 @@ class FeedsController extends AppController
'distribution',
'sharing_group_id',
'tag_id',
'tag_collection_id',
'event_id',
'publish',
'delta_merge',
@ -442,8 +463,17 @@ class FeedsController extends AppController
if (empty(Configure::read('Security.disable_local_feed_access'))) {
$inputSources['local'] = 'Local';
}
$tags = $this->Event->EventTag->Tag->find('all', [
'recursive' => -1,
'fields' => ['Tag.name', 'Tag.id'],
'order' => ['lower(Tag.name) asc']
]);
$tags = $this->Event->EventTag->Tag->find('list', array('fields' => array('Tag.name'), 'order' => array('lower(Tag.name) asc')));
$tags[0] = 'None';
$this->loadModel('TagCollection');
$tagCollections = $this->TagCollection->fetchTagCollection($this->Auth->user());
$tagCollections = Hash::combine($tagCollections, '{n}.TagCollection.id', '{n}.TagCollection.name');
$tagCollections[0] = 'None';
$this->loadModel('Server');
$allTypes = $this->Server->getAllTypes();
@ -457,6 +487,7 @@ class FeedsController extends AppController
'order' => 'LOWER(name)'
)),
'tags' => $tags,
'tag_collections' => $tagCollections,
'feedTypes' => $this->Feed->getFeedTypesOptions(),
'sharingGroups' => $sharingGroups,
'distributionLevels' => $distributionLevels,

View File

@ -202,7 +202,7 @@ class GalaxiesController extends AppController
}
$result = $this->Galaxy->save($galaxy);
if ($result) {
$message = __('Galaxy enabled');
$message = __('Galaxy %s', $enabled ? __('enabled') : __('disabled'));
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Galaxy', 'toggle', false, $this->response->type(), $message);
} else {

View File

@ -59,6 +59,8 @@ class GalaxyClustersController extends AppController
$contextConditions['GalaxyCluster.default'] = true;
} elseif ($filters['context'] == 'custom') {
$contextConditions['GalaxyCluster.default'] = false;
} elseif ($filters['context'] == 'orgc') {
$contextConditions['GalaxyCluster.orgc_id'] = $this->Auth->user('org_id');
} elseif ($filters['context'] == 'org') {
$contextConditions['GalaxyCluster.org_id'] = $this->Auth->user('org_id');
} elseif ($filters['context'] == 'deleted') {
@ -173,6 +175,8 @@ class GalaxyClustersController extends AppController
*/
public function view($id)
{
$this->GalaxyCluster->includeAnalystData = true;
$this->GalaxyCluster->includeAnalystDataRecursive = true;
$cluster = $this->GalaxyCluster->fetchIfAuthorized($this->Auth->user(), $id, 'view', $throwErrors=true, $full=true);
$tag = $this->GalaxyCluster->Tag->find('first', array(
'conditions' => array(
@ -208,6 +212,7 @@ class GalaxyClustersController extends AppController
$this->loadModel('Attribute');
$distributionLevels = $this->Attribute->distributionLevels;
$this->set('distributionLevels', $distributionLevels);
$this->set('shortDist', $this->Attribute->shortDist);
if (!$cluster['GalaxyCluster']['default'] && !$cluster['GalaxyCluster']['published'] && $cluster['GalaxyCluster']['orgc_id'] == $this->Auth->user()['org_id']) {
$this->Flash->warning(__('This cluster is not published. Users will not be able to use it'));
}

View File

@ -117,9 +117,15 @@ class LogsController extends AppController
$this->paginate['conditions']['Log.action'] = $validFilters[$this->params['named']['filter']]['values'];
}
foreach ($filters as $key => $value) {
if ($key == 'page' || $key == 'limit') { // These should not be part of the condition parameter
continue;
}
if ($key === 'created') {
$key = 'created >=';
}
if ($key == 'page' || $key == 'limit') {
continue;
}
$this->paginate['conditions']["Log.$key"] = $value;
}
$this->set('validFilters', $validFilters);

View File

@ -1410,4 +1410,34 @@ class ObjectsController extends AppController
}
return $conditions;
}
public function viewAnalystData($id, $seed = null)
{
$this->MispObject->includeAnalystDataRecursive = true;
$object = $this->MispObject->fetchObjects(
$this->Auth->user(),
[
'conditions' => $this->__objectIdToConditions($id)
]
);
if(empty($object)) {
throw new NotFoundException(__('Invalid Object.'));
} else {
$object[0]['Object'] = array_merge_recursive($object[0]['Object'], $this->MispObject->attachAnalystData($object[0]['Object']));
}
if ($this->_isRest()) {
$validFields = ['Note', 'Opinion', 'Relationship'];
$results = [];
foreach ($validFields as $field) {
if (!empty($object[0]['Object'][$field])) {
$results[$field] = $object[0]['Object'][$field];
}
}
return $this->RestResponse->viewData($results, $this->response->type());
}
$this->layout = null;
$this->set('shortDist', $this->MispObject->Attribute->shortDist);
$this->set('object', $object[0]['Object']);
$this->set('seed', $seed);
}
}

View File

@ -481,8 +481,8 @@ class OrganisationsController extends AppController
$extension = pathinfo($logo['name'], PATHINFO_EXTENSION);
$filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png');
if ($logo['size'] > 250*1024) {
$this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250kB.'));
if ($logo['size'] > 250 * 1024) {
$this->Flash->error(__('This organisation logo is too large, maximum file size allowed is 250 kB.'));
return false;
}
@ -490,12 +490,20 @@ class OrganisationsController extends AppController
$this->Flash->error(__('Invalid file extension, Only PNG and SVG images are allowed.'));
return false;
}
$imgMime = mime_content_type($logo['tmp_name']);
if ($extension === 'png' && !exif_imagetype($logo['tmp_name'])) {
$matches = null;
$tmp_name = $logo['tmp_name'];
if (preg_match_all('/[\w\/\-\.]*/', $tmp_name, $matches) && file_exists($logo['tmp_name'])) {
$tmp_name = $matches[0][0];
$imgMime = mime_content_type($tmp_name);
} else {
throw new NotFoundException(__('Invalid file.'));
}
if ($extension === 'png' && (function_exists('exif_imagetype') && !exif_imagetype($logo['tmp_name']))) {
$this->Flash->error(__('This is not a valid PNG image.'));
return false;
} else if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) {
}
if ($extension === 'svg' && !($imgMime === 'image/svg+xml' || $imgMime === 'image/svg')) {
$this->Flash->error(__('This is not a valid SVG image.'));
return false;
}
@ -505,8 +513,8 @@ class OrganisationsController extends AppController
return false;
}
if (!empty($logo['tmp_name']) && is_uploaded_file($logo['tmp_name'])) {
return move_uploaded_file($logo['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
if (!empty($tmp_name) && is_uploaded_file($tmp_name)) {
return move_uploaded_file($tmp_name, APP . 'files/img/orgs/' . $filename);
}
}

View File

@ -529,7 +529,7 @@ class ServersController extends AppController
if (!$fail) {
// say what fields are to be updated
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
$fieldList = array('id', 'url', 'push', 'pull', 'push_sightings', 'push_galaxy_clusters', 'pull_galaxy_clusters', 'push_analyst_data', 'pull_analyst_data', 'caching_enabled', 'unpublish_event', 'publish_without_email', 'remote_org_id', 'name' ,'self_signed', 'remove_missing_tags', 'cert_file', 'client_cert_file', 'push_rules', 'pull_rules', 'internal', 'skip_proxy');
$this->request->data['Server']['id'] = $id;
if (isset($this->request->data['Server']['authkey']) && "" != $this->request->data['Server']['authkey']) {
$fieldList[] = 'authkey';
@ -776,7 +776,7 @@ class ServersController extends AppController
if (!Configure::read('MISP.background_jobs')) {
$result = $this->Server->pull($this->Auth->user(), $technique, $s);
if (is_array($result)) {
$success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
$success = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled, %s analyst data pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4], $result[5]);
} else {
$error = $result;
}
@ -784,6 +784,7 @@ class ServersController extends AppController
$this->set('fails', $result[1]);
$this->set('pulledProposals', $result[2]);
$this->set('pulledSightings', $result[3]);
$this->set('pulledAnalystData', $result[5]);
} else {
$this->loadModel('Job');
$jobId = $this->Job->createJob(
@ -1769,6 +1770,7 @@ class ServersController extends AppController
$perm_sighting = isset($result['info']['perm_sighting']) ? $result['info']['perm_sighting'] : false;
$local_version = $this->Server->checkMISPVersion();
$version = explode('.', $result['info']['version']);
$uuid = isset($result['info']['uuid']) ? $result['info']['uuid'] : '?';
$mismatch = false;
$newer = false;
$parts = array('major', 'minor', 'hotfix');
@ -1804,6 +1806,7 @@ class ServersController extends AppController
'response_encoding' => isset($result['post']['content-encoding']) ? $result['post']['content-encoding'] : null,
'request_encoding' => isset($result['info']['request_encoding']) ? $result['info']['request_encoding'] : null,
'client_certificate' => $result['client_certificate'],
'uuid' => $uuid,
], 'json');
} else {
$result['status'] = 3;
@ -1887,6 +1890,8 @@ class ServersController extends AppController
'perm_sync' => (bool) $user['Role']['perm_sync'],
'perm_sighting' => (bool) $user['Role']['perm_sighting'],
'perm_galaxy_editor' => (bool) $user['Role']['perm_galaxy_editor'],
'perm_analyst_data' => (bool) $user['Role']['perm_analyst_data'],
'uuid' => $user['Role']['perm_sync'] ? Configure::read('MISP.uuid') : '-',
'request_encoding' => $this->CompressedRequestHandler->supportedEncodings(),
'filter_sightings' => true, // check if Sightings::filterSightingUuidsForPush method is supported
];
@ -2079,7 +2084,10 @@ class ServersController extends AppController
public function updateJSON()
{
$results = $this->Server->updateJSON();
$results = [];
foreach ($this->Server->updateJSON() as $type => $result) {
$results[$type] = $results['success'];
}
return $this->RestResponse->viewData($results, $this->response->type());
}

View File

@ -0,0 +1,47 @@
<?php
App::uses('AppController', 'Controller');
class SightingBlocklistsController extends AppController
{
public $components = array('Session', 'RequestHandler', 'BlockList');
public function beforeFilter()
{
parent::beforeFilter();
if (!$this->_isSiteAdmin()) {
$this->redirect('/');
}
if (Configure::check('MISP.enableSightingBlocklisting') && !Configure::read('MISP.enableSightingBlocklisting') !== false) {
$this->Flash->info(__('Sighting BlockListing is not currently enabled on this instance.'));
$this->redirect('/');
}
}
public $paginate = array(
'limit' => 60,
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
'order' => array(
'SightingBlocklist.created' => 'DESC'
),
);
public function index()
{
return $this->BlockList->index($this->_isRest());
}
public function add()
{
return $this->BlockList->add($this->_isRest());
}
public function edit($id)
{
return $this->BlockList->edit($this->_isRest(), $id);
}
public function delete($id)
{
return $this->BlockList->delete($this->_isRest(), $id);
}
}

View File

@ -357,7 +357,7 @@ class TagCollectionsController extends AppController
if (!$tagCollection) {
throw new NotFoundException(__('Invalid tag collection.'));
}
if ($this->ACL->canModifyTagCollection($this->Auth->user(), $tagCollection)) {
if (!$this->ACL->canModifyTagCollection($this->Auth->user(), $tagCollection)) {
throw new ForbiddenException(__('You dont have a permission to do that'));
}
$tagCollectionTag = $this->TagCollection->TagCollectionTag->find('first', [

View File

@ -820,6 +820,15 @@ class TagsController extends AppController
}
$message = 'Global tag ' . $existingTag['Tag']['name'] . '(' . $existingTag['Tag']['id'] . ') successfully attached to ' . $objectType . '(' . $object[$objectType]['id'] . ').';
}
$this->loadModel('Log');
$this->Log->createLogEntry(
$this->Auth->user(),
'attachTagToObject',
$objectType,
$object[$objectType]['id'],
$message,
null
);
$successes++;
} else {
$fails[] = __('Failed to attach tag to object.');
@ -896,6 +905,17 @@ class TagsController extends AppController
$result = $this->$objectType->$connectorObject->delete($existingAssociation[$connectorObject]['id']);
if ($result) {
$message = __('%s tag %s (%s) successfully removed from %s(%s).', $local ? __('Local') : __('Global'), $existingTag['Tag']['name'], $existingTag['Tag']['id'], $objectType, $object[$objectType]['id']);
$this->loadModel('Log');
$this->Log->createLogEntry(
$this->Auth->user(),
'removeTagFromObject',
$objectType,
$object[$objectType]['id'],
$message,
__(
'',
)
);
if (!$local) {
if ($objectType === 'Attribute') {
$this->Attribute->touch($object['Attribute']['id']);

View File

@ -1,5 +1,5 @@
<?php
App::uses('AppController', 'Controller', 'OTPHP\TOTP');
App::uses('AppController', 'Controller');
/**
* @property User $User
@ -1214,11 +1214,13 @@ class UsersController extends AppController
$this->Auth->constructAuthenticate();
}
// user has TOTP token, check creds and redirect to TOTP validation
if (!empty($unauth_user['User']['totp']) && !$unauth_user['User']['disabled'] && class_exists('\OTPHP\TOTP')) {
$user = $this->Auth->identify($this->request, $this->response);
if ($user && !$user['disabled']) {
$this->Session->write('otp_user', $user);
return $this->redirect('otp');
if (!Configure::read('Security.otp_disabled')) {
if (!empty($unauth_user['User']['totp']) && !$unauth_user['User']['disabled'] && class_exists('\OTPHP\TOTP')) {
$user = $this->Auth->identify($this->request, $this->response);
if ($user && !$user['disabled']) {
$this->Session->write('otp_user', $user);
return $this->redirect('otp');
}
}
}
}
@ -1811,6 +1813,7 @@ class UsersController extends AppController
$this->Flash->error(__("The required PHP libraries to support TOTP are not installed. Please contact your administrator to address this."));
$this->redirect($this->referer());
}
// only allow the users themselves to generate a TOTP secret.
// If TOTP is enforced they will be invited to generate it at first login
$user = $this->User->find('first', array(
@ -1880,8 +1883,9 @@ class UsersController extends AppController
$this->set('secret', $secret);
}
public function totp_delete($id) {
if ($this->request->is('post') || $this->request->is('delete')) {
public function totp_delete($id)
{
if ($this->request->is(['post', 'delete'])) {
$user = $this->User->find('first', array(
'conditions' => $this->__adminFetchConditions($id),
'recursive' => -1,
@ -1988,8 +1992,7 @@ class UsersController extends AppController
// shows some statistics about the instance
public function statistics($page = 'data')
{
$user = $this->Auth->user();
@session_write_close(); // loading this page can take long time, so we can close session
$user = $this->_closeSession(true); // loading this page can take long time, so we can close session
if (!$this->_isRest()) {
$pages = [
@ -2071,7 +2074,7 @@ class UsersController extends AppController
$stats['attribute_count'] = $this->User->Event->Attribute->find('count', array('conditions' => array('Attribute.deleted' => 0), 'recursive' => -1));
$stats['attribute_count_month'] = $this->User->Event->Attribute->find('count', array('conditions' => array('Attribute.timestamp >' => $this_month, 'Attribute.deleted' => 0), 'recursive' => -1));
$stats['attributes_per_event'] = round($stats['attribute_count'] / $stats['event_count']);
$stats['attributes_per_event'] = $stats['event_count'] != 0 ? round($stats['attribute_count'] / $stats['event_count']) : 0;
$stats['correlation_count'] = $this->User->Event->Attribute->Correlation->find('count', array('recursive' => -1));
@ -2082,7 +2085,7 @@ class UsersController extends AppController
$stats['org_count'] = count($orgs);
$stats['local_org_count'] = $local_orgs_count;
$stats['contributing_org_count'] = $this->User->Event->find('count', array('recursive' => -1, 'group' => array('Event.orgc_id')));
$stats['average_user_per_org'] = round($stats['user_count'] / $stats['local_org_count'], 1);
$stats['average_user_per_org'] = $stats['local_org_count'] != 0 ? round($stats['user_count'] / $stats['local_org_count'], 1) : 0;
$this->loadModel('Thread');
$stats['thread_count'] = $this->Thread->find('count', array('conditions' => array('Thread.post_count >' => 0), 'recursive' => -1));
@ -2603,6 +2606,7 @@ class UsersController extends AppController
'org_name',
'org_uuid',
'message',
'pgp',
'custom_perms',
'perm_sync',
'perm_publish',
@ -3186,10 +3190,6 @@ class UsersController extends AppController
public function forgot()
{
if (empty(Configure::read('Security.allow_password_forgotten'))) {
$this->Flash->error(__('This feature is disabled.'));
$this->redirect('/');
}
if (!empty($this->Auth->user()) && !$this->_isRest()) {
$this->Flash->info(__('You are already logged in, no need to ask for a password reset. Log out first.'));
$this->redirect('/');
@ -3215,10 +3215,6 @@ class UsersController extends AppController
public function password_reset($token)
{
if (empty(Configure::read('Security.allow_password_forgotten'))) {
$this->Flash->error(__('This feature is disabled.'));
$this->redirect('/');
}
$this->loadModel('Server');
$this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']);
$this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']);
@ -3235,7 +3231,7 @@ class UsersController extends AppController
$this->redirect('/');
}
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->request->is(['post', 'put'])) {
$abortPost = false;
return $this->__pw_change(['User' => $user], 'password_reset', $abortPost, $token, true);
}

View File

@ -0,0 +1,77 @@
<?php
App::uses('ExceptionRenderer', 'Error');
class AppExceptionRenderer extends ExceptionRenderer {
public function __construct($exception) {
$this->controller = $this->_getController($exception);
if (method_exists($this->controller, 'appError')) {
$this->controller->appError($exception);
return;
}
$method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));
$code = $exception->getCode();
$methodExists = method_exists($this, $method);
if ($exception instanceof CakeException && !$methodExists) {
$this->_customErrorLogging($exception);
$method = '_cakeError';
if (empty($template) || $template === 'internalError') {
$template = 'error500';
}
} elseif ($exception instanceof PDOException) {
$method = 'pdoError';
$template = 'pdo_error';
$code = 500;
} elseif (!$methodExists) {
$method = 'error500';
if ($code >= 400 && $code < 500) {
$method = 'error400';
}
}
$isNotDebug = !Configure::read('debug');
if ($isNotDebug && $method === '_cakeError') {
$method = 'error400';
}
if ($isNotDebug && $code == 500) {
$method = 'error500';
}
$this->template = $template;
$this->method = $method;
$this->error = $exception;
}
protected function _customErrorLogging($exception): bool
{
$message = $exception->getMessage();
$errorDetection = [
'Maximum execution time of' => 'timeout',
'Allowed memory size of' => 'out of memory'
];
$user = $this->controller->Auth->user();
$ua = env('HTTP_USER_AGENT');
$source = $this->controller->IndexFilter->isRest() ? 'API' : 'web UI';
if (strpos($ua, 'MISP ') !== false) {
$source = 'MISP sync';
}
foreach ($errorDetection as $search => $errorName) {
if (strpos($message, $search) !== false) {
$logMessage = sprintf(
'%s %s error triggered by User %s (%s) via the %s on %s.',
date('Y-m-d H:i:s'),
$errorName,
$user['id'],
$user['email'],
$source,
$this->controller->request->here()
);
file_put_contents(LOGS . 'fatal_error.log', $logMessage . PHP_EOL, FILE_APPEND);
return true;
}
}
return true;
}
}

View File

@ -191,4 +191,3 @@ class AchievementsWidget
return $result;
}
}
?>

View File

@ -35,4 +35,3 @@ class AttackWidget
return $data;
}
}
?>

View File

@ -0,0 +1,115 @@
<?php
class BenchmarkTopListWidget
{
public $title = 'Benchmark top list';
public $render = 'MultiLineChart';
public $width = 3;
public $height = 3;
public $description = 'A graph showing the top list for a given scope and field in the captured metrics.';
public $cacheLifetime = false;
public $autoRefreshDelay = 30;
public $params = array(
'days' => 'Number of days to consider for the graph. There will be a data entry for each day (assuming the benchmarking has been enabled). Defaults to returning all data.',
'weeks' => 'Number of weeks to consider for the graph. There will be a data entry for each day (assuming the benchmarking has been enabled). Defaults to returning all data.',
'months' => 'Number of months to consider for the graph. There will be a data entry for each day (assuming the benchmarking has been enabled). Defaults to returning all data.',
'scope' => 'The scope of the benchmarking refers to what was being tracked. The following scopes are valid: user, endpoint, user_agent',
'field' => 'The individual metric to be queried from the benchmark results. Valid values are: time, sql_time, sql_queries, memory, endpoint',
'average' => 'If you wish to view the averages per scope/field, set this variable to true. It will divide the result by the number of executions recorded for the scope/field combination for the given day.'
);
public $Benchmark;
public $User;
public $placeholder =
'{
"days": "30",
"scope": "endpoints",
"field": "sql_time"
}';
public function handler($user, $options = array())
{
$this->User = ClassRegistry::init('User');
$currentTime = strtotime("now");
$endOfDay = strtotime("tomorrow", $currentTime) - 1;
if (!empty($options['days'])) {
$limit = (int)($options['days']);
$delta = 'day';
} else if (!empty($options['weeks'])) {
$limit = (int)($options['weeks']);
$delta = 'week';
} else if (!empty($options['months'])) {
$limit = (int)($options['months']);
$delta = 'month';
} else {
$limit = 30;
$delta = 'day';
}
$axis_info = [
'time' => 'Total time taken (ms)',
'sql_time' => 'SQL time taken (ms)',
'sql_queries' => 'Queries (#)',
'memory' => 'Memory (MB)',
'endpoint' => 'Queries to endpoint (#)'
];
$y_axis = $axis_info[isset($options['field']) ? $options['field'] : 'time'];
$data = ['y-axis' => $y_axis];
$data['data'] = array();
// Add total users data for all timestamps
for ($i = 0; $i < $limit; $i++) {
$itemTime = strtotime('- ' . $i . $delta, $endOfDay);
$item = array();
$date = strftime('%Y-%m-%d', $itemTime);
$item = $this->getData($date, $options);
if (!empty($item)) {
$item['date'] = $date;
$data['data'][] = $item;
}
}
$keys = [];
foreach ($data['data'] as $day_data) {
foreach ($day_data as $key => $temp) {
$keys[$key] = 1;
}
}
$keys = array_keys($keys);
foreach ($data['data'] as $k => $day_data) {
foreach ($keys as $key) {
if (!isset($day_data[$key])) {
$data['data'][$k][$key] = 0;
}
}
foreach ($day_data as $key => $temp) {
$keys[$key] = 1;
}
}
return $data;
}
private function getData($time, $options)
{
$dates = [$time];
$this->Benchmark = new BenchmarkTool($this->User);
$result = $this->Benchmark->getTopList(
isset($options['scope']) ? $options['scope'] : 'endpoint',
isset($options['field']) ? $options['field'] : 'memory',
$dates,
isset($options['limit']) ? $options['limit'] : 5,
isset($options['average']) ? $options['average'] : false,
);
if (!empty($result)) {
return $result[$time];
}
return false;
}
public function checkPermissions($user)
{
if (empty($user['Role']['perm_site_admin'])) {
return false;
}
return true;
}
}

View File

@ -58,9 +58,11 @@ class EventEvolutionLineWidget
'recursive' => -1
];
$eparams = [];
$filteringOnOrg = false;
if (!empty($options['filter']) && is_array($options['filter'])) {
foreach ($this->validFilterKeys as $filterKey) {
if (!empty($options['filter'][$filterKey])) {
$filteringOnOrg = true;
if (!is_array($options['filter'][$filterKey])) {
$options['filter'][$filterKey] = [$options['filter'][$filterKey]];
}
@ -87,6 +89,9 @@ class EventEvolutionLineWidget
'conditions' => $oparams['conditions'],
'fields' => ['id']
]);
if ($filteringOnOrg) {
$eparams['conditions']['AND']['Event.orgc_id IN'] = !empty($org_ids) ? $org_ids : [-1];
}
$this->Event->virtualFields = [
'published_date' => null
];

View File

@ -37,6 +37,10 @@ class MispAdminSyncTestWidget
$colour = 'orange';
$message .= ' ' . __('No sighting access.');
}
if (empty($result['info']['perm_analyst_data'])) {
$colour = 'orange';
$message .= ' ' . __('No analyst data sync access.');
}
} else {
$colour = 'red';
$message = $syncTestErrorCodes[$result['status']];

View File

@ -137,4 +137,3 @@ class OrgContributionToplistWidget
return ['data' => $results];
}
}
?>

View File

@ -78,4 +78,3 @@ class OrganisationListWidget
return ['data' => $results];
}
}
?>

View File

@ -85,4 +85,3 @@ class OrganisationMapWidget
return $results;
}
}
?>

View File

@ -19,4 +19,3 @@ class OrgsContributorLastMonthWidget extends OrgsContributorsGeneric
return count($results) > 0;
}
}
?>

View File

@ -45,4 +45,3 @@ class OrgsContributorsGeneric
return $result;
}
}
?>

View File

@ -32,4 +32,3 @@ class OrgsUsingMitreWidget extends OrgsContributorsGeneric
return count($events) > 0;
}
}
?>

View File

@ -25,4 +25,3 @@ class OrgsUsingObjectsWidget extends OrgsContributorsGeneric
return count($eventsIds) > 0;
}
}
?>

View File

@ -243,7 +243,8 @@ class UsageDataWidget
$count = $this->Event->Attribute->find('count', [
'conditions' => $conditions,
'contain' => ['Event'],
'recursive' => -1
'recursive' => -1,
'ignoreIndexHint' => 'deleted'
]);
$this->redis->setEx('misp:dashboard:attribute_count:' . $hash, 3600, $count);
}

View File

@ -145,4 +145,3 @@ class UserContributionToplistWidget
return true;
}
}
?>

View File

@ -556,7 +556,12 @@ class AttributeValidationTool
if (!is_numeric($value) || $value < 0 || $value > 10) {
return __('The value has to be a number between 0 and 10.');
}
return true;*/
return true;*/
case 'integer':
if (is_int($value)) {
return true;
}
return __('The value has to be an integer value.');
case 'iban':
case 'bic':
case 'btc':

View File

@ -0,0 +1,178 @@
<?php
/**
* Get filter parameters from index searches
*/
class BenchmarkTool
{
/** @var Model */
public $Model;
/** @var redis */
public $redis;
/** @var retention */
private $retention = 0;
const BENCHMARK_SCOPES = ['user', 'endpoint', 'user_agent'];
const BENCHMARK_FIELDS = ['time', 'sql_time', 'sql_queries', 'memory'];
const BENCHMARK_UNITS = [
'time' => 's',
'sql_time' => 'ms',
'sql_queries' => '',
'memory' => 'MB'
];
public $namespace = 'misp:benchmark:';
function __construct(Model $model) {
$this->Model = $model;
}
public function getSettings()
{
return [
'scope' => self::BENCHMARK_SCOPES,
'field' => self::BENCHMARK_FIELDS,
'average' => [0, 1],
'aggregate' => [0, 1]
];
}
public function getUnits()
{
return self::BENCHMARK_UNITS;
}
public function startBenchmark()
{
$start_time = microtime(true);
$this->redis = $this->Model->setupRedis();
$this->retention = Configure::check('Plugin.benchmark_retention') ? Configure::read('Plugin.benchmark_retention') : 0;
return $start_time;
}
public function stopBenchmark(array $options)
{
$start_time = $options['start_time'];
if (!empty($options['user'])) {
$sql = $this->Model->getDataSource()->getLog(false, false);
$benchmarkData = [
'user' => $options['user'],
'endpoint' => $options['controller'] . '/' . $options['action'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'sql_queries' => $sql['count'],
'sql_time' => $sql['time'],
'time' => (microtime(true) - $start_time),
'memory' => (int)(memory_get_peak_usage(true) / 1024 / 1024),
//'date' => date('Y-m-d', strtotime("-3 days"))
'date' => date('Y-m-d')
];
$this->pushBenchmarkDataToRedis($benchmarkData);
} else {
$sql = $this->Model->getDataSource()->getLog(false, false);
$benchmarkData = [
'user' => 'SYSTEM',
'endpoint' => $options['controller'] . '/' . $options['action'],
'user_agent' => 'CLI',
'sql_queries' => $sql['count'],
'sql_time' => $sql['time'],
'time' => (microtime(true) - $start_time),
'memory' => (int)(memory_get_peak_usage(true) / 1024 / 1024),
//'date' => date('Y-m-d', strtotime("-3 days"))
'date' => date('Y-m-d')
];
$this->pushBenchmarkDataToRedis($benchmarkData);
}
}
private function pushBenchmarkDataToRedis($benchmarkData)
{
$this->redis = $this->Model->setupRedis();
$this->redis->pipeline();
$this->redis->sAdd(
$this->namespace . 'days',
$benchmarkData['date']
);
foreach (self::BENCHMARK_SCOPES as $scope) {
$this->redis->sAdd(
$this->namespace . $scope . ':list',
$benchmarkData[$scope]
);
$this->redis->zIncrBy(
$this->namespace . $scope . ':count:' . $benchmarkData['date'],
1,
$benchmarkData[$scope]
);
foreach (self::BENCHMARK_FIELDS as $field) {
$this->redis->zIncrBy(
$this->namespace . $scope . ':' . $field . ':' . $benchmarkData['date'],
$benchmarkData[$field],
$benchmarkData[$scope]
);
}
$this->redis->zIncrBy(
$this->namespace . $scope . ':endpoint:' . $benchmarkData['date'] . ':' . $benchmarkData['user'],
1,
$benchmarkData['endpoint']
);
}
$this->redis->exec();
}
public function getTopList(string $scope, string $field, array $days = [], $limit = 10, $average = false, $aggregate = false)
{
if (empty($this->redis)) {
$this->redis = $this->Model->setupRedis();
}
$results = [];
if (is_string($days)) {
$days = [$days];
}
foreach ($days as $day) {
$temp = $this->redis->zrevrange($this->namespace . $scope . ':' . $field . ':' . $day, 0, $limit, true);
foreach ($temp as $k => $v) {
if ($average) {
$divisor = $this->redis->zscore($this->namespace . $scope . ':count:' . $day, $k);
if ($aggregate) {
$results['aggregate'][$k] = empty($results['aggregate'][$k]) ? ($v / $divisor) : ($results['aggregate'][$k] + ($v / $divisor));
} else {
$results[$day][$k] = (int)($v / $divisor);
}
} else {
if ($aggregate) {
$results['aggregate'][$k] = empty($results['aggregate'][$k]) ? $v : ($results['aggregate'][$k] + $v);
} else {
$results[$day][$k] = $v;
}
}
}
}
if ($aggregate && $average) {
$count_days = count($days);
foreach ($results['aggregate'] as $k => $result) {
$results['aggregate'][$k] = (int)($result / $count_days);
}
}
return $results;
}
public function getAllTopLists(array $days = null, $limit = 10, $average = false, $aggregate = false, $scope_filter = [])
{
if (empty($this->redis)) {
$this->redis = $this->Model->setupRedis();
}
if ($days === null) {
$days = $this->redis->smembers($this->namespace . 'days');
}
foreach (self::BENCHMARK_SCOPES as $scope) {
if (empty($scope_filter) || in_array($scope, $scope_filter)) {
foreach (self::BENCHMARK_FIELDS as $field) {
$results[$scope][$field] = $this->getTopList($scope, $field, $days, $limit, $average, $aggregate);
}
}
}
return $results;
}
}

View File

@ -1,4 +1,6 @@
<?php
App::uses('OrgImgHelper', 'View/Helper');
class CorrelationGraphTool
{
private $__lookupTables = array();
@ -9,20 +11,24 @@
/** @var Taxonomy */
private $__taxonomyModel;
private $__galaxyClusterModel = false;
private $__user = false;
private $__json = array();
/** @var User */
private $__user;
/** @var array */
private $data;
private $orgImgHelper;
public function construct(Event $eventModel, $taxonomyModel, $galaxyClusterModel, $user, $json)
public function __construct(Event $eventModel, $taxonomyModel, $galaxyClusterModel, array $user, array $data)
{
$this->__eventModel = $eventModel;
$this->__taxonomyModel = $taxonomyModel;
$this->__galaxyClusterModel = $galaxyClusterModel;
$this->__user = $user;
$this->__json = $json;
$this->data = $data;
$this->__lookupTables = array(
'analysisLevels' => $this->__eventModel->analysisLevels,
'distributionLevels' => $this->__eventModel->Attribute->distributionLevels
);
'analysisLevels' => $eventModel->analysisLevels,
'distributionLevels' => $eventModel->Attribute->distributionLevels
);
$this->orgImgHelper = new OrgImgHelper(new View());
return true;
}
@ -38,7 +44,7 @@
'sgReferenceOnly' => true,
));
if (empty($event)) {
return $this->__json;
return $this->data;
}
$this->cleanLinks();
$event[0]['Event']['Orgc'] = $event[0]['Orgc'];
@ -75,7 +81,7 @@
public function buildGraphJson($id, $type = 'event', $action = 'create')
{
if ($action == 'delete') {
return $this->__json;
return $this->data;
}
switch ($type) {
case 'event':
@ -88,13 +94,13 @@
$this->__expandTag($id);
break;
}
return $this->__json;
return $this->data;
}
private function __deleteObject($id)
{
$this->cleanLinks();
return $this->__json;
return $this->data;
}
private function __handleObjects($objects, $anchor_id, $full = false)
@ -193,8 +199,8 @@
private function __expandGalaxy($id)
{
if (!empty($this->__json['nodes'])) {
foreach ($this->__json['nodes'] as $k => $node) {
if (!empty($this->data['nodes'])) {
foreach ($this->data['nodes'] as $k => $node) {
if ($node['type'] == 'galaxy' && $node['id'] == $id) {
$current_galaxy_id = $k;
$tag_name = $node['tag_name'];
@ -205,7 +211,7 @@
$current_galaxy_id = $this->__addGalaxy($id);
}
$this->cleanLinks();
$events = $this->__eventModel->EventTag->Tag->fetchSimpleEventsForTag($this->__json['nodes'][$current_galaxy_id]['tag_name'], $this->__user, true);
$events = $this->__eventModel->EventTag->Tag->fetchSimpleEventsForTag($this->data['nodes'][$current_galaxy_id]['tag_name'], $this->__user, true);
foreach ($events as $event) {
$current_event_id = $this->__createNode('event', $event);
$this->__addLink($current_event_id, $current_galaxy_id);
@ -229,7 +235,7 @@
{
$link = $this->graphJsonContainsLink($from_id, $to_id);
if ($link === false) {
$this->__json['links'][] = array('source' => $from_id, 'target' => $to_id, 'linkDistance' => $linkDistance);
$this->data['links'][] = array('source' => $from_id, 'target' => $to_id, 'linkDistance' => $linkDistance);
}
}
@ -240,7 +246,7 @@
if ($from_uuid == $to_uuid) {
return false;
}
foreach ($this->__json['nodes'] as $k => $node) {
foreach ($this->data['nodes'] as $k => $node) {
if ($node['uuid'] === $from_uuid) {
$from_id = $k;
}
@ -277,10 +283,9 @@
);
break;
case 'event':
if ($this->orgImgExists($data['Orgc']['name'])) {
$image = Configure::read('MISP.baseurl') . '/img/orgs/' . h($data['Orgc']['name']) . '.png';
} else {
$image = Configure::read('MISP.baseurl') . '/img/orgs/MISP.png';
$orgImage = $this->orgImgHelper->getOrgLogoAsBase64($data['Orgc']);
if ($orgImage === null) {
$orgImage = Configure::read('MISP.baseurl') . '/img/misp-org.png';
}
$node = array(
'unique_id' => 'event-' . $data['id'],
@ -289,7 +294,7 @@
'id' => $data['id'],
'expanded' => $expand,
'uuid' => $data['uuid'],
'image' => $image,
'image' => $orgImage,
'info' => $data['info'],
'org' => $data['Orgc']['name'],
'analysis' => $this->__lookupTables['analysisLevels'][$data['analysis']],
@ -345,11 +350,11 @@
);
break;
}
$this->__json['nodes'][] = $node;
$current_id = count($this->__json['nodes'])-1;
$this->data['nodes'][] = $node;
$current_id = count($this->data['nodes'])-1;
} else {
if ($expand) {
$this->__json['nodes'][$current_id]['expanded'] = 1;
$this->data['nodes'][$current_id]['expanded'] = 1;
}
}
return $current_id;
@ -357,11 +362,11 @@
public function cleanLinks()
{
if (isset($this->__json['nodes']) && isset($this->__json['links'])) {
if (isset($this->data['nodes']) && isset($this->data['links'])) {
$links = array();
foreach ($this->__json['links'] as $link) {
foreach ($this->data['links'] as $link) {
$temp = array();
foreach ($this->__json['nodes'] as $k => $node) {
foreach ($this->data['nodes'] as $k => $node) {
if ($link['source'] == $node) {
$temp['source'] = $k;
}
@ -372,32 +377,24 @@
$temp['linkDistance'] = $link['linkDistance'];
$links[] = $temp;
}
$this->__json['links'] = $links;
$this->data['links'] = $links;
} else {
if (!isset($this->__json['links'])) {
$this->__json['links'] = array();
if (!isset($this->data['links'])) {
$this->data['links'] = array();
}
if (!isset($this->__json['nodes'])) {
$this->__json['nodes'] = array();
if (!isset($this->data['nodes'])) {
$this->data['nodes'] = array();
}
}
return true;
}
public function orgImgExists($org)
{
if (file_exists(APP . 'webroot' . DS . 'img' . DS . 'orgs' . DS . $org . '.png')) {
return true;
}
return false;
}
public function graphJsonContains($type, $element)
{
if (!isset($this->__json['nodes'])) {
if (!isset($this->data['nodes'])) {
return false;
}
foreach ($this->__json['nodes'] as $k => $node) {
foreach ($this->data['nodes'] as $k => $node) {
if ($type == 'event' && $node['type'] == 'event' && $node['id'] == $element['id']) {
return $k;
}
@ -418,10 +415,10 @@
}
public function graphJsonContainsLink($id1, $id2)
{
if (!isset($this->__json['links'])) {
if (!isset($this->data['links'])) {
return false;
}
foreach ($this->__json['links'] as $k => $link) {
foreach ($this->data['links'] as $k => $link) {
if (($link['source'] == $id1 && $link['target'] == $id2) || ($link['source'] == $id2 && $link['target'] == $id1)) {
return $k;
}

View File

@ -6,8 +6,12 @@ class CurlClient extends HttpSocketExtended
/** @var resource */
private $ch;
/** @var int */
private $timeout = 30;
/**
* Maximum time the transfer is allowed to complete in seconds
* 300 seconds is recommended timeout for MISP servers
* @var int
*/
private $timeout = 300;
/** @var string|null */
private $caFile;
@ -168,6 +172,7 @@ class CurlClient extends HttpSocketExtended
return;
}
$this->proxy = compact('host', 'port', 'method', 'user', 'pass');
$this->defaultOptions = $this->generateDefaultOptions(); // regenerate default options in case proxy setting is changed
}
/**
@ -198,7 +203,7 @@ class CurlClient extends HttpSocketExtended
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
}
$options = $this->defaultOptions;
$options = $this->defaultOptions; // this will copy default options
$options[CURLOPT_URL] = $url;
$options[CURLOPT_CUSTOMREQUEST] = $method;
@ -206,6 +211,10 @@ class CurlClient extends HttpSocketExtended
$options[CURLOPT_POSTFIELDS] = $query;
}
if ($method === 'HEAD') {
$options[CURLOPT_NOBODY] = true;
}
if (!empty($request['header'])) {
$headers = [];
foreach ($request['header'] as $key => $value) {
@ -235,7 +244,6 @@ class CurlClient extends HttpSocketExtended
}
return $len;
};
if (!curl_setopt_array($this->ch, $options)) {
throw new \RuntimeException('curl error: Could not set options');
}
@ -272,16 +280,6 @@ class CurlClient extends HttpSocketExtended
*/
private function constructResponse($body, array $headers, $code)
{
if (isset($responseHeaders['content-encoding']) && $responseHeaders['content-encoding'] === 'zstd') {
if (!function_exists('zstd_uncompress')) {
throw new SocketException('Response is zstd encoded, but PHP do not support zstd decoding.');
}
$body = zstd_uncompress($body);
if ($body === false) {
throw new SocketException('Could not decode zstd encoded response.');
}
}
$response = new HttpSocketResponseExtended();
$response->code = $code;
$response->body = $body;
@ -320,7 +318,7 @@ class CurlClient extends HttpSocketExtended
CURLOPT_RETURNTRANSFER => true, // Should cURL return or print out the data? (true = return, false = print)
CURLOPT_HEADER => false, // Include header in result?
CURLOPT_TIMEOUT => $this->timeout, // Timeout in seconds
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP, // be sure that only HTTP and HTTPS protocols are enabled,
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS | CURLPROTO_HTTP, // be sure that only HTTP and HTTPS protocols are enabled
];
if ($this->caFile) {
@ -336,7 +334,7 @@ class CurlClient extends HttpSocketExtended
}
if ($this->compress) {
$options[CURLOPT_ACCEPT_ENCODING] = $this->supportedEncodings();
$options[CURLOPT_ACCEPT_ENCODING] = ''; // empty string means all encodings supported by curl
}
if ($this->allowSelfSigned) {
@ -353,25 +351,4 @@ class CurlClient extends HttpSocketExtended
return $options;
}
/**
* @return string
*/
private function supportedEncodings()
{
$encodings = [];
// zstd is not supported by curl itself, but add support if PHP zstd extension is installed
if (function_exists('zstd_uncompress')) {
$encodings[] = 'zstd';
}
// brotli and gzip is supported by curl itself if it is compiled with these features
$info = curl_version();
if (defined('CURL_VERSION_BROTLI') && $info['features'] & CURL_VERSION_BROTLI) {
$encodings[] = 'br';
}
if ($info['features'] & CURL_VERSION_LIBZ) {
$encodings[] = 'gzip, deflate';
}
return implode(', ', $encodings);
}
}

View File

@ -24,7 +24,7 @@ class HttpSocketHttpException extends Exception
$message .= " for URL $url";
}
if ($response->body) {
$message .= ': ' . substr($response->body, 0, 100);
$message .= ': ' . substr(ltrim($response->body), 0, 100);
}
parent::__construct($message, (int)$response->code);
@ -114,10 +114,15 @@ class HttpSocketResponseExtended extends HttpSocketResponse
*/
public function json()
{
if (strlen($this->body) === 0) {
throw new HttpSocketJsonException('Could not parse empty response as JSON.', $this);
}
try {
return JsonTool::decode($this->body);
} catch (Exception $e) {
throw new HttpSocketJsonException('Could not parse response as JSON.', $this, $e);
$contentType = $this->getHeader('content-type');
throw new HttpSocketJsonException("Could not parse HTTP response as JSON. Received Content-Type $contentType.", $this, $e);
}
}
}

View File

@ -21,7 +21,7 @@ class JSONConverterTool
public static function convertObject($object, $isSiteAdmin = false, $raw = false)
{
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event', 'CryptographicKey');
$toRearrange = array('SharingGroup', 'Attribute', 'ShadowAttribute', 'Event', 'CryptographicKey', 'Note', 'Opinion', 'Relationship');
foreach ($toRearrange as $element) {
if (isset($object[$element])) {
$object['Object'][$element] = $object[$element];
@ -40,7 +40,7 @@ class JSONConverterTool
public static function convert($event, $isSiteAdmin=false, $raw = false)
{
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport', 'CryptographicKey');
$toRearrange = array('Org', 'Orgc', 'SharingGroup', 'Attribute', 'ShadowAttribute', 'RelatedAttribute', 'RelatedEvent', 'Galaxy', 'Object', 'EventReport', 'CryptographicKey', 'Note', 'Opinion', 'Relationship');
foreach ($toRearrange as $object) {
if (isset($event[$object])) {
$event['Event'][$object] = $event[$object];
@ -69,6 +69,16 @@ class JSONConverterTool
}
}
if (isset($event['Event']['Note'])) {
$event['Event']['Note'] = self::__cleanAnalystData($event['Event']['Note']);
}
if (isset($event['Event']['Opinion'])) {
$event['Event']['Opinion'] = self::__cleanAnalystData($event['Event']['Opinion']);
}
if (isset($event['Event']['Relationship'])) {
$event['Event']['Relationship'] = self::__cleanAnalystData($event['Event']['Relationship']);
}
// cleanup the array from things we do not want to expose
$tempSightings = array();
if (!empty($event['Sighting'])) {
@ -209,6 +219,17 @@ class JSONConverterTool
return $objects;
}
private function __cleanAnalystData($data)
{
foreach ($data as $k => $entry) {
if (empty($entry['SharingGroup'])) {
unset($data[$k]['SharingGroup']);
}
}
$data = array_values($data);
return $data;
}
public static function arrayPrinter($array, $root = true)
{
if (is_array($array)) {

View File

@ -0,0 +1,245 @@
<?php
declare(strict_types=1);
class LanguageRFC5646Tool
{
const RFC5646_LANGUAGE = [
'af' => 'Afrikaans',
'af-ZA' => 'Afrikaans (South Africa)',
'ar' => 'Arabic',
'ar-AE' => 'Arabic (U.A.E.)',
'ar-BH' => 'Arabic (Bahrain)',
'ar-DZ' => 'Arabic (Algeria)',
'ar-EG' => 'Arabic (Egypt)',
'ar-IQ' => 'Arabic (Iraq)',
'ar-JO' => 'Arabic (Jordan)',
'ar-KW' => 'Arabic (Kuwait)',
'ar-LB' => 'Arabic (Lebanon)',
'ar-LY' => 'Arabic (Libya)',
'ar-MA' => 'Arabic (Morocco)',
'ar-OM' => 'Arabic (Oman)',
'ar-QA' => 'Arabic (Qatar)',
'ar-SA' => 'Arabic (Saudi Arabia)',
'ar-SY' => 'Arabic (Syria)',
'ar-TN' => 'Arabic (Tunisia)',
'ar-YE' => 'Arabic (Yemen)',
'az' => 'Azeri (Latin)',
'az-AZ' => 'Azeri (Latin) (Azerbaijan)',
'az-Cyrl-AZ' => 'Azeri (Cyrillic) (Azerbaijan)',
'be' => 'Belarusian',
'be-BY' => 'Belarusian (Belarus)',
'bg' => 'Bulgarian',
'bg-BG' => 'Bulgarian (Bulgaria)',
'bs-BA' => 'Bosnian (Bosnia and Herzegovina)',
'ca' => 'Catalan',
'ca-ES' => 'Catalan (Spain)',
'cs' => 'Czech',
'cs-CZ' => 'Czech (Czech Republic)',
'cy' => 'Welsh',
'cy-GB' => 'Welsh (United Kingdom)',
'da' => 'Danish',
'da-DK' => 'Danish (Denmark)',
'de' => 'German',
'de-AT' => 'German (Austria)',
'de-CH' => 'German (Switzerland)',
'de-DE' => 'German (Germany)',
'de-LI' => 'German (Liechtenstein)',
'de-LU' => 'German (Luxembourg)',
'dv' => 'Divehi',
'dv-MV' => 'Divehi (Maldives)',
'el' => 'Greek',
'el-GR' => 'Greek (Greece)',
'en' => 'English',
'en-AU' => 'English (Australia)',
'en-BZ' => 'English (Belize)',
'en-CA' => 'English (Canada)',
'en-CB' => 'English (Caribbean)',
'en-GB' => 'English (United Kingdom)',
'en-IE' => 'English (Ireland)',
'en-JM' => 'English (Jamaica)',
'en-NZ' => 'English (New Zealand)',
'en-PH' => 'English (Republic of the Philippines)',
'en-TT' => 'English (Trinidad and Tobago)',
'en-US' => 'English (United States)',
'en-ZA' => 'English (South Africa)',
'en-ZW' => 'English (Zimbabwe)',
'eo' => 'Esperanto',
'es' => 'Spanish',
'es-AR' => 'Spanish (Argentina)',
'es-BO' => 'Spanish (Bolivia)',
'es-CL' => 'Spanish (Chile)',
'es-CO' => 'Spanish (Colombia)',
'es-CR' => 'Spanish (Costa Rica)',
'es-DO' => 'Spanish (Dominican Republic)',
'es-EC' => 'Spanish (Ecuador)',
'es-ES' => 'Spanish (Spain)',
'es-GT' => 'Spanish (Guatemala)',
'es-HN' => 'Spanish (Honduras)',
'es-MX' => 'Spanish (Mexico)',
'es-NI' => 'Spanish (Nicaragua)',
'es-PA' => 'Spanish (Panama)',
'es-PE' => 'Spanish (Peru)',
'es-PR' => 'Spanish (Puerto Rico)',
'es-PY' => 'Spanish (Paraguay)',
'es-SV' => 'Spanish (El Salvador)',
'es-UY' => 'Spanish (Uruguay)',
'es-VE' => 'Spanish (Venezuela)',
'et' => 'Estonian',
'et-EE' => 'Estonian (Estonia)',
'eu' => 'Basque',
'eu-ES' => 'Basque (Spain)',
'fa' => 'Farsi',
'fa-IR' => 'Farsi (Iran)',
'fi' => 'Finnish',
'fi-FI' => 'Finnish (Finland)',
'fo' => 'Faroese',
'fo-FO' => 'Faroese (Faroe Islands)',
'fr' => 'French',
'fr-BE' => 'French (Belgium)',
'fr-CA' => 'French (Canada)',
'fr-CH' => 'French (Switzerland)',
'fr-FR' => 'French (France)',
'fr-LU' => 'French (Luxembourg)',
'fr-MC' => 'French (Principality of Monaco)',
'gl' => 'Galician',
'gl-ES' => 'Galician (Spain)',
'gu' => 'Gujarati',
'gu-IN' => 'Gujarati (India)',
'he' => 'Hebrew',
'he-IL' => 'Hebrew (Israel)',
'hi' => 'Hindi',
'hi-IN' => 'Hindi (India)',
'hr' => 'Croatian',
'hr-BA' => 'Croatian (Bosnia and Herzegovina)',
'hr-HR' => 'Croatian (Croatia)',
'hu' => 'Hungarian',
'hu-HU' => 'Hungarian (Hungary)',
'hy' => 'Armenian',
'hy-AM' => 'Armenian (Armenia)',
'id' => 'Indonesian',
'id-ID' => 'Indonesian (Indonesia)',
'is' => 'Icelandic',
'is-IS' => 'Icelandic (Iceland)',
'it' => 'Italian',
'it-CH' => 'Italian (Switzerland)',
'it-IT' => 'Italian (Italy)',
'ja' => 'Japanese',
'ja-JP' => 'Japanese (Japan)',
'ka' => 'Georgian',
'ka-GE' => 'Georgian (Georgia)',
'kk' => 'Kazakh',
'kk-KZ' => 'Kazakh (Kazakhstan)',
'kn' => 'Kannada',
'kn-IN' => 'Kannada (India)',
'ko' => 'Korean',
'ko-KR' => 'Korean (Korea)',
'kok' => 'Konkani',
'kok-IN' => 'Konkani (India)',
'ky' => 'Kyrgyz',
'ky-KG' => 'Kyrgyz (Kyrgyzstan)',
'lt' => 'Lithuanian',
'lt-LT' => 'Lithuanian (Lithuania)',
'lv' => 'Latvian',
'lv-LV' => 'Latvian (Latvia)',
'mi' => 'Maori',
'mi-NZ' => 'Maori (New Zealand)',
'mk' => 'FYRO Macedonian',
'mk-MK' => 'FYRO Macedonian (Former Yugoslav Republic of Macedonia)',
'mn' => 'Mongolian',
'mn-MN' => 'Mongolian (Mongolia)',
'mr' => 'Marathi',
'mr-IN' => 'Marathi (India)',
'ms' => 'Malay',
'ms-BN' => 'Malay (Brunei Darussalam)',
'ms-MY' => 'Malay (Malaysia)',
'mt' => 'Maltese',
'mt-MT' => 'Maltese (Malta)',
'nb' => 'Norwegian (Bokm?l)',
'nb-NO' => 'Norwegian (Bokm?l) (Norway)',
'nl' => 'Dutch',
'nl-BE' => 'Dutch (Belgium)',
'nl-NL' => 'Dutch (Netherlands)',
'nn-NO' => 'Norwegian (Nynorsk) (Norway)',
'ns' => 'Northern Sotho',
'ns-ZA' => 'Northern Sotho (South Africa)',
'pa' => 'Punjabi',
'pa-IN' => 'Punjabi (India)',
'pl' => 'Polish',
'pl-PL' => 'Polish (Poland)',
'ps' => 'Pashto',
'ps-AR' => 'Pashto (Afghanistan)',
'pt' => 'Portuguese',
'pt-BR' => 'Portuguese (Brazil)',
'pt-PT' => 'Portuguese (Portugal)',
'qu' => 'Quechua',
'qu-BO' => 'Quechua (Bolivia)',
'qu-EC' => 'Quechua (Ecuador)',
'qu-PE' => 'Quechua (Peru)',
'ro' => 'Romanian',
'ro-RO' => 'Romanian (Romania)',
'ru' => 'Russian',
'ru-RU' => 'Russian (Russia)',
'sa' => 'Sanskrit',
'sa-IN' => 'Sanskrit (India)',
'se' => 'Sami',
'se-FI' => 'Sami (Finland)',
'se-NO' => 'Sami (Norway)',
'se-SE' => 'Sami (Sweden)',
'sk' => 'Slovak',
'sk-SK' => 'Slovak (Slovakia)',
'sl' => 'Slovenian',
'sl-SI' => 'Slovenian (Slovenia)',
'sq' => 'Albanian',
'sq-AL' => 'Albanian (Albania)',
'sr-BA' => 'Serbian (Latin) (Bosnia and Herzegovina)',
'sr-Cyrl-BA' => 'Serbian (Cyrillic) (Bosnia and Herzegovina)',
'sr-SP' => 'Serbian (Latin) (Serbia and Montenegro)',
'sr-Cyrl-SP' => 'Serbian (Cyrillic) (Serbia and Montenegro)',
'sv' => 'Swedish',
'sv-FI' => 'Swedish (Finland)',
'sv-SE' => 'Swedish (Sweden)',
'sw' => 'Swahili',
'sw-KE' => 'Swahili (Kenya)',
'syr' => 'Syriac',
'syr-SY' => 'Syriac (Syria)',
'ta' => 'Tamil',
'ta-IN' => 'Tamil (India)',
'te' => 'Telugu',
'te-IN' => 'Telugu (India)',
'th' => 'Thai',
'th-TH' => 'Thai (Thailand)',
'tl' => 'Tagalog',
'tl-PH' => 'Tagalog (Philippines)',
'tn' => 'Tswana',
'tn-ZA' => 'Tswana (South Africa)',
'tr' => 'Turkish',
'tr-TR' => 'Turkish (Turkey)',
'tt' => 'Tatar',
'tt-RU' => 'Tatar (Russia)',
'ts' => 'Tsonga',
'uk' => 'Ukrainian',
'uk-UA' => 'Ukrainian (Ukraine)',
'ur' => 'Urdu',
'ur-PK' => 'Urdu (Islamic Republic of Pakistan)',
'uz' => 'Uzbek (Latin)',
'uz-UZ' => 'Uzbek (Latin) (Uzbekistan)',
'uz-Cyrl-UZ' => 'Uzbek (Cyrillic) (Uzbekistan)',
'vi' => 'Vietnamese',
'vi-VN' => 'Vietnamese (Viet Nam)',
'xh' => 'Xhosa',
'xh-ZA' => 'Xhosa (South Africa)',
'zh' => 'Chinese',
'zh-CN' => 'Chinese (S)',
'zh-HK' => 'Chinese (Hong Kong)',
'zh-MO' => 'Chinese (Macau)',
'zh-SG' => 'Chinese (Singapore)',
'zh-TW' => 'Chinese (T)',
'zu' => 'Zulu',
'zu-ZA' => 'Zulu (South Africa)'
];
public static function getLanguages()
{
return self::RFC5646_LANGUAGE;
}
}

View File

@ -55,8 +55,19 @@ class ProcessTool
if ($logToFile) {
self::logMessage('Running command ' . implode(' ', $command));
}
$process = proc_open($command, $descriptorSpec, $pipes, $cwd);
if (version_compare(phpversion(), '7.4.0', '<')) {
$temp = [];
foreach ($command as $k => $part) {
if ($k >= 1) {
$part = escapeshellarg($part);
}
$temp[] = $part;
}
$command_stringified = implode(' ', $temp);
$process = proc_open($command_stringified, $descriptorSpec, $pipes, $cwd);
} else {
$process = proc_open($command, $descriptorSpec, $pipes, $cwd);
}
if (!$process) {
$commandForException = self::commandFormat($command);
throw new Exception("Command '$commandForException' could be started.");

View File

@ -14,6 +14,7 @@ class ServerSyncTool
FEATURE_EDIT_OF_GALAXY_CLUSTER = 'edit_of_galaxy_cluster',
PERM_SYNC = 'perm_sync',
PERM_GALAXY_EDITOR = 'perm_galaxy_editor',
PERM_ANALYST_DATA = 'perm_analyst_data',
FEATURE_SIGHTING_REST_SEARCH = 'sighting_rest';
/** @var array */
@ -215,6 +216,72 @@ class ServerSyncTool
return $this->post('/galaxies/pushCluster', [$cluster], $logMessage);
}
/**
* @param array $candidates
* @return HttpSocketResponseExtended
* @throws HttpSocketHttpException
* @throws HttpSocketJsonException
*/
public function filterAnalystDataForPush(array $candidates)
{
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
throw new RuntimeException("Remote server do not support analyst data");
}
return $this->post('/analyst_data/filterAnalystDataForPush', $candidates);
}
/**
* @param array $rules
* @return HttpSocketResponseExtended
* @throws HttpSocketHttpException
* @throws HttpSocketJsonException
*/
public function fetchIndexMinimal(array $rules)
{
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
throw new RuntimeException("Remote server do not support analyst data");
}
return $this->post('/analyst_data/indexMinimal', $rules);
}
/**
* @param string $type
* @param array $uuids
* @return HttpSocketResponseExtended
* @throws HttpSocketJsonException
* @throws HttpSocketHttpException
*/
public function fetchAnalystData($type, array $uuids)
{
if (!$this->isSupported(self::PERM_ANALYST_DATA)) {
throw new RuntimeException("Remote server do not support analyst data");
}
$params = [
'uuid' => $uuids,
];
$url = '/analyst_data/index/' . $type;
$url .= $this->createParams($params);
$url .= '.json';
return $this->get($url);
}
/**
* @param string $type
* @param array $analystData
* @return HttpSocketResponseExtended
* @throws HttpSocketHttpException
* @throws HttpSocketJsonException
*/
public function pushAnalystData($type, array $analystData)
{
$logMessage = "Pushing Analyst Data #{$analystData[$type]['uuid']} to Server #{$this->serverId()}";
return $this->post('/analyst_data/pushAnalystData', $analystData, $logMessage);
}
/**
* @param array $params
* @return HttpSocketResponseExtended
@ -230,19 +297,26 @@ class ServerSyncTool
/**
* @param array $eventUuids
* @param array $blockedOrgs Blocked organisation UUIDs
* @return array
* @throws HttpSocketHttpException
* @throws HttpSocketJsonException
* @throws JsonException
*/
public function fetchSightingsForEvents(array $eventUuids)
public function fetchSightingsForEvents(array $eventUuids, array $blockedOrgs = [])
{
return $this->post('/sightings/restSearch/event', [
$postParams = [
'returnFormat' => 'json',
'last' => 0, // fetch all
'includeUuid' => true,
'uuid' => $eventUuids,
])->json()['response'];
];
if (!empty($blockedOrgs)) {
$postParams['org_id'] = array_map(function ($uuid) {
return "!$uuid";
}, $blockedOrgs);
}
return $this->post('/sightings/restSearch/event', $postParams)->json()['response'];
}
/**
@ -414,6 +488,8 @@ class ServerSyncTool
return isset($info['perm_sync']) && $info['perm_sync'];
case self::PERM_GALAXY_EDITOR:
return isset($info['perm_galaxy_editor']) && $info['perm_galaxy_editor'];
case self::PERM_ANALYST_DATA:
return isset($info['perm_analyst_data']) && $info['perm_analyst_data'];
case self::FEATURE_SIGHTING_REST_SEARCH:
$version = explode('.', $info['version']);
return $version[0] == 2 && $version[1] == 4 && $version[2] > 164;
@ -430,6 +506,16 @@ class ServerSyncTool
return $this->socket->getMetaData();
}
/**
* @param string $message
* @return void
*/
public function debug($message)
{
$memoryUsage = round(memory_get_usage() / 1024 / 1024, 2);
CakeLog::debug("[Server sync #{$this->serverId()}]: $message. Memory: $memoryUsage MB");
}
/**
* @params string $url Relative URL
* @return HttpSocketResponseExtended
@ -480,6 +566,7 @@ class ServerSyncTool
if ($etag) {
// Remove compression marks that adds Apache for compressed content
// This can be removed in future as this is already checked by MISP itself since 2024-03
$etagWithoutQuotes = trim($etag, '"');
$dashPos = strrpos($etagWithoutQuotes, '-');
if ($dashPos && in_array(substr($etagWithoutQuotes, $dashPos + 1), ['br', 'gzip'], true)) {

View File

@ -1,7 +1,6 @@
<?php
class SyncTool
{
const ALLOWED_CERT_FILE_EXTENSIONS = ['pem', 'crt'];
/**
@ -50,7 +49,7 @@ class SyncTool
* @return HttpSocketExtended
* @throws Exception
*/
public function createHttpSocket($params = array())
public function createHttpSocket(array $params = [])
{
// Use own CA PEM file
$caPath = Configure::read('MISP.ca_path');
@ -84,6 +83,9 @@ class SyncTool
}
if (function_exists('curl_init')) {
if (!isset($params['timeout']) && Configure::check('MISP.curl_request_timeout')) {
$params['timeout'] = (int)Configure::read('MISP.curl_request_timeout');
}
App::uses('CurlClient', 'Tools');
$HttpSocket = new CurlClient($params);
} else {

View File

@ -14679,7 +14679,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14651,7 +14651,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14624,7 +14624,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -15934,7 +15934,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14622,7 +14622,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14626,7 +14626,7 @@ msgstr "Par défaut (0), tout les attributs qui correspondent aux autres paramè
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "Si le paramètre est défini à 1, cela va retourner les attributs mis à la corbeille ainsi que les attributs actifs. En utilisant \"only\" en tant que paramètre, cela va seulement retourner les données mises à la corbeille."
#: View/Events/automation.ctp:68

View File

@ -14622,7 +14622,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14625,7 +14625,7 @@ msgstr "Per impostazione predefinita (0) tutti gli attributi restituiti rispondo
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "Se questo parametro è impostato a 1, verranno restituiti attributi \"soft-deleted\" insieme a quelli attivi. Utilizzando \"only\" come parametro verranno restituiti solo gli attributi \"soft-deleted\"."
#: View/Events/automation.ctp:68

View File

@ -14608,7 +14608,7 @@ msgstr "デフォルトの (0) では、to_ids の設定に関係なく、他の
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "このパラメーターを 1 に設定すると、ソフト削除されたアトリビュートがアクティなアトリビュートと共に返されます。\"only\"をパラメーターとして使用すると、返されるデータはソフト削除されたデータのみに制限されます。"
#: View/Events/automation.ctp:68

View File

@ -14609,7 +14609,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14625,7 +14625,7 @@ msgstr "Som standard (0) returneres alle attributter som samsvarer med de andre
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "Hvis denne parameteren er satt til 1, vil den returnere myke slettede attributter sammen med aktive. Ved å bruke \"only\" som en parameter, vil det begrense det returnerte datasettet til bare slettede data."
#: View/Events/automation.ctp:68

View File

@ -14651,7 +14651,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -7964,7 +7964,7 @@ msgstr ""
#: View/Events/automation.ctp:42
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:43

View File

@ -14623,7 +14623,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14636,7 +14636,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -14650,7 +14650,7 @@ msgstr "По-умолчанию (а также при значении 0) в п
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "По-умолчанию (а также при значении 0) в поиск попадают только активные атрибуты. Если параметр равен 1, то в поиск попадут дополнительно удаленные атрибуты. Если используется ключевое слово \"only\", то в результаты поиска попадут только удаленные атрибуты. "
#: View/Events/automation.ctp:68

View File

@ -14638,7 +14638,7 @@ msgstr "පෙරනිමියෙන් (0) to_ids සිටුවම් න
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "මෙම පරාමිතිය 1 ලෙස සකසා ඇත්නම්, එය සක්‍රිය ඒවා සමඟ මෘදු-මකා දැමූ ගුණාංග ලබා දෙනු ඇත. පරාමිතියක් ලෙස \"පමණක්\" භාවිතා කිරීමෙන් එය ආපසු ලබා දෙන දත්ත කට්ටලය මෘදු-මකා දැමූ දත්ත වලට පමණක් සීමා කරයි."
#: View/Events/automation.ctp:68

View File

@ -14624,7 +14624,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -10898,7 +10898,7 @@ msgstr ""
#: View/Events/automation.ctp:52
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:53

View File

@ -14620,7 +14620,7 @@ msgstr ""
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:68

View File

@ -6030,7 +6030,7 @@ msgid "By default (0) all attributes are returned that match the other filter pa
msgstr ""
#: View/Events/automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr ""
#: View/Events/automation.ctp:316

View File

@ -14619,7 +14619,7 @@ msgstr "默认情况下(0), 返回所有与其他过滤器参数匹配的属性,
#: View/Events/automation.ctp:67
#: View/Events/legacy_automation.ctp:315
msgid "If this parameter is set to 1, it will return soft-deleted attributes along with active ones. By using \"only\" as a parameter it will limit the returned data set to soft-deleted data only."
msgid "Default value 0. If set to 1, only soft-deleted attributes will be returned. If set to [0,1] , both deleted and non-deleted attributes wil be returned."
msgstr "如果这个参数被设置为1, 它将返回软删除的属性和活动属性. 如果使用\"only\"作为参数, 则返回的数据集将只限于软删除的数据."
#: View/Events/automation.ctp:68

1138
app/Model/AnalystData.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
<?php
App::uses('AppModel', 'Model');
class AnalystDataBlocklist extends AppModel
{
public $useTable = 'analyst_data_blocklists';
public $recursive = -1;
public $actsAs = array(
'AuditLog',
'SysLogLogable.SysLogLogable' => array( // TODO Audit, logable
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'full'
),
'Containable',
);
public $blocklistFields = array('analyst_data_uuid', 'comment', 'analyst_data_info', 'analyst_data_orgc');
public $blocklistTarget = 'analyst_data';
public $validate = array(
'analyst_data_uuid' => array(
'unique' => array(
'rule' => 'isUnique',
'message' => 'Analyst Data already blocklisted.'
),
'uuid' => array(
'rule' => array('uuid'),
'message' => 'Please provide a valid UUID'
),
)
);
public function beforeValidate($options = array())
{
parent::beforeValidate();
if (empty($this->data['AnalystDataBlocklist']['id'])) {
$this->data['AnalystDataBlocklist']['date_created'] = date('Y-m-d H:i:s');
}
if (empty($this->data['AnalystDataBlocklist']['comment'])) {
$this->data['AnalystDataBlocklist']['comment'] = '';
}
return true;
}
/**
* @param string $analystDataUUID
* @return bool
*/
public function checkIfBlocked($analystDataUUID)
{
return $this->hasAny([
'analyst_data_uuid' => $analystDataUUID,
]);
}
}

View File

@ -27,6 +27,7 @@ App::uses('FileAccessTool', 'Tools');
App::uses('JsonTool', 'Tools');
App::uses('RedisTool', 'Tools');
App::uses('BetterCakeEventManager', 'Tools');
App::uses('Folder', 'Utility');
class AppModel extends Model
{
@ -47,6 +48,9 @@ class AppModel extends Model
/** @var Workflow|null */
private $Workflow;
public $includeAnalystData;
public $includeAnalystDataRecursive;
// deprecated, use $db_changes
// major -> minor -> hotfix -> requires_logout
const OLD_DB_CHANGES = array(
@ -86,7 +90,8 @@ class AppModel extends Model
99 => false, 100 => false, 101 => false, 102 => false, 103 => false, 104 => false,
105 => false, 106 => false, 107 => false, 108 => false, 109 => false, 110 => false,
111 => false, 112 => false, 113 => true, 114 => false, 115 => false, 116 => false,
117 => false, 118 => false, 119 => false,
117 => false, 118 => false, 119 => false, 120 => false, 121 => false, 122 => false,
123 => false, 124 => false, 125 => false,
);
const ADVANCED_UPDATES_DESCRIPTION = array(
@ -272,6 +277,9 @@ class AppModel extends Model
$this->removeDuplicatedUUIDs();
$dbUpdateSuccess = $this->updateDatabase('createUUIDsConstraints');
break;
case 120:
$dbUpdateSuccess = $this->moveImages();
break;
default:
$dbUpdateSuccess = $this->updateDatabase($command);
break;
@ -1982,7 +1990,7 @@ class AppModel extends Model
$sqlArray[] = "ALTER TABLE `event_reports` modify `content` mediumtext";
break;
case 117:
$sqlArray[] = "CREATE TABLE `user_login_profiles` (
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `user_login_profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`user_id` int(11) NOT NULL,
@ -2009,6 +2017,168 @@ class AppModel extends Model
case 119:
$sqlArray[] = "ALTER TABLE `access_logs` MODIFY `action` varchar(191) NOT NULL";
break;
case 121:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `notes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`authors` text,
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`distribution` tinyint(4) NOT NULL,
`sharing_group_id` int(10) unsigned,
`locked` tinyint(1) NOT NULL DEFAULT 0,
`note` mediumtext,
`language` varchar(16) DEFAULT 'en',
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `object_uuid` (`object_uuid`),
KEY `object_type` (`object_type`),
KEY `org_uuid` (`org_uuid`),
KEY `orgc_uuid` (`orgc_uuid`),
KEY `distribution` (`distribution`),
KEY `sharing_group_id` (`sharing_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `opinions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`authors` text,
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`distribution` tinyint(4) NOT NULL,
`sharing_group_id` int(10) unsigned,
`locked` tinyint(1) NOT NULL DEFAULT 0,
`opinion` int(10) unsigned,
`comment` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `object_uuid` (`object_uuid`),
KEY `object_type` (`object_type`),
KEY `org_uuid` (`org_uuid`),
KEY `orgc_uuid` (`orgc_uuid`),
KEY `distribution` (`distribution`),
KEY `sharing_group_id` (`sharing_group_id`),
KEY `opinion` (`opinion`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `relationships` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) CHARACTER SET ascii NOT NULL,
`object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`authors` text,
`org_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`orgc_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`distribution` tinyint(4) NOT NULL,
`sharing_group_id` int(10) unsigned,
`locked` tinyint(1) NOT NULL DEFAULT 0,
`relationship_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci,
`related_object_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`related_object_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `object_uuid` (`object_uuid`),
KEY `object_type` (`object_type`),
KEY `org_uuid` (`org_uuid`),
KEY `orgc_uuid` (`orgc_uuid`),
KEY `distribution` (`distribution`),
KEY `sharing_group_id` (`sharing_group_id`),
KEY `relationship_type` (`relationship_type`),
KEY `related_object_uuid` (`related_object_uuid`),
KEY `related_object_type` (`related_object_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `analyst_data_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`analyst_data_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
`analyst_data_info` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`comment` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci,
`analyst_data_orgc` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
KEY `analyst_data_uuid` (`analyst_data_uuid`),
KEY `analyst_data_orgc` (`analyst_data_orgc`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;";
$sqlArray[] = "ALTER TABLE `roles` ADD `perm_analyst_data` tinyint(1) NOT NULL DEFAULT 0;";
$sqlArray[] = "UPDATE `roles` SET `perm_analyst_data`=1 WHERE `perm_add` = 1;";
$sqlArray[] = "ALTER TABLE `servers` ADD `push_analyst_data` tinyint(1) NOT NULL DEFAULT 0 AFTER `push_galaxy_clusters`;";
$sqlArray[] = "ALTER TABLE `servers` ADD `pull_analyst_data` tinyint(1) NOT NULL DEFAULT 0 AFTER `push_analyst_data`;";
break;
case 122:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `collections` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`org_id` int(10) unsigned NOT NULL,
`orgc_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`distribution` tinyint(4) NOT NULL,
`sharing_group_id` int(10) unsigned,
`name` varchar(191) NOT NULL,
`type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`description` mediumtext,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `name` (`name`),
KEY `type` (`type`),
KEY `org_id` (`org_id`),
KEY `orgc_id` (`orgc_id`),
KEY `user_id` (`user_id`),
KEY `distribution` (`distribution`),
KEY `sharing_group_id` (`sharing_group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `collection_elements` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`element_uuid` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`element_type` varchar(80) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`collection_id` int(10) unsigned NOT NULL,
`description` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `element_uuid` (`element_uuid`),
KEY `element_type` (`element_type`),
KEY `collection_id` (`collection_id`),
UNIQUE KEY `unique_element` (`element_uuid`, `collection_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
break;
case 123:
$sqlArray[] = 'ALTER TABLE `notes` MODIFY `created` datetime NOT NULL';
$sqlArray[] = 'ALTER TABLE `opinions` MODIFY `created` datetime NOT NULL;';
$sqlArray[] = 'ALTER TABLE `relationships` MODIFY `created` datetime NOT NULL;';
$sqlArray[] = 'ALTER TABLE `notes` MODIFY `modified` datetime NOT NULL;';
$sqlArray[] = 'ALTER TABLE `opinions` MODIFY `modified` datetime NOT NULL;';
$sqlArray[] = 'ALTER TABLE `relationships` MODIFY `modified` datetime NOT NULL;';
break;
case 124:
$sqlArray[] = 'CREATE TABLE IF NOT EXISTS `sighting_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`org_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
`org_name` varchar(255) COLLATE utf8_bin NOT NULL,
`comment` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci,
PRIMARY KEY (`id`),
INDEX `org_uuid` (`org_uuid`),
INDEX `org_name` (`org_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;';
break;
case 125:
$sqlArray[] = "ALTER TABLE `feeds` ADD COLUMN `tag_collection_id` INT(11) NOT NULL DEFAULT 0;";
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;';
@ -3658,7 +3828,7 @@ class AppModel extends Model
protected function logException($message, Exception $exception, $type = LOG_ERR)
{
// If Sentry is installed, send exception to Sentry
if (function_exists('\Sentry\captureException') && $type === LOG_ERR) {
if (function_exists('\Sentry\captureException') && $type <= LOG_ERR) {
\Sentry\captureException(new Exception($message, $type, $exception));
}
@ -4047,8 +4217,17 @@ class AppModel extends Model
if (!empty($query['order']) && $this->validOrderClause($query['order']) === false) {
throw new InvalidArgumentException('Invalid order clause');
}
return parent::find($type, $query);
$results = parent::find($type, $query);
if (!empty($query['includeAnalystData']) && $this->Behaviors->enabled('AnalystDataParent')) {
if ($type === 'first') {
$results[$this->alias] = array_merge($results[$this->alias], $this->attachAnalystData($results[$this->alias]));
} else if ($type === 'all') {
foreach ($results as $k => $result) {
$results[$k][$this->alias] = array_merge($results[$k][$this->alias], $this->attachAnalystData($results[$k][$this->alias]));
}
}
}
return $results;
}
private function validOrderClause($order)
@ -4076,4 +4255,31 @@ class AppModel extends Model
{
return preg_match('/^[\w\_\-\. ]+$/', $param);
}
public function moveImages()
{
$oldImageDir = APP . 'webroot/img';
$newImageDir = APP . 'files/img';
$oldOrgDir = new Folder($oldImageDir . '/orgs');
$oldCustomDir = new Folder($oldImageDir . '/custom');
$result = $oldOrgDir->copy([
'from' => $oldImageDir . '/orgs',
'to' => $newImageDir . '/orgs',
'scheme' => Folder::OVERWRITE,
'recursive' => true
]);
if ($result) {
$oldOrgDir->delete();
}
$result = $oldCustomDir->copy([
'from' => $oldImageDir . '/custom',
'to' => $newImageDir . '/custom',
'scheme' => Folder::OVERWRITE,
'recursive' => true
]);
if ($result) {
$oldCustomDir->delete();
}
return true;
}
}

View File

@ -258,7 +258,7 @@ class AttachmentScan extends AppModel
$scanned++;
}
} catch (Exception $e) {
$this->logException("Could not scan attachment for $type {$attribute['Attribute']['id']}", $e, LOG_WARNING);
$this->logException("Could not scan attachment for $type {$attribute[$type]['id']}", $e, LOG_WARNING);
$fails++;
}

View File

@ -36,7 +36,8 @@ class Attribute extends AppModel
'Trim',
'Containable',
'Regexp' => array('fields' => array('value')),
'LightPaginator'
'LightPaginator',
'AnalystDataParent',
);
public $displayField = 'value';
@ -117,6 +118,7 @@ class Attribute extends AppModel
'aba-rtn',
'gender',
'counter',
'integer',
'float',
'port',
'nationality',
@ -968,7 +970,7 @@ class Attribute extends AppModel
$maxWidth = $maxWidth ?: $defaultMaxSize;
$maxHeight = $maxHeight ?: $defaultMaxSize;
$suffix = null;
if ($maxWidth == $defaultMaxSize && $maxHeight == $defaultMaxSize) {
$thumbnailInRedis = Configure::read('MISP.thumbnail_in_redis');
if ($thumbnailInRedis) {
@ -1140,29 +1142,36 @@ class Attribute extends AppModel
}
$temp = array();
if (!empty($tagArray[1])) {
if ($options['scope'] == 'all' || $options['scope'] == 'Event') {
$subquery_options = array(
'conditions' => array(
'tag_id' => $tagArray[1]
),
'fields' => array(
'event_id'
)
);
$lookup_field = ($options['scope'] === 'Event') ? 'Event.id' : 'Attribute.event_id';
$conditions['AND'][] = array_merge($temp, $this->subQueryGenerator($tag->EventTag, $subquery_options, $lookup_field, 1));
}
if ($options['scope'] == 'all' || $options['scope'] == 'Attribute') {
$subquery_options = array(
'conditions' => array(
'tag_id' => $tagArray[1]
),
'fields' => array(
$options['scope'] === 'Event' ? 'event.id' : 'attribute_id'
)
);
$lookup_field = $options['scope'] === 'Event' ? 'Event.id' : 'Attribute.id';
$conditions['AND'][] = array_merge($temp, $this->subQueryGenerator($tag->AttributeTag, $subquery_options, $lookup_field, 1));
/*
* If we didn't find the given negation tag, no need to use the -1 trick,
* it is basically a hack to block the search from finding anything if no positive lookup was valid.
* However, if none of the negated tags exist, there's nothing to filter here
*/
if (count($tagArray[1]) !== 1 || $tagArray[1][0] != -1) {
if ($options['scope'] == 'all' || $options['scope'] == 'Event') {
$subquery_options = array(
'conditions' => array(
'tag_id' => $tagArray[1]
),
'fields' => array(
'event_id'
)
);
$lookup_field = ($options['scope'] === 'Event') ? 'Event.id' : 'Attribute.event_id';
$conditions['AND'][] = array_merge($temp, $this->subQueryGenerator($tag->EventTag, $subquery_options, $lookup_field, 1));
}
if ($options['scope'] == 'all' || $options['scope'] == 'Attribute') {
$subquery_options = array(
'conditions' => array(
'tag_id' => $tagArray[1]
),
'fields' => array(
$options['scope'] === 'Event' ? 'event.id' : 'attribute_id'
)
);
$lookup_field = $options['scope'] === 'Event' ? 'Event.id' : 'Attribute.id';
$conditions['AND'][] = array_merge($temp, $this->subQueryGenerator($tag->AttributeTag, $subquery_options, $lookup_field, 1));
}
}
}
$temp = array();
@ -1547,33 +1556,57 @@ class Attribute extends AppModel
$conditions = array();
if (!$user['Role']['perm_site_admin']) {
$sgids = $this->SharingGroup->authorizedIds($user);
$eventConditions = $this->Event->createEventConditions($user);
$conditions = array(
'AND' => array(
$eventConditions['AND'],
array(
'OR' => array(
'Event.org_id' => $user['org_id'],
'Attribute.distribution' => array(1, 2, 3, 5),
'AND '=> array(
'Attribute.distribution' => 4,
'Attribute.sharing_group_id' => $sgids,
)
)
),
array(
'OR' => array(
'Attribute.object_id' => 0,
'Event.org_id' => $user['org_id'],
'Object.distribution' => array(1, 2, 3, 5),
'AND' => array(
'Object.distribution' => 4,
'Object.sharing_group_id' => $sgids,
)
)
)
)
);
$subQuery1 = [
'conditions' => ['org_id' => $user['org_id']],
'fields' => ['id']
];
$subQuery2 = [
'conditions' => [
'distribution IN' => [1, 2, 3]
],
'fields' => ['id']
];
$subQuery3 = [
'conditions' => [
'Event.distribution' => 4,
'Event.sharing_group_id IN' => $sgids
],
'fields' => ['id']
];
if (Configure::read('MISP.unpublishedprivate')) {
$subQuery2['conditions']['Event.published'] = 1;
$subQuery3['conditions']['Event.published'] = 1;
}
$conditions = [
'OR' => [
$this->subQueryGenerator($this->Event, $subQuery1, 'Attribute.event_id'),
'AND' => [
'OR' => [
$this->subQueryGenerator($this->Event, $subQuery2, 'Attribute.event_id'),
$this->subQueryGenerator($this->Event, $subQuery3, 'Attribute.event_id')
],
[
'OR' => [
'Attribute.distribution' => [1, 2, 3, 5],
'AND '=> [
'Attribute.distribution' => 4,
'Attribute.sharing_group_id' => $sgids,
]
]
],
[
'OR' => [
'Attribute.object_id' => 0,
'Object.distribution' => [1, 2, 3, 5],
'AND' => [
'Object.distribution' => 4,
'Object.sharing_group_id' => $sgids,
]
]
]
]
]
];
}
return $conditions;
}
@ -1786,7 +1819,7 @@ class Attribute extends AppModel
if (isset($options['fields'])) {
$params['fields'] = $options['fields'];
}
if (isset($options['conditions'])) {
if (!empty($options['conditions'])) {
$params['conditions']['AND'][] = $options['conditions'];
}
if (empty($options['flatten'])) {
@ -1882,11 +1915,10 @@ class Attribute extends AppModel
return [];
}
}
$eventTags = []; // tag cache
$attributes = [];
$params['ignoreIndexHint'] = 'deleted';
do {
$continue = true;
$results = $this->find('all', $params);
if (empty($results)) {
break;
@ -2402,11 +2434,15 @@ class Attribute extends AppModel
$timestamp[0] = $timestamp[1];
$timestamp[1] = $temp;
}
$conditions['AND'][] = array($scope . ' >=' => $timestamp[0]);
if ($timestamp[0] != 0) {
$conditions['AND'][] = array($scope . ' >=' => $timestamp[0]);
}
$conditions['AND'][] = array($scope . ' <=' => $timestamp[1]);
} else {
$timestamp = $this->resolveTimeDelta($timestamp);
$conditions['AND'][] = array($scope . ' >=' => $timestamp);
if ($timestamp !== 0) {
$conditions['AND'][] = array($scope . ' >=' => $timestamp);
}
}
if ($returnRaw) {
return $timestamp;
@ -2428,7 +2464,7 @@ class Attribute extends AppModel
$conditions['AND'][] = array($scope . ' <=' => $timestamp[1]);
} else {
$timestamp = intval($this->resolveTimeDelta($timestamp)) * 1000000; // seen in stored in micro-seconds in the DB
if ($scope == 'Attribute.first_seen') {
if ($scope == 'Attribute.first_seen' || $scope == 'Object.first_seen') {
$conditions['AND'][] = array($scope . ' >=' => $timestamp);
} else {
$conditions['AND'][] = array($scope . ' <=' => $timestamp);
@ -2616,7 +2652,8 @@ class Attribute extends AppModel
$this->id = $attribute['id'];
}
}
if (!$this->save(['Attribute' => $attribute], $params)) {
$savedAttribute = $this->save(['Attribute' => $attribute], $params);
if (!$savedAttribute) {
$this->logDropped($user, $attribute);
} else {
if (!empty($attribute['AttributeTag'])) {
@ -2654,6 +2691,7 @@ class Attribute extends AppModel
if (!empty($attribute['Sighting'])) {
$this->Sighting->captureSightings($attribute['Sighting'], $this->id, $eventId, $user);
}
$this->Event->captureAnalystData($user, $attribute, 'Attribute', $savedAttribute['Attribute']['uuid']);
}
if (!empty($this->validationErrors)) {
$validationErrors = $this->validationErrors;
@ -2794,6 +2832,7 @@ class Attribute extends AppModel
if (!empty($attribute['Sighting'])) {
$this->Sighting->captureSightings($attribute['Sighting'], $attributeId, $eventId, $user);
}
$this->Event->captureAnalystData($user, $attribute, 'Attribute', $attribute['uuid']);
if ($user['Role']['perm_tagger']) {
/*
We should unwrap the line below and remove the server option in the future once we have tag soft-delete
@ -2858,7 +2897,7 @@ class Attribute extends AppModel
if (!empty($tagActions['attach'])) {
$this->AttributeTag->saveMany($tagActions['attach']);
}
}
if (!empty($tagActions['detach'])) {
foreach ($tagActions['detach'] as $detach) {
@ -2876,7 +2915,7 @@ class Attribute extends AppModel
$this->Correlation->generateCorrelation(false, $event['Event']['id'], $attributeId);
}
}
// Instead of incrementing / decrementing the event
// Instead of incrementing / decrementing the event
$attribute_count = $this->find('count', [
'conditions' => [
'Attribute.event_id' => $event['Event']['id'],
@ -2893,7 +2932,7 @@ class Attribute extends AppModel
}
return true;
}
/**
* @param int $id Attribute ID
@ -2973,9 +3012,15 @@ class Attribute extends AppModel
return $adata;
}
public function buildFilterConditions(array $user, array &$params)
public function buildFilterConditions(array $user, array &$params, $skipBuildConditions = false)
{
$conditions = $this->buildConditions($user);
// in some cases we'll build the user ACL conditions elsewhere,
// for example when calling this function via restsearch
if ($skipBuildConditions) {
$conditions = [];
} else {
$conditions = $this->buildConditions($user);
}
if (isset($params['wildcard'])) {
$temp = array();
$options = array(
@ -3099,7 +3144,7 @@ class Attribute extends AppModel
$subqueryElements = $this->Event->harvestSubqueryElements($filters);
$filters = $this->Event->addFiltersFromSubqueryElements($filters, $subqueryElements, $user);
$filters = $this->Event->addFiltersFromUserSettings($user, $filters);
$conditions = $this->buildFilterConditions($user, $filters);
$conditions = $this->buildFilterConditions($user, $filters, true);
$params = array(
'conditions' => $conditions,
'fields' => array('Attribute.*', 'Event.org_id', 'Event.distribution'),
@ -3119,6 +3164,7 @@ class Attribute extends AppModel
'includeFullModel' => !empty($filters['includeFullModel']) ? $filters['includeFullModel'] : 0,
'allow_proposal_blocking' => !empty($filters['allow_proposal_blocking']) ? $filters['allow_proposal_blocking'] : 0
);
if (!empty($filters['attackGalaxy'])) {
$params['attackGalaxy'] = $filters['attackGalaxy'];
}
@ -3344,20 +3390,60 @@ class Attribute extends AppModel
if (!empty($params['uuid'])) {
$params['uuid'] = $this->convert_filters($params['uuid']);
if (!empty($params['uuid']['OR'])) {
$conditions['AND'][] = array(
'OR' => array(
'Event.uuid' => $params['uuid']['OR'],
'Attribute.uuid' => $params['uuid']['OR']
)
);
if ($options['scope'] == 'Attribute') {
$subQuery = [
'conditions' => ['uuid' => $params['uuid']['OR']],
'fields' => ['id']
];
$pre_lookup = $this->Event->find('first', [
'conditions' => ['Event.uuid' => $params['uuid']['OR']],
'recursive' => -1,
'fields' => ['Event.id']
]);
if (empty($pre_lookup)) {
$conditions['AND'][] = array(
'OR' => array(
'Attribute.uuid' => $params['uuid']['OR']
)
);
} else {
$conditions['AND'][] = array(
'OR' => array(
$this->subQueryGenerator($this->Event, $subQuery, 'Attribute.event_id'),
'Attribute.uuid' => $params['uuid']['OR']
)
);
}
} else {
$conditions['AND'][] = array(
'OR' => array(
'Event.uuid' => $params['uuid']['OR'],
'Attribute.uuid' => $params['uuid']['OR']
)
);
}
}
if (!empty($params['uuid']['NOT'])) {
$conditions['AND'][] = array(
'NOT' => array(
'Event.uuid' => $params['uuid']['NOT'],
'Attribute.uuid' => $params['uuid']['NOT']
)
);
if ($options['scope'] == 'Attribute') {
$subQuery = [
'conditions' => ['uuid' => $params['uuid']['OR']],
'fields' => ['id']
];
$conditions['AND'][] = [
'NOT' => [
$this->subQueryGenerator($this->Event, $subQuery, 'Attribute.event_id'),
'Attribute.uuid' => $params['uuid']['NOT']
]
];
} else {
$conditions['AND'][] = array(
'NOT' => array(
'Event.uuid' => $params['uuid']['NOT'],
'Attribute.uuid' => $params['uuid']['NOT']
)
);
}
}
}
return $conditions;
@ -3539,7 +3625,7 @@ class Attribute extends AppModel
),
'Other' => array(
'desc' => __('Attributes that are not part of any other category or are meant to be used as a component in MISP objects in the future'),
'types' => array('comment', 'text', 'other', 'size-in-bytes', 'counter', 'datetime', 'cpe', 'port', 'float', 'hex', 'phone-number', 'boolean', 'anonymised', 'pgp-public-key', 'pgp-private-key')
'types' => array('comment', 'text', 'other', 'size-in-bytes', 'counter', 'integer', 'datetime', 'cpe', 'port', 'float', 'hex', 'phone-number', 'boolean', 'anonymised', 'pgp-public-key', 'pgp-private-key')
)
);
}
@ -3692,6 +3778,7 @@ class Attribute extends AppModel
'dns-soa-email' => array('desc' => __('RFC 1035 mandates that DNS zones should have a SOA (Statement Of Authority) record that contains an email address where a PoC for the domain could be contacted. This can sometimes be used for attribution/linkage between different domains even if protected by whois privacy'), 'default_category' => 'Attribution', 'to_ids' => 0),
'size-in-bytes' => array('desc' => __('Size expressed in bytes'), 'default_category' => 'Other', 'to_ids' => 0),
'counter' => array('desc' => __('An integer counter, generally to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0),
'integer' => array('desc' => __('A generic integer generally to be used in objects'), 'default_category' => 'Other', 'to_ids' => 0),
'datetime' => array('desc' => __('Datetime in the ISO 8601 format'), 'default_category' => 'Other', 'to_ids' => 0),
'port' => array('desc' => __('Port number'), 'default_category' => 'Network activity', 'to_ids' => 0),
'ip-dst|port' => array('desc' => __('IP destination and port number separated by a |'), 'default_category' => 'Network activity', 'to_ids' => 1),
@ -3777,7 +3864,7 @@ class Attribute extends AppModel
if (isset($attribute['id'])) {
$conditions['Attribute.id !='] = $attribute['id'];
}
return $this->find('first', [
'recursive' => -1,
'conditions' => $conditions,

View File

@ -0,0 +1,88 @@
<?php
/**
* Common functions for the 3 analyst objects
*/
class AnalystDataBehavior extends ModelBehavior
{
public $SharingGroup;
private $__current_type = null;
protected $__valid_sharing_groups = null;
public function setup(Model $Model, $settings = array()) {
// We want to know whether we're a Note, Opinion or Relationship
$this->__current_type = $Model->alias;
}
// Return the analystData of the current type for a given UUID (this only checks the ACL of the analystData, NOT of the parent.)
public function fetchForUuid(Model $Model, $uuid, $user = null)
{
$conditions = [
'object_uuid' => $uuid
];
$type = $Model->current_type;
if (empty($user['Role']['perm_site_admin'])) {
if ($this->__valid_sharing_groups === null) {
$this->__valid_sharing_groups = $Model->SharingGroup->authorizedIds($user, true);
}
$conditions['AND'][] = [
'OR' => [
$type . '.orgc_uuid' => $user['Organisation']['uuid'],
$type . '.org_uuid' => $user['Organisation']['uuid'],
$type . '.distribution IN' => [1, 2, 3],
'AND' => [
$type . '.distribution' => 4,
$type . '.sharing_group_id IN' => $this->__valid_sharing_groups
]
]
];
}
return $Model->find('all', [
'recursive' => -1,
'conditions' => $conditions,
'contain' => ['Org', 'Orgc', 'SharingGroup'],
]);
}
// Return the analystData of the current type for a given UUID (this only checks the ACL of the analystData, NOT of the parent.)
public function fetchForUuids(Model $Model, $uuids, $user = null)
{
$conditions = [
'object_uuid' => $uuids
];
$type = $Model->current_type;
if (empty($user['Role']['perm_site_admin'])) {
if ($this->__valid_sharing_groups === null) {
$this->__valid_sharing_groups = $Model->SharingGroup->authorizedIds($user, true);
}
$conditions['AND'][] = [
'OR' => [
$type . '.orgc_uuid' => $user['Organisation']['uuid'],
$type . '.org_uuid' => $user['Organisation']['uuid'],
$type . '.distribution IN' => [1, 2, 3],
'AND' => [
$type . '.distribution' => 4,
$type . '.sharing_group_id IN' => $this->__valid_sharing_groups
]
]
];
}
$temp = $Model->find('all', [
'recursive' => -1,
'conditions' => $conditions,
'contain' => ['Org', 'Orgc', 'SharingGroup'],
]);
$results = [];
foreach ($temp as $result) {
$results[$result[$type]['object_uuid']][$type][] = $result[$type];
}
return $results;
}
public function checkACL()
{
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* Common functions for the 3 analyst objects
*/
class AnalystDataParentBehavior extends ModelBehavior
{
private $__currentUser = null;
public $User;
public function attachAnalystData(Model $model, array $object, array $types = ['Note', 'Opinion', 'Relationship'])
{
// No uuid, nothing to attach
if (empty($object['uuid'])) {
return $object;
}
if (empty($this->__currentUser)) {
$user_id = Configure::read('CurrentUserId');
$this->User = ClassRegistry::init('User');
if ($user_id) {
$this->__currentUser = $this->User->getAuthUser($user_id);
}
}
$data = [];
foreach ($types as $type) {
$this->{$type} = ClassRegistry::init($type);
$this->{$type}->fetchRecursive = !empty($model->includeAnalystDataRecursive);
$temp = $this->{$type}->fetchForUuid($object['uuid'], $this->__currentUser);
if (!empty($temp)) {
foreach ($temp as $k => $temp_element) {
if (in_array($type, ['Note', 'Opinion', 'Relationship'])) {
$temp_element[$type] = $this->{$type}->fetchChildNotesAndOpinions($this->__currentUser, $temp_element[$type], 1);
}
$data[$type][] = $temp_element[$type];
}
}
}
// include inbound relationship
$data['RelationshipInbound'] = Hash::extract($this->Relationship->getInboundRelationships($this->__currentUser, $model->alias, $object['uuid']), '{n}.Relationship');
return $data;
}
public function fetchAnalystDataBulk(Model $model, array $uuids, array $types = ['Note', 'Opinion', 'Relationship']) {
$uuids = array_chunk($uuids, 100000);
if (empty($this->__currentUser)) {
$user_id = Configure::read('CurrentUserId');
$this->User = ClassRegistry::init('User');
if ($user_id) {
$this->__currentUser = $this->User->getAuthUser($user_id);
}
}
$results = [];
foreach ($uuids as $uuid_chunk) {
foreach ($types as $type) {
$this->{$type} = ClassRegistry::init($type);
$this->{$type}->fetchRecursive = !empty($model->includeAnalystDataRecursive);
$temp = $this->{$type}->fetchForUuids($uuid_chunk, $this->__currentUser);
$results = array_merge_recursive($results, $temp);
}
}
return $results;
}
public function attachAnalystDataBulk(Model $model, array $objects, array $types = ['Note', 'Opinion', 'Relationship'])
{
$uuids = [];
$objects = array_chunk($objects, 100000, true);
if (empty($this->__currentUser)) {
$user_id = Configure::read('CurrentUserId');
$this->User = ClassRegistry::init('User');
if ($user_id) {
$this->__currentUser = $this->User->getAuthUser($user_id);
}
}
foreach ($objects as $chunk => $chunked_objects) {
foreach ($chunked_objects as $k => $object) {
if (!empty($object['uuid'])) {
$uuids[] = $object['uuid'];
}
}
// No uuids, nothing to attach
if (empty($uuids)) {
continue;
}
foreach ($types as $type) {
$this->{$type} = ClassRegistry::init($type);
$this->{$type}->fetchRecursive = !empty($model->includeAnalystDataRecursive);
$temp = $this->{$type}->fetchForUuids($uuids, $this->__currentUser);
if (!empty($temp)) {
foreach ($chunked_objects as $k => $object) {
if (!empty($temp[$object['uuid']])) {
$objects[$chunk][$k][$type] = !empty($objects[$chunk][$k][$type]) ? $objects[$chunk][$k][$type] : [];
$objects[$chunk][$k][$type] = array_merge($objects[$chunk][$k][$type], $temp[$object['uuid']][$type]);
}
}
}
}
}
$objects = call_user_func_array('array_merge', $objects);
return $objects;
}
public function afterFind(Model $model, $results, $primary = false)
{
if (!empty($model->includeAnalystData)) {
foreach ($results as $k => $item) {
if (isset($item[$model->alias])) {
$results[$k] = array_merge($results[$k], $this->attachAnalystData($model, $item[$model->alias]));
}
}
}
return $results;
}
}

6
app/Model/Benchmark.php Normal file
View File

@ -0,0 +1,6 @@
<?php
App::uses('AppModel', 'Model');
class Benchmark extends AppModel
{
}

145
app/Model/Collection.php Normal file
View File

@ -0,0 +1,145 @@
<?php
App::uses('AppModel', 'Model');
class Collection extends AppModel
{
public $recursive = -1;
public $actsAs = array(
'Containable'
);
public $belongsTo = [
'Orgc' => array(
'className' => 'Organisation',
'foreignKey' => 'orgc_id',
'fields' => [
'Orgc.id',
'Orgc.uuid',
'Orgc.name'
]
),
'Org' => array(
'className' => 'Organisation',
'foreignKey' => 'org_id',
'fields' => [
'Org.id',
'Org.uuid',
'Org.name'
]
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'fields' => [
'User.id',
'User.email'
]
)
];
public $hasMany = [
'CollectionElement' => [
'dependent' => true
]
];
public $valid_targets = [
'Attribute',
'Event',
'GalaxyCluster',
'Galaxy',
'Object',
'Note',
'Opinion',
'Relationship',
'Organisation',
'SharingGroup'
];
public $current_user = null;
public function beforeValidate($options = array())
{
if (empty($this->data['Collection'])) {
$this->data = ['Collection' => $this->data];
}
if (empty($this->id) && empty($this->data['Collection']['uuid'])) {
$this->data['Collection']['uuid'] = CakeText::uuid();
}
if (empty($this->id)) {
$this->data['Collection']['user_id'] = $this->current_user['id'];
if (empty($this->data['Collection']['orgc_id']) || empty($this->current_user['Role']['perm_sync'])) {
$this->data['Collection']['orgc_id'] = $this->current_user['Organisation']['id'];
}
$this->data['Collection']['org_id'] = $this->current_user['Organisation']['id'];
$this->data['Collection']['user_id'] = $this->current_user['id'];
}
return true;
}
public function mayModify($user_id, $collection_id)
{
$user = $this->User->getAuthUser($user_id);
$collection = $this->find('first', [
'recursive' => -1,
'conditions' => ['Collection.id' => $collection_id]
]);
if ($user['Role']['perm_site_admin']) {
return true;
}
if (empty($user['Role']['perm_modify'])) {
return false;
}
if (!empty($user['Role']['perm_modify_org'])) {
if ($user['org_id'] == $collection['Collection']['Orgc_id']) {
return true;
}
if ($user['Role']['perm_sync'] && $user['org_id'] == $collection['Collection']['Org_id']) {
return true;
}
}
if (!empty($user['Role']['perm_modify']) && $user['id'] === $collection['Collection']['user_id']) {
}
}
public function mayView($user_id, $collection_id)
{
$user = $this->User->getAuthUser($user_id);
$collection = $this->find('first', [
'recursive' => -1,
'conditions' => ['Collection.id' => $collection_id]
]);
if ($user['Role']['perm_site_admin']) {
return true;
}
if ($collection['Collection']['org_id'] == $user('org_id')) {
return true;
}
if (in_array($collection['Collection']['distribution'], [1,2,3])) {
return true;
}
if ($collection['Collection']['distribution'] === 4) {
$SharingGroup = ClassRegistry::init('SharingGroup');
$sgs = $this->SharingGroup->fetchAllAuthorised($user, 'uuid');
if (isset($sgs[$collection['Collection']['sharing_group_id']])) {
return true;
} else {
return false;
}
}
return false;
}
public function rearrangeCollection(array $collection) {
foreach ($collection as $key => $elements) {
if ($key !== 'Collection') {
$collection['Collection'][$key] = $elements;
unset($collection[$key]);
}
}
return $collection;
}
}

View File

@ -0,0 +1,205 @@
<?php
App::uses('AppModel', 'Model');
class CollectionElement extends AppModel
{
public $recursive = -1;
public $actsAs = array(
'Containable'
);
public $belongsTo = array(
'Collection' => array(
'className' => 'Collection',
'foreignKey' => 'collection_id'
)
);
// Make sure you also update the validation for element_type to include anything you add here.
public $valid_types = [
'Event',
'GalaxyCluster'
];
public $validate = [
'collection_id' => [
'numeric' => [
'rule' => ['numeric']
]
],
'uuid' => [
'uuid' => [
'rule' => 'uuid',
'message' => 'Please provide a valid RFC 4122 UUID'
]
],
'element_uuid' => [
'element_uuid' => [
'rule' => 'uuid',
'message' => 'Please provide a valid RFC 4122 UUID'
]
],
'element_type' => [
'element_type' => [
'rule' => ['inList', ['Event', 'GalaxyCluster']],
'message' => 'Invalid object type.'
]
]
];
public function beforeValidate($options = array())
{
// Massage to a common format
if (empty($this->data['CollectionElement'])) {
$this->data = ['CollectionElement' => $this->data];
}
// if we're creating a new element, assign a uuid (unless provided)
if (empty($this->id) && empty($this->data['CollectionElement']['uuid'])) {
$this->data['CollectionElement']['uuid'] = CakeText::uuid();
}
if (
empty($this->id) &&
empty($this->data['CollectionElement']['element_type']) &&
!empty($this->data['CollectionElement']['element_uuid'])
) {
$this->data['CollectionElement']['element_type'] = $this->deduceType($this->data['CollectionElement']['element_uuid']);
}
return true;
}
public function mayModify(int $user_id, int $collection_id)
{
$user = $this->User->getAuthUser($user_id);
$collection = $this->find('first', [
'recursive' => -1,
'conditions' => ['Collection.id' => $collection_id]
]);
if ($user['Role']['perm_site_admin']) {
return true;
}
if (empty($user['Role']['perm_modify'])) {
return false;
}
if (!empty($user['Role']['perm_modify_org'])) {
if ($user['org_id'] == $collection['Collection']['Orgc_id']) {
return true;
}
if ($user['Role']['perm_sync'] && $user['org_id'] == $collection['Collection']['Org_id']) {
return true;
}
}
if (!empty($user['Role']['perm_modify']) && $user['id'] === $collection['Collection']['user_id']) {
}
}
public function mayView(int $user_id, int $collection_id)
{
$user = $this->User->getAuthUser($user_id);
$collection = $this->find('first', [
'recursive' => -1,
'conditions' => ['Collection.id' => $collection_id]
]);
if ($user['Role']['perm_site_admin']) {
return true;
}
if ($collection['Collection']['org_id'] == $user('org_id')) {
return true;
}
if (in_array($collection['Collection']['distribution'], [1,2,3])) {
return true;
}
if ($collection['Collection']['distribution'] === 4) {
$SharingGroup = ClassRegistry::init('SharingGroup');
$sgs = $this->SharingGroup->fetchAllAuthorised($user, 'uuid');
if (isset($sgs[$collection['Collection']['sharing_group_id']])) {
return true;
} else {
return false;
}
}
return false;
}
public function deduceType(string $uuid)
{
foreach ($this->valid_types as $valid_type) {
$this->{$valid_type} = ClassRegistry::init($valid_type);
$result = $this->$valid_type->find('first', [
'conditions' => [$valid_type.'.uuid' => $uuid],
'recursive' => -1
]);
if (!empty($result)) {
return $valid_type;
}
}
throw new NotFoundException(__('Invalid UUID'));
}
/*
* Pass a Collection as received from another instance to this function to capture the elements
* The received object is authoritative, so all elements that no longer exist in the upstream will be culled.
*/
public function captureElements($data) {
$temp = $this->find('all', [
'recursive' => -1,
'conditions' => ['CollectionElement.collection_id' => $data['Collection']['id']]
]);
$oldElements = [];
foreach ($temp as $oldElement) {
$oldElements[$oldElement['CollectionElement']['uuid']] = $oldElement['CollectionElement'];
}
if (isset($data['Collection']['CollectionElement'])) {
$elementsToSave = [];
foreach ($data['Collection']['CollectionElement'] as $k => $element) {
if (empty($element['uuid'])) {
$element['uuid'] = CakeText::uuid();
}
if (isset($oldElements[$element['uuid']])) {
if (isset($element['description'])) {
$oldElements[$element['uuid']]['description'] = $element['description'];
}
$elementsToSave[$k] = $oldElements[$element['uuid']];
unset($oldElements[$element['uuid']]);
} else {
$elementsToSave[$k] = [
'CollectionElement' => [
'uuid' => $element['uuid'],
'element_uuid' => $element['element_uuid'],
'element_type' => $element['element_type'],
'description' => $element['description'],
'collection_id' => $data['Collection']['id']
]
];
}
}
foreach ($elementsToSave as $k => $element) {
if (empty($element['CollectionElement']['id'])) {
$this->create();
}
try{
$this->save($element);
} catch (PDOException $e) {
// duplicate value?
}
}
foreach ($oldElements as $toDelete) {
$this->delete($toDelete['id']);
}
$temp = $this->find('all', [
'conditions' => ['CollectionElement.collection_id' => $data['Collection']['id']],
'recursive' => -1
]);
$data['Collection']['CollectionElement'] = [];
foreach ($temp as $element) {
$data['Collection']['CollectionElement'][] = $element['CollectionElement'];
}
}
return $data;
}
}

View File

@ -58,6 +58,7 @@ class MysqlExtended extends Mysql
'having' => $this->having($query['having'], true, $Model),
'lock' => $this->getLockingHint($query['lock']),
'indexHint' => $this->__buildIndexHint($query['forceIndexHint'] ?? null),
'ignoreIndexHint' => $this->__buildIgnoreIndexHint($query['ignoreIndexHint'] ?? null)
));
}
@ -90,6 +91,7 @@ class MysqlExtended extends Mysql
'having' => $queryData['having'],
'lock' => $queryData['lock'],
'forceIndexHint' => $queryData['forceIndexHint'] ?? null,
'ignoreIndexHint' => $queryData['ignoreIndexHint'] ?? null,
),
$Model
);
@ -117,14 +119,25 @@ class MysqlExtended extends Mysql
}
/**
* Builds the index hint for the query
* Builds the force index hint for the query
*
* @param string|null $forceIndexHint FORCE INDEX hint
* @param string|null $forceIndexHint INDEX hint
* @return string
*/
private function __buildIndexHint($forceIndexHint = null): ?string
{
return isset($forceIndexHint) ? ('FORCE INDEX ' . $forceIndexHint) : null;
return isset($forceIndexHint) ? ('FORCE INDEX (' . $forceIndexHint . ')') : null;
}
/**
* Builds the ignore index hint for the query
*
* @param string|null $ignoreIndexHint INDEX hint
* @return string
*/
private function __buildIgnoreIndexHint($ignoreIndexHint = null): ?string
{
return isset($ignoreIndexHint) ? ('IGNORE INDEX (' . $ignoreIndexHint . ')') : null;
}
/**
@ -139,7 +152,9 @@ class MysqlExtended extends Mysql
public function execute($sql, $options = [], $params = [])
{
$log = $options['log'] ?? $this->fullDebug;
if (Configure::read('Plugin.Benchmarking_enable')) {
$log = true;
}
if ($log) {
$t = microtime(true);
$this->_result = $this->_execute($sql, $params);
@ -164,6 +179,10 @@ class MysqlExtended extends Mysql
*/
public function insertMulti($table, $fields, $values)
{
if (empty($values)) {
return true;
}
$table = $this->fullTableName($table);
$holder = substr(str_repeat('?,', count($fields)), 0, -1);
$fields = implode(',', array_map([$this, 'name'], $fields));

View File

@ -50,6 +50,7 @@ class MysqlObserverExtended extends Mysql
'having' => $this->having($query['having'], true, $Model),
'lock' => $this->getLockingHint($query['lock']),
'indexHint' => $this->__buildIndexHint($query['forceIndexHint'] ?? null),
'ignoreIndexHint' => $this->__buildIgnoreIndexHint($query['ignoreIndexHint'] ?? null),
));
}
@ -82,6 +83,7 @@ class MysqlObserverExtended extends Mysql
'having' => $queryData['having'],
'lock' => $queryData['lock'],
'forceIndexHint' => $queryData['forceIndexHint'] ?? null,
'ignoreIndexHint' => $queryData['ignoreIndexHint'] ?? null,
),
$Model
);
@ -103,20 +105,31 @@ class MysqlObserverExtended extends Mysql
extract($data);
$having = !empty($having) ? " $having" : '';
$lock = !empty($lock) ? " $lock" : '';
return rtrim("SELECT {$fields} FROM {$table} {$alias} {$indexHint} {$joins} {$conditions} {$group}{$having} {$order} {$limit}{$lock}");
return rtrim("SELECT {$fields} FROM {$table} {$alias} {$indexHint} {$ignoreIndexHint} {$joins} {$conditions} {$group}{$having} {$order} {$limit}{$lock}");
}
return parent::renderStatement($type, $data);
}
/**
* Builds the index hint for the query
* Builds the force index hint for the query
*
* @param string|null $forceIndexHint FORCE INDEX hint
* @param string|null $forceIndexHint INDEX hint
* @return string
*/
private function __buildIndexHint($forceIndexHint = null): ?string
{
return isset($forceIndexHint) ? ('FORCE INDEX ' . $forceIndexHint) : null;
return isset($forceIndexHint) ? ('FORCE INDEX (' . $forceIndexHint . ')') : null;
}
/**
* Builds the ignore index hint for the query
*
* @param string|null $ignoreIndexHint INDEX hint
* @return string
*/
private function __buildIgnoreIndexHint($ignoreIndexHint = null): ?string
{
return isset($ignoreIndexHint) ? ('IGNORE INDEX (' . $ignoreIndexHint . ')') : null;
}
/**
@ -131,6 +144,9 @@ class MysqlObserverExtended extends Mysql
public function execute($sql, $options = [], $params = [])
{
$log = $options['log'] ?? $this->fullDebug;
if (Configure::read('Plugin.Benchmarking_enable')) {
$log = true;
}
$comment = sprintf(
'%s%s%s',
empty(Configure::read('CurrentUserId')) ? '' : sprintf(

Some files were not shown because too many files have changed in this diff Show More