diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60b3c26377..4936324ce4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Change Log
+## [4.1.16](https://github.com/TheHive-Project/TheHive/milestone/86) (2021-12-17)
+
+**Implemented enhancements:**
+
+- [Feature Request] Remove persistent filters on "Similar Cases" tab [\#2282](https://github.com/TheHive-Project/TheHive/issues/2282)
+- [Enhancement] When observable data is too big, use hash [\#2288](https://github.com/TheHive-Project/TheHive/issues/2288)
+- Remove unnecessary log4j dependency [\#2291](https://github.com/TheHive-Project/TheHive/issues/2291)
+
+**Fixed bugs:**
+
+- [Bug] Index fails with immense terms [\#2289](https://github.com/TheHive-Project/TheHive/issues/2289)
+- [Bug] Marking an alert as read do not update it's "updatedAt" nor "updatedBy" field [\#2292](https://github.com/TheHive-Project/TheHive/issues/2292)
+
## [4.1.15](https://github.com/TheHive-Project/TheHive/milestone/85) (2021-12-06)
**Implemented enhancements:**
diff --git a/ScalliGraph b/ScalliGraph
index f24ff5ed42..e3d3fce06b 160000
--- a/ScalliGraph
+++ b/ScalliGraph
@@ -1 +1 @@
-Subproject commit f24ff5ed42f6c0b8ae7f9548af008bbc66fff337
+Subproject commit e3d3fce06baec550c9597df4d9f2ced50bc527a2
diff --git a/build.sbt b/build.sbt
index 61003d9d43..87487d86f2 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.15-1"
+val thehiveVersion = "4.1.16-1"
val scala212 = "2.12.13"
val scala213 = "2.13.1"
val supportedScalaVersions = List(scala212, scala213)
@@ -63,8 +63,9 @@ libraryDependencies in ThisBuild ++= {
}
dependencyOverrides in ThisBuild ++= Seq(
// "org.locationtech.spatial4j" % "spatial4j" % "0.6",
-// "org.elasticsearch.client" % "elasticsearch-rest-client" % "6.7.2"
- akkaActor
+// "org.elasticsearch.client" % "elasticsearch-rest-client" % "6.7.2
+ akkaActor,
+ logbackClassic
)
PlayKeys.includeDocumentationInBinary := false
milestoneFilter := ((milestone: Milestone) => milestone.title.startsWith("4"))
diff --git a/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js b/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js
index 4ed19defc1..9faa729ca3 100644
--- a/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js
+++ b/frontend/app/scripts/components/alert/AlertSimilarCaseListCmp.js
@@ -39,7 +39,7 @@
this.filtering = new FilteringSrv('case', 'alert.dialog.similar-cases', {
version: 'v1',
defaults: {
- showFilters: true,
+ showFilters: false,
showStats: false,
pageSize: 2,
sort: ['-startDate']
@@ -47,7 +47,8 @@
defaultFilter: []
});
- self.filtering.initContext('alert.dialog.similar-cases')
+ //self.filtering.initContext('alert.dialog.similar-cases')
+ self.filtering.initContext()
.then(function () {
var defaultFilter = AlertingSrv.getSimilarityFilter(self.state.defaultAlertSimilarCaseFilter);
diff --git a/frontend/app/views/partials/admin/platform/status.html b/frontend/app/views/partials/admin/platform/status.html
index 6a25e82efd..fbc776d1eb 100644
--- a/frontend/app/views/partials/admin/platform/status.html
+++ b/frontend/app/views/partials/admin/platform/status.html
@@ -61,7 +61,7 @@
Data index status
Reindex the data
-
+
Drop and rebuild the index
diff --git a/frontend/bower.json b/frontend/bower.json
index d4252f1517..eb85ae583c 100644
--- a/frontend/bower.json
+++ b/frontend/bower.json
@@ -1,6 +1,6 @@
{
"name": "thehive",
- "version": "4.1.15-1",
+ "version": "4.1.16-1",
"license": "AGPL-3.0",
"dependencies": {
"jquery": "^3.4.1",
diff --git a/frontend/package.json b/frontend/package.json
index 2cdc8bcad6..b0369ef0f4 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "thehive",
- "version": "4.1.15-1",
+ "version": "4.1.16-1",
"license": "AGPL-3.0",
"repository": {
"type": "git",
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 5fdfdfd814..0eafba74e5 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
@@ -630,9 +630,11 @@ class Output @Inject() (
} yield IdMapping(inputLog.metaData.id, log._id)
}
- private def getData(value: String)(implicit graph: Graph, authContext: AuthContext): Try[Data with Entity] =
- if (observableDataIsIndexed) dataSrv.create(Data(value))
- else dataSrv.createEntity(Data(value))
+ private def getData(value: String)(implicit graph: Graph, authContext: AuthContext): Try[Data with Entity] = {
+ val (dataOrHash, fullData) = UseHashToIndex.hashToIndex(value).fold[(String, Option[String])](value -> None)(_ -> Some(value))
+ if (observableDataIsIndexed) dataSrv.create(Data(dataOrHash, fullData))
+ else dataSrv.createEntity(Data(dataOrHash, fullData))
+ }
private def createSimpleObservable(observable: Observable, observableType: ObservableType with Entity, dataValue: String)(implicit
graph: Graph,
@@ -700,7 +702,8 @@ class Output @Inject() (
richObservable <- createObservable(caseId, inputObservable, organisations.map(_._id).toSet)
_ <- reportTagSrv.updateTags(richObservable, inputObservable.reportTags)
case0 <- getCase(caseId)
- _ <- organisations.toTry(o => shareSrv.shareObservable(RichObservable(richObservable, None, None, Nil), case0, o._id))
+ // the data in richObservable is not set because it is not used in shareSrv
+ _ <- organisations.toTry(o => shareSrv.shareObservable(RichObservable(richObservable, None, None, None, Nil), case0, o._id))
} yield IdMapping(inputObservable.metaData.id, richObservable._id)
}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 9c904e468a..bfa3e79cf4 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -9,6 +9,7 @@ object Dependencies {
lazy val playLogback = "com.typesafe.play" %% "play-logback" % play.core.PlayVersion.current
lazy val playGuice = "com.typesafe.play" %% "play-guice" % play.core.PlayVersion.current
lazy val playFilters = "com.typesafe.play" %% "filters-helpers" % play.core.PlayVersion.current
+ lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.2.8"
lazy val playMockws = "de.leanovate.play-mockws" %% "play-mockws" % "2.8.0"
lazy val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion
lazy val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % akkaVersion
@@ -35,7 +36,7 @@ object Dependencies {
lazy val elastic4sHttpStreams = "com.sksamuel.elastic4s" %% "elastic4s-http-streams" % elastic4sVersion
lazy val elastic4sClient = "com.sksamuel.elastic4s" %% "elastic4s-client-esjava" % elastic4sVersion
lazy val reflections = "org.reflections" % "reflections" % "0.9.12"
- lazy val hadoopClient = "org.apache.hadoop" % "hadoop-client" % "3.3.0"
+ lazy val hadoopClient = "org.apache.hadoop" % "hadoop-client" % "3.3.0" exclude ("log4j", "log4j")
lazy val zip4j = "net.lingala.zip4j" % "zip4j" % "2.6.4"
lazy val alpakka = "com.lightbend.akka" %% "akka-stream-alpakka-json-streaming" % "2.0.2"
lazy val handlebars = "com.github.jknack" % "handlebars" % "4.2.0"
diff --git a/thehive/app/org/thp/thehive/TheHiveModule.scala b/thehive/app/org/thp/thehive/TheHiveModule.scala
index 988e8101b2..e2ab245244 100644
--- a/thehive/app/org/thp/thehive/TheHiveModule.scala
+++ b/thehive/app/org/thp/thehive/TheHiveModule.scala
@@ -6,11 +6,11 @@ import com.google.inject.AbstractModule
import net.codingwell.scalaguice.{ScalaModule, ScalaMultibinder}
import org.thp.scalligraph.SingleInstance
import org.thp.scalligraph.auth._
-import org.thp.scalligraph.janus.JanusDatabaseProvider
+import org.thp.scalligraph.janus.{ImmenseTermProcessor, JanusDatabaseProvider}
import org.thp.scalligraph.models.{Database, UpdatableSchema}
import org.thp.scalligraph.services.{GenIntegrityCheckOps, HadoopStorageSrv, S3StorageSrv}
import org.thp.thehive.controllers.v0.QueryExecutorVersion0Provider
-import org.thp.thehive.models.TheHiveSchemaDefinition
+import org.thp.thehive.models.{TheHiveSchemaDefinition, UseHashToIndex}
import org.thp.thehive.services.notification.notifiers._
import org.thp.thehive.services.notification.triggers._
import org.thp.thehive.services.{UserSrv => _, _}
@@ -112,6 +112,8 @@ class TheHiveModule(environment: Environment, configuration: Configuration) exte
bind[ActorRef].annotatedWithName("flow-actor").toProvider[FlowActorProvider]
bind[SingleInstance].to[ClusterSetup].asEagerSingleton()
+
+ ImmenseTermProcessor.registerStrategy("observableHashToIndex", _ => UseHashToIndex)
()
}
}
diff --git a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala
index babf09ca4f..934a544409 100644
--- a/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala
+++ b/thehive/app/org/thp/thehive/controllers/v0/Conversion.scala
@@ -50,6 +50,8 @@ object Conversion {
.withFieldComputed(_.customFields, rc => JsObject(rc.customFields.map(cf => cf.name -> Json.obj(cf.typeName -> cf.toJson))))
.withFieldRenamed(_._createdAt, _.createdAt)
.withFieldRenamed(_._createdBy, _.createdBy)
+ .withFieldRenamed(_._updatedAt, _.updatedAt)
+ .withFieldRenamed(_._updatedBy, _.updatedBy)
.withFieldComputed(_._id, _._id.toString)
.withFieldComputed(_.id, _._id.toString)
.withFieldComputed(_.id, _._id.toString)
@@ -81,6 +83,8 @@ object Conversion {
.withFieldComputed(_.id, _._id.toString)
.withFieldRenamed(_._createdAt, _.createdAt)
.withFieldRenamed(_._createdBy, _.createdBy)
+ .withFieldRenamed(_._updatedAt, _.updatedAt)
+ .withFieldRenamed(_._updatedBy, _.updatedBy)
.withFieldConst(_._type, "alert")
.withFieldComputed(_.tags, _.tags.toSet)
.withFieldComputed(_.`case`, _.caseId.map(_.toString))
diff --git a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala
index 0ef5aceb1d..4ca40c5ddd 100644
--- a/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala
+++ b/thehive/app/org/thp/thehive/controllers/v0/ObservableCtrl.scala
@@ -2,13 +2,16 @@ package org.thp.thehive.controllers.v0
import net.lingala.zip4j.ZipFile
import net.lingala.zip4j.model.FileHeader
+import org.apache.tinkerpop.gremlin.process.traversal.Compare
import org.thp.scalligraph._
import org.thp.scalligraph.auth.AuthContext
import org.thp.scalligraph.controllers._
import org.thp.scalligraph.models.{Database, Entity, UMapping}
+import org.thp.scalligraph.query.PredicateOps.PredicateOpsDefs
import org.thp.scalligraph.query._
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{IteratorOutput, Traversal}
+import org.thp.scalligraph.utils.Hasher
import org.thp.thehive.controllers.v0.Conversion._
import org.thp.thehive.dto.v0.{InputAttachment, InputObservable}
import org.thp.thehive.models._
@@ -445,7 +448,15 @@ class PublicObservable @Inject() (
_ <- observableSrv.updateType(observable, newDataType)(graph, authContext)
} yield Json.obj("dataType" -> value)
})
- .property("data", UMapping.string.optional)(_.field.readonly)
+ .property("data", UMapping.string.optional)(
+ _.select(_.value(_.data))
+ .filter[String] {
+ case (_, observables, _, Right(predicate)) => observables.has(_.data, predicate.mapValue(v => UseHashToIndex.hashToIndex(v).getOrElse(v)))
+ case (_, observables, _, Left(true)) => observables.has(_.data)
+ case (_, observables, _, Left(false)) => observables.hasNot(_.data)
+ }
+ .readonly
+ )
.property("attachment.name", UMapping.string.optional)(_.select(_.attachments.value(_.name)).readonly)
.property("attachment.hashes", UMapping.hash.sequence)(_.select(_.attachments.value(_.hashes)).readonly)
.property("attachment.size", UMapping.long.optional)(_.select(_.attachments.value(_.size)).readonly)
diff --git a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala
index 588c83efc7..f07b9cbf7f 100644
--- a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala
+++ b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala
@@ -1,11 +1,13 @@
package org.thp.thehive.controllers.v1
+import org.apache.tinkerpop.gremlin.process.traversal.Compare
import org.apache.tinkerpop.gremlin.structure.T
import org.thp.scalligraph.controllers.{FPathElem, FPathEmpty, FString}
import org.thp.scalligraph.models.{Database, UMapping}
import org.thp.scalligraph.query.PredicateOps._
import org.thp.scalligraph.query.{PublicProperties, PublicPropertyListBuilder}
import org.thp.scalligraph.traversal.TraversalOps._
+import org.thp.scalligraph.utils.Hasher
import org.thp.scalligraph.{BadRequestError, EntityId, EntityIdOrName, InvalidFormatAttributeError, RichSeq}
import org.thp.thehive.dto.v1.InputCustomFieldValue
import org.thp.thehive.models._
@@ -28,7 +30,7 @@ import org.thp.thehive.services._
import play.api.libs.json.{JsObject, JsValue, Json}
import javax.inject.{Inject, Singleton}
-import scala.util.{Failure, Success, Try}
+import scala.util.{Failure, Success}
@Singleton
class Properties @Inject() (
@@ -452,7 +454,15 @@ class Properties @Inject() (
_ <- observableSrv.updateType(observable, newDataType)(graph, authContext)
} yield Json.obj("dataType" -> value)
})
- .property("data", UMapping.string.optional)(_.field.readonly)
+ .property("data", UMapping.string.optional)(
+ _.select(_.value(_.data))
+ .filter[String] {
+ case (_, observables, _, Right(predicate)) => observables.has(_.data, predicate.mapValue(v => UseHashToIndex.hashToIndex(v).getOrElse(v)))
+ case (_, observables, _, Left(true)) => observables.has(_.data)
+ case (_, observables, _, Left(false)) => observables.hasNot(_.data)
+ }
+ .readonly
+ )
.property("attachment.name", UMapping.string.optional)(_.select(_.attachments.value(_.name)).readonly)
.property("attachment.hashes", UMapping.hash.sequence)(_.select(_.attachments.value(_.hashes)).readonly)
.property("attachment.size", UMapping.long.optional)(_.select(_.attachments.value(_.size)).readonly)
diff --git a/thehive/app/org/thp/thehive/models/Observable.scala b/thehive/app/org/thp/thehive/models/Observable.scala
index b0a92a85e8..62902a69f0 100644
--- a/thehive/app/org/thp/thehive/models/Observable.scala
+++ b/thehive/app/org/thp/thehive/models/Observable.scala
@@ -1,6 +1,9 @@
package org.thp.thehive.models
-import org.thp.scalligraph.models.{DefineIndex, Entity, IndexType}
+import org.apache.tinkerpop.gremlin.structure.{Vertex, VertexProperty}
+import org.thp.scalligraph.janus.{ImmenseStringTermFilter, ImmenseTermProcessor}
+import org.thp.scalligraph.models.{DefineIndex, Entity, IndexType, UMapping}
+import org.thp.scalligraph.utils.Hasher
import org.thp.scalligraph.{BuildEdgeEntity, BuildVertexEntity, EntityId}
import java.util.Date
@@ -46,6 +49,7 @@ case class Observable(
case class RichObservable(
observable: Observable with Entity,
+ fullData: Option[Data with Entity],
attachment: Option[Attachment with Entity],
seen: Option[Boolean],
reportTags: Seq[ReportTag with Entity]
@@ -60,12 +64,50 @@ case class RichObservable(
def ioc: Boolean = observable.ioc
def sighted: Boolean = observable.sighted
def ignoreSimilarity: Option[Boolean] = observable.ignoreSimilarity
- def dataOrAttachment: Either[String, Attachment with Entity] = observable.data.toLeft(attachment.get)
+ def dataOrAttachment: Either[String, Attachment with Entity] = data.toLeft(attachment.get)
def dataType: String = observable.dataType
- def data: Option[String] = observable.data
+ def data: Option[String] = fullData.map(d => d.fullData.getOrElse(d.data))
def tags: Seq[String] = observable.tags
}
@DefineIndex(IndexType.standard, "data")
@BuildVertexEntity
-case class Data(data: String)
+case class Data(data: String, fullData: Option[String])
+
+object UseHashToIndex extends ImmenseTermProcessor with ImmenseStringTermFilter {
+ override val termSizeLimit: Int = 8191
+ private val hasher: Hasher = Hasher("SHA-256")
+
+ def hashToIndex(value: String): Option[String] =
+ if (value.length > termSizeLimit) Some("sha256/" + hasher.fromString(value).head.toString)
+ else None
+
+ override def apply[V](vertex: Vertex, property: VertexProperty[V]): Boolean = {
+ if (property.key() == "data")
+ vertex.label() match {
+ case "Observable" =>
+ collect(vertex, property).foreach { strProp =>
+ val currentValue = strProp.value()
+ logger.info(s"""Use hash for observable ~${vertex.id()}:
+ | dataType=${UMapping.string.getProperty(vertex, "dataType")}
+ | data=$currentValue
+ | message=${UMapping.string.optional.getProperty(vertex, "message").getOrElse("")}
+ | tags=${UMapping.string.sequence.getProperty(vertex, "message").mkString(", ")}""".stripMargin)
+ strProp.remove()
+ vertex.property(strProp.key(), "sha256/" + hasher.fromString(currentValue).head.toString)
+ }
+
+ case "Data" =>
+ collect(vertex, property).foreach { strProp =>
+ val currentValue = strProp.value()
+ logger.info(s"Use hash and move data for $vertex/${strProp.key()}: $currentValue")
+ strProp.remove()
+ vertex.property(strProp.key(), hasher.fromString(currentValue).head.toString)
+ vertex.property("fullData", currentValue)
+ }
+
+ case _ =>
+ }
+ false
+ }
+}
diff --git a/thehive/app/org/thp/thehive/services/ObservableSrv.scala b/thehive/app/org/thp/thehive/services/ObservableSrv.scala
index 985b7a2302..91ffe952fb 100644
--- a/thehive/app/org/thp/thehive/services/ObservableSrv.scala
+++ b/thehive/app/org/thp/thehive/services/ObservableSrv.scala
@@ -10,7 +10,7 @@ import org.thp.scalligraph.services._
import org.thp.scalligraph.traversal.Converter.Identity
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{Converter, Graph, StepLabel, Traversal}
-import org.thp.scalligraph.utils.Hash
+import org.thp.scalligraph.utils.{Hash, Hasher}
import org.thp.scalligraph.{BadRequestError, CreateError, EntityId, EntityIdOrName, EntityName, RichSeq}
import org.thp.thehive.models._
import org.thp.thehive.services.AlertOps._
@@ -76,7 +76,7 @@ class ObservableSrv @Inject() (
_ <- observableObservableTypeSrv.create(ObservableObservableType(), createdObservable, observableType)
_ <- observableAttachmentSrv.create(ObservableAttachment(), createdObservable, attachment)
_ <- tags.toTry(observableTagSrv.create(ObservableTag(), createdObservable, _))
- } yield RichObservable(createdObservable, Some(attachment), None, Nil)
+ } yield RichObservable(createdObservable, None, Some(attachment), None, Nil)
}
def create(
@@ -86,10 +86,11 @@ class ObservableSrv @Inject() (
graph: Graph,
authContext: AuthContext
): Try[RichObservable] = {
+ val (dataOrHash, fullData) = UseHashToIndex.hashToIndex(dataValue).fold[(String, Option[String])](dataValue -> None)(_ -> Some(dataValue))
val alreadyExists = startTraversal
.has(_.organisationIds, organisationSrv.currentId)
.has(_.relatedId, observable.relatedId)
- .has(_.data, dataValue)
+ .has(_.data, dataOrHash)
.has(_.dataType, observable.dataType)
.exists
if (alreadyExists) Failure(CreateError("Observable already exists"))
@@ -100,12 +101,12 @@ class ObservableSrv @Inject() (
if (observableType.isAttachment) Failure(BadRequestError("A attachment observable doesn't accept string value"))
else Success(())
tags <- observable.tags.toTry(tagSrv.getOrCreate)
- data <- dataSrv.create(Data(dataValue))
- createdObservable <- createEntity(observable.copy(data = Some(dataValue)))
+ data <- dataSrv.create(Data(dataOrHash, fullData))
+ createdObservable <- createEntity(observable.copy(data = Some(dataOrHash)))
_ <- observableObservableTypeSrv.create(ObservableObservableType(), createdObservable, observableType)
_ <- observableDataSrv.create(ObservableData(), createdObservable, data)
_ <- tags.toTry(observableTagSrv.create(ObservableTag(), createdObservable, _))
- } yield RichObservable(createdObservable, None, None, Nil)
+ } yield RichObservable(createdObservable, Some(data), None, None, Nil)
}
def addTags(observable: Observable with Entity, tags: Set[String])(implicit graph: Graph, authContext: AuthContext): Try[Seq[Tag with Entity]] = {
@@ -289,14 +290,16 @@ object ObservableOps {
traversal
.project(
_.by
- .by(_.attachments.fold)
+ .by(_.data.option)
+ .by(_.attachments.option)
.by(_.reportTags.fold)
)
.domainMap {
- case (observable, attachment, reportTags) =>
+ case (observable, data, attachment, reportTags) =>
RichObservable(
observable,
- attachment.headOption,
+ data,
+ attachment,
None,
reportTags
)
@@ -308,15 +311,17 @@ object ObservableOps {
traversal
.project(
_.by
- .by(_.attachments.fold)
+ .by(_.data.option)
+ .by(_.attachments.option)
.by(_.filteredSimilar.visible(organisationSrv).limit(1).count)
.by(_.reportTags.fold)
)
.domainMap {
- case (observable, attachment, count, reportTags) =>
+ case (observable, data, attachment, count, reportTags) =>
RichObservable(
observable,
- attachment.headOption,
+ data,
+ attachment,
Some(count != 0),
reportTags
)
@@ -329,16 +334,18 @@ object ObservableOps {
traversal
.project(
_.by
- .by(_.attachments.fold)
+ .by(_.data.option)
+ .by(_.attachments.option)
.by(_.filteredSimilar.visible(organisationSrv).limit(1).count)
.by(_.reportTags.fold)
.by(entityRenderer)
)
.domainMap {
- case (observable, attachment, count, reportTags, renderedEntity) =>
+ case (observable, data, attachment, count, reportTags, renderedEntity) =>
RichObservable(
observable,
- attachment.headOption,
+ data,
+ attachment,
Some(count != 0),
reportTags
) -> renderedEntity
diff --git a/thehive/test/org/thp/thehive/DatabaseBuilder.scala b/thehive/test/org/thp/thehive/DatabaseBuilder.scala
index eb3190e31e..84303cbbcc 100644
--- a/thehive/test/org/thp/thehive/DatabaseBuilder.scala
+++ b/thehive/test/org/thp/thehive/DatabaseBuilder.scala
@@ -3,7 +3,7 @@ package org.thp.thehive
import org.scalactic.Or
import org.thp.scalligraph.auth.{AuthContext, AuthContextImpl}
import org.thp.scalligraph.controllers._
-import org.thp.scalligraph.models.{Database, Entity, Schema}
+import org.thp.scalligraph.models.{Database, Entity}
import org.thp.scalligraph.services.{EdgeSrv, GenIntegrityCheckOps, VertexSrv}
import org.thp.scalligraph.traversal.Graph
import org.thp.scalligraph.traversal.TraversalOps._
@@ -29,7 +29,6 @@ import scala.util.{Failure, Success, Try}
@Singleton
class DatabaseBuilder @Inject() (
- schema: Schema,
alertSrv: AlertSrv,
attachmentSrv: AttachmentSrv,
caseSrv: CaseSrv,
@@ -233,7 +232,7 @@ class DatabaseBuilder @Inject() (
dataSrv
.getByName(data)
.getOrFail("data")
- .orElse(dataSrv.create(Data(data)))
+ .orElse(dataSrv.create(Data(data, None)))
.flatMap(observableSrv.observableDataSrv.create(ObservableData(), observable, _))
.get
)