Skip to content

Commit

Permalink
Merge branch 'feature-mitre-attack' into develop-th4
Browse files Browse the repository at this point in the history
  • Loading branch information
To-om committed Jan 21, 2021
2 parents 6c1f3a7 + c80e405 commit 9b6d825
Show file tree
Hide file tree
Showing 30 changed files with 1,464 additions and 108 deletions.
107 changes: 107 additions & 0 deletions dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.thp.thehive.dto.v1

import play.api.libs.json.{Format, Json, Reads, Writes}

import java.util.Date

case class InputPattern(
external_id: String,
name: String,
description: Option[String],
kill_chain_phases: Seq[InputKillChainPhase],
url: String,
`type`: String,
x_mitre_platforms: Seq[String],
x_mitre_data_sources: Seq[String],
x_mitre_is_subtechnique: Option[Boolean],
x_mitre_version: Option[String]
)

case class InputReference(
source_name: String,
external_id: Option[String],
url: String
)

case class InputKillChainPhase(
kill_chain_name: String,
phase_name: String
)

object InputReference {
implicit val reads: Reads[InputReference] = Reads[InputReference] { json =>
for {
source_name <- (json \ "source_name").validate[String]
external_id <- (json \ "external_id").validateOpt[String]
url <- (json \ "url").validate[String]
} yield InputReference(
source_name,
external_id,
url
)
}

implicit val writes: Writes[InputReference] = Json.writes[InputReference]
}

object InputKillChainPhase {
implicit val reads: Reads[InputKillChainPhase] = Json.reads[InputKillChainPhase]

implicit val writes: Writes[InputKillChainPhase] = Json.writes[InputKillChainPhase]
}

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))
name <- (json \ "name").validate[String]
description <- (json \ "description").validateOpt[String]
kill_chain_phases <- (json \ "kill_chain_phases").validateOpt[Seq[InputKillChainPhase]]
techniqueType <- (json \ "type").validate[String]
x_mitre_platforms <- (json \ "x_mitre_platforms").validateOpt[Seq[String]]
x_mitre_data_sources <- (json \ "x_mitre_data_sources").validateOpt[Seq[String]]
x_mitre_is_subtechnique <- (json \ "x_mitre_is_subtechnique").validateOpt[Boolean]
x_mitre_version <- (json \ "x_mitre_version").validateOpt[String]
} yield InputPattern(
mitreReference.flatMap(_.external_id).getOrElse(""),
name,
description,
kill_chain_phases.getOrElse(Seq()),
mitreReference.map(_.url).getOrElse(""),
techniqueType,
x_mitre_platforms.getOrElse(Seq()),
x_mitre_data_sources.getOrElse(Seq()),
x_mitre_is_subtechnique,
x_mitre_version
)
}

private def isSourceNameValid(reference: String): Boolean =
reference == "mitre-attack"

implicit val writes: Writes[InputPattern] = Json.writes[InputPattern]
}

case class OutputPattern(
_id: String,
_type: String,
_createdBy: String,
_updatedBy: Option[String],
_createdAt: Date,
_updatedAt: Option[Date],
patternId: String,
name: String,
description: Option[String],
tactics: Set[String],
url: String,
patternType: String,
platforms: Seq[String],
dataSources: Seq[String],
version: Option[String],
parent: Option[String]
)

object OutputPattern {
implicit val format: Format[OutputPattern] = Json.format[OutputPattern]
}
45 changes: 45 additions & 0 deletions dto/src/main/scala/org/thp/thehive/dto/v1/Procedure.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.thp.thehive.dto.v1

import play.api.libs.json.{Format, Json, Reads, Writes}

import java.util.Date

case class InputProcedure(
description: String,
occurence: Date,
caseId: String,
patternId: String
)

object InputProcedure {
implicit val reads: Reads[InputProcedure] = Reads[InputProcedure] { json =>
for {
description <- (json \ "description").validate[String]
occurence <- (json \ "occurence").validate[Date]
caseId <- (json \ "caseId").validate[String]
patternId <- (json \ "patternId").validate[String]
} yield InputProcedure(
description,
occurence,
caseId,
patternId
)
}

implicit val writes: Writes[InputProcedure] = Json.writes[InputProcedure]
}

case class OutputProcedure(
_id: String,
_createdAt: Date,
_createdBy: String,
_updatedAt: Option[Date],
_updatedBy: Option[String],
description: String,
occurence: Date,
patternId: String
)

