From 8b0e157b1a43b887ba751c13abb0fd237246852d Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Tue, 12 Feb 2019 14:54:56 +0100 Subject: [PATCH 01/53] #863 Add more details to the alert and case audit logs in search page --- ui/app/views/directives/search/audit.html | 125 ++++++++++++++++------ ui/app/views/directives/search/task.html | 2 +- 2 files changed, 95 insertions(+), 32 deletions(-) diff --git a/ui/app/views/directives/search/audit.html b/ui/app/views/directives/search/audit.html index 0458e7ab5b..215427b40c 100644 --- a/ui/app/views/directives/search/audit.html +++ b/ui/app/views/directives/search/audit.html @@ -1,39 +1,102 @@ -
- - {{value.operation}} of {{value.objectType}}
- {{value.stats.name}} -
- - {{value.operation}} of {{value.objectType}}
- {{value.stats.title || value.details.title}} -
- - {{value.operation}} of task
- {{value.stats.title || value.details.title}} -
- - {{value.operation}} of task log - - - {{value.operation}} of observable
- {{value.stats.data}} -
- - {{value.operation}} of observable job
- {{value.stats.analyzerName}} -
- - {{value.operation}} of alert
- {{value.stats.title}} -
-
-
+
- {{getUserInfo.get(value.createdBy) | getField: 'name'}} + by {{getUserInfo.get(value.createdBy) | getField: 'name'}} Occurred on
+ +
+
+ +
+
+ +
+ + {{getUserInfo.get(value.stats.owner) | getField: 'name'}} + + + + + + ( + Closed + on + {{value.stats.endDate | showDate}} + as + {{value.stats.resolutionStatus}}) + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + Date: + {{value.stats.date | showDate}} + + + Type: + {{value.stats.type}} + + + Reference: + {{value.stats.sourceRef}} + + + Source: + {{value.stats.source}} + +
+
+
Details (ExpandCollapse)
{{value.details | flattern | json}}
diff --git a/ui/app/views/directives/search/task.html b/ui/app/views/directives/search/task.html index fdf590e6ba..0eec85211a 100644 --- a/ui/app/views/directives/search/task.html +++ b/ui/app/views/directives/search/task.html @@ -7,7 +7,7 @@ {{getUserInfo.get(value.owner) | getField: 'name'}} - +
From e9ee614d58c427393bb16c61f53fee86e15ceabb Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Tue, 12 Feb 2019 16:27:37 +0100 Subject: [PATCH 02/53] #485 Disallow clicking on dashboard items in edit mode --- .../dashboard/DashboardViewCtrl.js | 20 +++++++++---------- .../scripts/directives/dashboard/counter.js | 18 +++++++---------- ui/app/scripts/directives/dashboard/donut.js | 7 +++++-- ui/app/scripts/directives/dashboard/item.js | 10 +++++----- ui/app/views/directives/dashboard/item.html | 6 +++--- ui/app/views/partials/dashboard/view.html | 2 +- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/ui/app/scripts/controllers/dashboard/DashboardViewCtrl.js b/ui/app/scripts/controllers/dashboard/DashboardViewCtrl.js index 4c2c404e28..7e3805a96d 100644 --- a/ui/app/scripts/controllers/dashboard/DashboardViewCtrl.js +++ b/ui/app/scripts/controllers/dashboard/DashboardViewCtrl.js @@ -82,7 +82,7 @@ this.periodFilter = this.buildDashboardPeriodFilter(period); $scope.$broadcast('refresh-chart', this.periodFilter); - } + }; this.removeContainer = function(index) { var row = this.definition.items[index]; @@ -95,28 +95,28 @@ promise = ModalUtilsSrv.confirm('Remove widget', 'Are you sure you want to remove this item', { okText: 'Yes, remove it', flavor: 'danger' - }) + }); } promise.then(function() { - self.definition.items.splice(index, 1) + self.definition.items.splice(index, 1); }); - } + }; this.saveDashboard = function() { var copy = _.pick(this.dashboard, 'title', 'description', 'status'); copy.definition = angular.toJson(this.definition); DashboardSrv.update(this.dashboard.id, copy) - .then(function(response) { + .then(function(/*response*/) { self.options.editLayout = false; self.resizeCharts(); NotificationSrv.log('The dashboard has been successfully updated', 'success'); }) .catch(function(err) { NotificationSrv.error('DashboardEditCtrl', err.data, err.status); - }) - } + }); + }; this.removeItem = function(rowIndex, colIndex) { @@ -151,15 +151,15 @@ } return item; - } + }; this.itemDragStarted = function(colIndex, row) { row.items.splice(colIndex, 1); - } + }; this.exportDashboard = function() { DashboardSrv.exportDashboard(this.dashboard); - } + }; this.resizeCharts = function() { $timeout(function() { diff --git a/ui/app/scripts/directives/dashboard/counter.js b/ui/app/scripts/directives/dashboard/counter.js index 8d8825e0a6..6997297622 100644 --- a/ui/app/scripts/directives/dashboard/counter.js +++ b/ui/app/scripts/directives/dashboard/counter.js @@ -56,29 +56,25 @@ name: name, label: serie.label, value: data[name] || 0 - } + }; }); - }, function(err) { + }, function(/*err*/) { scope.error = true; NotificationSrv.log('Failed to fetch data, please edit the widget definition', 'error'); }); }; scope.openSearch = function(item) { - var criteria = [{ _type: scope.options.entity }, item.serie.query]; - - if (scope.globalQuery && scope.globalQuery !== '*') { - criteria.push(scope.globalQuery); + if(scope.mode === 'edit') { + return; } - var searchQuery = { - _and: _.without(criteria, null, undefined, '') - }; + var filters = (scope.options.filters || []).concat(item.serie.filters || []); GlobalSearchSrv.saveSection(scope.options.entity, { - search: null, - filters: scope.options.filters.concat(item.serie.filters) + search: filters.length === 0 ? '*' : null, + filters: filters }); $state.go('app.search'); }; diff --git a/ui/app/scripts/directives/dashboard/donut.js b/ui/app/scripts/directives/dashboard/donut.js index a0f4393bc3..078d657031 100644 --- a/ui/app/scripts/directives/dashboard/donut.js +++ b/ui/app/scripts/directives/dashboard/donut.js @@ -81,8 +81,11 @@ names: scope.options.names || {}, colors: scope.options.colors || {}, onclick: function(d) { + if(scope.mode === 'edit') { + return; + } + var fieldDef = scope.entity.attributes[scope.options.field]; - var fieldType = fieldDef.type; var data = { field: scope.options.field, @@ -107,7 +110,7 @@ } }; }, - function(err) { + function(/*err*/) { scope.error = true; NotificationSrv.log('Failed to fetch data, please edit the widget definition', 'error'); } diff --git a/ui/app/scripts/directives/dashboard/item.js b/ui/app/scripts/directives/dashboard/item.js index 5863d8188c..837ea15e26 100644 --- a/ui/app/scripts/directives/dashboard/item.js +++ b/ui/app/scripts/directives/dashboard/item.js @@ -1,6 +1,6 @@ (function() { 'use strict'; - angular.module('theHiveDirectives').directive('dashboardItem', function(DashboardSrv, UserSrv, $uibModal, $timeout, $q) { + angular.module('theHiveDirectives').directive('dashboardItem', function(DashboardSrv, UserSrv, $uibModal, $timeout) { return { restrict: 'E', replace: true, @@ -13,13 +13,13 @@ autoload: '=', refreshOn: '@', resizeOn: '@', - mode: '@', + mode: '=', showEdit: '=', showRemove: '=', onRemove: '&' }, templateUrl: 'views/directives/dashboard/item.html', - link: function(scope, element) { + link: function(scope/*, element*/) { scope.typeClasses = DashboardSrv.typeClasses; scope.timeIntervals = DashboardSrv.timeIntervals; scope.aggregations = DashboardSrv.aggregations; @@ -35,7 +35,7 @@ scope.query = null; if(scope.component.id) { - scope.$on('edit-chart-' + scope.component.id, function(data) { + scope.$on('edit-chart-' + scope.component.id, function(/*data*/) { scope.editItem(); }); } @@ -72,7 +72,7 @@ if(serie.filters) { serie.query = DashboardSrv.buildFiltersQuery(scope.metadata[entity || serie.entity].attributes, serie.filters); } - }) + }); scope.component.options = definition; diff --git a/ui/app/views/directives/dashboard/item.html b/ui/app/views/directives/dashboard/item.html index adb6435556..08bd60745c 100644 --- a/ui/app/views/directives/dashboard/item.html +++ b/ui/app/views/directives/dashboard/item.html @@ -1,7 +1,7 @@

- {{component.options.title || 'No title'}} + {{component.options.title || 'No title'}} {{mode}}

@@ -13,11 +13,11 @@

-
+
dnd-drop="$vm.itemInserted(item, $vm.definition.items, rowIndex, index)"> Date: Tue, 12 Feb 2019 17:09:57 +0100 Subject: [PATCH 03/53] #829 Fix error handling on observable dialog --- ui/app/scripts/controllers/case/ObservableCreationCtrl.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ui/app/scripts/controllers/case/ObservableCreationCtrl.js b/ui/app/scripts/controllers/case/ObservableCreationCtrl.js index e644dd38a3..1dc5f1adb6 100644 --- a/ui/app/scripts/controllers/case/ObservableCreationCtrl.js +++ b/ui/app/scripts/controllers/case/ObservableCreationCtrl.js @@ -142,16 +142,13 @@ $scope.handleSaveFailure = function(response) { $scope.pendingAsync = false; - if (response.status === 400) { + if (response.status === 400 && response.data.type === 'ConflictError') { $scope.failedObservables = $scope.getFailedObservables(response.data); $scope.step = 'error'; - } else { - if(response.data.type === "java.io.IOException") { + if(response.data.type) { NotificationSrv.error('ObservableCreationCtrl', response.data.message, response.status); - } else if(response.data.type === "InternalError") { - NotificationSrv.error('ObservableCreationCtrl', response.data.message, response.status); } else { NotificationSrv.error('ObservableCreationCtrl', 'An unexpected error occurred while creating the observables', response.status); } From 2279f6adff4656007db7040b1b0c0f664c302924 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Tue, 12 Feb 2019 17:39:33 +0100 Subject: [PATCH 04/53] #829 Don't close observable creation dialog if it fails --- ui/app/scripts/controllers/case/ObservableCreationCtrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/scripts/controllers/case/ObservableCreationCtrl.js b/ui/app/scripts/controllers/case/ObservableCreationCtrl.js index 1dc5f1adb6..0e9b05c56e 100644 --- a/ui/app/scripts/controllers/case/ObservableCreationCtrl.js +++ b/ui/app/scripts/controllers/case/ObservableCreationCtrl.js @@ -153,7 +153,7 @@ NotificationSrv.error('ObservableCreationCtrl', 'An unexpected error occurred while creating the observables', response.status); } - $uibModalInstance.close(response); + //$uibModalInstance.close(response); } }; From cd436ea294289d3dce2c73c3cecddd3a59b7ae78 Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 12 Feb 2019 18:15:26 +0100 Subject: [PATCH 05/53] #871 Fix MISP synchro log message --- thehive-misp/app/connectors/misp/MispSynchro.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/thehive-misp/app/connectors/misp/MispSynchro.scala b/thehive-misp/app/connectors/misp/MispSynchro.scala index 37fbd84fe1..eca5e3ba7d 100644 --- a/thehive-misp/app/connectors/misp/MispSynchro.scala +++ b/thehive-misp/app/connectors/misp/MispSynchro.scala @@ -134,9 +134,10 @@ class MispSynchro @Inject() ( } def synchronize(mispConnection: MispConnection, lastSyncDate: Option[Date])(implicit authContext: AuthContext): Source[Try[Alert], NotUsed] = { - logger.info(s"Synchronize MISP ${mispConnection.name} from $lastSyncDate") + val syncFrom = mispConnection.syncFrom(lastSyncDate.getOrElse(new Date(0))) + logger.info(s"Last synchronization of MISP ${mispConnection.name} is ${lastSyncDate.fold("Never")(_.toString)}, synchronize from $syncFrom") // get events that have been published after the last synchronization - mispSrv.getEventsFromDate(mispConnection, mispConnection.syncFrom(lastSyncDate.getOrElse(new Date(0)))) + mispSrv.getEventsFromDate(mispConnection, syncFrom) // get related alert .mapAsyncUnordered(1) { event ⇒ logger.trace(s"Looking for alert misp:${event.source}:${event.sourceRef}") From adbec9085c6337b72e5c5e85b07ee4e88a098e51 Mon Sep 17 00:00:00 2001 From: ZAch Priddy Date: Tue, 12 Feb 2019 20:39:55 -0800 Subject: [PATCH 06/53] Fix Update Label to Warning --- ui/app/views/directives/search/alert.html | 2 +- ui/app/views/partials/alert/list.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/app/views/directives/search/alert.html b/ui/app/views/directives/search/alert.html index 603ac007b1..180e6627ca 100644 --- a/ui/app/views/directives/search/alert.html +++ b/ui/app/views/directives/search/alert.html @@ -39,7 +39,7 @@
Status:
- {{value.status}} + {{value.status}}
diff --git a/ui/app/views/partials/alert/list.html b/ui/app/views/partials/alert/list.html index 8b3bfcbab5..34a018504f 100644 --- a/ui/app/views/partials/alert/list.html +++ b/ui/app/views/partials/alert/list.html @@ -114,7 +114,7 @@

