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] - Fix for issue #3698 #3702

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ trait AstForGenDeclarationCreator(implicit withSchemaValidation: ValidationMode)
(valueSpec.json(ParserKeys.Names).arr.toList zip valueSpec.json(ParserKeys.Values).arr.toList)
.map { case (lhs, rhs) => (createParserNodeInfo(lhs), createParserNodeInfo(rhs)) }
.map { case (lhsParserNode, rhsParserNode) =>
astForAssignmentCallNode(lhsParserNode, rhsParserNode, typeFullName)
astForAssignmentCallNode(lhsParserNode, rhsParserNode, typeFullName, valueSpec.code)
}
.unzip
localAsts ++: assCallAsts
Expand All @@ -70,10 +70,11 @@ trait AstForGenDeclarationCreator(implicit withSchemaValidation: ValidationMode)

}

private def astForAssignmentCallNode(
protected def astForAssignmentCallNode(
lhsParserNode: ParserNodeInfo,
rhsParserNode: ParserNodeInfo,
typeFullName: Option[String]
typeFullName: Option[String],
code: String
): (Ast, Ast) = {
val rhsAst = astForBooleanLiteral(rhsParserNode)
val rhsTypeFullName = typeFullName.getOrElse(getTypeFullNameFromAstNode(rhsAst))
Expand All @@ -82,7 +83,7 @@ trait AstForGenDeclarationCreator(implicit withSchemaValidation: ValidationMode)
val arguments = lhsAst ++: rhsAst
val cNode = callNode(
rhsParserNode,
lhsParserNode.code + rhsParserNode.code,
code,
Operators.assignment,
Operators.assignment,
DispatchTypes.STATIC_DISPATCH,
Expand All @@ -94,8 +95,12 @@ trait AstForGenDeclarationCreator(implicit withSchemaValidation: ValidationMode)

protected def astForLocalNode(localParserNode: ParserNodeInfo, typeFullName: Option[String]): Ast = {
val name = localParserNode.json(ParserKeys.Name).str
val node = localNode(localParserNode, name, localParserNode.code, typeFullName.getOrElse(Defines.anyTypeName))
scope.addToScope(name, (node, typeFullName.getOrElse(Defines.anyTypeName)))
Ast(node)
if name != "_" then {
val node = localNode(localParserNode, name, localParserNode.code, typeFullName.getOrElse(Defines.anyTypeName))
scope.addToScope(name, (node, typeFullName.getOrElse(Defines.anyTypeName)))
Ast(node)
} else {
Ast()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,18 @@ trait AstForPrimitivesCreator(implicit withSchemaValidation: ValidationMode) { t

private def astForIdentifier(ident: ParserNodeInfo): Ast = {
val identifierName = ident.json(ParserKeys.Name).str
val variableOption = scope.lookupVariable(identifierName)
variableOption match {
case Some((variable, variableTypeName)) =>
val node = identifierNode(ident, identifierName, ident.code, variableTypeName)
Ast(node).withRefEdge(node, variable)
case _ =>
// TODO: something is wrong here. Refer to SwitchTests -> "be correct for switch case 4"
Ast(identifierNode(ident, identifierName, ident.json(ParserKeys.Name).str, Defines.anyTypeName))
if identifierName != "_" then {
val variableOption = scope.lookupVariable(identifierName)
variableOption match {
case Some((variable, variableTypeName)) =>
val node = identifierNode(ident, identifierName, ident.code, variableTypeName)
Ast(node).withRefEdge(node, variable)
case _ =>
// TODO: something is wrong here. Refer to SwitchTests -> "be correct for switch case 4"
Ast(identifierNode(ident, identifierName, ident.json(ParserKeys.Name).str, Defines.anyTypeName))
}
} else {
Ast()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,45 +64,50 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t
}

private def astForAssignStatement(assignStmt: ParserNodeInfo): Seq[Ast] = {
val op = assignStmt.json(ParserKeys.Tok).value match {
case "=" => Operators.assignment
case ":=" => Operators.assignment
case "*=" => Operators.assignmentMultiplication
case "/=" => Operators.assignmentDivision
case "%=" => Operators.assignmentModulo
case "+=" => Operators.assignmentPlus
case "-=" => Operators.assignmentMinus
case "<<=" => Operators.assignmentShiftLeft
case ">>=" => Operators.assignmentArithmeticShiftRight
case "&=" => Operators.assignmentAnd
case "^=" => Operators.assignmentXor
case "|=" => Operators.assignmentOr
case _ => Operator.unknown
assignStmt.json(ParserKeys.Tok).value match {
case "=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignment)
case ":=" => astForDeclrationAssignment(assignStmt, Operators.assignment)
case "*=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentMultiplication)
case "/=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentDivision)
case "%=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentModulo)
case "+=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentPlus)
case "-=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentMinus)
case "<<=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentShiftLeft)
case ">>=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentArithmeticShiftRight)
case "&=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentAnd)
case "^=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentXor)
case "|=" => astForOnlyAssignmentOperator(assignStmt, Operators.assignmentOr)
case _ => astForOnlyAssignmentOperator(assignStmt, Operator.unknown)
}
}

private def astForDeclrationAssignment(assignStmt: ParserNodeInfo, op: String): Seq[Ast] = {
val (assCallAsts, localAsts) =
(assignStmt.json(ParserKeys.Lhs).arr.toList zip assignStmt.json(ParserKeys.Rhs).arr.toList)
.map { case (lhs, rhs) => (createParserNodeInfo(lhs), createParserNodeInfo(rhs)) }
.map { case (lhsParserNode, rhsParserNode) =>
astForAssignmentCallNode(lhsParserNode, rhsParserNode, None, assignStmt.code)
}
.unzip
localAsts ++: assCallAsts
}

private def astForOnlyAssignmentOperator(assignStmt: ParserNodeInfo, op: String): Seq[Ast] = {
val rhsAst = assignStmt
.json(ParserKeys.Rhs)
.arr
.map(createParserNodeInfo)
.flatMap(astForBooleanLiteral)
val typeFullName = Some(getTypeFullNameFromAstNode(rhsAst.toSeq))
val (lhsAst, localAst) = assignStmt
val lhsAst = assignStmt
.json(ParserKeys.Lhs)
.arr
.map(lhsnode => {
val lhs = createParserNodeInfo(lhsnode)
// create corresponding local node as this is known as short variable declaration operator
val localNode =
if (assignStmt.json(ParserKeys.Tok).value == ":=") then Seq(astForLocalNode(lhs, typeFullName))
else Seq.empty
val lhsAst = astForNode(lhs)
(lhsAst, localNode)
})
.unzip
val arguments = lhsAst.flatten ++: rhsAst
.flatMap(astForNode)

val arguments = lhsAst ++: rhsAst
val cNode = callNode(assignStmt, assignStmt.code, op, op, DispatchTypes.STATIC_DISPATCH, None, typeFullName)
val callAst_ = Seq(callAst(cNode, arguments.toSeq))
localAst.flatten ++: callAst_
callAst_
}

private def astForIncDecStatement(incDecStatement: ParserNodeInfo): Ast = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,36 @@ class ParameterInDataFlowTests extends GoCodeToCpgSuite(withOssDataflow = true)
sink.reachableByFlows(source).size shouldBe 1
}
}

"Simple parameter in to call argument data flow use case" should {
val cpg = code("""
|package main
|func foo(argv string) {
| sample("sample", argv)
| var a, _ = sample("sample", argv)
| b, _ := sample("sample", argv)
| c, d := sample("sample", argv)
|}
|""".stripMargin)

"data flow from parameterIn to identifier" in {
val source = cpg.parameter("argv").l
val sink = cpg.identifier("argv").l
sink.reachableByFlows(source).size shouldBe 4
}

"data flow from parameterIn to CALL Node" in {
val source = cpg.parameter("argv").l
val sink = cpg.call("sample").l
sink.reachableByFlows(source).size shouldBe 4
}

"data flow from parameterIn to lhs varaible" in {
val source = cpg.parameter("argv").l
source.size shouldBe 1
val sink = cpg.identifier("[a|b|c]").l
sink.size shouldBe 3
sink.reachableByFlows(source).size shouldBe 3
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.joern.go2cpg.passes.ast

import io.joern.dataflowengineoss.language.*
import io.joern.go2cpg.testfixtures.GoCodeToCpgSuite
import io.shiftleft.codepropertygraph.generated.Operators
import io.shiftleft.semanticcpg.language.*

class AssignmentOperatorTests extends GoCodeToCpgSuite {
"Multiple varables declared and assigned with value in single statement" should {
val cpg = code("""
|package main
|func main() {
| var a, b = "first", "second"
| c, d := 10, "forth"
|}
|
|""".stripMargin)
"Check CALL Nodes" in {
cpg.call(Operators.assignment).size shouldBe 4
val List(a, b, c, d) = cpg.call(Operators.assignment).l
a.typeFullName shouldBe "string"
b.typeFullName shouldBe "string"
c.typeFullName shouldBe "int"
d.typeFullName shouldBe "string"
}
}

"Multiple varables declared and assigned with value in single statement with one function call" should {
val cpg = code("""
|package main
|func foo() int{
| return 0
|}
|func main() {
| var a, b = "first", "second"
| c, d, e := 10, "forth", foo()
|}
|""".stripMargin)
"Check CALL Nodes" in {
cpg.call(Operators.assignment).size shouldBe 5
val List(a, b, c, d, e) = cpg.call(Operators.assignment).l
a.typeFullName shouldBe "string"
b.typeFullName shouldBe "string"
c.typeFullName shouldBe "int"
d.typeFullName shouldBe "string"
e.typeFullName shouldBe "int"
}
}
}