diff --git a/CHANGELOG.md b/CHANGELOG.md index e6a42bac96..ac6646e61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Change Log +## [4.0.2](https://github.com/TheHive-Project/TheHive/milestone/64) (2020-11-20) + +**Implemented enhancements:** + +- [Feature Request] Add a dedicated permission to give access to TheHiveFS [\#1655](https://github.com/TheHive-Project/TheHive/issues/1655) +- [Feature Request] Normalize editable input fields [\#1669](https://github.com/TheHive-Project/TheHive/issues/1669) + +**Fixed bugs:** + +- [Bug] Unable to list Cases [\#1598](https://github.com/TheHive-Project/TheHive/issues/1598) +- [Bug] Alert to case merge is broken in v4.0.1 [\#1648](https://github.com/TheHive-Project/TheHive/issues/1648) +- [Bug] Attachment.* filters are broken under observable search in v4.0.1 [\#1649](https://github.com/TheHive-Project/TheHive/issues/1649) +- [Bug] Result of observable update API v0 is empty [\#1652](https://github.com/TheHive-Project/TheHive/issues/1652) +- [Bug] Display issue of custom fields [\#1653](https://github.com/TheHive-Project/TheHive/issues/1653) +- [Bug] Persistent AuditSrv:undefined error on 4.0.1 [\#1656](https://github.com/TheHive-Project/TheHive/issues/1656) +- [Bug] Issues with case attachments section [\#1657](https://github.com/TheHive-Project/TheHive/issues/1657) +- [Bug] API method broken: /api/case/artifact/_search in 4.0.1 [\#1659](https://github.com/TheHive-Project/TheHive/issues/1659) +- [Bug] API method broken: /api/case/task/log/_search in 4.0.1 [\#1660](https://github.com/TheHive-Project/TheHive/issues/1660) +- [Bug] Unable to define ES index on migration [\#1661](https://github.com/TheHive-Project/TheHive/issues/1661) +- [Bug] Dashboard max aggregation on custom-integer field does not work [\#1662](https://github.com/TheHive-Project/TheHive/issues/1662) +- [Bug] Missing the fix for errorMessage [\#1666](https://github.com/TheHive-Project/TheHive/issues/1666) +- [Bug] Fix alert details dialog [\#1672](https://github.com/TheHive-Project/TheHive/issues/1672) +- [Bug] error 500 with adding an empty file in Observables of an Alert [\#1673](https://github.com/TheHive-Project/TheHive/issues/1673) +- [Bug] Fix migration of audit logs [\#1676](https://github.com/TheHive-Project/TheHive/issues/1676) + ## [4.0.1](https://github.com/TheHive-Project/TheHive/milestone/60) (2020-11-13) **Implemented enhancements:** @@ -24,6 +49,7 @@ **Fixed bugs:** +- [Bug] MISP->THEHIVE4 'ExportOnly' and 'Exceptions' ignored in application.conf file [\#1482](https://github.com/TheHive-Project/TheHive/issues/1482) - [Bug] Mobile-responsive Hamburger not visible [\#1290](https://github.com/TheHive-Project/TheHive/issues/1290) - [Bug] Unable to start TheHive after migration [\#1450](https://github.com/TheHive-Project/TheHive/issues/1450) - [Bug] Expired session should show a dialog or login page on pageload [\#1456](https://github.com/TheHive-Project/TheHive/issues/1456) @@ -34,7 +60,6 @@ - [Bug] Dashboard shared/private [\#1474](https://github.com/TheHive-Project/TheHive/issues/1474) - [Bug]Migration tool date/number/duration params don't work [\#1478](https://github.com/TheHive-Project/TheHive/issues/1478) - [Bug] AuditSrv: undefined on non-case page(s), thehive4-4.0.0-1, Ubuntu [\#1479](https://github.com/TheHive-Project/TheHive/issues/1479) -- [Bug] MISP->THEHIVE4 'ExportOnly' and 'Exceptions' ignored in application.conf file [\#1482](https://github.com/TheHive-Project/TheHive/issues/1482) - [Bug] Unable to enumerate tasks via API [\#1483](https://github.com/TheHive-Project/TheHive/issues/1483) - [Bug] Case close notification displays "#undefined" instead of case number [\#1488](https://github.com/TheHive-Project/TheHive/issues/1488) - [Bug] Task under "Waiting tasks" and "My tasks" do not display the case number [\#1489](https://github.com/TheHive-Project/TheHive/issues/1489) diff --git a/ScalliGraph b/ScalliGraph index 0dc00d560b..f6a4d2165c 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 0dc00d560b7c48f5f7a781cd4c9861a64f56cd23 +Subproject commit f6a4d2165c26826c5b28db1a513ade15dfb060f2 diff --git a/build.sbt b/build.sbt index c93640d396..e4fa911b8b 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ import Dependencies._ import com.typesafe.sbt.packager.Keys.bashScriptDefines import org.thp.ghcl.Milestone -val thehiveVersion = "4.0.1-1" +val thehiveVersion = "4.0.2-1" val scala212 = "2.12.12" val scala213 = "2.13.1" val supportedScalaVersions = List(scala212, scala213) diff --git a/docker/README.md b/docker/README.md index 3aadbdb298..2bb97b37c2 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,14 +1,14 @@ ## Example of docker-compose (not for production) With this docker-compose.yml you will be able to run the following images: -- The Hive 4 +- The Hive 4.0.1-1 - Cassandra 3.11 - Cortex 3.1.0-1 - Elasticsearch 7.9.3 - Kibana 7.9.3 -- MISP 2.4.133 +- MISP 2.4.134 - Mysql 8.0.22 - Redis 6.0.9 -- Shuffle 0.7.1 +- Shuffle 0.7.6 ## Some Hint @@ -17,47 +17,46 @@ In docker-compose version is set 3.8, to run this version you need at least Dock ``` Compose file format Docker Engine release 3.8 19.03.0+ -3.7 18.06.0+ -3.6 18.02.0+ -3.5 17.12.0+ -3.4 17.09.0+ +3.7 18.06.0+ +3.6 18.02.0+ +3.5 17.12.0+ +3.4 17.09.0+ ``` If for some reason you have a previous version of Docker Engine or a previous version of Docker Compose and can't upgrade those, you can use 3.7 or 3.6 in docker-compose.yml ### Mapping volumes -If you take a look of docker-compose.yml you will see you need some local folder that needs to be mapped, so before do docker-compose up, ensure folders (and config files) exist: -- ./elasticsearch/data:/usr/share/elasticsearch/data -- ./elasticsearch/logs:/usr/share/elasticsearch/logs +If you take a look of docker-compose.yml you will see you need some local folder that needs to be mapped, so before do docker-compose up, ensure at least folders with config files exist: - ./cortex/application.conf:/etc/cortex/application.conf - ./thehive/application.conf:/etc/thehive/application.conf -- ./data:/data -- ./mysql:/var/lib/mysql Structure would look like: ``` ├── docker-compose.yml -├── elasticsearch -│ └── data -│ └── logs +├── elasticsearch_data +|── elasticsearch_logs ├── cortex │ └── application.conf -└── thehive - └── application.conf -└── data -└── mysql +|── thehive +| └── application.conf +|── data +|── mysql ``` +If you run docker-compose with sudo, ensure you have created elasticsearch_data and elasticsearch_logs folders with non root user, otherwise elasticsearch container will not start. ### ElasticSearch ElasticSearch container likes big mmap count (https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) so from shell you can change with ```sysctl -w vm.max_map_count=262144``` -Due you would run all on same system and maybe you have a limited amount of RAM, better to set some size, for ElasticSearch, in docker-compose.yml I added those: +To set this value permanently, update the vm.max_map_count setting in /etc/sysctl.conf. To verify after rebooting, run sysctl vm.max_map_count + +If you would run all containers on the same system - and maybe you have a limited amount of RAM - better to set some limit, for ElasticSearch, in docker-compose.yml I added those: ```- bootstrap.memory_lock=true``` ```- "ES_JAVA_OPTS=-Xms256m -Xmx256m"``` Adjust depending on your needs and your env. Without these settings in my environment ElasticSearch was using 1.5GB + ### Cassandra Like for ElasticSearch maybe you would run all on same system and maybe you don't have a limited amount of RAM, better to set some size, here for Cassandra, in docker-compose.yml I added those: @@ -68,7 +67,7 @@ Adjust depending on your needs and your env. Without these settings in my enviro ### Cortex-Analyzers - In order to use Analyzers in docker version, it is set the online json url instead absolute path of analyzers in the application.conf of Cortex: - https://dl.bintray.com/thehive-project/cortexneurons/analyzers.json + https://download.thehive-project.org/analyzers.json - In order to use Analyzers in docker version it is set the application.conf thejob: ``` job { runner = [docker] @@ -142,3 +141,21 @@ curl -XPUT -uuser@thehive.local:user@thehive.local -H 'Content-type: application ``` - Now are able to play automation with The Hive, Cortex-Analyzers, MISP thanks to SHUFFLE! + +### Result +In conclusion, after execute ```sudo docker-compose up``` you will have the following services running: + + +| Service | Address | User | Password | +|----------|:-------------:|:------:|------:| +| The Hive | http://localhost:9000 | admin@thehive.local | secret +| Cortex | http://localhost:9001 | | +| Elasticsearch | http://localhost:9200 | | +| Kibana | http://localhost:5601 | | +| MISP | https://localhost:443 | admin@admin.test | admin +| Shuffle | http://localhost:3001 | | + + + + + diff --git a/docker/cortex/application.conf b/docker/cortex/application.conf index 0fe0c01b63..6236c81902 100644 --- a/docker/cortex/application.conf +++ b/docker/cortex/application.conf @@ -179,7 +179,7 @@ analyzer { # - directory where analyzers are installed # - json file containing the list of analyzer descriptions urls = [ - "https://dl.bintray.com/thehive-project/cortexneurons/analyzers.json" + "https://download.thehive-project.org/analyzers.json" #"/absolute/path/of/analyzers" ] @@ -199,7 +199,7 @@ analyzer { responder { # responder location (same format as analyzer.urls) urls = [ - "https://dl.bintray.com/thehive-project/cortexneurons/responders.json" + "https://download.thehive-project.org/responders.json" #"/absolute/path/of/responders" ] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ebd066e945..1bb7b6b63e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -22,8 +22,8 @@ services: soft: 65536 hard: 65536 volumes: - - ./elasticsearch/data:/usr/share/elasticsearch/data - - ./elasticsearch/logs:/usr/share/elasticsearch/logs + - ./elasticsearch_data:/usr/share/elasticsearch/data + - ./elasticsearch_logs:/usr/share/elasticsearch/logs kibana: image: 'docker.elastic.co/kibana/kibana:7.9.3' container_name: kibana @@ -67,7 +67,7 @@ services: - '0.0.0.0:9000:9000' volumes: - ./thehive/application.conf:/etc/thehive/application.conf - - ./data:/data + - ./data:/opt/data command: '--no-config --no-config-secret' redis: @@ -77,6 +77,7 @@ services: db: image: mysql:latest + container_name: mysql restart: unless-stopped command: --default-authentication-plugin=mysql_native_password restart: always @@ -98,12 +99,19 @@ services: - "80:80" - "443:443" environment: - - "HOSTNAME=http://misp" + - "HOSTNAME=https://localhost" - "REDIS_FQDN=redis" - "INIT=true" # Initialze MISP, things includes, attempting to import SQL and the Files DIR - "CRON_USER_ID=1" # The MISP user ID to run cron jobs as - "DISIPV6=true" # Disable IPV6 in nginx - + misp-modules: + image: coolacid/misp-docker:modules-latest + container_name: misp-modules + environment: + - "REDIS_BACKEND=redis" + depends_on: + - redis + - db #READY FOR AUTOMATION ? frontend: diff --git a/docker/thehive/application.conf b/docker/thehive/application.conf index 793d661be0..b6ed0da698 100644 --- a/docker/thehive/application.conf +++ b/docker/thehive/application.conf @@ -20,7 +20,7 @@ db { storage { provider: localfs - localfs.directory: /opt/data + localfs.location: /opt/data } play.modules.enabled += org.thp.thehive.connector.cortex.CortexModule diff --git a/frontend/app/index.html b/frontend/app/index.html index 5460192e47..800b08a1ae 100644 --- a/frontend/app/index.html +++ b/frontend/app/index.html @@ -43,12 +43,14 @@ + + diff --git a/frontend/app/scripts/app.js b/frontend/app/scripts/app.js index 019121422f..800a72397d 100644 --- a/frontend/app/scripts/app.js +++ b/frontend/app/scripts/app.js @@ -411,11 +411,7 @@ angular.module('thehive', [ controller: 'CaseAlertsCtrl', resolve: { alerts: function($stateParams, CaseSrv) { - return CaseSrv.alerts({range: 'all'}, { - query: { - case: $stateParams.caseId - } - }).$promise; + return CaseSrv.alerts($stateParams.caseId); } }, guard: { diff --git a/frontend/app/scripts/controllers/SearchCtrl.js b/frontend/app/scripts/controllers/SearchCtrl.js index 0ba6db0c72..6eb5522550 100644 --- a/frontend/app/scripts/controllers/SearchCtrl.js +++ b/frontend/app/scripts/controllers/SearchCtrl.js @@ -1,7 +1,7 @@ (function() { 'use strict'; angular.module('theHiveControllers') - .controller('SearchCtrl', function($scope, $q, $stateParams, $uibModal, PSearchSrv, CaseTemplateSrv, CaseTaskSrv, NotificationSrv, EntitySrv, UserSrv, QueryBuilderSrv, GlobalSearchSrv, metadata) { + .controller('SearchCtrl', function($scope, $q, $stateParams, $uibModal, PSearchSrv, AlertingSrv, CaseTemplateSrv, CaseTaskSrv, NotificationSrv, EntitySrv, UserSrv, QueryBuilderSrv, GlobalSearchSrv, metadata) { $scope.metadata = metadata; $scope.toolbar = [ // {name: 'all', label: 'All', icon: 'glyphicon glyphicon-search'}, @@ -42,14 +42,24 @@ controllerAs: 'dialog', size: 'max', resolve: { - event: event, + event: function() { + return AlertingSrv.get(event.id); + }, templates: function() { return CaseTemplateSrv.list(); }, readonly: true } - }).result.then(function(/*response*/) { + }) + .result + .then(function(/*response*/) { $scope.searchResults.update(); + }) + .catch(function(err) { + if(err && !_.isString(err)) { + NotificationSrv.error('AlertPreview', err.data, err.status); + } + }); }; diff --git a/frontend/app/scripts/controllers/alert/AlertEventCtrl.js b/frontend/app/scripts/controllers/alert/AlertEventCtrl.js index c36e492711..c1b8d09075 100644 --- a/frontend/app/scripts/controllers/alert/AlertEventCtrl.js +++ b/frontend/app/scripts/controllers/alert/AlertEventCtrl.js @@ -39,11 +39,6 @@ AlertingSrv.get(eventId).then(function(data) { self.event = data; self.loading = false; - - self.dataTypes = _.countBy(self.event.artifacts, function(attr) { - return attr.dataType; - }); - }, function(response) { self.loading = false; NotificationSrv.error('AlertEventCtrl', response.data, response.status); @@ -185,6 +180,7 @@ self.copyId = function(id) { clipboard.copyText(id); + NotificationSrv.log('Alert ID has been copied to clipboard', 'success'); }; this.$onInit = function() { diff --git a/frontend/app/scripts/controllers/case/CaseAlertsCtrl.js b/frontend/app/scripts/controllers/case/CaseAlertsCtrl.js index f410a7e27f..b695e32698 100644 --- a/frontend/app/scripts/controllers/case/CaseAlertsCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseAlertsCtrl.js @@ -1,7 +1,7 @@ (function() { 'use strict'; angular.module('theHiveControllers').controller('CaseAlertsCtrl', - function($scope, $state, $stateParams, $uibModal, $timeout, CaseTabsSrv, VersionSrv, alerts) { + function($scope, $state, $stateParams, $uibModal, $timeout, CaseTabsSrv, VersionSrv, NotificationSrv, alerts) { $scope.caseId = $stateParams.caseId; $scope.alerts = alerts; $scope.alertStats = []; @@ -84,9 +84,16 @@ templates: function() { //return CaseTemplateSrv.list(); return []; - }, + }, readonly: true } + }) + .result + .catch(function(err) { + if(err && !_.isString(err)) { + NotificationSrv.error('AlertPreview', err.data, err.status); + } + }); }; diff --git a/frontend/app/scripts/controllers/case/CaseDetailsCtrl.js b/frontend/app/scripts/controllers/case/CaseDetailsCtrl.js index 699af3d81f..030302eaae 100644 --- a/frontend/app/scripts/controllers/case/CaseDetailsCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseDetailsCtrl.js @@ -17,13 +17,14 @@ version: 'v1', loadAll: false, filter: { - '_contains': 'attachment' + '_contains': 'attachment.id' }, extraData: ['taskId'], pageSize: 100, operations: [ { '_name': 'getCase', 'idOrName': $scope.caseId }, { '_name': 'tasks' }, + { '_name': 'filter', '_ne':{'_field': 'status', '_value': 'Cancel'}}, { '_name': 'logs' }, ] }); diff --git a/frontend/app/scripts/directives/responder-actions.js b/frontend/app/scripts/directives/responder-actions.js index 3dc14a528f..d8fbce6bb2 100644 --- a/frontend/app/scripts/directives/responder-actions.js +++ b/frontend/app/scripts/directives/responder-actions.js @@ -11,7 +11,7 @@ templateUrl: 'views/directives/responder-actions.html', controller: function($scope, $uibModal) { - $scope.$watch('actions', function(list) { + $scope.$watchCollection('actions.values', function(list) { if(!list) { return; } diff --git a/frontend/app/scripts/directives/updatableBoolean.js b/frontend/app/scripts/directives/updatableBoolean.js index e6e231e814..5d84fc826f 100644 --- a/frontend/app/scripts/directives/updatableBoolean.js +++ b/frontend/app/scripts/directives/updatableBoolean.js @@ -12,7 +12,8 @@ 'active': '=?', 'placeholder': '@', 'trueText': '@?', - 'falseText': '@?' + 'falseText': '@?', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableDataDropdown.js b/frontend/app/scripts/directives/updatableDataDropdown.js index 3b1b1598df..6d4670c71e 100644 --- a/frontend/app/scripts/directives/updatableDataDropdown.js +++ b/frontend/app/scripts/directives/updatableDataDropdown.js @@ -10,7 +10,8 @@ 'value': '=?', 'onUpdate': '&', 'active': '=?', - 'placeholder': '@' + 'placeholder': '@', + 'clearable': '' } }; diff --git a/frontend/app/scripts/directives/updatableDate.js b/frontend/app/scripts/directives/updatableDate.js index a8c0bc636f..da2c09d417 100644 --- a/frontend/app/scripts/directives/updatableDate.js +++ b/frontend/app/scripts/directives/updatableDate.js @@ -22,6 +22,12 @@ 'restrict': 'E', 'link': function(scope, element, attrs, ctrl, transclude) { UtilsSrv.updatableLink(scope, element, attrs, ctrl, transclude); + + scope.clear = function() { + scope.value = null; + scope.humanDate = null; + }; + $(element).find('.input-datetime').datetimepicker({ format: 'dd-mm-yyyy hh:ii', weekStart: 1, @@ -53,7 +59,8 @@ 'scope': { 'value': '=?', 'onUpdate': '&', - 'active': '=?' + 'active': '=?', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableSelect.js b/frontend/app/scripts/directives/updatableSelect.js index df9a7d4992..a7cf190707 100644 --- a/frontend/app/scripts/directives/updatableSelect.js +++ b/frontend/app/scripts/directives/updatableSelect.js @@ -11,7 +11,8 @@ 'value': '=?', 'onUpdate': '&', 'active': '=?', - 'placeholder': '@' + 'placeholder': '@', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableSimpleText.js b/frontend/app/scripts/directives/updatableSimpleText.js index 8a0e448dfc..fb724d202a 100644 --- a/frontend/app/scripts/directives/updatableSimpleText.js +++ b/frontend/app/scripts/directives/updatableSimpleText.js @@ -11,7 +11,8 @@ 'value': '=?', 'onUpdate': '&', 'active': '=?', - 'placeholder': '@' + 'placeholder': '@', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableTags.js b/frontend/app/scripts/directives/updatableTags.js index 20341ef70d..d70e9e2339 100644 --- a/frontend/app/scripts/directives/updatableTags.js +++ b/frontend/app/scripts/directives/updatableTags.js @@ -10,7 +10,8 @@ 'value': '=?', 'onUpdate': '&', 'active': '=?', - 'source': '=' + 'source': '=', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableText.js b/frontend/app/scripts/directives/updatableText.js index e074793712..050d1bddce 100644 --- a/frontend/app/scripts/directives/updatableText.js +++ b/frontend/app/scripts/directives/updatableText.js @@ -8,7 +8,8 @@ 'scope': { 'value': '=?', 'onUpdate': '&', - 'active': '=?' + 'active': '=?', + 'clearable': '' } }; }); diff --git a/frontend/app/scripts/directives/updatableUser.js b/frontend/app/scripts/directives/updatableUser.js index 4295438b6d..1722831dd6 100644 --- a/frontend/app/scripts/directives/updatableUser.js +++ b/frontend/app/scripts/directives/updatableUser.js @@ -49,7 +49,8 @@ query: '=', queryParams: '=', onUpdate: '&', - active: '=?' + active: '=?', + clearable: '' } }; }); diff --git a/frontend/app/scripts/services/api/AlertingSrv.js b/frontend/app/scripts/services/api/AlertingSrv.js index 954644aec3..0d20782547 100644 --- a/frontend/app/scripts/services/api/AlertingSrv.js +++ b/frontend/app/scripts/services/api/AlertingSrv.js @@ -1,7 +1,7 @@ (function() { 'use strict'; angular.module('theHiveServices') - .factory('AlertingSrv', function($q, $http, $rootScope, StatSrv, StreamSrv, PSearchSrv, PaginatedQuerySrv, QuerySrv) { + .factory('AlertingSrv', function($q, $http, $rootScope, StatSrv, StreamSrv, PSearchSrv, PaginatedQuerySrv) { var baseUrl = './api/alert'; @@ -144,15 +144,10 @@ }, get: function(alertId) { - return QuerySrv.call('v1', [{ - '_name': 'getAlert', - 'idOrName': alertId - } - ], { - name: 'get-alert-' + alertId - }).then(function(response) { - return response[0]; - }); + return $http.get('./api/v1/alert/' + alertId) + .then(function(response) { + return response.data; + }); }, create: function(alertId, data) { diff --git a/frontend/app/scripts/services/api/CaseSrv.js b/frontend/app/scripts/services/api/CaseSrv.js index e5e391ac98..1597dd9f9a 100644 --- a/frontend/app/scripts/services/api/CaseSrv.js +++ b/frontend/app/scripts/services/api/CaseSrv.js @@ -32,11 +32,11 @@ url: './api/case/_search', isArray: true }, - alerts: { - method: 'POST', - url: './api/alert/_search', - isArray: true - } + // alerts: { + // method: 'POST', + // url: './api/alert/_search', + // isArray: true + // } }); this.get = resource.get; @@ -48,6 +48,23 @@ this.merge = resource.merge; this.query = resource.query; + this.alerts = function(id) { + var defer = $q.defer(); + + QuerySrv.call('v1', [{ + '_name': 'getCase', + 'idOrName': id + }, {'_name': 'alerts'}], { + name:'get-case-alerts' + id + }).then(function(response) { + defer.resolve(response); + }).catch(function(err){ + defer.reject(err); + }); + + return defer.promise; + }; + this.getById = function(id, withStats) { var defer = $q.defer(); diff --git a/frontend/app/scripts/services/api/ProfileSrv.js b/frontend/app/scripts/services/api/ProfileSrv.js index e0c1934a94..40ebe5af40 100644 --- a/frontend/app/scripts/services/api/ProfileSrv.js +++ b/frontend/app/scripts/services/api/ProfileSrv.js @@ -42,7 +42,8 @@ 'manageObservable', 'manageTask', 'manageAction', - 'manageAnalyse' + 'manageAnalyse', + 'accessTheHiveFS' ], labels: { manageUser: 'Manage users', @@ -53,7 +54,8 @@ manageObservable: 'Manage observables', manageTask: 'Manage tasks', manageAction: 'Run Cortex responders', - manageAnalyse: 'Run Cortex analyzer' + manageAnalyse: 'Run Cortex analyzer', + accessTheHiveFS: 'Access to TheHiveFS service' } } }; diff --git a/frontend/app/scripts/services/common/UtilsSrv.js b/frontend/app/scripts/services/common/UtilsSrv.js index 35076d9807..4ff664a257 100644 --- a/frontend/app/scripts/services/common/UtilsSrv.js +++ b/frontend/app/scripts/services/common/UtilsSrv.js @@ -101,6 +101,9 @@ scope.value = scope.oldValue; scope.updatable.updating = false; }; + scope.clear = function() { + scope.value = null; + }; }, extractQueryParam: function(paramName, queryString) { diff --git a/frontend/app/scripts/services/common/ui/AppLayoutSrv.js b/frontend/app/scripts/services/common/ui/AppLayoutSrv.js index 123efd80e1..e5711dc80b 100644 --- a/frontend/app/scripts/services/common/ui/AppLayoutSrv.js +++ b/frontend/app/scripts/services/common/ui/AppLayoutSrv.js @@ -8,7 +8,9 @@ this.init = function() { this.layout = localStorageService.get(key) || { - showFlow: true + showFlow: true, + caseCustomFieldColumns: 3, + alertCustomFieldColumns: 2 }; this.saveLayout(); @@ -33,6 +35,16 @@ this.saveLayout(); }; + this.caseCustomFields = function(columns) { + this.layout.caseCustomFieldColumns = columns; + this.saveLayout(); + }; + + this.alertCustomFields = function(columns) { + this.layout.alertCustomFieldColumns = columns; + this.saveLayout(); + }; + this.detachFlow = function(/*root*/) { this.showFlow(false); $window.open($state.href('live'), 'TheHiveLive', 'width=500,height=700,menubar=no,status=no,toolbar=no,location=no,scrollbars=yes'); diff --git a/frontend/app/styles/custom-fields.css b/frontend/app/styles/custom-fields.css new file mode 100644 index 0000000000..06437a4ed3 --- /dev/null +++ b/frontend/app/styles/custom-fields.css @@ -0,0 +1,3 @@ +.custom-field-input dd { + overflow-x: auto; +} diff --git a/frontend/app/styles/filters.css b/frontend/app/styles/filters.css index 98bce17fd7..691502fd94 100644 --- a/frontend/app/styles/filters.css +++ b/frontend/app/styles/filters.css @@ -27,6 +27,14 @@ padding-left: 10px; } +.active-filters .filter-value { + max-width: 200px; +} + +.active-filters .kv-label .default { + background-color: #eee; +} + .filter-panel { padding: 10px; background-color: #f5f5f5; diff --git a/frontend/app/styles/label.css b/frontend/app/styles/label.css index 2aeadea342..c37c35e2c6 100644 --- a/frontend/app/styles/label.css +++ b/frontend/app/styles/label.css @@ -1,9 +1,9 @@ -.double-val-label { +.kv-label { display: table; margin-top: 0; margin-left: 0; } -.double-val-label>span { +.kv-label>span { color: #ffffff; display: table-cell; font-weight: 400; @@ -13,23 +13,45 @@ vertical-align: baseline; white-space: nowrap; } -.double-val-label>span:first-child { - border-bottom-left-radius: 0.25em; - border-top-left-radius: .25em; +.kv-label span.kv-label-key { + /* border-bottom-left-radius: 0.25em; + border-top-left-radius: .25em; */ border-left-color: #3c8dbc; border-left-width: 3px; border-left-style: solid; } -.double-val-label>span:last-child { +.kv-label span.kv-label-val { border-bottom-right-radius: 0.25em; border-top-right-radius: .25em; border-left: 1px dashed #3c8dbc; + + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; } -.double-val-label>span.primary { + +.kv-label.kv-label-addon span.kv-label-val { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} + +.kv-label.kv-label-addon span:last-child { + border-bottom-right-radius: 0.25em; + border-top-right-radius: .25em; +} + +.kv-label>span.primary { background-color: #3c8dbc; color: #fff; } -.double-val-label>span.default { +.kv-label>span.default { background-color: #d2d6de; color: #444; } + + +.kv-label .tooltip-inner{ + word-wrap: break-word; + text-align: left; + white-space: pre-line; +} diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index cfb024f4db..0425ebbcd9 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -160,7 +160,7 @@ pre.clearpre { /***************************/ .flexwrap { - display: flex; + display: flex !important; flex-wrap: wrap; justify-content: flex-start; align-items: flex-start; @@ -340,6 +340,11 @@ ul.observable-reports-summary li { width: 200px !important; } +.case-details dd, +.case-custom-fields dd { + margin-left: 200px !important; +} + .case-custom-fields dt { background-color: #f9f9f9; padding-left: 5px; @@ -761,3 +766,8 @@ table.tbody-stripped>tbody+tbody { table.tbody-stripped > tbody > tr > td { } + +.ellipsable { + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/frontend/app/styles/updatable.css b/frontend/app/styles/updatable.css new file mode 100644 index 0000000000..13a11cdb75 --- /dev/null +++ b/frontend/app/styles/updatable.css @@ -0,0 +1,42 @@ +.updatable-input .updatable-value { + vertical-align: top; + white-space: pre-wrap; +} + +.updatable-input .updatable-input-value-wrapper { + position: relative; + display: inline-block; + border-bottom: 1px solid #fff; + padding-right: 20px; +} + +.updatable-input .updatable-input-value-wrapper:hover{ + border-bottom: 1px solid #337ab7; + cursor: pointer; +} + +.updatable-input .updatable-input-value-wrapper .updatable-input-icon { + display: none; + float: right; + margin-left: 10px; + color: #337ab7; +} + +.updatable-input .updatable-input-value-wrapper .updatable-input-icon.lg { + line-height: 18px; +} + +.updatable-input .updatable-input-value-wrapper:hover .updatable-input-icon { + display: inline-block; + position: absolute; + top: 0; + right: 0; +} + +.updatable-input.updatable-input-text .updatable-input-markdown { + cursor: auto; +} +.updatable-input.updatable-input-text .updatable-input-markdown .updatable-input-icon { + /* float:left; + margin-left: 0; */ +} diff --git a/frontend/app/views/components/common/custom-field-input.component.html b/frontend/app/views/components/common/custom-field-input.component.html index cde52043e6..ec4ad46a5d 100644 --- a/frontend/app/views/components/common/custom-field-input.component.html +++ b/frontend/app/views/components/common/custom-field-input.component.html @@ -1,33 +1,35 @@ -