From 803e98daf334b8f017f799590ce680cea54d08ac Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 19 Apr 2022 12:03:11 +0200 Subject: [PATCH 1/6] #2374 Fix typo in http config --- .../scala/org/thp/thehive/migration/th3/ElasticClient.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/src/main/scala/org/thp/thehive/migration/th3/ElasticClient.scala b/migration/src/main/scala/org/thp/thehive/migration/th3/ElasticClient.scala index 3fda736894..e45e7684bb 100644 --- a/migration/src/main/scala/org/thp/thehive/migration/th3/ElasticClient.scala +++ b/migration/src/main/scala/org/thp/thehive/migration/th3/ElasticClient.scala @@ -56,7 +56,7 @@ class ElasticClientProvider @Inject() ( val followRedirects = config.getOptional[Boolean]("search.redirectsEnabled") val maxNumberOfRedirects = config.getOptional[Int]("search.maxRedirects") - val wsConfig = Try(Json.parse(config.underlying.getValue("search.trustStore.wsConfig").render(ConfigRenderOptions.concise())).as[ProxyWSConfig]) + val wsConfig = Try(Json.parse(config.underlying.getValue("search.wsConfig").render(ConfigRenderOptions.concise())).as[ProxyWSConfig]) .getOrElse(ProxyWSConfig(AhcWSClientConfig(), None)) .merge(trustManager) { (cfg, tm) => cfg.copy(wsConfig = From 342a9dd8d56268a65f3ebd37debb6a24bd119d33 Mon Sep 17 00:00:00 2001 From: To-om Date: Tue, 19 Apr 2022 12:04:17 +0200 Subject: [PATCH 2/6] #2316 Improve support of S3 storage --- ScalliGraph | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ScalliGraph b/ScalliGraph index 6a590119ff..bdf6b0beb7 160000 --- a/ScalliGraph +++ b/ScalliGraph @@ -1 +1 @@ -Subproject commit 6a590119ff69a3684e5f9dcc49a3f6e6e848c37f +Subproject commit bdf6b0beb75643fa4300b2d1391aa8d7b3da85b8 From 6cdcadafd4dd2b8dd5cb7821b4108465736bcc78 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 9 May 2022 16:25:02 +0200 Subject: [PATCH 3/6] #2383 Improve artifact creation from responder operation --- .../cortex/models/ActionOperation.scala | 29 ++++++++++++------- .../cortex/services/ActionOperationSrv.scala | 12 ++++---- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/ActionOperation.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/ActionOperation.scala index 477dd4c7c8..e47b76d642 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/ActionOperation.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/ActionOperation.scala @@ -7,16 +7,25 @@ import play.api.libs.json._ */ trait ActionOperation -case class AddTagToCase(tag: String) extends ActionOperation -case class AddTagToArtifact(tag: String) extends ActionOperation -case class CreateTask(title: String, description: String) extends ActionOperation -case class AddCustomFields(name: String, tpe: String, value: JsValue) extends ActionOperation -case class CloseTask() extends ActionOperation -case class MarkAlertAsRead() extends ActionOperation -case class AddLogToTask(content: String, owner: Option[String]) extends ActionOperation -case class AddTagToAlert(tag: String) extends ActionOperation -case class AddArtifactToCase(data: String, dataType: String, message: String) extends ActionOperation -case class AssignCase(owner: String) extends ActionOperation +case class AddTagToCase(tag: String) extends ActionOperation +case class AddTagToArtifact(tag: String) extends ActionOperation +case class CreateTask(title: String, description: String) extends ActionOperation +case class AddCustomFields(name: String, tpe: String, value: JsValue) extends ActionOperation +case class CloseTask() extends ActionOperation +case class MarkAlertAsRead() extends ActionOperation +case class AddLogToTask(content: String, owner: Option[String]) extends ActionOperation +case class AddTagToAlert(tag: String) extends ActionOperation +case class AddArtifactToCase( + data: String, + dataType: String, + message: String, + tlp: Option[Int], + ioc: Option[Boolean], + sighted: Option[Boolean], + ignoreSimilarity: Option[Boolean], + tags: Option[Seq[String]] +) extends ActionOperation +case class AssignCase(owner: String) extends ActionOperation object ActionOperation { val addTagToCaseFormat: OFormat[AddTagToCase] = Json.format[AddTagToCase] diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala index 8ff31506c7..40422083cb 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionOperationSrv.scala @@ -93,7 +93,7 @@ class ActionOperationSrv @Inject() ( _ <- logSrv.create(Log(content, new Date()), t, None) } yield updateOperation(operation) - case AddArtifactToCase(data, dataType, message) => + case AddArtifactToCase(data, dataType, message, tlp, ioc, sighted, ignoreSimilarity, tags) => for { c <- relatedCase.fold[Try[Case with Entity]](Failure(InternalError("Unable to apply action AddArtifactToCase without case")))(Success(_)) organisation <- organisationSrv.getOrFail(authContext.organisation) @@ -101,12 +101,12 @@ class ActionOperationSrv @Inject() ( c, Observable( message = Some(message), - tlp = 2, - ioc = false, - sighted = false, - ignoreSimilarity = None, + tlp = tlp.getOrElse(2), + ioc = ioc.getOrElse(false), + sighted = sighted.getOrElse(false), + ignoreSimilarity = ignoreSimilarity, dataType = dataType, - tags = Nil, + tags = tags.getOrElse(Nil), relatedId = c._id, organisationIds = Set(organisation._id) ), From 5ad9b0593bf740d990a4874f10406fe3b15c17b6 Mon Sep 17 00:00:00 2001 From: To-om Date: Wed, 11 May 2022 11:03:17 +0200 Subject: [PATCH 4/6] #2384 Add support of operations in analyzer reports --- .../cortex/controllers/v0/Conversion.scala | 2 + .../thehive/connector/cortex/models/Job.scala | 5 +- .../connector/cortex/services/ActionSrv.scala | 2 +- .../connector/cortex/services/JobSrv.scala | 33 ++++- .../src/test/resources/cortex-jobs.json | 11 +- .../cortex/services/JobSrvTest.scala | 121 +++++++++++------- .../thehive/connector/cortex/dto/v0/Job.scala | 3 +- .../thehive/migration/th3/Conversion.scala | 3 +- 8 files changed, 122 insertions(+), 58 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 b6fdd9b9a9..aea10bd44f 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 @@ -47,6 +47,7 @@ object Conversion { .withFieldComputed(_.id, _._id.toString) .withFieldConst(_._type, "case_artifact_job") .withFieldConst(_.case_artifact, None) + .withFieldComputed(_.operations, a => JsArray(a.operations).toString) .enableMethodAccessors .transform ) @@ -80,6 +81,7 @@ object Conversion { Some(observableWithExtraOutput.toValue((richObservable, JsObject.empty, Some(Left(richCase))))) } ) + .withFieldComputed(_.operations, a => JsArray(a.operations).toString) .enableMethodAccessors .transform } diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/Job.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/Job.scala index 60ca038fa7..b18a45cf86 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/Job.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/models/Job.scala @@ -29,7 +29,8 @@ case class Job( endDate: Date, // end date of the job or if it is not finished date of the last check report: Option[JsObject], cortexId: String, - cortexJobId: String + cortexJobId: String, + operations: Seq[JsObject] ) case class RichJob( @@ -50,5 +51,5 @@ case class RichJob( def report: Option[JsObject] = job.report def cortexId: String = job.cortexId def cortexJobId: String = job.cortexJobId - + def operations: Seq[JsObject] = job.operations } diff --git a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala index 320fc9e1a3..a702511b6f 100644 --- a/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala +++ b/cortex/connector/src/main/scala/org/thp/thehive/connector/cortex/services/ActionSrv.scala @@ -96,7 +96,7 @@ class ActionSrv @Inject() ( job.report.flatMap(_.full), client.name, job.id, - job.report.fold[Seq[JsObject]](Nil)(_.operations) + Nil ) createdAction <- Future.fromTry { db.tryTransaction { implicit graph => 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 519644bce9..3a21524d16 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 @@ -42,6 +42,7 @@ class JobSrv @Inject() ( observableTypeSrv: ObservableTypeSrv, attachmentSrv: AttachmentSrv, reportTagSrv: ReportTagSrv, + actionOperationSrv: ActionOperationSrv, serviceHelper: ServiceHelper, auditSrv: CortexAuditSrv, organisationSrv: OrganisationSrv, @@ -130,6 +131,7 @@ class JobSrv @Inject() ( .withFieldConst(_.report, None) .withFieldConst(_.cortexId, "tbd") .withFieldComputed(_.cortexJobId, _.id) + .withFieldConst(_.operations, Nil) .transform /** @@ -167,11 +169,31 @@ class JobSrv @Inject() ( .availableCortexClients(connector.clients, authContext.organisation) .find(_.name == cortexId) .fold[Future[CortexClient]](Future.failed(NotFoundError(s"Cortex $cortexId not found")))(Future.successful) - job <- Future.fromTry(updateJobStatus(jobId, cortexJob)) - _ <- importCortexArtifacts(job, cortexJob, cortexClient) - _ <- Future.fromTry(importAnalyzerTags(job, cortexJob)) + operations <- Future.fromTry(executeOperations(jobId, cortexJob)) + job <- Future.fromTry(updateJobStatus(jobId, cortexJob, operations)) + _ <- importCortexArtifacts(job, cortexJob, cortexClient) + _ <- Future.fromTry(importAnalyzerTags(job, cortexJob)) } yield job + def executeOperations(jobId: EntityId, cortexJob: CortexJob)(implicit authContext: AuthContext): Try[Seq[ActionOperationStatus]] = + db.tryTransaction { implicit graph => + get(jobId) + .observable + .project(_.by.by(_.`case`.option)) + .getOrFail("Observable") + .map { + case (relatedObservable, relatedCase) => + cortexJob + .report + .fold[Seq[ActionOperation]](Nil)(_.operations.map(_.as[ActionOperation])) + .map { operation => + actionOperationSrv + .execute(relatedObservable, operation, relatedCase, None) + .fold(t => ActionOperationStatus(operation, success = false, t.getMessage), identity) + } + } + } + /** * Update job status, set the endDate and remove artifacts from report * @@ -180,7 +202,9 @@ class JobSrv @Inject() ( * @param authContext the authentication context * @return the updated job */ - private def updateJobStatus(jobId: EntityId, cortexJob: CortexJob)(implicit authContext: AuthContext): Try[Job with Entity] = + private def updateJobStatus(jobId: EntityId, cortexJob: CortexJob, operations: Seq[ActionOperationStatus])(implicit + authContext: AuthContext + ): Try[Job with Entity] = db.tryTransaction { implicit graph => getOrFail(jobId).flatMap { job => val report = cortexJob.report.flatMap(r => r.full orElse r.errorMessage.map(m => Json.obj("errorMessage" -> m))) @@ -193,6 +217,7 @@ class JobSrv @Inject() ( .update(_.endDate, endDate) .update(_._updatedAt, Some(new Date)) .update(_._updatedBy, Some(authContext.userId)) + .update(_.operations, operations.map(o => Json.toJsObject(o))) .getOrFail("Job") observable <- get(job).observable.getOrFail("Observable") _ <- diff --git a/cortex/connector/src/test/resources/cortex-jobs.json b/cortex/connector/src/test/resources/cortex-jobs.json index 120d64cad7..99fce56568 100644 --- a/cortex/connector/src/test/resources/cortex-jobs.json +++ b/cortex/connector/src/test/resources/cortex-jobs.json @@ -87,12 +87,21 @@ { "data": "192.168.1.1", "message": "myIp", - "tags": [], + "tags": ["tag-test"], "tlp": 2, "dataType": "ip" } ], "operations": [ + { + "type": "AddArtifactToCase", + "data": "myData", + "dataType": "other", + "message": "test-operation", + "tlp": 3, + "ignoreSimilarity": false, + "tags": ["tag1", "tag2"] + } ] }, "tlp": 2, diff --git a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala index 1e059b90aa..87a1efdbb2 100644 --- a/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala +++ b/cortex/connector/src/test/scala/org/thp/thehive/connector/cortex/services/JobSrvTest.scala @@ -7,9 +7,10 @@ import org.thp.scalligraph.models.{Database, DummyUserSrv, Schema} import org.thp.scalligraph.traversal.TraversalOps._ import org.thp.scalligraph.{AppBuilder, EntityName} import org.thp.thehive.TestAppBuilder -import org.thp.thehive.connector.cortex.models.{Job, JobStatus, TheHiveCortexSchemaProvider} +import org.thp.thehive.connector.cortex.models.{ActionOperationStatus, Job, JobStatus, TheHiveCortexSchemaProvider} import org.thp.thehive.connector.cortex.services.JobOps._ import org.thp.thehive.models.Permissions +import org.thp.thehive.services.CaseOps._ import org.thp.thehive.services.ObservableOps._ import org.thp.thehive.services.UserOps._ import org.thp.thehive.services._ @@ -23,7 +24,8 @@ import scala.concurrent.duration.DurationInt import scala.io.Source class JobSrvTest extends PlaySpecification with TestAppBuilder { - implicit val authContext: AuthContext = DummyUserSrv(userId = "admin@thehive.local", permissions = Permissions.all).authContext + implicit val authContext: AuthContext = + DummyUserSrv(userId = "certuser@thehive.local", organisation = "cert", permissions = Permissions.all).authContext override def appConfigure: AppBuilder = super .appConfigure @@ -33,53 +35,76 @@ class JobSrvTest extends PlaySpecification with TestAppBuilder { .`override`(_.bindToProvider[Schema, TheHiveCortexSchemaProvider]) "job service" should { + val cortexOutputJob = { + val dataSource = Source.fromResource("cortex-jobs.json") + val data = dataSource.mkString + dataSource.close() + Json.parse(data).as[List[OutputJob]].find(_.id == "ZWu85Q1OCVNx03hXK4df").get + } + "handle creation and then finished job" in testApp { app => -// val job = Job( -// workerId = "anaTest2", -// workerName = "anaTest2", -// workerDefinition = "test2", -// status = JobStatus.Waiting, -// startDate = new Date(1561625908856L), -// endDate = new Date(1561625908856L), -// report = None, -// cortexId = "test", -// cortexJobId = "LVyYKFstq3Rtrdc9DFmL" -// ) -// -// val cortexOutputJob = { -// val dataSource = Source.fromResource("cortex-jobs.json") -// val data = dataSource.mkString -// dataSource.close() -// Json.parse(data).as[List[OutputJob]].find(_.id == "ZWu85Q1OCVNx03hXK4df").get -// } -// -// val createdJobTry = app[Database].tryTransaction { implicit graph => -// for { -// observable <- app[ObservableSrv].startTraversal.has(_.message, "hello world").getOrFail("Observable") -// createdJob <- app[JobSrv].create(job, observable) -// } yield createdJob -// } -// createdJobTry.map { createdJob => -// Await.result(app[JobSrv].finished(app[CortexClient].name, createdJob._id, cortexOutputJob), 20.seconds) -// } must beASuccessfulTry.which { updatedJob => -// updatedJob.status shouldEqual JobStatus.Success -// updatedJob.report must beSome -// (updatedJob.report.get \ "data").as[String] shouldEqual "imageedit_2_3904987689.jpg" -// -// app[Database].roTransaction { implicit graph => -// app[JobSrv].get(updatedJob).observable.has(_.message, "hello world").exists must beTrue -// app[JobSrv].get(updatedJob).reportObservables.toList.length must equalTo(2).updateMessage { s => -// s"$s\nreport observables are : ${app[JobSrv].get(updatedJob).reportObservables.richObservable.toList.mkString("\n")}" -// } -// -// for { -// audit <- app[AuditSrv].startTraversal.has(_.objectId, updatedJob._id.toString).getOrFail("Audit") -// organisation <- app[OrganisationSrv].getByName("cert").getOrFail("Organisation") -// user <- app[UserSrv].startTraversal.getByName("certuser@thehive.local").getOrFail("User") -// } yield new JobFinished().filter(audit, Some(updatedJob), organisation, Some(user)) -// } must beASuccessfulTry(true) -// } - pending("flaky test") + val job = Job( + workerId = "anaTest2", + workerName = "anaTest2", + workerDefinition = "test2", + status = JobStatus.Waiting, + startDate = new Date(1561625908856L), + endDate = new Date(1561625908856L), + report = None, + cortexId = "test", + cortexJobId = "LVyYKFstq3Rtrdc9DFmL", + operations = Nil + ) + + val createdJobTry = app[Database].tryTransaction { implicit graph => + for { + observable <- app[ObservableSrv].startTraversal.has(_.message, "hello world").getOrFail("Observable") + createdJob <- app[JobSrv].create(job, observable) + } yield createdJob + } + val finishedJobTry = createdJobTry.map { createdJob => + Await.result(app[JobSrv].finished(app[CortexClient].name, createdJob._id, cortexOutputJob), 20.seconds) + } + finishedJobTry must beASuccessfulTry + val updatedJob = finishedJobTry.get + updatedJob.status shouldEqual JobStatus.Success + updatedJob.report must beSome + (updatedJob.report.get \ "data").as[String] shouldEqual "imageedit_2_3904987689.jpg" + updatedJob.operations must haveSize(1) + updatedJob.operations.map(o => (o \ "status").as[String]) must contain(beEqualTo("Success")) + .forall + .updateMessage(s => s"$s\nOperation has failed: ${updatedJob.operations.map("\n -" + _).mkString}") + + app[Database].roTransaction { implicit graph => + app[JobSrv].get(updatedJob).observable.has(_.message, "hello world").exists must beTrue + val reportObservables = app[JobSrv].get(updatedJob).reportObservables.toSeq + reportObservables.length must equalTo(2).updateMessage { s => + s"$s\nreport observables are : ${app[JobSrv].get(updatedJob).reportObservables.richObservable.toList.mkString("\n")}" + } + val ipObservable = reportObservables.find(_.dataType == "ip").get + ipObservable.data must beSome("192.168.1.1") + ipObservable.message must beSome("myIp") + ipObservable.tags must contain(exactly("tag-test")) + ipObservable.tlp must beEqualTo(2) + + val operationObservableMaybe = app[JobSrv] + .get(updatedJob) + .observable + .`case` + .observables + .has(_.message, "test-operation") + .headOption + operationObservableMaybe must beSome.which { operationObservable => + operationObservable.data must beSome("myData") + operationObservable.tlp must beEqualTo(3) + operationObservable.tags must contain(exactly("tag1", "tag2")) + } + for { + audit <- app[AuditSrv].startTraversal.has(_.objectId, updatedJob._id.toString).getOrFail("Audit") + organisation <- app[OrganisationSrv].getByName("cert").getOrFail("Organisation") + user <- app[UserSrv].startTraversal.getByName("certadmin@thehive.local").getOrFail("User") + } yield JobFinished.filter(audit, Some(updatedJob), organisation, Some(user)) + } must beASuccessfulTry(true).setMessage("The audit doesn't match the expected criteria") } "submit a job" in testApp { app => diff --git a/dto/src/main/scala/org/thp/thehive/connector/cortex/dto/v0/Job.scala b/dto/src/main/scala/org/thp/thehive/connector/cortex/dto/v0/Job.scala index d3e04c6403..1f02266efe 100644 --- a/dto/src/main/scala/org/thp/thehive/connector/cortex/dto/v0/Job.scala +++ b/dto/src/main/scala/org/thp/thehive/connector/cortex/dto/v0/Job.scala @@ -17,7 +17,8 @@ case class OutputJob( cortexId: String, cortexJobId: String, id: String, - case_artifact: Option[OutputObservable] + case_artifact: Option[OutputObservable], + operations: String ) object OutputJob { 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 34bfc3da95..17b7ca36d0 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 @@ -479,7 +479,8 @@ trait Conversion { endDate, report, cortexId, - cortexJobId + cortexJobId, + Nil ) ) } From b8b00730ac3d1d769d7dc9f0135441d57b420192 Mon Sep 17 00:00:00 2001 From: To-om Date: Thu, 12 May 2022 10:15:39 +0200 Subject: [PATCH 5/6] #2385 Add query filters on observables --- .../org/thp/thehive/controllers/v0/ObservableCtrl.scala | 8 +++++++- .../org/thp/thehive/controllers/v1/ObservableCtrl.scala | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala index be48165643..9aeeb8957b 100644 --- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala @@ -428,7 +428,13 @@ class PublicObservable @Inject() ( (observableSteps, authContext) => observableSteps.filteredSimilar.visible(organisationSrv)(authContext) ), Query[Traversal.V[Observable], Traversal.V[Case]]("case", (observableSteps, _) => observableSteps.`case`), - Query[Traversal.V[Observable], Traversal.V[Alert]]("alert", (observableSteps, _) => observableSteps.alert) + Query[Traversal.V[Observable], Traversal.V[Alert]]("alert", (observableSteps, _) => observableSteps.alert), + Query[Traversal.V[Observable], Traversal.V[Observable]]("fromCase", (observableSteps, _) => observableSteps.filter(_.shares)), + Query[Traversal.V[Observable], Traversal.V[Observable]]("fromAlert", (observableSteps, _) => observableSteps.filter(_.alert)), + Query[Traversal.V[Observable], Traversal.V[Observable]]( + "fromJobReport", + (observableSteps, _) => observableSteps.filter(_.inE("ReportObservable")) + ) ) override val publicProperties: PublicProperties = PublicPropertyListBuilder[Observable] .property("status", UMapping.string)(_.select(_.constant("Ok")).readonly) diff --git a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala index 8afa023441..a798019e6b 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/ObservableCtrl.scala @@ -105,7 +105,13 @@ class ObservableCtrl @Inject() ( ), Query[Traversal.V[Observable], Traversal.V[Case]]("case", (observableSteps, _) => observableSteps.`case`), Query[Traversal.V[Observable], Traversal.V[Alert]]("alert", (observableSteps, _) => observableSteps.alert), - Query[Traversal.V[Observable], Traversal.V[Share]]("shares", (observableSteps, authContext) => observableSteps.shares.visible(authContext)) + Query[Traversal.V[Observable], Traversal.V[Share]]("shares", (observableSteps, authContext) => observableSteps.shares.visible(authContext)), + Query[Traversal.V[Observable], Traversal.V[Observable]]("fromCase", (observableSteps, _) => observableSteps.filter(_.shares)), + Query[Traversal.V[Observable], Traversal.V[Observable]]("fromAlert", (observableSteps, _) => observableSteps.filter(_.alert)), + Query[Traversal.V[Observable], Traversal.V[Observable]]( + "fromJobReport", + (observableSteps, _) => observableSteps.filter(_.inE("ReportObservable")) + ) ) def createInCase(caseId: String): Action[AnyContent] = From 2b827262d1fe78116a32b081bfe8f5854c9f87c1 Mon Sep 17 00:00:00 2001 From: To-om Date: Mon, 16 May 2022 13:34:09 +0200 Subject: [PATCH 6/6] Release 4.1.20 --- CHANGELOG.md | 13 +++++++++++++ build.sbt | 2 +- frontend/bower.json | 2 +- frontend/package.json | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8857f7681..15504cae24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## [4.1.20](https://github.com/TheHive-Project/TheHive/milestone/90) (2022-05-16) + +**Implemented enhancements:** + +- [Enhancement] Improve artifact creation from responder operation [\#2383](https://github.com/TheHive-Project/TheHive/issues/2383) +- [Enhancement] Accept operations in analyzer reports [\#2384](https://github.com/TheHive-Project/TheHive/issues/2384) +- [Enhancement] Add queries to filter observables based on the type of object it belongs to [\#2385](https://github.com/TheHive-Project/TheHive/issues/2385) + +**Fixed bugs:** + +- [Bug] Unable to use AWS S3 as storage backend [\#2316](https://github.com/TheHive-Project/TheHive/issues/2316) +- [Bug] Typo on migration elasticsearch http config [\#2374](https://github.com/TheHive-Project/TheHive/issues/2374) + ## [4.1.19](https://github.com/TheHive-Project/TheHive/milestone/89) (2022-04-07) **Implemented enhancements:** diff --git a/build.sbt b/build.sbt index ec7354e37d..ea95e71193 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.19-1" +val thehiveVersion = "4.1.20-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 6dcd9df3c8..6dae84e2a2 100644 --- a/frontend/bower.json +++ b/frontend/bower.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.19-1", + "version": "4.1.20-1", "license": "AGPL-3.0", "dependencies": { "jquery": "^3.4.1", diff --git a/frontend/package.json b/frontend/package.json index 9b26aef3f8..dae4bc7984 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "thehive", - "version": "4.1.19-1", + "version": "4.1.20-1", "license": "AGPL-3.0", "repository": { "type": "git",