Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gosrc2cpg] - Go array index access call node handling #3674

Merged
merged 9 commits into from
Sep 19, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@ package io.joern.gosrc2cpg.astcreation
import io.joern.gosrc2cpg.parser.ParserAst.*
import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo}
import io.joern.gosrc2cpg.utils.Operator
import io.joern.x2cpg.utils.NodeBuilders.*
import io.joern.x2cpg.{Ast, ValidationMode}
import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewFieldIdentifier}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators}
import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewIdentifier}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators, PropertyNames}
import ujson.Value

import scala.collection.immutable.Seq

trait AstForExpressionCreator(implicit withSchemaValidation: ValidationMode) { this: AstCreator =>
def astsForExpression(expr: ParserNodeInfo): Seq[Ast] = {
expr.node match {
case BinaryExpr => astForBinaryExpr(expr)
case StarExpr => astForStarExpr(expr)
case UnaryExpr => astForUnaryExpr(expr)
case ParenExpr => astsForExpression(createParserNodeInfo(expr.json(ParserKeys.X)))
case StructType => astForStructType(expr)
case TypeAssertExpr => astForNode(expr.json(ParserKeys.X))
case CallExpr => astForCallExpression(expr)
case SelectorExpr => astForFieldAccess(expr)
case KeyValueExpr => astForNode(createParserNodeInfo(expr.json(ParserKeys.Value)))
case IndexExpr => astForIndexExpression(expr)
case _ => Seq(Ast())
}
}
Expand Down Expand Up @@ -54,8 +53,12 @@ trait AstForExpressionCreator(implicit withSchemaValidation: ValidationMode) { t
}

private def astForStarExpr(starExpr: ParserNodeInfo): Seq[Ast] = {
val cNode = createCallNodeForOperator(starExpr, Operators.indirection)
val operand = astForNode(starExpr.json(ParserKeys.X))
val typeFullName = operand.head.root.get.properties
pandurangpatil marked this conversation as resolved.
Show resolved Hide resolved
.get(PropertyNames.TYPE_FULL_NAME)
.getOrElse(Defines.anyTypeName)
.toString
val cNode = createCallNodeForOperator(starExpr, Operators.indirection, typeFullName = Some(typeFullName))
Seq(callAst(cNode, operand))
}

Expand All @@ -75,11 +78,43 @@ trait AstForExpressionCreator(implicit withSchemaValidation: ValidationMode) { t
Seq(callAst(cNode, operand))
}

private def astForIndexExpression(indexNode: ParserNodeInfo): Seq[Ast] = {
val indexAst = astForNode(indexNode.json(ParserKeys.Index))
val (indexIdentifier, callNodeTypeFullName) = processIndexIdentifier(indexNode.json(ParserKeys.X))
val callNode =
createCallNodeForOperator(indexNode, Operators.indexAccess, typeFullName = Some(callNodeTypeFullName))
Seq(callAst(callNode, indexIdentifier ++ indexAst))
}

private def processIndexIdentifier(identNode: Value): (Seq[Ast], String) = {
val identifierAst = astForNode(identNode)
val identifierTypeFullName =
identifierAst.head.root.get.properties
.get(PropertyNames.TYPE_FULL_NAME)
.getOrElse(Defines.anyTypeName)
.toString
.stripPrefix("*")
.stripPrefix("[]")
(identifierAst, identifierTypeFullName)
}
//
// private def extractBaseType(input: String): String = {
DavidBakerEffendi marked this conversation as resolved.
Show resolved Hide resolved
// if (input.matches("""(\[\])*(\w|\.)+""")) {
// input.substring(input.lastIndexOf("]") + 1, input.length)
// } else if (input.matches("""map\[(.*?)\]""")) {
// input.stripPrefix("map[").stripSuffix("]")
// } else {
// Defines.anyTypeName
// }
// }

private def createCallNodeForOperator(
node: ParserNodeInfo,
operatorMethod: String,
DispatchType: String = DispatchTypes.STATIC_DISPATCH
DispatchType: String = DispatchTypes.STATIC_DISPATCH,
signature: Option[String] = None,
typeFullName: Option[String] = None
): NewCall = {
callNode(node, node.code, operatorMethod, operatorMethod, DispatchType)
callNode(node, node.code, operatorMethod, operatorMethod, DispatchType, signature, typeFullName)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.joern.gosrc2cpg.astcreation

import io.joern.gosrc2cpg.datastructures.GoGlobal
import io.joern.gosrc2cpg.parser.ParserAst.*
import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo}
import io.joern.x2cpg.datastructures.Stack.*
Expand Down Expand Up @@ -87,27 +86,26 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
protected def getReceiverInfo(receiver: Try[Value]): Option[(String, String, String, ParserNodeInfo)] = {
receiver match
case Success(rec) if rec != null =>
val recnode = createParserNodeInfo(rec)
val recValue = rec(ParserKeys.List).arr.head
val recName = Try(recValue(ParserKeys.Names).arr.head(ParserKeys.Name).str) match
case Success(recName) => recName
case _ => Defines.This
val typeParserNode = createParserNodeInfo(recValue(ParserKeys.Type))
val (typeFullName, evaluationStrategy) = typeParserNode.node match
case Ident =>
(
generateTypeFullName(typeName = typeParserNode.json(ParserKeys.Name).strOpt),
EvaluationStrategies.BY_VALUE
)
case StarExpr =>
(
generateTypeFullName(typeName = typeParserNode.json(ParserKeys.X)(ParserKeys.Name).strOpt),
EvaluationStrategies.BY_SHARING
)
case x =>
logger.warn(s"Unhandled class ${x.getClass} under getReceiverInfo!")
("", "")
Some(recName, typeFullName, evaluationStrategy, recnode)
val recnode = createParserNodeInfo(rec)
rec(ParserKeys.List).arr.headOption.map(recValue => {
val recName = Try(recValue(ParserKeys.Names).arr.head(ParserKeys.Name).str).getOrElse(Defines.This)
val typeParserNode = createParserNodeInfo(recValue(ParserKeys.Type))
val (typeFullName, evaluationStrategy) = typeParserNode.node match
case Ident =>
(
generateTypeFullName(typeName = typeParserNode.json(ParserKeys.Name).strOpt),
EvaluationStrategies.BY_VALUE
)
case StarExpr =>
(
generateTypeFullName(typeName = typeParserNode.json(ParserKeys.X)(ParserKeys.Name).strOpt),
EvaluationStrategies.BY_SHARING
)
case x =>
logger.warn(s"Unhandled class ${x.getClass} under getReceiverInfo! file -> ${parserResult.fullPath}")
("", "")
(recName, typeFullName, evaluationStrategy, recnode)
})
case _ => None
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ trait AstForMethodCallExpressionCreator(implicit withSchemaValidation: Validatio
val xNode = createParserNodeInfo(funcDetails.json(ParserKeys.X))
(Some(xNode), funcDetails.json(ParserKeys.Sel)(ParserKeys.Name).str)
case x =>
logger.warn(s"Unhandled class ${x.getClass} under astForCallExpression!")
logger.warn(s"Unhandled class ${x.getClass} under astForCallExpression! file -> ${parserResult.fullPath}")
(None, "")
callMethodFullNameTypeFullNameAndSignature(methodName, aliasOpt)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package io.joern.gosrc2cpg.astcreation
import io.joern.gosrc2cpg.parser.ParserAst.*
import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo}
import io.joern.gosrc2cpg.utils.Operator
import io.joern.x2cpg.utils.NodeBuilders.{newCallNode, newOperatorCallNode}
import io.joern.x2cpg.{Ast, ValidationMode}
import io.shiftleft.codepropertygraph.generated.nodes.ExpressionNew
import io.shiftleft.codepropertygraph.generated.nodes.{ExpressionNew, NewCall, NewIdentifier, NewLiteral, NewLocal}
import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators}
import ujson.Value

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ object ParserAst {
object SelectorExpr extends BaseExpr
object CallExpr extends BaseExpr
object StructType extends BaseExpr
object IndexExpr extends BaseExpr
sealed trait BaseStmt extends ParserNode
object BlockStmt extends BaseStmt
object DeclStmt extends BaseStmt
Expand Down Expand Up @@ -65,7 +66,6 @@ object ParserAst {
object ArrayType extends ParserNode
object MapType extends ParserNode
object ChanType extends ParserNode
object Field extends ParserNode
object TypeSpec extends ParserNode
}

Expand Down Expand Up @@ -116,4 +116,5 @@ object ParserKeys {
val TypeParams = "TypeParams"
val Args = "Args"
val Recv = "Recv"
val Index = "Index"
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,32 @@ class ArrayDataflowTests extends GoCodeToCpgSuite(withOssDataflow = true) {
sink.reachableByFlows(source).size shouldBe 2
}
}
"Data flows around multidimentional Arrays" should {
val cpg = code("""
|package main
|func main() {
| var myArray [][]string = [][]string{{"1", "2"}, {"3", "4"}}
| first := myArray[0][1]
| second := myArray[1][0]
| third := myArray
|}
|""".stripMargin)

"data flow check one" ignore {
val src = cpg.identifier("myArray").l
val sink = cpg.identifier("first")
sink.reachableByFlows(src).size shouldBe 2
}
"data flow check two" ignore {
val src = cpg.literal("\"2\"").l
val sink = cpg.identifier("first")
sink.reachableByFlows(src).size shouldBe 1
}

"data flow check three" in {
val src = cpg.identifier("myArray").l
val sink = cpg.identifier("third")
sink.reachableByFlows(src).size shouldBe 2
}
}
}
Loading