object OutputProcedure {
implicit val format: Format[OutputProcedure] = Json.format[OutputProcedure]
}
43 changes: 18 additions & 25 deletions thehive/app/org/thp/thehive/controllers/v0/CaseCtrl.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package org.thp.thehive.controllers.v0

import java.lang.{Long => JLong}
import java.util.Date

import javax.inject.{Inject, Named, Singleton}
import org.thp.scalligraph.controllers.{Entrypoint, FPathElem, FPathEmpty, FieldsParser}
import org.thp.scalligraph.models.{Database, UMapping}
import org.thp.scalligraph.query._
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{Converter, IteratorOutput, Traversal}
import org.thp.scalligraph.{RichSeq, _}
import org.thp.scalligraph._
import org.thp.thehive.controllers.v0.Conversion._
import org.thp.thehive.dto.v0.{InputCase, InputTask}
import org.thp.thehive.dto.v1.InputCustomFieldValue
Expand All @@ -24,6 +20,8 @@ import org.thp.thehive.services._
import play.api.libs.json._
import play.api.mvc.{Action, AnyContent, Results}

import java.util.Date
import javax.inject.{Inject, Named, Singleton}
import scala.util.{Failure, Success}

@Singleton
Expand Down Expand Up @@ -195,9 +193,7 @@ class PublicCase @Inject() (
with CaseRenderer {
override val entityName: String = "case"
override val initialQuery: Query =
Query.init[Traversal.V[Case]]("listCase", (graph, authContext) =>
organisationSrv.get(authContext.organisation)(graph).cases
)
Query.init[Traversal.V[Case]]("listCase", (graph, authContext) => organisationSrv.get(authContext.organisation)(graph).cases)
override val getQuery: ParamQuery[EntityIdOrName] =
Query.initWithParam[EntityIdOrName, Traversal.V[Case]](
"getCase",
Expand All @@ -206,23 +202,20 @@ class PublicCase @Inject() (
)
override val pageQuery: ParamQuery[OutputParam] =
Query.withParam[OutputParam, Traversal.V[Case], IteratorOutput](
"page",
FieldsParser[OutputParam],
{
case (OutputParam(from, to, withStats, _), caseSteps, authContext) =>
caseSteps
.richPage(from, to, withTotal = true) {
case c if withStats =>
c.richCaseWithCustomRenderer(caseStatsRenderer(authContext))(authContext)
case c =>
c.richCase(authContext).domainMap(_ -> JsObject.empty)
}
}
)
override val outputQuery:
Query = Query.outputWithContext[RichCase, Traversal.V[Case]]((caseSteps, authContext) =>
caseSteps.richCase(authContext)
"page",
FieldsParser[OutputParam],
{
case (OutputParam(from, to, withStats, _), caseSteps, authContext) =>
caseSteps
.richPage(from, to, withTotal = true) {
case c if withStats =>
c.richCaseWithCustomRenderer(caseStatsRenderer(authContext))(authContext)
case c =>
c.richCase(authContext).domainMap(_ -> JsObject.empty)
}
}
)
override val outputQuery: Query = Query.outputWithContext[RichCase, Traversal.V[Case]]((caseSteps, authContext) => caseSteps.richCase(authContext))
override val extraQueries: Seq[ParamQuery[_]] = Seq(
Query[Traversal.V[Case], Traversal.V[Observable]]("observables", (caseSteps, authContext) => caseSteps.observables(authContext)),
Query[Traversal.V[Case], Traversal.V[Task]]("tasks", (caseSteps, authContext) => caseSteps.tasks(authContext))
Expand Down Expand Up @@ -335,7 +328,7 @@ class PublicCase @Inject() (
case CustomFieldType.integer => new Converter[Any, JsValue] { def apply(x: JsValue): Any = x.as[Long] }
case CustomFieldType.string => new Converter[Any, JsValue] { def apply(x: JsValue): Any = x.as[String] }
}
.getOrElse(new Converter[Any, JsValue] { def apply(x: JsValue): Any = x })
.getOrElse((x: JsValue) => x)
case _ => (x: JsValue) => x
}
.custom {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ object Conversion {
implicit val reportTagWrites: Writes[ReportTag] = Writes[ReportTag] { tag =>
Json.obj("level" -> tag.level.toString, "namespace" -> tag.namespace, "predicate" -> tag.predicate, "value" -> tag.value)
}

implicit val observableOutput: Renderer.Aux[RichObservable, OutputObservable] = Renderer.toJson[RichObservable, OutputObservable](
_.into[OutputObservable]
.withFieldConst(_._type, "case_artifact")
Expand Down
45 changes: 41 additions & 4 deletions thehive/app/org/thp/thehive/controllers/v1/Conversion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ object Conversion {
.transform
)

implicit val organiastionRenderer: Renderer.Aux[Organisation with Entity, OutputOrganisation] =
implicit val organisationRenderer: Renderer.Aux[Organisation with Entity, OutputOrganisation] =
Renderer.toJson[Organisation with Entity, OutputOrganisation](organisation =>
OutputOrganisation(
organisation._id.toString,
Expand Down Expand Up @@ -272,7 +272,8 @@ object Conversion {

implicit val taxonomyWithStatsOutput: Renderer.Aux[(RichTaxonomy, JsObject), OutputTaxonomy] =
Renderer.toJson[(RichTaxonomy, JsObject), OutputTaxonomy] { taxoWithExtraData =>
taxoWithExtraData._1
taxoWithExtraData
._1
.into[OutputTaxonomy]
.withFieldComputed(_._id, _._id.toString)
.withFieldConst(_._type, "Taxonomy")
Expand All @@ -283,8 +284,7 @@ object Conversion {

implicit val tagOutput: Renderer.Aux[Tag, OutputTag] =
Renderer.toJson[Tag, OutputTag](
_.into[OutputTag]
.transform
_.into[OutputTag].transform
)

implicit class InputUserOps(inputUser: InputUser) {
Expand Down Expand Up @@ -494,4 +494,41 @@ object Conversion {
.transform
}

implicit class InputPatternOps(inputPattern: InputPattern) {
def toPattern: Pattern =
inputPattern
.into[Pattern]
.withFieldRenamed(_.external_id, _.patternId)
.withFieldComputed(_.tactics, _.kill_chain_phases.map(_.phase_name).toSet)
.withFieldRenamed(_.`type`, _.patternType)
.withFieldRenamed(_.x_mitre_platforms, _.platforms)
.withFieldRenamed(_.x_mitre_data_sources, _.dataSources)
.withFieldRenamed(_.x_mitre_version, _.revision)
.transform
}

implicit val richPatternRenderer: Renderer.Aux[RichPattern, OutputPattern] =
Renderer.toJson[RichPattern, OutputPattern](
_.into[OutputPattern]
.withFieldComputed(_._id, _._id.toString)
.withFieldConst(_._type, "Pattern")
.withFieldComputed(_.parent, _.parent.map(_.patternId))
.transform
)

implicit class InputProcedureOps(inputProcedure: InputProcedure) {
def toProcedure: Procedure =
inputProcedure
.into[Procedure]
.transform
}

implicit val richProcedureRenderer: Renderer.Aux[RichProcedure, OutputProcedure] =
Renderer.toJson[RichProcedure, OutputProcedure](
_.into[OutputProcedure]
.withFieldComputed(_._id, _._id.toString)
.withFieldComputed(_.patternId, _.pattern.patternId)
.transform
)

}
4 changes: 4 additions & 0 deletions thehive/app/org/thp/thehive/controllers/v1/DescribeCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class DescribeCtrl @Inject() (
observableTypeCtrl: ObservableTypeCtrl,
organisationCtrl: OrganisationCtrl,
// pageCtrl: PageCtrl,
patternCtrl: PatternCtrl,
procedureCtrl: ProcedureCtrl,
profileCtrl: ProfileCtrl,
taskCtrl: TaskCtrl,
taxonomyCtrl: TaxonomyCtrl,
Expand Down Expand Up @@ -108,6 +110,8 @@ class DescribeCtrl @Inject() (
),
EntityDescription("organisation", "listOrganisation", organisationCtrl.publicProperties.list.flatMap(propertyToJson("organisation", _))),
// EntityDescription("page", "listPage", pageCtrl.publicProperties.list.flatMap(propertyToJson("page", _)))
EntityDescription("pattern", "listPattern", patternCtrl.publicProperties.list.flatMap(propertyToJson("pattern", _))),
EntityDescription("procedure", "listProcedure", procedureCtrl.publicProperties.list.flatMap(propertyToJson("procedure", _))),
EntityDescription("profile", "listProfile", profileCtrl.publicProperties.list.flatMap(propertyToJson("profile", _))),
EntityDescription("task", "listTask", taskCtrl.publicProperties.list.flatMap(propertyToJson("case_task", _))),
EntityDescription("taxonomy", "listTaxonomy", taxonomyCtrl.publicProperties.list.flatMap(propertyToJson("taxonomy", _))),
Expand Down
Loading

0 comments on commit 9b6d825

Please sign in to comment.