Skip to content

Commit

Permalink
Added parent & children extraData for patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
rriclet committed Feb 3, 2021
1 parent 14561f5 commit 2bb2618
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 98 deletions.
4 changes: 2 additions & 2 deletions dto/src/main/scala/org/thp/thehive/dto/v1/Pattern.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.thp.thehive.dto.v1

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

import java.util.Date

Expand Down Expand Up @@ -121,7 +121,7 @@ case class OutputPattern(
remoteSupport: Boolean,
systemRequirements: Seq[String],
version: Option[String],
parent: Option[String]
extraData: JsObject
)

object OutputPattern {
Expand Down
15 changes: 13 additions & 2 deletions thehive/app/org/thp/thehive/controllers/v1/Conversion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -511,15 +511,26 @@ object Conversion {
.transform
}

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

implicit val patternWithStatsOutput: Renderer.Aux[(RichPattern, JsObject), OutputPattern] =
Renderer.toJson[(RichPattern, JsObject), OutputPattern] { patternWithExtraData =>
patternWithExtraData
._1
.into[OutputPattern]
.withFieldComputed(_._id, _._id.toString)
.withFieldConst(_._type, "Pattern")
.withFieldConst(_.extraData, patternWithExtraData._2)
.transform
}

implicit class InputProcedureOps(inputProcedure: InputProcedure) {
def toProcedure: Procedure =
inputProcedure
Expand Down
30 changes: 17 additions & 13 deletions thehive/app/org/thp/thehive/controllers/v1/LogRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,38 @@ import play.api.libs.json._

trait LogRenderer extends BaseRenderer[Log] {

def caseParent(implicit
private def caseParent(implicit
authContext: AuthContext
): Traversal.V[Log] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.`case`.richCase.fold.domainMap(_.headOption.fold[JsValue](JsNull)(_.toJson))

def taskParent(implicit
private def taskParent(implicit
authContext: AuthContext
): Traversal.V[Log] => Traversal[JsValue, JMap[String, Any], Converter[JsValue, JMap[String, Any]]] =
_.task.project(_.by(_.richTask.fold).by(_.`case`.richCase.fold)).domainMap {
case (task, case0) =>
task.headOption.fold[JsValue](JsNull)(_.toJson.as[JsObject] + ("case" -> case0.headOption.fold[JsValue](JsNull)(_.toJson)))
}

def taskParentId: Traversal.V[Log] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
private def taskParentId: Traversal.V[Log] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
_.task.fold.domainMap(_.headOption.fold[JsValue](JsNull)(c => JsString(c._id.toString)))

def actionCount: Traversal.V[Log] => Traversal[JsValue, JLong, Converter[JsValue, JLong]] =
private def actionCount: Traversal.V[Log] => Traversal[JsValue, JLong, Converter[JsValue, JLong]] =
_.in("ActionContext").count.domainMap(JsNumber(_))

def logStatsRenderer(extraData: Set[String])(
implicit authContext: AuthContext
def logStatsRenderer(extraData: Set[String])(implicit
authContext: AuthContext
): Traversal.V[Log] => JsTraversal = { implicit traversal =>
baseRenderer(extraData, traversal, {
case (f, "case") => addData("case", f)(caseParent)
case (f, "task") => addData("task", f)(taskParent)
case (f, "taskId") => addData("taskId", f)(taskParentId)
case (f, "actionCount") => addData("actionCount", f)(actionCount)
case (f, _) => f
})
baseRenderer(
extraData,
traversal,
{
case (f, "case") => addData("case", f)(caseParent)
case (f, "task") => addData("task", f)(taskParent)
case (f, "taskId") => addData("taskId", f)(taskParentId)
case (f, "actionCount") => addData("actionCount", f)(actionCount)
case (f, _) => f
}
)
}
}
8 changes: 6 additions & 2 deletions thehive/app/org/thp/thehive/controllers/v1/PatternCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class PatternCtrl @Inject() (
properties: Properties,
patternSrv: PatternSrv,
@Named("with-thehive-schema") implicit val db: Database
) extends QueryableCtrl {
) extends QueryableCtrl
with PatternRenderer {
override val entityName: String = "pattern"
override val publicProperties: PublicProperties = properties.pattern
override val initialQuery: Query = Query.init[Traversal.V[Pattern]](
Expand All @@ -38,7 +39,10 @@ class PatternCtrl @Inject() (
override val pageQuery: ParamQuery[OutputParam] = Query.withParam[OutputParam, Traversal.V[Pattern], IteratorOutput](
"page",
FieldsParser[OutputParam],
(range, patternSteps, _) => patternSteps.richPage(range.from, range.to, range.extraData.contains("total"))(_.richPattern)
{
case (OutputParam(from, to, extraData), patternSteps, _) =>
patternSteps.richPage(from, to, extraData.contains("total"))(_.richPatternWithCustomRenderer(patternRenderer(extraData - "total")))
}
)
override val outputQuery: Query = Query.output[RichPattern, Traversal.V[Pattern]](_.richPattern)
override val getQuery: ParamQuery[EntityIdOrName] = Query.initWithParam[EntityIdOrName, Traversal.V[Pattern]](
Expand Down
31 changes: 31 additions & 0 deletions thehive/app/org/thp/thehive/controllers/v1/PatternRenderer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.thp.thehive.controllers.v1

import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{Converter, Traversal}
import org.thp.thehive.controllers.v1.Conversion._
import org.thp.thehive.models.Pattern
import org.thp.thehive.services.PatternOps._
import play.api.libs.json._

import java.util.{List => JList, Map => JMap}

trait PatternRenderer extends BaseRenderer[Pattern] {

private def parent: Traversal.V[Pattern] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.parent.richPattern.fold.domainMap(_.headOption.fold[JsValue](JsNull)(_.toJson))

private def children: Traversal.V[Pattern] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.children.richPattern.fold.domainMap(_.toJson)

def patternRenderer(extraData: Set[String]): Traversal.V[Pattern] => JsTraversal = { implicit traversal =>
baseRenderer(
extraData,
traversal,
{
case (f, "children") => addData("children", f)(children)
case (f, "parent") => addData("parent", f)(parent)
case (f, _) => f
}
)
}
}
15 changes: 14 additions & 1 deletion thehive/app/org/thp/thehive/controllers/v1/ProcedureCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.thp.thehive.controllers.v1
import org.thp.scalligraph.EntityIdOrName
import org.thp.scalligraph.controllers.{Entrypoint, FieldsParser}
import org.thp.scalligraph.models.Database
import org.thp.scalligraph.query.{ParamQuery, PublicProperties, Query}
import org.thp.scalligraph.query.{ParamQuery, PropertyUpdater, PublicProperties, Query}
import org.thp.scalligraph.traversal.TraversalOps.TraversalOpsDefs
import org.thp.scalligraph.traversal.{IteratorOutput, Traversal}
import org.thp.thehive.controllers.v1.Conversion._
Expand Down Expand Up @@ -62,6 +62,19 @@ class ProcedureCtrl @Inject() (
.map(richProcedure => Results.Ok(richProcedure.toJson))
}

def update(procedureId: String): Action[AnyContent] =
entrypoint("update procedure")
.extract("procedure", FieldsParser.update("procedure", properties.procedure))
.authTransaction(db) { implicit request => implicit graph =>
val propertyUpdaters: Seq[PropertyUpdater] = request.body("procedure")
procedureSrv
.update(
_.get(EntityIdOrName(procedureId)),
propertyUpdaters
)
.map(_ => Results.NoContent)
}

def delete(procedureId: String): Action[AnyContent] =
entrypoint("delete procedure")
.authPermittedTransaction(db, Permissions.manageProcedure) { implicit request => implicit graph =>
Expand Down
7 changes: 6 additions & 1 deletion thehive/app/org/thp/thehive/controllers/v1/Properties.scala
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,13 @@ class Properties @Inject() (
.property("tactics", UMapping.string.set)(_.field.readonly)
.property("url", UMapping.string)(_.field.updatable)
.property("patternType", UMapping.string)(_.field.readonly)
.property("platforms", UMapping.string.sequence)(_.field.readonly)
.property("revoked", UMapping.boolean)(_.field.readonly)
.property("dataSources", UMapping.string.sequence)(_.field.readonly)
.property("defenseBypassed", UMapping.string.sequence)(_.field.readonly)
.property("detection", UMapping.string.optional)(_.field.readonly)
.property("platforms", UMapping.string.sequence)(_.field.readonly)
.property("remoteSupport", UMapping.boolean)(_.field.readonly)
.property("systemRequirements", UMapping.string.sequence)(_.field.readonly)
.property("version", UMapping.string.optional)(_.field.readonly)
.build

Expand Down
49 changes: 27 additions & 22 deletions thehive/app/org/thp/thehive/controllers/v1/TaskRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,51 @@ import play.api.libs.json._

trait TaskRenderer extends BaseRenderer[Task] {

def caseParent(implicit
private def caseParent(implicit
authContext: AuthContext
): Traversal.V[Task] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.`case`.richCase.fold.domainMap(_.headOption.fold[JsValue](JsNull)(_.toJson))

def caseParentId: Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
private def caseParentId: Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
_.`case`.fold.domainMap(_.headOption.fold[JsValue](JsNull)(c => JsString(c._id.toString)))

def caseTemplateParent: Traversal.V[Task] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
private def caseTemplateParent: Traversal.V[Task] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.caseTemplate.richCaseTemplate.fold.domainMap(_.headOption.fold[JsValue](JsNull)(_.toJson))

def caseTemplateParentId: Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
private def caseTemplateParentId: Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
_.caseTemplate.fold.domainMap(_.headOption.fold[JsValue](JsNull)(ct => JsString(ct._id.toString)))

def shareCount: Traversal.V[Task] => Traversal[JsValue, JLong, Converter[JsValue, JLong]] =
private def shareCount: Traversal.V[Task] => Traversal[JsValue, JLong, Converter[JsValue, JLong]] =
_.organisations.count.domainMap(count => JsNumber(count - 1))

def isOwner(implicit authContext: AuthContext): Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
private def isOwner(implicit authContext: AuthContext): Traversal.V[Task] => Traversal[JsValue, JList[Vertex], Converter[JsValue, JList[Vertex]]] =
_.origin.get(authContext.organisation).fold.domainMap(l => JsBoolean(l.nonEmpty))

def actionRequired(implicit authContext: AuthContext): Traversal.V[Task] => Traversal[JsValue, JBoolean, Converter[JsValue, JBoolean]] =
private def actionRequired(implicit authContext: AuthContext): Traversal.V[Task] => Traversal[JsValue, JBoolean, Converter[JsValue, JBoolean]] =
_.actionRequired.domainMap(JsBoolean(_))

def actionRequiredMap(implicit authContext: AuthContext):
Traversal.V[Task] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
private def actionRequiredMap(implicit
authContext: AuthContext
): Traversal.V[Task] => Traversal[JsValue, JList[JMap[String, Any]], Converter[JsValue, JList[JMap[String, Any]]]] =
_.actionRequiredMap.fold.domainMap(_.toMap.toJson)

def taskStatsRenderer(extraData: Set[String])(
implicit authContext: AuthContext
def taskStatsRenderer(extraData: Set[String])(implicit
authContext: AuthContext
): Traversal.V[Task] => JsTraversal = { implicit traversal =>
baseRenderer(extraData, traversal, {
case (f, "case") => addData("case", f)(caseParent)
case (f, "caseId") => addData("caseId", f)(caseParentId)
case (f, "caseTemplate") => addData("caseTemplate", f)(caseTemplateParent)
case (f, "caseTemplateId") => addData("caseTemplateId", f)(caseTemplateParentId)
case (f, "isOwner") => addData("isOwner", f)(isOwner)
case (f, "shareCount") => addData("shareCount", f)(shareCount)
case (f, "actionRequired") => addData("actionRequired", f)(actionRequired)
case (f, "actionRequiredMap") => addData("actionRequiredMap", f)(actionRequiredMap)
case (f, _) => f
})
baseRenderer(
extraData,
traversal,
{
case (f, "case") => addData("case", f)(caseParent)
case (f, "caseId") => addData("caseId", f)(caseParentId)
case (f, "caseTemplate") => addData("caseTemplate", f)(caseTemplateParent)
case (f, "caseTemplateId") => addData("caseTemplateId", f)(caseTemplateParentId)
case (f, "isOwner") => addData("isOwner", f)(isOwner)
case (f, "shareCount") => addData("shareCount", f)(shareCount)
case (f, "actionRequired") => addData("actionRequired", f)(actionRequired)
case (f, "actionRequiredMap") => addData("actionRequiredMap", f)(actionRequiredMap)
case (f, _) => f
}
)
}
}
6 changes: 2 additions & 4 deletions thehive/app/org/thp/thehive/controllers/v1/TaxonomyCtrl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ class TaxonomyCtrl @Inject() (
"page",
FieldsParser[OutputParam],
{
case (OutputParam(from, to, extraData), taxoSteps, authContext) =>
taxoSteps.richPage(from, to, extraData.contains("total")) {
_.richTaxonomyWithCustomRenderer(taxoStatsRenderer(extraData - "total"))
}
case (OutputParam(from, to, extraData), taxoSteps, _) =>
taxoSteps.richPage(from, to, extraData.contains("total"))(_.richTaxonomyWithCustomRenderer(taxoStatsRenderer(extraData - "total")))
}
)
override val outputQuery: Query =
Expand Down
36 changes: 8 additions & 28 deletions thehive/app/org/thp/thehive/controllers/v1/TaxonomyRenderer.scala
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
package org.thp.thehive.controllers.v1

import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.{Converter, Traversal}
import org.thp.thehive.models.Taxonomy
import org.thp.thehive.services.TaxonomyOps._
import play.api.libs.json._

import java.util.{Map => JMap}

trait TaxonomyRenderer {
trait TaxonomyRenderer extends BaseRenderer[Taxonomy] {

def enabledStats: Traversal.V[Taxonomy] => Traversal[JsValue, Boolean, Converter[JsValue, Boolean]] =
_.enabled.domainMap(l => JsBoolean(l))

def taxoStatsRenderer(extraData: Set[String]):
Traversal.V[Taxonomy] => Traversal[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]] = { traversal =>
def addData[G](
name: String
)(f: Traversal.V[Taxonomy] => Traversal[JsValue, G, Converter[JsValue, G]]): Traversal[JsObject, JMap[String, Any], Converter[
JsObject,
JMap[String, Any]
]] => Traversal[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]] = { t =>
val dataTraversal = f(traversal.start)
t.onRawMap[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]](_.by(dataTraversal.raw)) { jmap =>
t.converter(jmap) + (name -> dataTraversal.converter(jmap.get(name).asInstanceOf[G]))
}
}

if (extraData.isEmpty) traversal.constant2[JsObject, JMap[String, Any]](JsObject.empty)
else {
val dataName = extraData.toSeq
dataName.foldLeft[Traversal[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]]](
traversal.onRawMap[JsObject, JMap[String, Any], Converter[JsObject, JMap[String, Any]]](_.project(dataName.head, dataName.tail: _*))(_ =>
JsObject.empty
)
) {
case (f, "enabled") => addData("enabled")(enabledStats)(f)
def taxoStatsRenderer(extraData: Set[String]): Traversal.V[Taxonomy] => JsTraversal = { implicit traversal =>
baseRenderer(
extraData,
traversal,
{
case (f, "enabled") => addData("enabled", f)(enabledStats)
case (f, _) => f
}
}
)
}
}
20 changes: 19 additions & 1 deletion thehive/app/org/thp/thehive/services/PatternSrv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import org.thp.scalligraph.EntityIdOrName
import org.thp.scalligraph.auth.AuthContext
import org.thp.scalligraph.models.{Database, Entity}
import org.thp.scalligraph.services._
import org.thp.scalligraph.traversal.TraversalOps._
import org.thp.scalligraph.traversal.TraversalOps.TraversalOpsDefs
import org.thp.scalligraph.traversal.{Converter, Traversal}
import org.thp.thehive.models._
import org.thp.thehive.services.CaseOps._
Expand Down Expand Up @@ -52,11 +52,15 @@ class PatternSrv @Inject() (

object PatternOps {
implicit class PatternOpsDefs(traversal: Traversal.V[Pattern]) {

def getByPatternId(patternId: String): Traversal.V[Pattern] = traversal.has(_.patternId, patternId)

def parent: Traversal.V[Pattern] =
traversal.in[PatternPattern].v[Pattern]

def children: Traversal.V[Pattern] =
traversal.out[PatternPattern].v[Pattern]

def procedure: Traversal.V[Procedure] =
traversal.in[ProcedurePattern].v[Procedure]

Expand All @@ -74,5 +78,19 @@ object PatternOps {
RichPattern(pattern, parent.headOption)
}

def richPatternWithCustomRenderer[D, G, C <: Converter[D, G]](
entityRenderer: Traversal.V[Pattern] => Traversal[D, G, C]
): Traversal[(RichPattern, D), JMap[String, Any], Converter[(RichPattern, D), JMap[String, Any]]] =
traversal
.project(
_.by
.by(_.in[PatternPattern].v[Pattern].fold)
.by(entityRenderer)
)
.domainMap {
case (pattern, parent, renderedEntity) =>
RichPattern(pattern, parent.headOption) -> renderedEntity
}

}
}
Loading

0 comments on commit 2bb2618

Please sign in to comment.