List of alerts ({{$vm.list.total || 0}} of {{alertEvents.c {{::event.type}} - {{::event.status}} + {{::event.status}}
From 11831c5277d9edaa96e1f475f7fb93288e5fdcad Mon Sep 17 00:00:00 2001 From: Saad Kadhi Date: Wed, 13 Feb 2019 06:39:58 +0100 Subject: [PATCH 07/53] Add Crowdstrike2TH (WIP) and Synapse to the alert feeder list --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 19f564f124..67d73e3dc2 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,24 @@ TheHive can be configured to import events from one or multiple [MISP](http://ww [Cortex](https://github.com/TheHive-Project/Cortex/) is the perfect companion for TheHive. Use one or several to analyze observables at scale and respond to incidents. -### Integration with Digital Shadows -TheHive Project provides [DigitalShadows2TH](https://github.com/TheHive-Project/DigitalShadows2TH), a free, open source [Digital Shadows](https://www.digitalshadows.com/) alert feeder for TheHive. You can use it to import Digital Shadows *incidents* and *intel-incidents* as alerts in TheHive, where they can be previewed and transformed into new cases using pre-defined incident response templates or added into existing ones. +### Alert Feeders by TheHive Project -### Integration with Zerofox +#### DigitalShadows2TH +[DigitalShadows2TH](https://github.com/TheHive-Project/DigitalShadows2TH) is a free, open source [Digital Shadows](https://www.digitalshadows.com/) alert feeder for TheHive. You can use it to import Digital Shadows *incidents* and *intel-incidents* as alerts in TheHive, where they can be previewed and transformed into new cases using pre-defined incident response templates or added into existing ones. + +#### Synapse +[Synapse](https://github.com/TheHive-Project/Synapse) is a meta-alert feeder that allows you to centrally feed TheHive from multiple alert sources. It leverages TheHive's API to automate case and alert creation. Case creation from email or alert creation from SIEM event are typical use cases. Currently, Synapse allows you to integrate Exchange, O365 & QRadar. + +#### Zerofox2TH [Zerofox2TH](https://github.com/TheHive-Project/Zerofox2TH) is a free, open source [ZeroFOX](https://www.zerofox.com/) alert feeder for TheHive, written by TheHive Project. You can use it to feed ZeroFOX alerts into TheHive, where they can be previewed and transformed into new cases using pre-defined incident response templates or added into existing ones. +### Alert Feeders from the User Community + +### Integration with Crowdstrike Falcon (WIP) +[Crowdstrike2TH](https://github.com/xg5-simon/CrowdStrike2TH) is a [Crowdstrike Falcon](https://www.crowdstrike.com/endpoint-security-products/) alert feeder for TheHive, written by [Simon](https://github.com/xg5-simon). You can use it to feed Crowdstrike alerts into TheHive, where they can be previewed and transformed into new cases using pre-defined incident response templates or added into existing ones. + +**Note**: this is a work in progress. Currently, the code licensing is unclear. + ### Integration with FireEye iSIGHT [FireEye2TH](https://github.com/LDO-CERT/FireEye2TH) is a free, open source [FireEye iSIGHT](https://www.fireeye.com/) alert feeder for TheHive, written by LDO-CERT. You can use it to feed FireEye iSIGHT alerts into TheHive, where they can be previewed and transformed into new cases using pre-defined incident response templates or added into existing ones. From e179b6e4ad346336cb82de3e9117d9e416215df1 Mon Sep 17 00:00:00 2001 From: To-om Date: Wed, 13 Feb 2019 09:19:24 +0100 Subject: [PATCH 08/53] #869 Use observable TLP if possible when executing a responder --- .../connectors/cortex/services/ActionOperation.scala | 12 ++++++++++++ .../connectors/cortex/services/CortexActionSrv.scala | 12 ++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala b/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala index 061d5dc5dd..8a13182648 100644 --- a/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala +++ b/thehive-cortex/app/connectors/cortex/services/ActionOperation.scala @@ -139,6 +139,18 @@ class ActionOperationSrv @Inject() ( } } + def findArtifactEntity(entity: BaseEntity): Future[Artifact] = { + import org.elastic4play.services.QueryDSL._ + + (entity, entity.model) match { + case (a: Artifact, _) ⇒ Future.successful(a) + case (_, model: ChildModelDef[_, _, _, _]) ⇒ + findSrv(model.parentModel, "_id" ~= entity.parentId.getOrElse(throw InternalError(s"Child entity $entity has no parent ID")), Some("0-1"), Nil) + ._1.runWith(Sink.head).flatMap(findArtifactEntity _) + case _ ⇒ Future.failed(BadRequestError("Artifact not found")) + } + } + def execute(entity: BaseEntity, operation: ActionOperation)(implicit authContext: AuthContext): Future[ActionOperation] = { if (operation.status == ActionOperationStatus.Waiting) { Retry()(classOf[VersionConflictEngineException]) { diff --git a/thehive-cortex/app/connectors/cortex/services/CortexActionSrv.scala b/thehive-cortex/app/connectors/cortex/services/CortexActionSrv.scala index 6b6f068c30..35819f25b1 100644 --- a/thehive-cortex/app/connectors/cortex/services/CortexActionSrv.scala +++ b/thehive-cortex/app/connectors/cortex/services/CortexActionSrv.scala @@ -72,10 +72,14 @@ class CortexActionSrv @Inject() ( def findResponderFor(entityType: String, entityId: String): Future[Seq[Responder]] = { for { - (tlp, pap) ← getEntity(entityType, entityId) - .flatMap(actionOperationSrv.findCaseEntity) - .map { caze ⇒ (caze.tlp(), caze.pap()) } - .recover { case _ ⇒ (0L, 0L) } + entity ← getEntity(entityType, entityId) + artifactTlp ← actionOperationSrv + .findArtifactEntity(entity) + .map(a ⇒ Some(a.tlp())) + .recover { case _ ⇒ None } + (tlp, pap) ← actionOperationSrv.findCaseEntity(entity) + .map { caze ⇒ (artifactTlp.getOrElse(caze.tlp()), caze.pap()) } + .recover { case _ ⇒ (artifactTlp.getOrElse(0L), 0L) } query = Json.obj( "dataTypeList" → s"thehive:$entityType") responders ← findResponders(query) From 639558ff08ec0b3100ce4d7fc1d0797994ffdd82 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Wed, 13 Feb 2019 11:55:17 +0100 Subject: [PATCH 09/53] #870 Add alert id copy link --- ui/app/scripts/controllers/alert/AlertEventCtrl.js | 6 +++++- ui/app/views/partials/alert/event.dialog.html | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/app/scripts/controllers/alert/AlertEventCtrl.js b/ui/app/scripts/controllers/alert/AlertEventCtrl.js index 692c86733e..1176842eee 100644 --- a/ui/app/scripts/controllers/alert/AlertEventCtrl.js +++ b/ui/app/scripts/controllers/alert/AlertEventCtrl.js @@ -1,7 +1,7 @@ (function() { 'use strict'; angular.module('theHiveControllers') - .controller('AlertEventCtrl', function($scope, $rootScope, $state, $uibModal, $uibModalInstance, CustomFieldsCacheSrv, CaseResolutionStatus, AlertingSrv, NotificationSrv, event, templates) { + .controller('AlertEventCtrl', function($scope, $rootScope, $state, $uibModal, $uibModalInstance, CustomFieldsCacheSrv, CaseResolutionStatus, AlertingSrv, NotificationSrv, clipboard, event, templates) { var self = this; var eventId = event.id; @@ -254,6 +254,10 @@ } }; + self.copyId = function(id) { + clipboard.copyText(id); + }; + self.load(); }); })(); diff --git a/ui/app/views/partials/alert/event.dialog.html b/ui/app/views/partials/alert/event.dialog.html index 50d1a948ad..84d5c44c2e 100644 --- a/ui/app/views/partials/alert/event.dialog.html +++ b/ui/app/views/partials/alert/event.dialog.html @@ -1,5 +1,7 @@