From db427253406298ad8abc55054b38e49a66fa219b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Mieczkowski?= Date: Mon, 20 Sep 2021 17:48:08 +0200 Subject: [PATCH 1/7] Migrate observable raxonomies and fix for migrate analyzer reports --- .../migration/dto/InputObservable.scala | 7 +-- .../thehive/migration/th3/Conversion.scala | 54 ++++++++++++++++--- .../thp/thehive/migration/th4/Output.scala | 5 +- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala index 2fb3a179c4..58ba1e2df6 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala @@ -1,10 +1,11 @@ package org.thp.thehive.migration.dto -import org.thp.thehive.models.Observable +import org.thp.thehive.models.{Observable, ReportTag} case class InputObservable( metaData: MetaData, observable: Observable, organisations: Set[String], - dataOrAttachment: Either[String, InputAttachment] -) + dataOrAttachment: Either[String, InputAttachment], + reportTags: List[ReportTag] +) \ No newline at end of file diff --git a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala index 1189893faf..dae65d51b7 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala @@ -10,9 +10,10 @@ import org.thp.thehive.dto.v1.InputCustomFieldValue import org.thp.thehive.migration.dto._ import org.thp.thehive.models._ import play.api.libs.functional.syntax._ +import play.api.libs.json.JsValue.jsValueToJsLookup import play.api.libs.json._ - import java.util.{Base64, Date} +import scala.collection.mutable case class Attachment(name: String, hashes: Seq[Hash], size: Long, contentType: String, id: String) trait Conversion { @@ -101,6 +102,40 @@ trait Conversion { ) } + def getTaxonomies(input: Map[String, JsValue]): List[ReportTag] = { + val taxonomies = mutable.MutableList[ReportTag]() + input.foreach{ case (origin: String, value: JsValue) => + (jsValueToJsLookup(value) \ "taxonomies").asOpt[List[Map[String, JsValue]]].getOrElse(List.empty).foreach(taxonomy => { + taxonomies += ReportTag( + origin, + taxonomy.get("level") match { + case Some(x) => x.asOpt[String].getOrElse("") match { + case "malicious" => ReportTagLevel.malicious + case "suspicious" => ReportTagLevel.suspicious + case "safe" => ReportTagLevel.safe + case "info" => ReportTagLevel.info + } + case None => throw new Exception + }, + taxonomy.get("namespace") match { + case Some(x) => x.asOpt[String].getOrElse("") + case None => throw new Exception + }, + taxonomy.get("predicate") match { + case Some(x) => x.asOpt[String].getOrElse("") + case None => throw new Exception + }, + taxonomy.get("value") match { + case Some(x) => x + case None => throw new Exception + } + ) + }) + } + + return taxonomies.toList + } + implicit val observableReads: Reads[InputObservable] = Reads[InputObservable] { json => for { metaData <- json.validate[MetaData] @@ -109,7 +144,9 @@ trait Conversion { ioc <- (json \ "ioc").validate[Boolean] sighted <- (json \ "sighted").validate[Boolean] dataType <- (json \ "dataType").validate[String] + tags = (json \ "tags").asOpt[Set[String]].getOrElse(Set.empty) + taxonomiesList = getTaxonomies(Json.parse((json \ "reports").asOpt[String].getOrElse("{}")).as[Map[String, JsValue]]) dataOrAttachment <- (json \ "data") .validate[String] @@ -131,7 +168,8 @@ trait Conversion { tags = tags.toSeq ), Set(mainOrganisation), - dataOrAttachment + dataOrAttachment, + taxonomiesList ) } @@ -258,7 +296,8 @@ trait Conversion { tags = tags.toSeq ), Set(mainOrganisation), - dataOrAttachment + dataOrAttachment, + List() ) } @@ -429,8 +468,8 @@ trait Conversion { for { metaData <- json.validate[MetaData] workerId <- (json \ "analyzerId").validate[String] - workerName <- (json \ "analyzerId").validate[String] - workerDefinition <- (json \ "analyzerId").validate[String] + workerName <- (json \ "analyzerName").validate[String] + workerDefinition <- (json \ "analyzerDefinition").validate[String] status <- (json \ "status").validate[JobStatus.Value] startDate <- (json \ "createdAt").validate[Date] endDate <- (json \ "endDate").validate[Date] @@ -484,7 +523,8 @@ trait Conversion { tags = tags.toSeq ), Set(mainOrganisation), - dataOrAttachment + dataOrAttachment, + List() ) } @@ -552,4 +592,4 @@ trait Conversion { ) ) } -} +} \ No newline at end of file diff --git a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala index 89a97b32a3..60ae2e6f4d 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala @@ -93,6 +93,7 @@ class Output @Inject() ( caseSrv: CaseSrv, observableSrvProvider: Provider[ObservableSrv], dataSrv: DataSrv, + reportTagSrv: ReportTagSrv, userSrv: UserSrv, tagSrv: TagSrv, caseTemplateSrv: CaseTemplateSrv, @@ -697,6 +698,7 @@ class Output @Inject() ( for { organisations <- inputObservable.organisations.toTry(getOrganisation) richObservable <- createObservable(caseId, inputObservable, organisations.map(_._id).toSet) + _ <- reportTagSrv.updateTags(richObservable, "not_existing_origin", inputObservable.reportTags) case0 <- getCase(caseId) _ <- organisations.toTry(o => shareSrv.shareObservable(RichObservable(richObservable, None, None, Nil), case0, o._id)) } yield IdMapping(inputObservable.metaData.id, richObservable._id) @@ -730,6 +732,7 @@ class Output @Inject() ( authTransaction(inputAlert.metaData.createdBy) { implicit graph => implicit authContext => logger.debug(s"Create alert ${inputAlert.alert.`type`}:${inputAlert.alert.source}:${inputAlert.alert.sourceRef}") val `case` = inputAlert.caseId.flatMap(c => getCase(EntityId.read(c)).toOption) + for { organisation <- getOrganisation(inputAlert.organisation) createdAlert <- alertSrv.createEntity(inputAlert.alert.copy(organisationId = organisation._id, caseId = `case`.fold(EntityId.empty)(_._id))) @@ -818,4 +821,4 @@ class Output @Inject() ( _ <- context.map(auditSrv.auditContextSrv.create(AuditContext(), createdAudit, _)).flip } yield () } -} +} \ No newline at end of file From 2b6550a5aaa7e9fda248954019966380cc11becc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Mieczkowski?= Date: Mon, 20 Sep 2021 17:49:23 +0200 Subject: [PATCH 2/7] Fix new lines --- .../scala/org/thp/thehive/migration/dto/InputObservable.scala | 2 +- .../main/scala/org/thp/thehive/migration/th3/Conversion.scala | 2 +- .../src/main/scala/org/thp/thehive/migration/th4/Output.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala index 58ba1e2df6..76a4aeaac8 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala @@ -8,4 +8,4 @@ case class InputObservable( organisations: Set[String], dataOrAttachment: Either[String, InputAttachment], reportTags: List[ReportTag] -) \ No newline at end of file +) diff --git a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala index dae65d51b7..bb20dcc465 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala @@ -592,4 +592,4 @@ trait Conversion { ) ) } -} \ No newline at end of file +} diff --git a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala index 60ae2e6f4d..025800229d 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala @@ -821,4 +821,4 @@ class Output @Inject() ( _ <- context.map(auditSrv.auditContextSrv.create(AuditContext(), createdAudit, _)).flip } yield () } -} \ No newline at end of file +} From 56caa31ffcb83dfc9e1ed2224e2812119299b9aa Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 1 Oct 2021 15:57:58 +0200 Subject: [PATCH 3/7] #2206 Rewrite using scala style --- .../connector/cortex/services/JobSrv.scala | 2 +- .../org/thp/thehive/migration/Migrate.scala | 2 +- .../migration/dto/InputObservable.scala | 2 +- .../thehive/migration/th3/Conversion.scala | 54 +++++++------------ .../thp/thehive/migration/th4/Output.scala | 2 +- .../thp/thehive/services/ReportTagSrv.scala | 6 ++- 6 files changed, 27 insertions(+), 41 deletions(-) diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala index 34d5df72bf..7152f60ca6 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/JobSrv.scala @@ -195,7 +195,7 @@ class JobSrv @Inject() ( val tags = cortexJob.report.fold[Seq[ReportTag]](Nil)(_.summary.map(_.toAnalyzerTag(job.workerName))) for { observable <- get(job).observable.getOrFail("Observable") - _ <- reportTagSrv.updateTags(observable, job.workerName, tags) + _ <- reportTagSrv.updateTags(observable, tags) } yield () } else Success(()) diff --git a/migration/src/main/scala/org/thp/thehive/migration/Migrate.scala b/migration/src/main/scala/org/thp/thehive/migration/Migrate.scala index 32d32e2267..8bf1397ba5 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/Migrate.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/Migrate.scala @@ -60,7 +60,7 @@ object Migrate extends App with MigrationOps { .valueName("http://ip1:port,ip2:port") .text("TheHive3 ElasticSearch URI") .action((u, c) => addConfig(c, "input.search.uri", u)), - opt[String]('i', "es-index") + opt[String]('e', "es-index") .valueName("") .text("TheHive3 ElasticSearch index name") .action((i, c) => addConfig(c, "input.search.index", i)), diff --git a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala index 76a4aeaac8..a4154c54fe 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/dto/InputObservable.scala @@ -7,5 +7,5 @@ case class InputObservable( observable: Observable, organisations: Set[String], dataOrAttachment: Either[String, InputAttachment], - reportTags: List[ReportTag] + reportTags: Seq[ReportTag] ) diff --git a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala index bb20dcc465..575a119432 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th3/Conversion.scala @@ -12,8 +12,9 @@ import org.thp.thehive.models._ import play.api.libs.functional.syntax._ import play.api.libs.json.JsValue.jsValueToJsLookup import play.api.libs.json._ + import java.util.{Base64, Date} -import scala.collection.mutable +import scala.util.Try case class Attachment(name: String, hashes: Seq[Hash], size: Long, contentType: String, id: String) trait Conversion { @@ -102,38 +103,22 @@ trait Conversion { ) } - def getTaxonomies(input: Map[String, JsValue]): List[ReportTag] = { - val taxonomies = mutable.MutableList[ReportTag]() - input.foreach{ case (origin: String, value: JsValue) => - (jsValueToJsLookup(value) \ "taxonomies").asOpt[List[Map[String, JsValue]]].getOrElse(List.empty).foreach(taxonomy => { - taxonomies += ReportTag( - origin, - taxonomy.get("level") match { - case Some(x) => x.asOpt[String].getOrElse("") match { - case "malicious" => ReportTagLevel.malicious - case "suspicious" => ReportTagLevel.suspicious - case "safe" => ReportTagLevel.safe - case "info" => ReportTagLevel.info - } - case None => throw new Exception - }, - taxonomy.get("namespace") match { - case Some(x) => x.asOpt[String].getOrElse("") - case None => throw new Exception - }, - taxonomy.get("predicate") match { - case Some(x) => x.asOpt[String].getOrElse("") - case None => throw new Exception - }, - taxonomy.get("value") match { - case Some(x) => x - case None => throw new Exception + implicit val taxonomiesReads: Reads[Seq[ReportTag]] = Reads.JsObjectReads.map { input => + input.fields.flatMap { + case (origin, value) => + (value \ "taxonomies") + .asOpt[Seq[JsValue]] + .getOrElse(Nil) + .flatMap { taxonomy => + for { + namespace <- (taxonomy \ "namespace").asOpt[String] + predicate <- (taxonomy \ "predicate").asOpt[String] + value = (taxonomy \ "value").getOrElse(JsNull) + levelName <- (taxonomy \ "level").asOpt[String] + level <- Try(ReportTagLevel.withName(levelName)).toOption + } yield ReportTag(origin, level, namespace, predicate, value) } - ) - }) } - - return taxonomies.toList } implicit val observableReads: Reads[InputObservable] = Reads[InputObservable] { json => @@ -144,9 +129,8 @@ trait Conversion { ioc <- (json \ "ioc").validate[Boolean] sighted <- (json \ "sighted").validate[Boolean] dataType <- (json \ "dataType").validate[String] - tags = (json \ "tags").asOpt[Set[String]].getOrElse(Set.empty) - taxonomiesList = getTaxonomies(Json.parse((json \ "reports").asOpt[String].getOrElse("{}")).as[Map[String, JsValue]]) + taxonomiesList <- Json.parse((json \ "reports").asOpt[String].getOrElse("{}")).validate[Seq[ReportTag]] dataOrAttachment <- (json \ "data") .validate[String] @@ -297,7 +281,7 @@ trait Conversion { ), Set(mainOrganisation), dataOrAttachment, - List() + Nil ) } @@ -524,7 +508,7 @@ trait Conversion { ), Set(mainOrganisation), dataOrAttachment, - List() + Nil ) } diff --git a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala index 025800229d..7ae698c83b 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th4/Output.scala @@ -698,7 +698,7 @@ class Output @Inject() ( for { organisations <- inputObservable.organisations.toTry(getOrganisation) richObservable <- createObservable(caseId, inputObservable, organisations.map(_._id).toSet) - _ <- reportTagSrv.updateTags(richObservable, "not_existing_origin", inputObservable.reportTags) + _ <- reportTagSrv.updateTags(richObservable, inputObservable.reportTags) case0 <- getCase(caseId) _ <- organisations.toTry(o => shareSrv.shareObservable(RichObservable(richObservable, None, None, Nil), case0, o._id)) } yield IdMapping(inputObservable.metaData.id, richObservable._id) diff --git a/thehive/app/org/thp/thehive/services/ReportTagSrv.scala b/thehive/app/org/thp/thehive/services/ReportTagSrv.scala index ef2c33d214..bc622d5f6f 100644 --- a/thehive/app/org/thp/thehive/services/ReportTagSrv.scala +++ b/thehive/app/org/thp/thehive/services/ReportTagSrv.scala @@ -17,11 +17,13 @@ import scala.util.Try class ReportTagSrv @Inject() (observableSrv: ObservableSrv) extends VertexSrv[ReportTag] { val observableReportTagSrv = new EdgeSrv[ObservableReportTag, Observable, ReportTag] - def updateTags(observable: Observable with Entity, origin: String, reportTags: Seq[ReportTag])(implicit + def updateTags(observable: Observable with Entity, reportTags: Seq[ReportTag])(implicit graph: Graph, authContext: AuthContext ): Try[Unit] = { - observableSrv.get(observable).reportTags.fromOrigin(origin).remove() + reportTags.map(_.origin).distinct.foreach { origin => + observableSrv.get(observable).reportTags.fromOrigin(origin).remove() + } reportTags .toTry { tag => createEntity(tag).flatMap(t => observableReportTagSrv.create(ObservableReportTag(), observable, t)) From c88f3e98d5f179f835bf5ab132584d5e3faf1143 Mon Sep 17 00:00:00 2001 From: Vincent Debergue Date: Thu, 22 Jul 2021 18:10:58 +0200 Subject: [PATCH 4/7] #2125 Be able to update the type of observable --- .../thehive/controllers/v0/ObservableCtrl.scala | 12 +++++++++++- .../thp/thehive/controllers/v1/Properties.scala | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index 70ae84b648..e42332554d 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -358,6 +358,7 @@ class ObservableCtrl @Inject() ( @Singleton class PublicObservable @Inject() ( + observableTypeSrv: ObservableTypeSrv, observableSrv: ObservableSrv, organisationSrv: OrganisationSrv ) extends PublicData @@ -424,7 +425,16 @@ class PublicObservable @Inject() ( ) .property("message", UMapping.string)(_.field.updatable) .property("tlp", UMapping.int)(_.field.updatable) - .property("dataType", UMapping.string)(_.field.readonly) + .property("dataType", UMapping.string)(_.field.custom { (_, value, vertex, graph, _) => + val observable = observableSrv.model.converter(vertex) + for { + currentDataType <- observableTypeSrv.getByName(observable.dataType)(graph).getOrFail("ObservableType") + newDataType <- observableTypeSrv.getByName(value)(graph).getOrFail("ObservableType") + isSameType = currentDataType.isAttachment == newDataType.isAttachment + _ <- if (isSameType) Success(()) else Failure(BadRequestError("Can not update dataType: isAttachment does not match")) + _ <- Try(observableSrv.get(vertex)(graph).update(_.dataType, value).iterate()) + } yield Json.obj("dataType" -> value) + }) .property("data", UMapping.string.optional)(_.field.readonly) .property("attachment.name", UMapping.string.optional)(_.select(_.attachments.value(_.name)).readonly) .property("attachment.hashes", UMapping.hash.sequence)(_.select(_.attachments.value(_.hashes)).readonly) diff --git a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala index a262ff3bf0..4992217ba7 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala @@ -28,7 +28,7 @@ import org.thp.thehive.services._ import play.api.libs.json.{JsObject, JsValue, Json} import javax.inject.{Inject, Singleton} -import scala.util.Failure +import scala.util.{Failure, Success, Try} @Singleton class Properties @Inject() ( @@ -39,6 +39,7 @@ class Properties @Inject() ( dashboardSrv: DashboardSrv, caseTemplateSrv: CaseTemplateSrv, observableSrv: ObservableSrv, + observableTypeSrv: ObservableTypeSrv, customFieldSrv: CustomFieldSrv, organisationSrv: OrganisationSrv, db: Database @@ -440,7 +441,16 @@ class Properties @Inject() ( ) .property("message", UMapping.string)(_.field.updatable) .property("tlp", UMapping.int)(_.field.updatable) - .property("dataType", UMapping.string)(_.field.readonly) + .property("dataType", UMapping.string)(_.field.custom { (_, value, vertex, graph, _) => + val observable = observableSrv.model.converter(vertex) + for { + currentDataType <- observableTypeSrv.getByName(observable.dataType)(graph).getOrFail("ObservableType") + newDataType <- observableTypeSrv.getByName(value)(graph).getOrFail("ObservableType") + isSameType = currentDataType.isAttachment == newDataType.isAttachment + _ <- if (isSameType) Success(()) else Failure(BadRequestError("Can not update dataType: isAttachment does not match")) + _ <- Try(observableSrv.get(vertex)(graph).update(_.dataType, value).iterate()) + } yield Json.obj("dataType" -> value) + }) .property("data", UMapping.string.optional)(_.field.readonly) .property("attachment.name", UMapping.string.optional)(_.select(_.attachments.value(_.name)).readonly) .property("attachment.hashes", UMapping.hash.sequence)(_.select(_.attachments.value(_.hashes)).readonly) From 7625676ca2136a6243ca677f53faa82f57487845 Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Wed, 6 Oct 2021 07:55:53 +0200 Subject: [PATCH 5/7] #2125 Allow observable datatype update in bullk edit modal --- .../controllers/case/CaseObservablesCtrl.js | 8 +- .../controllers/case/ObservableUpdateCtrl.js | 142 +++++++++++------- .../observables/observable.update.html | 87 +++++++---- 3 files changed, 156 insertions(+), 81 deletions(-) diff --git a/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js b/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js index 95d3b631fb..c86a5fe39f 100644 --- a/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js +++ b/frontend/app/scripts/controllers/case/CaseObservablesCtrl.js @@ -1,7 +1,7 @@ (function () { 'use strict'; angular.module('theHiveControllers').controller('CaseObservablesCtrl', - function ($scope, $q, $state, $stateParams, $filter, $uibModal, SecuritySrv, ModalUtilsSrv, FilteringSrv, StreamSrv, CaseTabsSrv, PaginatedQuerySrv, CaseArtifactSrv, NotificationSrv, AnalyzerSrv, CortexSrv, VersionSrv) { + function ($scope, $q, $state, $stateParams, $filter, $uibModal, SecuritySrv, ModalUtilsSrv, FilteringSrv, StreamSrv, CaseTabsSrv, PaginatedQuerySrv, ObservableTypeSrv, CaseArtifactSrv, NotificationSrv, AnalyzerSrv, CortexSrv, VersionSrv) { CaseTabsSrv.activateTab($state.current.data.tab); @@ -241,6 +241,12 @@ resolve: { selection: function () { return $scope.selection.artifacts; + }, + dataTypes: function () { + return ObservableTypeSrv.list() + .then(function (response) { + return response.data; + }); } } }); diff --git a/frontend/app/scripts/controllers/case/ObservableUpdateCtrl.js b/frontend/app/scripts/controllers/case/ObservableUpdateCtrl.js index f596796264..04691a77aa 100644 --- a/frontend/app/scripts/controllers/case/ObservableUpdateCtrl.js +++ b/frontend/app/scripts/controllers/case/ObservableUpdateCtrl.js @@ -1,34 +1,49 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveControllers').controller('ObservableUpdateCtrl', - function($scope, $uibModalInstance, TagSrv, TaxonomyCacheSrv, selection) { + function ($scope, $uibModalInstance, TagSrv, TaxonomyCacheSrv, selection, dataTypes) { var self = this; this.selection = selection; - this.state = { - all: false, - enableTlp: false, - enableIoc: false, - enableSighted: false, - enableIgnoreSimilarity: false, - enableAddTags: false, - enableRemoveTags: false - }; + this.dataTypes = _.pluck(_.filter(dataTypes, function (item) { + return !item.isAttachment + }), 'name').sort(); + + this.$onInit = function () { + this.state = { + all: false, + enableDataType: false, + enableTlp: false, + enableIoc: false, + enableSighted: false, + enableIgnoreSimilarity: false, + enableAddTags: false, + enableRemoveTags: false + }; - this.activeTlp = 'active'; - this.params = { - ioc: false, - sighted: false, - ignoreSimilarity: false, - tlp: 2, - addTagNames: '', - removeTagNames: '' - }; + this.activeTlp = 'active'; + this.params = { + dataType: null, + ioc: false, + sighted: false, + ignoreSimilarity: false, + tlp: 2, + addTagNames: '', + removeTagNames: '' + }; + + var selectionTypes = _.unique(_.pluck(this.selection, 'dataType')); - this.toggleAll = function() { + this.canUpdateDataType = selectionTypes.length === 1 && this.dataTypes.indexOf(selectionTypes[0]) !== -1; + }; + this.toggleAll = function () { this.state.all = !this.state.all; + if (this.canUpdateDataType) { + this.state.enableDataType = this.state.all; + } + this.state.enableTlp = this.state.all; this.state.enableIoc = this.state.all; this.state.enableSighted = this.state.all; @@ -37,14 +52,14 @@ this.state.enableRemoveTags = this.state.all; }; - this.categorizeObservables = function() { + this.categorizeObservables = function () { var data = { withTags: [], withoutTags: [] }; - _.each(this.selection, function(item) { - if(item.tags.length > 0) { + _.each(this.selection, function (item) { + if (item.tags.length > 0) { data.withTags.push(item); } else { data.withoutTags.push(item); @@ -54,11 +69,17 @@ return data; }; - this.buildOperations = function(postData) { - var flags = _.pick(postData, 'ioc', 'sighted', 'ignoreSimilarity', 'tlp'); + this.buildOperations = function (postData) { + var flags = {}; + + if (this.canUpdateDataType) { + flags = _.pick(postData, 'dataType', 'ioc', 'sighted', 'ignoreSimilarity', 'tlp'); + } else { + flags = _.pick(postData, 'ioc', 'sighted', 'ignoreSimilarity', 'tlp'); + } // Handle updates without tag changes - if(!postData.addTags && !postData.removeTags) { + if (!postData.addTags && !postData.removeTags) { return [ { ids: _.pluck(this.selection, '_id'), @@ -70,28 +91,28 @@ // Handle update with tag changes var input = this.categorizeObservables(); var operations = []; - if(input.withoutTags.length > 0) { - var tags = (postData.addTags || []).filter(function(i) { + if (input.withoutTags.length > 0) { + var tags = (postData.addTags || []).filter(function (i) { return (postData.removeTags || []).indexOf(i) === -1; }); operations.push({ ids: _.pluck(input.withoutTags, '_id'), - patch: _.extend({}, flags ,{ + patch: _.extend({}, flags, { tags: _.unique(tags) }) }); } - if(input.withTags.length > 0) { - _.each(input.withTags, function(observable) { - tags = observable.tags.concat(postData.addTags || []).filter(function(i) { + if (input.withTags.length > 0) { + _.each(input.withTags, function (observable) { + tags = observable.tags.concat(postData.addTags || []).filter(function (i) { return (postData.removeTags || []).indexOf(i) === -1; }); operations.push({ ids: [observable._id], - patch: _.extend({}, flags ,{ + patch: _.extend({}, flags, { tags: _.unique(tags) }) }); @@ -101,49 +122,53 @@ return operations; }; - this.save = function() { + this.save = function () { var postData = {}; - if(this.state.enableIoc) { + if (this.state.enableDataType && this.canUpdateDataType) { + postData.dataType = this.params.dataType; + } + + if (this.state.enableIoc) { postData.ioc = this.params.ioc; } - if(this.state.enableSighted) { + if (this.state.enableSighted) { postData.sighted = this.params.sighted; } - if(this.state.enableIgnoreSimilarity) { + if (this.state.enableIgnoreSimilarity) { postData.ignoreSimilarity = this.params.ignoreSimilarity; } - if(this.state.enableTlp) { + if (this.state.enableTlp) { postData.tlp = this.params.tlp; } - if(this.state.enableAddTags) { + if (this.state.enableAddTags) { postData.addTags = _.pluck(this.params.addTags, 'text'); } - if(this.state.enableRemoveTags) { + if (this.state.enableRemoveTags) { postData.removeTags = _.pluck(this.params.removeTags, 'text'); } $uibModalInstance.close(this.buildOperations(postData)); }; - this.cancel = function() { + this.cancel = function () { $uibModalInstance.dismiss(); }; - this.getTags = function(query) { + this.getTags = function (query) { return TagSrv.autoComplete(query); }; - self.fromTagLibrary = function(field) { + self.fromTagLibrary = function (field) { TaxonomyCacheSrv.openTagLibrary() - .then(function(tags){ - if(field === 'add') { + .then(function (tags) { + if (field === 'add') { self.params.addTags = (self.params.addTags || []).concat(tags); self.toggleAddTags(); } else if (field === 'remove') { @@ -153,40 +178,49 @@ }) }; - this.toogleIoc = function() { + this.toggleDataType = function () { + if (!this.params.dataType || this.params.dataType.length === 0) { + this.params.dataType = null; + this.state.enableDataType = false; + } else { + this.state.enableDataType = true; + } + }; + + this.toogleIoc = function () { this.params.ioc = !this.params.ioc; this.state.enableIoc = true; }; - this.toogleSighted = function() { + this.toogleSighted = function () { this.params.sighted = !this.params.sighted; this.state.enableSighted = true; }; - this.toogleIgnoreSimilarity = function() { + this.toogleIgnoreSimilarity = function () { this.params.ignoreSimilarity = !this.params.ignoreSimilarity; this.state.enableIgnoreSimilarity = true; }; - this.toggleTlp = function(value) { + this.toggleTlp = function (value) { this.params.tlp = value; this.activeTlp = 'active'; this.state.enableTlp = true; }; - this.toggleAddTags = function() { + this.toggleAddTags = function () { this.state.enableAddTags = true; }; - this.toggleRemoveTags = function() { + this.toggleRemoveTags = function () { this.state.enableRemoveTags = true; }; - $scope.$watchCollection('$dialog.params.addTags', function(value) { + $scope.$watchCollection('$dialog.params.addTags', function (value) { self.params.addTagNames = _.pluck(value, 'text').join(','); }); - $scope.$watchCollection('$dialog.params.removeTags', function(value) { + $scope.$watchCollection('$dialog.params.removeTags', function (value) { self.params.removeTagNames = _.pluck(value, 'text').join(','); }); } diff --git a/frontend/app/views/partials/observables/observable.update.html b/frontend/app/views/partials/observables/observable.update.html index ebb3dc5bf1..1a7aac2b46 100644 --- a/frontend/app/views/partials/observables/observable.update.html +++ b/frontend/app/views/partials/observables/observable.update.html @@ -5,67 +5,94 @@