From 537238482b42b9d92ce2958e1faf724486dd3035 Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 29 Oct 2021 18:20:46 +0200 Subject: [PATCH 1/5] #2232 Fix broken link between case and alert during migration --- .../src/main/scala/org/thp/thehive/migration/th4/Output.scala | 1 + 1 file changed, 1 insertion(+) 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 7ae698c83b..eaeff8995a 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 @@ -736,6 +736,7 @@ class Output @Inject() ( for { organisation <- getOrganisation(inputAlert.organisation) createdAlert <- alertSrv.createEntity(inputAlert.alert.copy(organisationId = organisation._id, caseId = `case`.fold(EntityId.empty)(_._id))) + _ <- `case`.map(alertSrv.alertCaseSrv.create(AlertCase(), createdAlert, _)).flip tags = inputAlert.alert.tags.flatMap(getTag(_, organisation._id.value).toOption) _ = updateMetaData(createdAlert, inputAlert.metaData) _ <- alertSrv.alertOrganisationSrv.create(AlertOrganisation(), createdAlert, organisation) From 98628acd0028ec625289ea42955c889bec1b0526 Mon Sep 17 00:00:00 2001 From: To-om Date: Fri, 5 Nov 2021 10:19:04 +0100 Subject: [PATCH 2/5] #2233 Fix output of alert observables --- .../cortex/controllers/v0/Conversion.scala | 2 +- .../org/thp/thehive/dto/v0/Observable.scala | 1 + .../thehive/controllers/v0/Conversion.scala | 11 +++++++---- .../controllers/v0/ObservableCtrl.scala | 18 ++++++++++++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/Conversion.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/Conversion.scala index 32b7d2e32e..b6fdd9b9a9 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/Conversion.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/controllers/v0/Conversion.scala @@ -77,7 +77,7 @@ object Conversion { _.case_artifact, jobWithParent._2.fold[Option[OutputObservable]](None) { case (richObservable, richCase) => - Some(observableWithExtraOutput.toValue((richObservable, JsObject.empty, Some(richCase)))) + Some(observableWithExtraOutput.toValue((richObservable, JsObject.empty, Some(Left(richCase))))) } ) .enableMethodAccessors diff --git a/dto/src/main/scala/org/thp/thehive/dto/v0/Observable.scala b/dto/src/main/scala/org/thp/thehive/dto/v0/Observable.scala index 02e7d4fd89..13ea1d7783 100644 --- a/dto/src/main/scala/org/thp/thehive/dto/v0/Observable.scala +++ b/dto/src/main/scala/org/thp/thehive/dto/v0/Observable.scala @@ -71,6 +71,7 @@ case class OutputObservable( stats: JsObject, seen: Option[Boolean], `case`: Option[OutputCase], + alert: Option[OutputAlert], ignoreSimilarity: Option[Boolean] ) diff --git a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala index 5c4047304c..3a66b8fd87 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala @@ -408,13 +408,14 @@ object Conversion { ) .withFieldConst(_.stats, JsObject.empty) .withFieldConst(_.`case`, None) + .withFieldConst(_.alert, None) .enableMethodAccessors .transform ) - implicit val observableWithExtraOutput: Renderer.Aux[(RichObservable, JsObject, Option[RichCase]), OutputObservable] = - Renderer.toJson[(RichObservable, JsObject, Option[RichCase]), OutputObservable] { - case (richObservable, stats, richCase) => + implicit val observableWithExtraOutput: Renderer.Aux[(RichObservable, JsObject, Option[Either[RichCase, RichAlert]]), OutputObservable] = + Renderer.toJson[(RichObservable, JsObject, Option[Either[RichCase, RichAlert]]), OutputObservable] { + case (richObservable, stats, caseOrAlert) => richObservable .into[OutputObservable] .withFieldConst(_._type, "case_artifact") @@ -438,7 +439,8 @@ object Conversion { }) ) .withFieldConst(_.stats, stats) - .withFieldConst(_.`case`, richCase.map(_.toValue)) + .withFieldConst(_.`case`, caseOrAlert.flatMap(_.swap.map(_.toValue).toOption)) + .withFieldConst(_.alert, caseOrAlert.flatMap(_.map(_.toValue).toOption)) .enableMethodAccessors .transform } @@ -470,6 +472,7 @@ object Conversion { ) .withFieldConst(_.stats, stats) .withFieldConst(_.`case`, None) + .withFieldConst(_.alert, None) .enableMethodAccessors .transform } diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index c71af8ca49..5a4d08efe9 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -16,7 +16,6 @@ import org.thp.thehive.services.AlertOps._ import org.thp.thehive.services.CaseOps._ import org.thp.thehive.services.ObservableOps._ import org.thp.thehive.services.OrganisationOps._ -import org.thp.thehive.services.ShareOps._ import org.thp.thehive.services._ import play.api.Configuration import play.api.libs.Files.DefaultTemporaryFileCreator @@ -382,14 +381,25 @@ class PublicObservable @Inject() ( .richPage(from, to, withTotal = true) { case o if withStats => o.richObservableWithCustomRenderer(organisationSrv, observableStatsRenderer(organisationSrv)(authContext))(authContext) - .domainMap(ros => (ros._1, ros._2, None: Option[RichCase])) + .domainMap(ros => (ros._1, ros._2, None: Option[Either[RichCase, RichAlert]])) case o => o.richObservable.domainMap(ro => (ro, JsObject.empty, None)) } case (OutputParam(from, to, _, _), observableSteps, authContext) => observableSteps.richPage(from, to, withTotal = true)( - _.richObservableWithCustomRenderer(organisationSrv, o => o.`case`.richCase(authContext))(authContext).domainMap(roc => - (roc._1, JsObject.empty, Some(roc._2): Option[RichCase]) + _.richObservableWithCustomRenderer( + organisationSrv, + o => o.project(_.by(_.`case`.richCase(authContext).option).by(_.alert.richAlert.option)) + )(authContext).domainMap(roc => + ( + roc._1, + JsObject.empty, + roc._2 match { + case (Some(c), _) => Some(Left(c)) + case (_, Some(a)) => Some(Right(a)) + case _ => None + } + ) ) ) } From aaf62c7610c426befa4e8e22ecf5304eb20d232f Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 8 Nov 2021 09:52:02 +0100 Subject: [PATCH 3/5] #2238 Add API to link alert and case after a broken migration from TH3 --- .../thehive/controllers/v1/AlertCtrl.scala | 23 +++++++++++++++++-- .../thp/thehive/controllers/v1/Router.scala | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala index ecd6ed2077..c520f10cde 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/AlertCtrl.scala @@ -5,7 +5,7 @@ import org.thp.scalligraph.models.Database import org.thp.scalligraph.query._ import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.traversal.{Converter, IteratorOutput, Traversal} -import org.thp.scalligraph.{EntityIdOrName, RichOptionTry} +import org.thp.scalligraph.{BadRequestError, EntityIdOrName, EntityName, RichOptionTry} import org.thp.thehive.controllers.v1.Conversion._ import org.thp.thehive.dto.v1.{InputAlert, InputCustomFieldValue} import org.thp.thehive.models._ @@ -19,7 +19,7 @@ import play.api.mvc.{Action, AnyContent, Results} import java.util.{Map => JMap} import javax.inject.{Inject, Singleton} import scala.reflect.runtime.{universe => ru} -import scala.util.Success +import scala.util.{Failure, Success} case class SimilarCaseFilter() @Singleton @@ -27,6 +27,7 @@ class AlertCtrl @Inject() ( entrypoint: Entrypoint, properties: Properties, alertSrv: AlertSrv, + caseSrv: CaseSrv, caseTemplateSrv: CaseTemplateSrv, userSrv: UserSrv, organisationSrv: OrganisationSrv, @@ -211,4 +212,22 @@ class AlertCtrl @Inject() ( Results.NoContent } } + + def fixCaseLink: Action[AnyContent] = + entrypoint("fix link between case and alert") + .extract("alertName", FieldsParser.string.on("alertName")) + .extract("caseNumber", FieldsParser.string.on("caseNumber")) + .extract("organisation", FieldsParser.string.on("organisation")) + .authPermittedTransaction(db, Permissions.managePlatform) { implicit request => implicit graph => + val alertName: String = request.body("alertName") + val caseNumber: String = request.body("caseNumber") + val organisation: String = request.body("organisation") + for { + organisation <- organisationSrv.getOrFail(EntityIdOrName(organisation)) + alert <- alertSrv.startTraversal.has(_.organisationId, organisation._id).get(EntityName(alertName)).getOrFail("Alert") + _ <- if (alertSrv.get(alert).`case`.exists) Failure(BadRequestError("The alert is already linked to a case")) else Success(()) + c <- caseSrv.getOrFail(EntityName(caseNumber)) + _ <- alertSrv.alertCaseSrv.create(AlertCase(), alert, c) + } yield Results.NoContent + } } diff --git a/thehive/app/org/thp/thehive/controllers/v1/Router.scala b/thehive/app/org/thp/thehive/controllers/v1/Router.scala index 0b98838365..f3c8b9cd0c 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Router.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Router.scala @@ -140,6 +140,7 @@ class Router @Inject() ( case POST(p"/alert/$alertId/follow") => alertCtrl.followAlert(alertId) case POST(p"/alert/$alertId/unfollow") => alertCtrl.unfollowAlert(alertId) case POST(p"/alert/$alertId/case") => alertCtrl.createCase(alertId) + case POST(p"/alert/fixCaseLink") => alertCtrl.fixCaseLink // PATCH /alert/_bulk controllers.AlertCtrl.bulkUpdate() // DELETE /alert/:alertId controllers.AlertCtrl.delete(alertId) // POST /alert/:alertId/merge/:caseId controllers.AlertCtrl.mergeWithCase(alertId, caseId) From 1b5372cc0ee3a8933908e102005579aa07730c47 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 8 Nov 2021 16:04:39 +0100 Subject: [PATCH 4/5] #2240 Accept slash in attachment filename --- ScalliGraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScalliGraph b/ScalliGraph index 837d461fc1..524d49d18b 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 837d461fc13908a1aabb3712baee7c5f37e7a55f +Subproject commit 524d49d18bac9962e2818f41f4e6e617ef79b265 From a87f9485c13212f904fc14bf008055e772c8d992 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 8 Nov 2021 16:08:53 +0100 Subject: [PATCH 5/5] Release 4.1.13 --- CHANGELOG.md | 12 ++++++++++++ build.sbt | 2 +- frontend/bower.json | 2 +- frontend/package.json | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c49ee55a06..78cbb68db5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## [4.1.13](https://github.com/TheHive-Project/TheHive/milestone/83) (2021-11-08) + +**Implemented enhancements:** + +- [Feature Request] Add API to link alert and case after a broken migration from TH3 [\#2238](https://github.com/TheHive-Project/TheHive/issues/2238) + +**Fixed bugs:** + +- [Bug] Migration breaks links between alerts and cases thus rendering all alert statuses as ignored [\#2232](https://github.com/TheHive-Project/TheHive/issues/2232) +- [Bug] Search Section results missing (Observables) [\#2233](https://github.com/TheHive-Project/TheHive/issues/2233) +- [Enhancement] Accept slash in attachment filename [\#2240](https://github.com/TheHive-Project/TheHive/issues/2240) + ## [4.1.12](https://github.com/TheHive-Project/TheHive/milestone/82) (2021-10-29) **Fixed bugs:** diff --git a/build.sbt b/build.sbt index 875d8ecd00..fb81380327 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.1.12-1" +val thehiveVersion = "4.1.13-1" val scala212 = "2.12.13" val scala213 = "2.13.1" val supportedScalaVersions = List(scala212, scala213) diff --git a/frontend/bower.json b/frontend/bower.json index 34f799e860..3a58156006 100644 --- a/frontend/bower.json +++ b/frontend/bower.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.12-1", + "version": "4.1.13-1", "license": "AGPL-3.0", "dependencies": { "jquery": "^3.4.1", diff --git a/frontend/package.json b/frontend/package.json index 76a4762a14..381c114c35 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.12-1", + "version": "4.1.13-1", "license": "AGPL-3.0", "repository": { "type": "git",