diff --git a/build.sbt b/build.sbt index b91c194ce2..3a57100786 100644 --- a/build.sbt +++ b/build.sbt @@ -170,7 +170,10 @@ lazy val thehiveDto = (project in file("dto")) .dependsOn(scalligraph) .settings( name := "thehive-dto", - version := thehiveVersion + version := thehiveVersion, + libraryDependencies ++= Seq( + aix + ) ) lazy val thehiveClient = (project in file("client")) diff --git a/dto/src/main/scala/org/thp/thehive/dto/v1/Alert.scala b/dto/src/main/scala/org/thp/thehive/dto/v1/Alert.scala index 994b315bcd..ca58be6432 100644 --- a/dto/src/main/scala/org/thp/thehive/dto/v1/Alert.scala +++ b/dto/src/main/scala/org/thp/thehive/dto/v1/Alert.scala @@ -1,10 +1,12 @@ package org.thp.thehive.dto.v1 -import java.util.Date - +import ai.x.play.json.Encoders.encoder +import ai.x.play.json.Jsonx import org.thp.scalligraph.controllers.WithParser import play.api.libs.json._ +import java.util.Date + case class InputAlert( `type`: String, source: String, @@ -54,87 +56,5 @@ case class OutputAlert( ) object OutputAlert { - implicit val reads: Reads[OutputAlert] = Reads[OutputAlert] { json => - for { - _id <- (json \ "_id").validate[String] - _type <- (json \ "_type").validate[String] - _createdBy <- (json \ "_createdBy").validate[String] - _updatedBy <- (json \ "_updatedBy").validateOpt[String] - _createdAt <- (json \ "_createdAt").validate[Date] - _updatedAt <- (json \ "_updatedAt").validateOpt[Date] - tpe <- (json \ "type").validate[String] - source <- (json \ "source").validate[String] - sourceRef <- (json \ "sourceRef").validate[String] - externalLink <- (json \ "externalLink").validateOpt[String] - title <- (json \ "title").validate[String] - description <- (json \ "description").validate[String] - severity <- (json \ "severity").validate[Int] - date <- (json \ "date").validate[Date] - tags <- (json \ "tags").validate[Set[String]] - tlp <- (json \ "tlp").validate[Int] - pap <- (json \ "pap").validate[Int] - read <- (json \ "read").validate[Boolean] - follow <- (json \ "follow").validate[Boolean] - customFields <- (json \ "customFields").validate[Seq[OutputCustomFieldValue]] - caseTemplate <- (json \ "caseTemplate").validateOpt[String] - observableCount <- (json \ "observableCount").validate[Long] - caseId <- (json \ "caseId").validateOpt[String] - extraData <- (json \ "extraData").validate[JsObject] - } yield OutputAlert( - _id, - _type, - _createdBy, - _updatedBy, - _createdAt, - _updatedAt, - tpe, - source, - sourceRef, - externalLink, - title, - description, - severity, - date, - tags, - tlp, - pap, - read, - follow, - customFields, - caseTemplate, - observableCount, - caseId, - extraData - ) - } - implicit val writes: OWrites[OutputAlert] = OWrites[OutputAlert] { outputAlert => - Json.obj( - "_id" -> outputAlert._id, - "_type" -> outputAlert._type, - "_createdBy" -> outputAlert._createdBy, - "_updatedBy" -> outputAlert._updatedBy, - "_createdAt" -> outputAlert._createdAt, - "_updatedAt" -> outputAlert._updatedAt, - "type" -> outputAlert.`type`, - "source" -> outputAlert.source, - "sourceRef" -> outputAlert.sourceRef, - "externalLink" -> outputAlert.externalLink, - "title" -> outputAlert.title, - "description" -> outputAlert.description, - "severity" -> outputAlert.severity, - "date" -> outputAlert.date, - "tags" -> outputAlert.tags, - "tlp" -> outputAlert.tlp, - "pap" -> outputAlert.pap, - "read" -> outputAlert.read, - "follow" -> outputAlert.follow, - "customFields" -> outputAlert.customFields, - "caseTemplate" -> outputAlert.caseTemplate, - "observableCount" -> outputAlert.observableCount, - "caseId" -> outputAlert.caseId, - "extraData" -> outputAlert.extraData - ) - } - - implicit val format: OFormat[OutputAlert] = OFormat(reads, writes) + implicit val format: OFormat[OutputAlert] = Jsonx.formatCaseClass[OutputAlert] } diff --git a/dto/src/main/scala/org/thp/thehive/dto/v1/Case.scala b/dto/src/main/scala/org/thp/thehive/dto/v1/Case.scala index bd2fa69f4b..2e37575466 100644 --- a/dto/src/main/scala/org/thp/thehive/dto/v1/Case.scala +++ b/dto/src/main/scala/org/thp/thehive/dto/v1/Case.scala @@ -1,7 +1,9 @@ package org.thp.thehive.dto.v1 -import java.util.Date +import ai.x.play.json.Encoders.encoder +import ai.x.play.json.Jsonx +import java.util.Date import org.thp.scalligraph.controllers.WithParser import play.api.libs.json._ @@ -53,86 +55,5 @@ case class OutputCase( ) object OutputCase { - - val reads: Reads[OutputCase] = Reads[OutputCase] { json => - for { - _id <- (json \ "_id").validate[String] - _type <- (json \ "_type").validate[String] - _createdBy <- (json \ "_createdBy").validate[String] - _updatedBy <- (json \ "_updatedBy").validateOpt[String] - _createdAt <- (json \ "_createdAt").validate[Date] - _updatedAt <- (json \ "_updatedAt").validateOpt[Date] - number <- (json \ "number").validate[Int] - title <- (json \ "title").validate[String] - description <- (json \ "description").validate[String] - severity <- (json \ "severity").validate[Int] - startDate <- (json \ "startDate").validate[Date] - endDate <- (json \ "endDate").validateOpt[Date] - tags <- (json \ "tags").validate[Set[String]] - flag <- (json \ "flag").validate[Boolean] - tlp <- (json \ "tlp").validate[Int] - pap <- (json \ "pap").validate[Int] - status <- (json \ "status").validate[String] - summary <- (json \ "summary").validateOpt[String] - impactStatus <- (json \ "impactStatus").validateOpt[String] - resolutionStatus <- (json \ "resolutionStatus").validateOpt[String] - assignee <- (json \ "assignee").validateOpt[String] - customFields <- (json \ "customFields").validate[Seq[OutputCustomFieldValue]] - extraData <- (json \ "extraData").validate[JsObject] - } yield OutputCase( - _id, - _type, - _createdBy, - _updatedBy, - _createdAt, - _updatedAt, - number, - title, - description, - severity, - startDate, - endDate, - tags, - flag, - tlp, - pap, - status, - summary, - impactStatus, - resolutionStatus, - assignee, - customFields, - extraData - ) - } - - val writes: OWrites[OutputCase] = OWrites[OutputCase] { outputCase => - Json.obj( - "_id" -> outputCase._id, - "_type" -> outputCase._type, - "_createdBy" -> outputCase._createdBy, - "_updatedBy" -> outputCase._updatedBy, - "_createdAt" -> outputCase._createdAt, - "_updatedAt" -> outputCase._updatedAt, - "number" -> outputCase.number, - "title" -> outputCase.title, - "description" -> outputCase.description, - "severity" -> outputCase.severity, - "startDate" -> outputCase.startDate, - "endDate" -> outputCase.endDate, - "tags" -> outputCase.tags, - "flag" -> outputCase.flag, - "tlp" -> outputCase.tlp, - "pap" -> outputCase.pap, - "status" -> outputCase.status, - "summary" -> outputCase.summary, - "impactStatus" -> outputCase.impactStatus, - "resolutionStatus" -> outputCase.resolutionStatus, - "assignee" -> outputCase.assignee, - "customFields" -> outputCase.customFields, - "extraData" -> outputCase.extraData - ) - } - - implicit val format: OFormat[OutputCase] = OFormat(reads, writes) + implicit val format: OFormat[OutputCase] = Jsonx.formatCaseClass[OutputCase] } diff --git a/dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala b/dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala index 6574766035..5395f90116 100644 --- a/dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala +++ b/dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala @@ -1,6 +1,8 @@ package org.thp.thehive.dto.v1 -import play.api.libs.json.{Format, JsObject, Json, Reads, Writes} +import ai.x.play.json.Encoders.encoder +import ai.x.play.json.Jsonx +import play.api.libs.json._ import java.util.Date @@ -11,6 +13,8 @@ case class InputPattern( kill_chain_phases: Seq[InputKillChainPhase], url: String, `type`: String, + capec_id: Option[String], + capec_url: Option[String], revoked: Boolean, x_mitre_data_sources: Seq[String], x_mitre_defense_bypassed: Seq[String], @@ -60,7 +64,8 @@ object InputPattern { implicit val reads: Reads[InputPattern] = Reads[InputPattern] { json => for { references <- (json \ "external_references").validate[Seq[InputReference]] - mitreReference = references.find(ref => isSourceNameValid(ref.source_name)) + mitreReference = references.find(ref => isSourceNameMitre(ref.source_name)) + capecReference = references.find(ref => isSourceNameCapec(ref.source_name)) name <- (json \ "name").validate[String] description <- (json \ "description").validateOpt[String] kill_chain_phases <- (json \ "kill_chain_phases").validateOpt[Seq[InputKillChainPhase]] @@ -82,6 +87,8 @@ object InputPattern { kill_chain_phases.getOrElse(Seq()), mitreReference.flatMap(_.url).getOrElse(""), techniqueType, + capecReference.flatMap(_.external_id), + capecReference.flatMap(_.url), revoked.getOrElse(false), x_mitre_data_sources.getOrElse(Seq()), x_mitre_defense_bypassed.getOrElse(Seq()), @@ -95,9 +102,12 @@ object InputPattern { ) } - private def isSourceNameValid(reference: String): Boolean = + private def isSourceNameMitre(reference: String): Boolean = reference == "mitre-attack" + private def isSourceNameCapec(reference: String): Boolean = + reference == "capec" + implicit val writes: Writes[InputPattern] = Json.writes[InputPattern] } @@ -114,6 +124,8 @@ case class OutputPattern( tactics: Set[String], url: String, patternType: String, + capecId: Option[String], + capecUrl: Option[String], revoked: Boolean, dataSources: Seq[String], defenseBypassed: Seq[String], @@ -127,5 +139,5 @@ case class OutputPattern( ) object OutputPattern { - implicit val format: Format[OutputPattern] = Json.format[OutputPattern] + implicit val format: OFormat[OutputPattern] = Jsonx.formatCaseClass[OutputPattern] } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 8b8c345296..254f53801a 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,47 +15,48 @@ object Dependencies { lazy val akkaClusterTyped = "com.typesafe.akka" %% "akka-cluster-typed" % akkaVersion lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.1.12" lazy val akkaHttpXml = "com.typesafe.akka" %% "akka-http-xml" % "10.1.12" - lazy val janusGraph = "org.janusgraph" % "janusgraph" % janusVersion - lazy val janusGraphCore = "org.janusgraph" % "janusgraph-core" % janusVersion - lazy val janusGraphBerkeleyDB = "org.janusgraph" % "janusgraph-berkeleyje" % janusVersion - lazy val janusGraphHBase = "org.janusgraph" % "janusgraph-hbase" % janusVersion - lazy val janusGraphLucene = "org.janusgraph" % "janusgraph-lucene" % janusVersion - lazy val janusGraphElasticSearch = "org.janusgraph" % "janusgraph-es" % janusVersion - lazy val janusGraphCassandra = "org.janusgraph" % "janusgraph-cql" % janusVersion - lazy val janusGraphInMemory = "org.janusgraph" % "janusgraph-inmemory" % janusVersion - lazy val janusGraphDriver = "org.janusgraph" % "janusgraph-driver" % janusVersion - lazy val tinkerpop = "org.apache.tinkerpop" % "gremlin-core" % "3.4.7" + lazy val janusGraph = "org.janusgraph" % "janusgraph" % janusVersion + lazy val janusGraphCore = "org.janusgraph" % "janusgraph-core" % janusVersion + lazy val janusGraphBerkeleyDB = "org.janusgraph" % "janusgraph-berkeleyje" % janusVersion + lazy val janusGraphHBase = "org.janusgraph" % "janusgraph-hbase" % janusVersion + lazy val janusGraphLucene = "org.janusgraph" % "janusgraph-lucene" % janusVersion + lazy val janusGraphElasticSearch = "org.janusgraph" % "janusgraph-es" % janusVersion + lazy val janusGraphCassandra = "org.janusgraph" % "janusgraph-cql" % janusVersion + lazy val janusGraphInMemory = "org.janusgraph" % "janusgraph-inmemory" % janusVersion + lazy val janusGraphDriver = "org.janusgraph" % "janusgraph-driver" % janusVersion + lazy val tinkerpop = "org.apache.tinkerpop" % "gremlin-core" % "3.4.7" lazy val gremlinScala = "com.michaelpollmeier" %% "gremlin-scala" % "3.4.4.5" - lazy val gremlinOrientdb = "com.orientechnologies" % "orientdb-gremlin" % "3.0.18" - lazy val hbaseClient = "org.apache.hbase" % "hbase-shaded-client" % "1.4.9" exclude ("org.slf4j", "slf4j-log4j12") + lazy val gremlinOrientdb = "com.orientechnologies" % "orientdb-gremlin" % "3.0.18" + lazy val hbaseClient = "org.apache.hbase" % "hbase-shaded-client" % "1.4.9" exclude ("org.slf4j", "slf4j-log4j12") lazy val scalactic = "org.scalactic" %% "scalactic" % "3.1.1" lazy val scalaGuice = "net.codingwell" %% "scala-guice" % "4.2.6" lazy val sangria = "org.sangria-graphql" %% "sangria" % "1.4.2" lazy val sangriaPlay = "org.sangria-graphql" %% "sangria-play-json" % "1.0.5" lazy val shapeless = "com.chuusai" %% "shapeless" % "2.3.3" - lazy val bouncyCastle = "org.bouncycastle" % "bcprov-jdk15on" % "1.65" - lazy val neo4jGremlin = "org.apache.tinkerpop" % "neo4j-gremlin" % "3.3.4" - lazy val neo4jTinkerpop = "org.neo4j" % "neo4j-tinkerpop-api-impl" % "0.7-3.2.3" exclude ("org.slf4j", "slf4j-nop") - lazy val apacheConfiguration = "commons-configuration" % "commons-configuration" % "1.10" - lazy val macroParadise = "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full + lazy val bouncyCastle = "org.bouncycastle" % "bcprov-jdk15on" % "1.65" + lazy val neo4jGremlin = "org.apache.tinkerpop" % "neo4j-gremlin" % "3.3.4" + lazy val neo4jTinkerpop = "org.neo4j" % "neo4j-tinkerpop-api-impl" % "0.7-3.2.3" exclude ("org.slf4j", "slf4j-nop") + lazy val apacheConfiguration = "commons-configuration" % "commons-configuration" % "1.10" + lazy val macroParadise = "org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full lazy val chimney = "io.scalaland" %% "chimney" % "0.4.0" lazy val elastic4sCore = "com.sksamuel.elastic4s" %% "elastic4s-core" % elastic4sVersion lazy val elastic4sHttpStreams = "com.sksamuel.elastic4s" %% "elastic4s-http-streams" % elastic4sVersion lazy val elastic4sHttp = "com.sksamuel.elastic4s" %% "elastic4s-http" % elastic4sVersion - lazy val log4jOverSlf4j = "org.slf4j" % "log4j-over-slf4j" % "1.7.25" - lazy val log4jToSlf4j = "org.apache.logging.log4j" % "log4j-to-slf4j" % "2.9.1" - lazy val reflections = "org.reflections" % "reflections" % "0.9.12" - lazy val hadoopClient = "org.apache.hadoop" % "hadoop-client" % "3.2.1" - lazy val zip4j = "net.lingala.zip4j" % "zip4j" % "2.3.1" + lazy val log4jOverSlf4j = "org.slf4j" % "log4j-over-slf4j" % "1.7.25" + lazy val log4jToSlf4j = "org.apache.logging.log4j" % "log4j-to-slf4j" % "2.9.1" + lazy val reflections = "org.reflections" % "reflections" % "0.9.12" + lazy val hadoopClient = "org.apache.hadoop" % "hadoop-client" % "3.2.1" + lazy val zip4j = "net.lingala.zip4j" % "zip4j" % "2.3.1" lazy val alpakka = "com.lightbend.akka" %% "akka-stream-alpakka-json-streaming" % "1.1.2" - lazy val handlebars = "com.github.jknack" % "handlebars" % "4.1.2" + lazy val handlebars = "com.github.jknack" % "handlebars" % "4.1.2" lazy val playMailer = "com.typesafe.play" %% "play-mailer" % "7.0.1" lazy val playMailerGuice = "com.typesafe.play" %% "play-mailer-guice" % "7.0.1" - lazy val jts = "com.vividsolutions" % "jts" % "1.13" + lazy val jts = "com.vividsolutions" % "jts" % "1.13" lazy val pbkdf2 = "io.github.nremond" %% "pbkdf2-scala" % "0.6.5" lazy val alpakkaS3 = "com.lightbend.akka" %% "akka-stream-alpakka-s3" % "1.1.2" - lazy val commonCodec = "commons-codec" % "commons-codec" % "1.11" + lazy val commonCodec = "commons-codec" % "commons-codec" % "1.11" lazy val scopt = "com.github.scopt" %% "scopt" % "4.0.0-RC2" + lazy val aix = "ai.x" %% "play-json-extensions" % "0.42.0" def scalaReflect(scalaVersion: String) = "org.scala-lang" % "scala-reflect" % scalaVersion def scalaCompiler(scalaVersion: String) = "org.scala-lang" % "scala-compiler" % scalaVersion diff --git a/thehive/app/org/thp/thehive/controllers/v1/Conversion.scala b/thehive/app/org/thp/thehive/controllers/v1/Conversion.scala index e897061fe1..de76eef468 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Conversion.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Conversion.scala @@ -501,6 +501,8 @@ object Conversion { .withFieldRenamed(_.external_id, _.patternId) .withFieldComputed(_.tactics, _.kill_chain_phases.map(_.phase_name).toSet) .withFieldRenamed(_.`type`, _.patternType) + .withFieldRenamed(_.capec_id, _.capecId) + .withFieldRenamed(_.capec_url, _.capecUrl) .withFieldRenamed(_.x_mitre_data_sources, _.dataSources) .withFieldRenamed(_.x_mitre_defense_bypassed, _.defenseBypassed) .withFieldRenamed(_.x_mitre_detection, _.detection) diff --git a/thehive/app/org/thp/thehive/controllers/v1/PatternCtrl.scala b/thehive/app/org/thp/thehive/controllers/v1/PatternCtrl.scala index 6fd574b3ca..e157b79f40 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/PatternCtrl.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/PatternCtrl.scala @@ -111,9 +111,17 @@ class PatternCtrl @Inject() ( private def createFromInput(inputPattern: InputPattern)(implicit graph: Graph, authContext: AuthContext): Try[Pattern with Entity] = if (inputPattern.external_id.isEmpty) Failure(BadRequestError(s"A pattern with no MITRE id cannot be imported")) - else if (patternSrv.startTraversal.alreadyImported(inputPattern.external_id)) - Failure(BadRequestError(s"A pattern with MITRE id '${inputPattern.external_id}' already exists in this organisation")) - else if (inputPattern.`type` != "attack-pattern") + else if (patternSrv.startTraversal.alreadyImported(inputPattern.external_id)) { + // TODO update pattern + def patternTraversal = patternSrv.get(EntityIdOrName(inputPattern.external_id)) + for { + pattern <- + patternSrv + .update(patternTraversal, Seq()) + .flatMap(_ => patternTraversal.getOrFail("Pattern")) + _ = if (inputPattern.x_mitre_is_subtechnique) linkPattern(pattern) + } yield pattern + } else if (inputPattern.`type` != "attack-pattern") Failure(BadRequestError(s"Only patterns with type attack-pattern are imported, this one is ${inputPattern.`type`}")) else for { diff --git a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala index 5a06ea1e25..d82e1c80d4 100644 --- a/thehive/app/org/thp/thehive/controllers/v1/Properties.scala +++ b/thehive/app/org/thp/thehive/controllers/v1/Properties.scala @@ -347,6 +347,8 @@ class Properties @Inject() ( .property("tactics", UMapping.string.set)(_.field.readonly) .property("url", UMapping.string)(_.field.updatable) .property("patternType", UMapping.string)(_.field.readonly) + .property("capecId", UMapping.string.optional)(_.field.readonly) + .property("capecUrl", UMapping.string.optional)(_.field.readonly) .property("revoked", UMapping.boolean)(_.field.readonly) .property("dataSources", UMapping.string.sequence)(_.field.readonly) .property("defenseBypassed", UMapping.string.sequence)(_.field.readonly) diff --git a/thehive/app/org/thp/thehive/models/Pattern.scala b/thehive/app/org/thp/thehive/models/Pattern.scala index aa9c9ef5c5..8fd1b87f36 100644 --- a/thehive/app/org/thp/thehive/models/Pattern.scala +++ b/thehive/app/org/thp/thehive/models/Pattern.scala @@ -13,6 +13,8 @@ case class Pattern( tactics: Set[String], url: String, patternType: String, + capecId: Option[String], + capecUrl: Option[String], revoked: Boolean, dataSources: Seq[String], defenseBypassed: Seq[String], @@ -34,6 +36,8 @@ case class RichPattern(pattern: Pattern with Entity, parent: Option[Pattern with def tactics: Set[String] = pattern.tactics def url: String = pattern.url def patternType: String = pattern.patternType + def capecId: Option[String] = pattern.capecId + def capecUrl: Option[String] = pattern.capecUrl def revoked: Boolean = pattern.revoked def dataSources: Seq[String] = pattern.dataSources def defenseBypassed: Seq[String] = pattern.defenseBypassed diff --git a/thehive/app/org/thp/thehive/services/PatternSrv.scala b/thehive/app/org/thp/thehive/services/PatternSrv.scala index f61112af40..a02201a838 100644 --- a/thehive/app/org/thp/thehive/services/PatternSrv.scala +++ b/thehive/app/org/thp/thehive/services/PatternSrv.scala @@ -4,6 +4,7 @@ import org.apache.tinkerpop.gremlin.structure.Graph import org.thp.scalligraph.EntityIdOrName import org.thp.scalligraph.auth.AuthContext import org.thp.scalligraph.models.{Database, Entity} +import org.thp.scalligraph.query.PropertyUpdater import org.thp.scalligraph.services._ import org.thp.scalligraph.traversal.TraversalOps.TraversalOpsDefs import org.thp.scalligraph.traversal.{Converter, Traversal} @@ -11,6 +12,7 @@ import org.thp.thehive.models._ import org.thp.thehive.services.CaseOps._ import org.thp.thehive.services.PatternOps._ import org.thp.thehive.services.ProcedureOps._ +import play.api.libs.json.JsObject import java.util.{Map => JMap} import javax.inject.{Inject, Named, Singleton} @@ -42,6 +44,15 @@ class PatternSrv @Inject() ( patterns = caseSrv.get(caze).procedure.pattern.richPattern.toSeq } yield patterns.map(_.patternId) + override def update( + traversal: Traversal.V[Pattern], + propertyUpdaters: Seq[PropertyUpdater] + )(implicit graph: Graph, authContext: AuthContext): Try[(Traversal.V[Pattern], JsObject)] = + auditSrv.mergeAudits(super.update(traversal, propertyUpdaters)) { + case (patternSteps, updatedFields) => + patternSteps.clone().getOrFail("Pattern").flatMap(pattern => auditSrv.pattern.update(pattern, updatedFields)) + } + def remove(pattern: Pattern with Entity)(implicit graph: Graph, authContext: AuthContext): Try[Unit] = for { organisation <- organisationSrv.getOrFail(authContext.organisation) diff --git a/thehive/test/org/thp/thehive/controllers/v1/PatternCtrlTest.scala b/thehive/test/org/thp/thehive/controllers/v1/PatternCtrlTest.scala index 4fd2f61125..158ecbedbc 100644 --- a/thehive/test/org/thp/thehive/controllers/v1/PatternCtrlTest.scala +++ b/thehive/test/org/thp/thehive/controllers/v1/PatternCtrlTest.scala @@ -4,7 +4,7 @@ import io.scalaland.chimney.dsl._ import org.thp.scalligraph.controllers.FakeTemporaryFile import org.thp.thehive.TestAppBuilder import org.thp.thehive.dto.v1.OutputPattern -import play.api.libs.json.JsArray +import play.api.libs.json.{JsArray, JsString} import play.api.mvc.MultipartFormData.FilePart import play.api.mvc.{AnyContentAsMultipartFormData, MultipartFormData} import play.api.test.{FakeRequest, PlaySpecification} @@ -16,6 +16,8 @@ case class TestPattern( tactics: Set[String], url: String, patternType: String, + capecId: Option[String], + capecUrl: Option[String], revoked: Boolean, dataSources: Seq[String], defenseBypassed: Seq[String], @@ -50,7 +52,10 @@ class PatternCtrlTest extends PlaySpecification with TestAppBuilder { val result = app[PatternCtrl].importMitre(request) status(result) must beEqualTo(201).updateMessage(s => s"$s\n${contentAsString(result)}") - contentAsJson(result).as[JsArray].value.size must beEqualTo(8) + contentAsJson(result) + .as[JsArray] + .value + .count(jsValue => (jsValue \ "status").get == JsString("Success")) must beEqualTo(8) } "get a existing pattern" in testApp { app => @@ -68,6 +73,8 @@ class PatternCtrlTest extends PlaySpecification with TestAppBuilder { Set("testTactic1", "testTactic2"), "http://test.pattern.url", "unit-test", + None, + None, revoked = false, Seq(), Seq(),