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

io.shiftleft.semanticcpg.language.AccessPathHandling.scala Maven / Gradle / Ivy

There is a newer version: 4.0.77
Show newest version
package io.shiftleft.semanticcpg.language

import io.shiftleft.codepropertygraph.generated.{Operators, Properties}
import io.shiftleft.codepropertygraph.generated.nodes.*
import io.shiftleft.semanticcpg.accesspath.*
import io.shiftleft.semanticcpg.language.*
import org.slf4j.LoggerFactory

import scala.jdk.CollectionConverters.IteratorHasAsScala

object AccessPathHandling {

  def leafToTrackedBaseAndAccessPathInternal(node: StoredNode): Option[(TrackedBase, List[AccessElement])] = {
    node match {
      case node: MethodParameterIn  => Some((TrackedNamedVariable(node.name), Nil))
      case node: MethodParameterOut => Some((TrackedNamedVariable(node.name), Nil))
      case node: Identifier         => Some((TrackedNamedVariable(node.name), Nil))
      case node: Literal            => Some((TrackedLiteral(node), Nil))
      case node: MethodRef          => Some((TrackedMethod(node), Nil))
      case node: TypeRef            => Some((TrackedTypeRef(node), Nil))
      case _: Return                => Some((TrackedFormalReturn, Nil))
      case _: MethodReturn          => Some((TrackedFormalReturn, Nil))
      case _: Unknown               => Some((TrackedUnknown, Nil))
      case _: ControlStructure      => Some((TrackedUnknown, Nil))
      // FieldIdentifiers are only fake arguments, hence should not be tracked
      case _: FieldIdentifier => Some((TrackedUnknown, Nil))
      case _                  => None
    }
  }

  private val logger                = LoggerFactory.getLogger(getClass)
  private var hasWarnedDeprecations = false

  def memberAccessToPath(memberAccess: Call, tail: List[AccessElement]): List[AccessElement] = {
    memberAccess.name match {
      case Operators.memberAccess | Operators.indirectMemberAccess =>
        if (!hasWarnedDeprecations) {
          logger.info(s"Deprecated Operator ${memberAccess.name} on $memberAccess")
          hasWarnedDeprecations = true
        }
        memberAccess
          .argumentOption(2)
          .collect {
            case node: Literal    => ConstantAccess(node.code)
            case node: Identifier => ConstantAccess(node.name)
            case other if other.propertyOption(Properties.Name).isDefined =>
              val properties = other.propertiesMap
              logger.warn(s"unexpected/deprecated node encountered: $other with properties: $properties")
              ConstantAccess(other.property(Properties.Name))
          }
          .getOrElse(VariableAccess) :: tail

      case Operators.computedMemberAccess | Operators.indirectComputedMemberAccess =>
        if (!hasWarnedDeprecations) {
          logger.info(s"Deprecated Operator ${memberAccess.name} on $memberAccess")
          hasWarnedDeprecations = true
        }
        memberAccess
          .argumentOption(2)
          .collect { case lit: Literal =>
            ConstantAccess(lit.code)
          }
          .getOrElse(VariableAccess) :: tail
      case Operators.indirection =>
        IndirectionAccess :: tail
      case Operators.addressOf =>
        AddressOf :: tail
      case Operators.fieldAccess =>
        extractAccessStringTokenForFieldAccess(memberAccess) :: tail
      case Operators.indexAccess =>
        extractAccessStringToken(memberAccess) :: tail
      case Operators.indirectFieldAccess =>
        // we will reverse the list in the end
        extractAccessStringTokenForFieldAccess(memberAccess) :: IndirectionAccess :: tail
      case Operators.indirectIndexAccess =>
        // we will reverse the list in the end
        IndirectionAccess :: extractAccessIntToken(memberAccess) :: tail
      case Operators.pointerShift =>
        extractAccessIntToken(memberAccess) :: tail
      case Operators.getElementPtr =>
        // we will reverse the list in the end
        AddressOf :: extractAccessStringTokenForFieldAccess(memberAccess) :: IndirectionAccess :: tail
    }
  }

  private def extractAccessStringTokenForFieldAccess(memberAccess: Call): AccessElement = {
    memberAccess.argumentOption(2) match {
      case None =>
        logger.warn(
          s"Invalid AST: Found member access without second argument." +
            s" Member access CODE: ${memberAccess.code}" +
            s" In method ${memberAccess.method.fullName}"
        )
        VariableAccess
      case Some(literal: Literal) => ConstantAccess(literal.code)
      case Some(fieldIdentifier: FieldIdentifier) =>
        ConstantAccess(fieldIdentifier.canonicalName)
      case Some(identifier: Identifier) =>
        // TODO remove this case.
        // This is handling a very old CPG format version where IDENTIFIER was used instead of FIELD_IDENTIFIER.
        // Sadly we need this for now to support a GO cpg.
        ConstantAccess(identifier.name)
      case _ => VariableAccess
    }
  }

  private def extractAccessStringToken(memberAccess: Call): AccessElement = {
    memberAccess.argumentOption(2) match {
      case None =>
        logger.warn(
          s"Invalid AST: Found member access without second argument." +
            s" Member access CODE: ${memberAccess.code}" +
            s" In method ${memberAccess.method.fullName}"
        )
        VariableAccess
      case Some(literal: Literal) => ConstantAccess(literal.code)
      case Some(fieldIdentifier: FieldIdentifier) =>
        ConstantAccess(fieldIdentifier.canonicalName)
      case _ => VariableAccess
    }
  }

  private def extractAccessIntToken(memberAccess: Call): AccessElement = {
    memberAccess.argumentOption(2) match {
      case None =>
        logger.warn(
          s"Invalid AST: Found member access without second argument." +
            s" Member access CODE: ${memberAccess.code}" +
            s" In method ${memberAccess.method.fullName}"
        )
        VariablePointerShift
      case Some(literal: Literal) =>
        literal.code.toIntOption.map(PointerShift.apply).getOrElse(VariablePointerShift)
      case Some(fieldIdentifier: FieldIdentifier) =>
        fieldIdentifier.canonicalName.toIntOption
          .map(PointerShift.apply)
          .getOrElse(VariablePointerShift)
      case _ => VariablePointerShift
    }
  }

  def lastExpressionInBlock(block: Block): Option[Expression] =
    block._astOut
      .collect {
        case node: Expression if !node.isInstanceOf[Local] && !node.isInstanceOf[Method] => node
      }
      .toVector
      .sortBy(_.order)
      .lastOption

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy