From 7eb1156fc54d85f6a594a9d0a2f2d6582483181f Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Wed, 5 Jul 2023 11:55:17 +0530 Subject: [PATCH 1/4] add - ast generation for switch case --- .../astcreation/AstCreatorHelper.scala | 10 +- .../AstForGenDeclarationCreator.scala | 3 +- .../astcreation/AstForStatementsCreator.scala | 62 +++++++- .../io/joern/gosrc2cpg/parser/ParserAst.scala | 13 +- .../joern/go2cpg/dataflow/DataflowTests.scala | 47 ++++++ .../passes/ast/AstCreationPassTests.scala | 145 ++++++++++++++++++ .../io/joern/kotlin2cpg/ast/KtPsiToAst.scala | 2 +- .../scala/io/joern/kotlin2cpg/ast/Nodes.scala | 15 -- .../scala/io/joern/x2cpg/AstNodeBuilder.scala | 15 ++ 9 files changed, 283 insertions(+), 29 deletions(-) diff --git a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstCreatorHelper.scala b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstCreatorHelper.scala index 3950cd2780fa..05669bafa997 100644 --- a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstCreatorHelper.scala +++ b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstCreatorHelper.scala @@ -1,6 +1,6 @@ package io.joern.gosrc2cpg.astcreation -import io.joern.gosrc2cpg.parser.ParserAst.{ParserNode, fromString} +import io.joern.gosrc2cpg.parser.ParserAst.{Ident, ParserNode, fromString} import ujson.Value import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo} import org.apache.commons.lang.StringUtils @@ -66,5 +66,11 @@ trait AstCreatorHelper { this: AstCreator => } .toMap } - + protected def getTypeForJsonNode(jsonNode: Value): String = { + val nodeInfo = createParserNodeInfo(jsonNode) + nodeInfo.node match { + case Ident => jsonNode.obj(ParserKeys.Name).str + case _ => Defines.anyTypeName + } + } } diff --git a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForGenDeclarationCreator.scala b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForGenDeclarationCreator.scala index df1320f58099..43a233209a03 100644 --- a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForGenDeclarationCreator.scala +++ b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForGenDeclarationCreator.scala @@ -4,6 +4,7 @@ import io.joern.gosrc2cpg.parser.{ParserKeys, ParserNodeInfo} import io.joern.x2cpg.Ast import io.joern.gosrc2cpg.parser.ParserAst._ import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators} +import ujson.Value import scala.util.Try @@ -50,7 +51,7 @@ trait AstForGenDeclarationCreator { this: AstCreator => val localParserNode = createParserNodeInfo(parserNode) val name = parserNode(ParserKeys.Name).str - val typ = valueSpec.json(ParserKeys.Type).obj(ParserKeys.Name).str + val typ = getTypeForJsonNode(valueSpec.json(ParserKeys.Type)) val node = localNode(localParserNode, name, localParserNode.code, typ) scope.addToScope(name, (node, typ)) Ast(node) diff --git a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala index dd92ae297d95..03d10029a5c9 100644 --- a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala +++ b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala @@ -6,6 +6,7 @@ import io.joern.gosrc2cpg.utils.Operator import io.joern.x2cpg.Ast import io.shiftleft.codepropertygraph.generated.{ControlStructureTypes, DispatchTypes, Operators} +import scala.annotation.tailrec import scala.util.Try trait AstForStatementsCreator { this: AstCreator => @@ -23,14 +24,20 @@ trait AstForStatementsCreator { this: AstCreator => blockAst(newBlockNode, childAsts.toList) } + @tailrec private def astsForStatement(statement: ParserNodeInfo, argIndex: Int = -1): Seq[Ast] = { statement.node match { - case AssignStmt => astForAssignStatement(statement) - case BlockStmt => Seq(astForBlockStatement(statement, argIndex)) - case DeclStmt => astForDeclStatement(statement) - case IfStmt => Seq(astForIfStatement(statement)) - case IncDecStmt => Seq(astForIncDecStatement(statement)) - case _ => Seq() + case AssignStmt => astForAssignStatement(statement) + case BlockStmt => Seq(astForBlockStatement(statement, argIndex)) + case CaseClause => astForCaseClause(statement) + case DeclStmt => astForDeclStatement(statement) + case ExprStmt => astsForStatement(createParserNodeInfo(statement.json(ParserKeys.X))) + case IfStmt => Seq(astForIfStatement(statement)) + case IncDecStmt => Seq(astForIncDecStatement(statement)) + case SwitchStmt => Seq(astForSwitchStatement(statement)) + case TypeAssertExpr => astForNode(statement.json(ParserKeys.X)) + case TypeSwitchStmt => Seq(astForTypeSwitchStatement(statement)) + case _ => astForNode(statement.json) } } @@ -100,7 +107,7 @@ trait AstForStatementsCreator { this: AstCreator => private def astForConditionExpression(condStmt: ParserNodeInfo): Ast = { condStmt.node match { case ParenExpr => astForNode(condStmt.json(ParserKeys.X)).head - case _ => Ast() + case _ => astsForStatement(condStmt).head } } @@ -133,4 +140,45 @@ trait AstForStatementsCreator { this: AstCreator => controlStructureAst(ifNode, Some(conditionAst), Seq(thenAst, elseAst)) } + private def astForSwitchStatement(switchStmt: ParserNodeInfo): Ast = { + + val conditionParserNode = Try(createParserNodeInfo(switchStmt.json(ParserKeys.Tag))) + val (code, conditionAst) = conditionParserNode.toOption match { + case Some(node) => (node.code, Some(astForConditionExpression(node))) + case _ => ("", None) + } + val switchNode = controlStructureNode(switchStmt, ControlStructureTypes.SWITCH, s"switch $code") + val stmtAsts = astsForStatement(createParserNodeInfo(switchStmt.json(ParserKeys.Body))) + controlStructureAst(switchNode, conditionAst, stmtAsts) + } + + private def astForTypeSwitchStatement(typeSwitchStmt: ParserNodeInfo): Ast = { + + val conditionParserNode = Try(createParserNodeInfo(typeSwitchStmt.json(ParserKeys.Assign))) + val (code, conditionAst) = conditionParserNode.toOption match { + case Some(node) => (node.code, Some(astForConditionExpression(node))) + case _ => ("", None) + } + val switchNode = controlStructureNode(typeSwitchStmt, ControlStructureTypes.SWITCH, s"switch $code") + val stmtAsts = astsForStatement(createParserNodeInfo(typeSwitchStmt.json(ParserKeys.Body))) + controlStructureAst(switchNode, conditionAst, stmtAsts) + } + + private def astForCaseClause(caseStmt: ParserNodeInfo): Seq[Ast] = { + val caseClauseAst = caseStmt.json(ParserKeys.List).arrOpt match { + case Some(caseConditionList) => + caseConditionList.flatMap { caseConditionNode => + val caseConditionParserNode = createParserNodeInfo(caseConditionNode) + val jumpTarget = jumpTargetNode(caseStmt, "case", s"case ${caseConditionParserNode.code}") + val labelAsts = astForNode(caseConditionNode).toList + Ast(jumpTarget) :: labelAsts + } + case _ => + val target = jumpTargetNode(caseStmt, "default", "default") + Seq(Ast(target)) + } + + val caseBodyAst = caseStmt.json(ParserKeys.Body).arr.map(createParserNodeInfo).flatMap(astsForStatement(_)).toList + caseClauseAst ++: caseBodyAst + } } diff --git a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/parser/ParserAst.scala b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/parser/ParserAst.scala index fea2e4c7fd49..1b8d0643cd9a 100644 --- a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/parser/ParserAst.scala +++ b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/parser/ParserAst.scala @@ -29,13 +29,19 @@ object ParserAst { object UnaryExpr extends BaseExprStmt object StarExpr extends BaseExprStmt - object IncDecStmt extends ParserNode - object IfStmt extends ParserNode - object ParenExpr extends BaseExprStmt + object IncDecStmt extends ParserNode + object IfStmt extends ParserNode + object ParenExpr extends BaseExprStmt + object SwitchStmt extends ParserNode + object CaseClause extends ParserNode + object TypeSwitchStmt extends ParserNode + object TypeAssertExpr extends BaseExprStmt + object InterfaceType extends ParserNode } object ParserKeys { + val Assign = "Assign" val Body = "Body" val Cond = "Cond" val Decl = "Decl" @@ -56,6 +62,7 @@ object ParserKeys { val Path = "Path" val Rhs = "Rhs" val Specs = "Specs" + val Tag = "Tag" val Tok = "Tok" val Type = "Type" val Value = "Value" diff --git a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala index 2c236f0c5fe1..59116120f172 100644 --- a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala +++ b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala @@ -129,4 +129,51 @@ class DataflowTests extends GoCodeToCpgSuite(withOssDataflow = true) { } } + "Source to sink dataflow through switch case" should { + "be reachable" in { + val cpg = code(""" + |package main + |func method() { + | var marks int = 90 + | var grade string = "B" + | switch marks { + | case 90: myGrade := grade + | case 50,60,70: grade = "C" + | default: grade = "D" + | } + |} + """.stripMargin) + "(case in)" in { + val source = cpg.identifier("grade").lineNumber(5) + val sink = cpg.identifier("myGrade").lineNumber(7) + sink.reachableByFlows(source).size shouldBe 1 + } + "(case 2)" in { + val source = cpg.identifier("grade").lineNumber(7) + val sink = cpg.identifier("myGrade").lineNumber(7) + sink.reachableByFlows(source).size shouldBe 1 + } + } + + "be reachable for empty condition" ignore { + // TODO (BUG)dataflow doesn't work for empty condition in switch case + val cpg = code(""" + |package main + |func method() { + | var marks int = 90 + | var grade string = "B" + | switch { + | case grade == "A" : + | mymarks := grade + | case grade == "B": + | marks = 80 + | } + |} + """.stripMargin) + val source = cpg.identifier("grade").lineNumber(5).l + val sink = cpg.identifier("mymarks").lineNumber(8).l + sink.reachableByFlows(source).size shouldBe 1 + } + } + } diff --git a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/passes/ast/AstCreationPassTests.scala b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/passes/ast/AstCreationPassTests.scala index 571327b83633..9186dc4dc79e 100644 --- a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/passes/ast/AstCreationPassTests.scala +++ b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/passes/ast/AstCreationPassTests.scala @@ -169,4 +169,149 @@ class AstCreationPassTests extends GoCodeToCpgSuite { .headOption shouldBe Some(("y", "1")) } } + + "be correct for switch case 1" in { + + val cpg = code(""" + |package main + |func method() { + | var marks int = 90 + | var grade string = "B" + | switch marks { + | case 90: grade = "A" + | case 50,60,70: grade = "C" + | default: grade = "D" + | } + |} + """.stripMargin) + inside(cpg.method.name("method").controlStructure.l) { case List(controlStruct: ControlStructure) => + controlStruct.code shouldBe "switch marks" + controlStruct.controlStructureType shouldBe ControlStructureTypes.SWITCH + inside(controlStruct.astChildren.l) { case List(cond: Identifier, switchBlock: Block) => + cond.code shouldBe "marks" + switchBlock.astChildren.size shouldBe 12 + switchBlock.astChildren.code.l shouldBe List( + "case 90", + "90", + "grade = \"A\"", + "case 50", + "50", + "case 60", + "60", + "case 70", + "70", + "grade = \"C\"", + "default", + "grade = \"D\"" + ) + } + } + } + + "be correct for switch case 2" in { + + val cpg = code(""" + |package main + |func method() { + | var marks int = 90 + | var grade string = "B" + | switch { + | case grade == "A" : + | marks = 95 + | case grade == "B": + | marks = 80 + | } + |} + """.stripMargin) + inside(cpg.method.name("method").controlStructure.l) { case List(controlStruct: ControlStructure) => + controlStruct.code shouldBe "switch " + controlStruct.controlStructureType shouldBe ControlStructureTypes.SWITCH + inside(controlStruct.astChildren.l) { case List(switchBlock: Block) => + switchBlock.astChildren.size shouldBe 6 + switchBlock.astChildren.code.l shouldBe List( + "case grade == \"A\"", + "grade == \"A\"", + "marks = 95", + "case grade == \"B\"", + "grade == \"B\"", + "marks = 80" + ) + } + } + } + + "be correct for switch case 3" ignore { + + val cpg = code(""" + |package main + |func method() { + | var x interface{} + | var y int = 6 + | switch i := x.(type) { + | case nil: + | y = 5 + | case int: + | y = 8 + | case float64: + | y= 12 + | } + |} + """.stripMargin) + inside(cpg.method.name("method").controlStructure.l) { case List(controlStruct: ControlStructure) => + controlStruct.code shouldBe "switch i := x.(type)" + controlStruct.controlStructureType shouldBe ControlStructureTypes.SWITCH + inside(controlStruct.astChildren.l) { case List(assignment: Call, switchBlock: Block) => + switchBlock.astChildren.size shouldBe 9 + switchBlock.astChildren.code.l shouldBe List( + "case nil", + "nil", + "y = 5", + "case int", + "int", + "y = 8", + "case float64", + "float64", + "y = 12" + ) + } + } + } + + "be correct for switch case 4" in { + + val cpg = code(""" + |package main + |func method() { + | var x interface{} + | var y int = 6 + | switch x.(type) { + | case nil: + | y = 5 + | case int: + | y = 8 + | case float64: + | y = 12 + | } + |} + """.stripMargin) + inside(cpg.method.name("method").controlStructure.l) { case List(controlStruct: ControlStructure) => + controlStruct.code shouldBe "switch x.(type)" + controlStruct.controlStructureType shouldBe ControlStructureTypes.SWITCH + inside(controlStruct.astChildren.l) { case List(identifier: Identifier, switchBlock: Block) => + identifier.code shouldBe "x" + switchBlock.astChildren.size shouldBe 9 + switchBlock.astChildren.code.l shouldBe List( + "case nil", + "nil", + "y = 5", + "case int", + "int", + "y = 8", + "case float64", + "float64", + "y = 12" + ) + } + } + } } diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/KtPsiToAst.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/KtPsiToAst.scala index 0af21ded0192..74c63376a8ac 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/KtPsiToAst.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/KtPsiToAst.scala @@ -1697,7 +1697,7 @@ trait KtPsiToAst { val name = if (entry.getElseKeyword == null) Constants.defaultCaseNode else s"${Constants.caseNodePrefix}$argIdx" - val jumpNode = jumpTargetNode(entry.getText, name, Constants.caseNodeParserTypeName, line(entry), column(entry)) + val jumpNode = jumpTargetNode(entry, name, entry.getText, Some(Constants.caseNodeParserTypeName)) .argumentIndex(argIdx) val exprNode = astsForExpression(entry.getExpression, Some(argIdx + 1)).headOption.getOrElse(Ast()) Seq(Ast(jumpNode), exprNode) diff --git a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala index 4fa1a918f0bf..b53cd8453477 100644 --- a/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala +++ b/joern-cli/frontends/kotlin2cpg/src/main/scala/io/joern/kotlin2cpg/ast/Nodes.scala @@ -5,21 +5,6 @@ import io.shiftleft.codepropertygraph.generated.nodes.{NewCall, NewJumpTarget, N object Nodes { - def jumpTargetNode( - code: String, - name: String, - parserTypeName: String, - line: Option[Integer] = None, - column: Option[Integer] = None - ): NewJumpTarget = { - NewJumpTarget() - .code(code) - .name(name) - .parserTypeName(parserTypeName) - .lineNumber(line) - .columnNumber(column) - } - def modifierNode(_type: String): NewModifier = { NewModifier() .modifierType(_type) diff --git a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/AstNodeBuilder.scala b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/AstNodeBuilder.scala index 73afb44fde25..21d372571d39 100644 --- a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/AstNodeBuilder.scala +++ b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/AstNodeBuilder.scala @@ -8,6 +8,7 @@ import io.shiftleft.codepropertygraph.generated.nodes.{ NewFieldIdentifier, NewIdentifier, NewImport, + NewJumpTarget, NewLiteral, NewLocal, NewMember, @@ -298,4 +299,18 @@ trait AstNodeBuilder[Node, NodeProcessor] { this: NodeProcessor => protected def methodReturnNode(node: Node, typeFullName: String): NewMethodReturn = { newMethodReturnNode(typeFullName, None, line(node), column(node)) } + + protected def jumpTargetNode( + node: Node, + name: String, + code: String, + parserTypeName: Option[String] = None + ): NewJumpTarget = { + NewJumpTarget() + .parserTypeName(parserTypeName.getOrElse(node.getClass.getSimpleName)) + .name(name) + .code(code) + .lineNumber(line(node)) + .columnNumber(column(node)) + } } From e90022c8cf561ee4cc63e6a56aa4cf93989f022b Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Wed, 5 Jul 2023 12:20:58 +0530 Subject: [PATCH 2/4] fix - failing test case --- .../io/joern/go2cpg/dataflow/DataflowTests.scala | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala index 59116120f172..8d4e4fe6d893 100644 --- a/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala +++ b/joern-cli/frontends/gosrc2cpg/src/test/scala/io/joern/go2cpg/dataflow/DataflowTests.scala @@ -130,7 +130,7 @@ class DataflowTests extends GoCodeToCpgSuite(withOssDataflow = true) { } "Source to sink dataflow through switch case" should { - "be reachable" in { + "be reachable for expression condition" in { val cpg = code(""" |package main |func method() { @@ -143,16 +143,10 @@ class DataflowTests extends GoCodeToCpgSuite(withOssDataflow = true) { | } |} """.stripMargin) - "(case in)" in { - val source = cpg.identifier("grade").lineNumber(5) - val sink = cpg.identifier("myGrade").lineNumber(7) - sink.reachableByFlows(source).size shouldBe 1 - } - "(case 2)" in { - val source = cpg.identifier("grade").lineNumber(7) - val sink = cpg.identifier("myGrade").lineNumber(7) - sink.reachableByFlows(source).size shouldBe 1 - } + val source = cpg.identifier("grade").lineNumber(5) + val sink = cpg.identifier("myGrade").lineNumber(7) + sink.reachableByFlows(source).size shouldBe 1 + } "be reachable for empty condition" ignore { From 9e2b9ac7ab16b28836f443a912d44e46da017036 Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Wed, 5 Jul 2023 13:43:40 +0530 Subject: [PATCH 3/4] trigger --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a79d5e65dcdf..c2e4c1fc005d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ Joern - The Bug Hunter's Workbench -=== +=== [![release](https://github.com/joernio/joern/actions/workflows/release.yml/badge.svg)](https://github.com/joernio/joern/actions/workflows/release.yml) [![Joern SBT](https://index.scala-lang.org/joernio/joern/latest.svg)](https://index.scala-lang.org/joernio/joern) From 089cdea474335866415b00164ad677cafa3d2b9f Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Tue, 11 Jul 2023 11:21:49 +0530 Subject: [PATCH 4/4] PR comments --- README.md | 2 +- .../joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c2e4c1fc005d..a79d5e65dcdf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ Joern - The Bug Hunter's Workbench -=== +=== [![release](https://github.com/joernio/joern/actions/workflows/release.yml/badge.svg)](https://github.com/joernio/joern/actions/workflows/release.yml) [![Joern SBT](https://index.scala-lang.org/joernio/joern/latest.svg)](https://index.scala-lang.org/joernio/joern) diff --git a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala index 03d10029a5c9..3683dd86e2a9 100644 --- a/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala +++ b/joern-cli/frontends/gosrc2cpg/src/main/scala/io/joern/gosrc2cpg/astcreation/AstForStatementsCreator.scala @@ -107,7 +107,7 @@ trait AstForStatementsCreator { this: AstCreator => private def astForConditionExpression(condStmt: ParserNodeInfo): Ast = { condStmt.node match { case ParenExpr => astForNode(condStmt.json(ParserKeys.X)).head - case _ => astsForStatement(condStmt).head + case _ => astsForStatement(condStmt).headOption.getOrElse(Ast()) } }