All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.dataflowengineoss.queryengine.package.scala Maven / Gradle / Ivy

package io.joern.dataflowengineoss

import io.shiftleft.codepropertygraph.generated.nodes.{AstNode, Call, CfgNode}

package object queryengine {

  /** The TaskFingerprint uniquely identifies a task.
    */
  case class TaskFingerprint(sink: CfgNode, callSiteStack: List[Call], callDepth: Int)

  /** A (partial) result, informing about a path that exists from a source to another node in the graph.
    *
    * @param taskStack
    *   The list of tasks that was solved to arrive at this task
    *
    * @param path
    *   A path to the sink.
    *
    * @param partial
    *   indicate whether this result stands on its own or requires further analysis, e.g., by expanding output arguments
    *   backwards into method output parameters.
    */
  case class ReachableByResult(taskStack: List[TaskFingerprint], path: Vector[PathElement], partial: Boolean = false) {

    def fingerprint: TaskFingerprint = taskStack.last
    def sink: CfgNode                = fingerprint.sink
    def callSiteStack: List[Call]    = fingerprint.callSiteStack

    def callDepth: Int = fingerprint.callDepth

    def startingPoint: CfgNode = path.head.node.asInstanceOf[CfgNode]

    /** If the result begins in an output argument, return it.
      */
    def outputArgument: Option[CfgNode] = {
      path.headOption.collect {
        case elem: PathElement if elem.isOutputArg =>
          elem.node.asInstanceOf[CfgNode]
      }
    }
  }

  /** We represent data flows as sequences of path elements, where each path element consists of a node, flags and the
    * label of its outgoing edge.
    *
    * @param node
    *   The parent node. This is actually always a CfgNode during data flow computation, however, since the source may
    *   be an arbitrary AST node, we may add an AST node to the start of the flow right before returning flows to the
    *   user.
    *
    * @param callSiteStack
    *   The call stack when this path element was created. Since we may enter the same function via two different call
    *   sites, path elements should only be treated as the same if they are the same node and we've reached them via the
    *   same call sequence.
    *
    * @param visible
    *   whether this path element should be shown in the flow
    * @param isOutputArg
    *   input and output arguments are the same node in the CPG, so, we need this additional flag to determine whether
    *   we are on an input or output argument. By default, we consider arguments to be input arguments, meaning that
    *   when tracking `x` at `f(x)`, we do not expand into `f` but rather upwards to producers of `x`.
    * @param outEdgeLabel
    *   label of the outgoing DDG edge
    */
  case class PathElement(
    node: AstNode,
    callSiteStack: List[Call] = List(),
    visible: Boolean = true,
    isOutputArg: Boolean = false,
    outEdgeLabel: String = ""
  )

  /** @param taskStack
    *   The list of tasks that was solved to arrive at this task, including the current task, which is to be solved. The
    *   current task is the last element of the list.
    *
    * @param initialPath
    *   The path from the current sink downwards to previous sinks.
    */
  case class ReachableByTask(taskStack: List[TaskFingerprint], initialPath: Vector[PathElement]) {

    /** This tasks fingerprint: if two tasks have the same fingerprint, then the TaskSolver MUST return the same result
      * for them. This is the basis of our caching scheme.
      */
    def fingerprint: TaskFingerprint = taskStack.last

    /** The sink at which we start the analysis (upwards)
      */
    def sink: CfgNode = fingerprint.sink

    /** The call sites we have expanded downwards during this analysis. We need to keep track of this so that we do not
      * end up expanding one call site and then returning to a different call site, which would produce an unreachable
      * path.
      */
    def callSiteStack: List[Call] = fingerprint.callSiteStack

    /** The call depth at which this task was created.
      */
    def callDepth: Int = fingerprint.callDepth

  }

  case class TaskSummary(tableEntries: Vector[(TaskFingerprint, TableEntry)], followupTasks: Vector[ReachableByTask])
  case class TableEntry(path: Vector[PathElement])

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy