diff --git a/build.sbt b/build.sbt index 99e91c3015b2..4d0ac6d063a1 100644 --- a/build.sbt +++ b/build.sbt @@ -98,7 +98,8 @@ ThisBuild / resolvers ++= Seq( Resolver.mavenLocal, "Sonatype OSS" at "https://oss.sonatype.org/content/repositories/public", "Atlassian" at "https://packages.atlassian.com/mvn/maven-atlassian-external", - "Gradle Releases" at "https://repo.gradle.org/gradle/libs-releases/" + "Gradle Releases" at "https://repo.gradle.org/gradle/libs-releases/", + "Google Maven" at "https://maven.google.com/" ) ThisBuild / Test / fork := true diff --git a/joern-cli/frontends/jimple2cpg/build.sbt b/joern-cli/frontends/jimple2cpg/build.sbt index 331ca6dea6e0..cc55aa124188 100644 --- a/joern-cli/frontends/jimple2cpg/build.sbt +++ b/joern-cli/frontends/jimple2cpg/build.sbt @@ -13,6 +13,7 @@ libraryDependencies ++= Seq( "org.scalatest" %% "scalatest" % Versions.scalatest % Test, "org.benf" % "cfr" % Versions.cfr ) +dependencyOverrides += "com.google.guava" % "guava" % "33.5.0-jre" // required currently because of the dependencies of soot 4.7.1 enablePlugins(JavaAppPackaging, LauncherJarPlugin) trapExit := false diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/Jimple2Cpg.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/Jimple2Cpg.scala index f4e42580fd4d..3a42f053b6db 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/Jimple2Cpg.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/Jimple2Cpg.scala @@ -41,7 +41,6 @@ class Jimple2Cpg extends X2CpgFrontend { case _ => Options.v().set_src_prec(Options.src_prec_apk_c_j) } - Options.v().set_process_multiple_dex(true) // workaround for Soot's bug while parsing large apk. // see: https://github.com/soot-oss/soot/issues/1256 Options.v().setPhaseOption("jb", "use-original-names:false") diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/AstCreator.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/AstCreator.scala index 1ee43adbddb6..fd2b311c01ea 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/AstCreator.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/AstCreator.scala @@ -41,6 +41,10 @@ class AstCreator( typeName } + protected def sootTypeToString(t: soot.Type): String = { + t.toString.replace("\'", "") + } + /** Entry point of AST creation. Translates a compilation unit created by JavaParser into a DiffGraph containing the * corresponding CPG AST. */ @@ -119,7 +123,7 @@ class AstCreator( .code(arrRef.toString()) .lineNumber(line(parentUnit)) .columnNumber(column(parentUnit)) - .typeFullName(registerType(arrRef.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(arrRef.getType))) val astChildren = astsForValue(arrRef.getBase, parentUnit) ++ astsForValue(arrRef.getIndex, parentUnit) Ast(indexAccess) @@ -129,7 +133,7 @@ class AstCreator( protected def astForLocal(local: soot.Local, parentUnit: SUnit): Ast = { val name = local.getName - val typeFullName = registerType(local.getType.toQuotedString) + val typeFullName = registerType(sootTypeToString(local.getType)) Ast( NewIdentifier() .name(name) @@ -145,7 +149,7 @@ class AstCreator( NewIdentifier() .code(x.toString()) .name(x.toString()) - .typeFullName(registerType(x.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(x.getType))) .lineNumber(line(parentUnit)) .columnNumber(column(parentUnit)) ) @@ -156,8 +160,8 @@ class AstCreator( NewIdentifier() .name("this") .code("this") - .typeFullName(registerType(method.getType.toQuotedString)) - .dynamicTypeHintFullName(Seq(registerType(method.getType.toQuotedString))) + .typeFullName(registerType(sootTypeToString(method.getType))) + .dynamicTypeHintFullName(Seq(registerType(sootTypeToString(method.getType)))) ) } @@ -165,7 +169,7 @@ class AstCreator( protected def createThisNode(method: SootMethodRef, builder: NewNode): Ast = { if (!method.isStatic || method.isConstructor) { - val parentType = registerType(Try(method.getDeclaringClass.getType.toQuotedString).getOrElse("ANY")) + val parentType = registerType(Try(sootTypeToString(method.getDeclaringClass.getType)).getOrElse("ANY")) Ast(builder match { case x: NewIdentifier => x.name("this") @@ -205,7 +209,7 @@ class AstCreator( val fieldAccessBlock = NewCall() .name(Operators.fieldAccess) .code(s"$leftOpString.${fieldRef.getFieldRef.name()}") - .typeFullName(registerType(fieldRef.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(fieldRef.getType))) .methodFullName(Operators.fieldAccess) .dispatchType(DispatchTypes.STATIC_DISPATCH) .lineNumber(line(parentUnit)) @@ -219,7 +223,7 @@ class AstCreator( .columnNumber(column(parentUnit)) .name(leftOpString) .code(leftOpString) - .typeFullName(registerType(leftOpType.toQuotedString)), + .typeFullName(registerType(sootTypeToString(leftOpType))), NewFieldIdentifier() .order(2) .argumentIndex(2) @@ -241,7 +245,7 @@ class AstCreator( .columnNumber(column(parentUnit)) .name(caughtException.toString()) .code(caughtException.toString()) - .typeFullName(registerType(caughtException.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(caughtException.getType))) ) } @@ -251,7 +255,7 @@ class AstCreator( Ast( NewLiteral() .code(s"${x.value.parseAsJavaType}.class") - .typeFullName(registerType(x.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(x.getType))) ) case _: NullConstant => Ast( @@ -263,7 +267,7 @@ class AstCreator( Ast( NewLiteral() .code(constant.toString) - .typeFullName(registerType(constant.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(constant.getType))) ) } } diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForMethodsCreator.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForMethodsCreator.scala index e4ecc3635b83..4908d7262477 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForMethodsCreator.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForMethodsCreator.scala @@ -68,7 +68,7 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this methodAstWithAnnotations( methodNode .lineNumberEnd(methodBody.toString.split('\n').filterNot(_.isBlank).length) - .code(methodBody.toString), + .code(methodBody.toString.replace("\'", "")), parameterAsts, astForMethodBody(methodBody, bodyStatementsInfo), astForMethodReturn(methodDeclaration), @@ -146,14 +146,14 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this } private def astForMethodReturn(methodDeclaration: SootMethod): NewMethodReturn = { - val typeFullName = registerType(methodDeclaration.getReturnType.toQuotedString) + val typeFullName = registerType(sootTypeToString(methodDeclaration.getReturnType)) methodReturnNode(methodDeclaration, typeFullName) } private def createMethodNode(methodDeclaration: SootMethod, typeDecl: RefType) = { val name = methodDeclaration.getName val fullName = methodFullName(typeDecl, methodDeclaration) - val methodDeclType = registerType(methodDeclaration.getReturnType.toQuotedString) + val methodDeclType = registerType(sootTypeToString(methodDeclaration.getReturnType)) val code = if (!methodDeclaration.isConstructor) { s"$methodDeclType $name${paramListSignature(methodDeclaration, withParams = true)}" } else { @@ -168,19 +168,19 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this Option(signature), filename, Option(NodeTypes.TYPE_DECL), - Option(typeDecl.toQuotedString) + Option(sootTypeToString(typeDecl)) ) } private def methodFullName(typeDecl: RefType, methodDeclaration: SootMethod): String = { - val typeName = registerType(typeDecl.toQuotedString) - val returnType = registerType(methodDeclaration.getReturnType.toQuotedString) + val typeName = registerType(sootTypeToString(typeDecl)) + val returnType = registerType(sootTypeToString(methodDeclaration.getReturnType)) val methodName = methodDeclaration.getName s"$typeName.$methodName:$returnType${paramListSignature(methodDeclaration)}" } private def paramListSignature(methodDeclaration: SootMethod, withParams: Boolean = false) = { - val paramTypes = methodDeclaration.getParameterTypes.asScala.map(x => registerType(x.toQuotedString)) + val paramTypes = methodDeclaration.getParameterTypes.asScala.map(x => registerType(sootTypeToString(x))) val paramNames = if (!methodDeclaration.isPhantom && Try(methodDeclaration.retrieveActiveBody()).isSuccess) @@ -198,7 +198,7 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this protected def astForParameterRef(parameterRef: ParameterRef, parentUnit: SUnit): Ast = { val name = s"@parameter${parameterRef.getIndex}" - Ast(identifierNode(parentUnit, name, name, registerType(parameterRef.getType.toQuotedString))) + Ast(identifierNode(parentUnit, name, name, registerType(sootTypeToString(parameterRef.getType)))) } private def astForParameter( @@ -207,7 +207,7 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this methodDeclaration: SootMethod, parameterAnnotations: Map[String, VisibilityAnnotationTag] ): Ast = { - val typeFullName = registerType(parameter.getType.toQuotedString) + val typeFullName = registerType(sootTypeToString(parameter.getType)) val paramAst = Ast( parameterInNode( @@ -236,7 +236,7 @@ trait AstForMethodsCreator(implicit withSchemaValidation: ValidationMode) { this val jimpleLocals = body.getLocals.asScala.filterNot(l => jimpleParams.contains(l) || l.getName == "this").toList val locals = jimpleLocals.map { local => val name = local.getName - val typeFullName = registerType(local.getType.toQuotedString) + val typeFullName = registerType(sootTypeToString(local.getType)) val code = s"$typeFullName $name" Ast(localNode(body, name, code, typeFullName)) } diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForTypeDeclsCreator.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForTypeDeclsCreator.scala index 0a56c19ba95f..44117c7f254c 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForTypeDeclsCreator.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/declarations/AstForTypeDeclsCreator.scala @@ -15,7 +15,7 @@ trait AstForTypeDeclsCreator(implicit withSchemaValidation: ValidationMode) { th /** Creates the AST root for type declarations and acts as the entry point for method generation. */ protected def astForTypeDecl(typ: RefType, namespaceBlockFullName: String): Ast = { - val fullName = registerType(typ.toQuotedString) + val fullName = registerType(sootTypeToString(typ)) val shortName = typ.getSootClass.getShortJavaStyleName val clz = typ.getSootClass val code = new mutable.StringBuilder() @@ -59,7 +59,7 @@ trait AstForTypeDeclsCreator(implicit withSchemaValidation: ValidationMode) { th } private def astForField(field: SootField): Ast = { - val typeFullName = registerType(field.getType.toQuotedString) + val typeFullName = registerType(sootTypeToString(field.getType)) val name = field.getName val annotations = field.getTags.asScala .collect { case x: VisibilityAnnotationTag => x } @@ -69,7 +69,7 @@ trait AstForTypeDeclsCreator(implicit withSchemaValidation: ValidationMode) { th tag.getConstant.toString } val code = - if (field.getDeclaration.contains("enum")) name + if (field.getQuotedDeclaration.contains("enum")) name else { constantValue match { case Some(value) => s"$typeFullName $name = $value" @@ -87,11 +87,11 @@ trait AstForTypeDeclsCreator(implicit withSchemaValidation: ValidationMode) { th */ private def inheritedAndImplementedClasses(clazz: SootClass): (List[String], List[String]) = { val implementsTypeFullName = clazz.getInterfaces.asScala.map { (i: SootClass) => - registerType(i.getType.toQuotedString) + registerType(sootTypeToString(i.getType)) }.toList val inheritsFromTypeFullName = - if (clazz.hasSuperclass && clazz.getSuperclass.getType.toQuotedString != "java.lang.Object") { - List(registerType(clazz.getSuperclass.getType.toQuotedString)) + if (clazz.hasSuperclass && sootTypeToString(clazz.getSuperclass.getType) != "java.lang.Object") { + List(registerType(sootTypeToString(clazz.getSuperclass.getType))) } else if (implementsTypeFullName.isEmpty) { List(registerType("java.lang.Object")) } else List() diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/expressions/AstForExpressionsCreator.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/expressions/AstForExpressionsCreator.scala index 4b5b93c1f9ac..687b5c02a533 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/expressions/AstForExpressionsCreator.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/expressions/AstForExpressionsCreator.scala @@ -76,8 +76,8 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { } val signature = - s"${registerType(callee.getReturnType.toQuotedString)}(${(for (i <- 0 until callee.getParameterTypes.size()) - yield registerType(callee.getParameterType(i).toQuotedString)).mkString(",")})" + s"${registerType(sootTypeToString(callee.getReturnType))}(${(for (i <- 0 until callee.getParameterTypes.size()) + yield registerType(sootTypeToString(callee.getParameterType(i)))).mkString(",")})" val thisAsts = invokeExpr match { case expr: InstanceInvokeExpr => astsForValue(expr.getBase, parentUnit) case _ => Seq(createThisNode(callee, NewIdentifier())) @@ -89,7 +89,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { else callee.getName - val calleeType = registerType(callee.getDeclaringClass.getType.toQuotedString) + val calleeType = registerType(sootTypeToString(callee.getDeclaringClass.getType)) val callType = if (callee.isConstructor) "void" else calleeType @@ -134,7 +134,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { case u: NewMultiArrayExpr => astForArrayCreateExpr(x, u.getSizes.asScala, parentUnit) case _ => - val parentType = registerType(x.getType.toQuotedString) + val parentType = registerType(sootTypeToString(x.getType)) Ast( NewCall() .name(Operators.alloc) @@ -151,7 +151,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { private def astForArrayCreateExpr(arrayInitExpr: Expr, sizes: Iterable[Value], parentUnit: soot.Unit): Ast = { // Jimple does not have Operators.arrayInitializer // to enforce 3 address code form - val arrayBaseType = registerType(arrayInitExpr.getType.toQuotedString) + val arrayBaseType = registerType(sootTypeToString(arrayInitExpr.getType)) val code = s"new ${arrayBaseType.substring(0, arrayBaseType.indexOf('['))}${sizes.map(s => s"[$s]").mkString}" val callBlock = NewCall() .name(Operators.alloc) @@ -172,7 +172,7 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { .methodFullName(methodName) .code(unaryExpr.toString()) .dispatchType(DispatchTypes.STATIC_DISPATCH) - .typeFullName(registerType(unaryExpr.getType.toQuotedString)) + .typeFullName(registerType(sootTypeToString(unaryExpr.getType))) .lineNumber(line(parentUnit)) .columnNumber(column(parentUnit)) @@ -190,10 +190,10 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { val valueAsts = unaryExpr match { case instanceOfExpr: InstanceOfExpr => - val t = registerType(instanceOfExpr.getCheckType.toQuotedString) + val t = registerType(sootTypeToString(instanceOfExpr.getCheckType)) astsForValue(op, parentUnit) ++ astForTypeRef(t) case castExpr: CastExpr => - val t = registerType(castExpr.getCastType.toQuotedString) + val t = registerType(sootTypeToString(castExpr.getCastType)) astForTypeRef(t) ++ astsForValue(op, parentUnit) case _ => astsForValue(op, parentUnit) } diff --git a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/statements/AstForStatementsCreator.scala b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/statements/AstForStatementsCreator.scala index 6e933221fc1e..84496669406f 100644 --- a/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/statements/AstForStatementsCreator.scala +++ b/joern-cli/frontends/jimple2cpg/src/main/scala/io/joern/jimple2cpg/astcreation/statements/AstForStatementsCreator.scala @@ -77,7 +77,7 @@ trait AstForStatementsCreator(implicit withSchemaValidation: ValidationMode) { t assignStmt, Operators.assignment, s"$lhsCode = $rhsCode", - Option(registerType(leftOp.getType.toQuotedString)) + Option(registerType(leftOp.getType.toString)) ) Seq(callAst(assignment, identifier ++ initAsts)) } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArithmeticOperationsTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArithmeticOperationsTests.scala index a3f48d06b293..49f11cd340b5 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArithmeticOperationsTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArithmeticOperationsTests.scala @@ -3,36 +3,29 @@ package io.joern.jimple2cpg.querying import io.joern.jimple2cpg.testfixtures.JimpleCode2CpgFixture import io.shiftleft.codepropertygraph.generated.Cpg import io.shiftleft.codepropertygraph.generated.Operators -import io.shiftleft.codepropertygraph.generated.nodes.Identifier +import io.shiftleft.codepropertygraph.generated.nodes.{Identifier, Literal, Local} import io.shiftleft.semanticcpg.language.{toNodeTypeStarters, _} class ArithmeticOperationsTests extends JimpleCode2CpgFixture { - lazy val cpg: Cpg = code(""" - | class Foo { - | static void main(int argc, char argv) { - | int a = 3; - | double b = 2.0; - | double c = a + b; - | double d = c - a; - | double e = a * b; - | double f = b / a; - | long g = 1L; - | float h = 3.4f; - | } + |import java.util.Random; + | + |class Foo { + | public static void main(String[] args) { + | Random rand = new Random(); + | double a = rand.nextDouble(); + | double b = rand.nextDouble(); + | double c = a + b; + | double d = c - a; + | double e = a * b; + | double f = b / a; + | System.out.printf("do not optimize away: %f %f %f %f", c, d, e, f); | } + |} |""".stripMargin).cpg - private val vars = Seq( - ("a", "byte"), - ("b", "double"), - ("c", "double"), - ("d", "double"), - ("e", "double"), - ("f", "double"), - ("g", "long"), - ("h", "float") - ) + private val vars = + Seq(("a", "double"), ("b", "double"), ("c", "double"), ("d", "double"), ("e", "double"), ("f", "double")) "should contain call nodes with .assignment for all variables" in { val assignments = cpg.assignment @@ -40,7 +33,7 @@ class ArithmeticOperationsTests extends JimpleCode2CpgFixture { .filterNot(x => List("argc", "argv", "this").contains(x.target.code)) .map(x => (x.target.code, x.typeFullName)) .l - assignments.size shouldBe 8 // includes casting and 3-address code manipulations + assignments.size shouldBe 7 vars.foreach { x => assignments contains x shouldBe true } @@ -49,7 +42,7 @@ class ArithmeticOperationsTests extends JimpleCode2CpgFixture { "should contain a call node for the addition operator" in { val List(op) = cpg.call.nameExact(Operators.addition).l val List(a: Identifier, b: Identifier) = op.astOut.l: @unchecked - a.name shouldBe "$stack16" + a.name shouldBe "a" b.name shouldBe "b" } @@ -57,20 +50,20 @@ class ArithmeticOperationsTests extends JimpleCode2CpgFixture { val List(op) = cpg.call(Operators.subtraction).l val List(c: Identifier, a: Identifier) = op.astOut.l: @unchecked c.name shouldBe "c" - a.name shouldBe "$stack17" + a.name shouldBe "a" } "should contain a call node for the multiplication operator" in { val List(op) = cpg.call(Operators.multiplication).l val List(a: Identifier, b: Identifier) = op.astOut.l: @unchecked - a.name shouldBe "$stack18" + a.name shouldBe "a" b.name shouldBe "b" } "should contain a call node for the division operator" in { val List(op) = cpg.call(Operators.division).l val List(b: Identifier, a: Identifier) = op.astOut.l: @unchecked - a.name shouldBe "$stack19" b.name shouldBe "b" + a.name shouldBe "a" } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArrayTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArrayTests.scala index cd97abf470bf..6b3ad7385d7d 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArrayTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ArrayTests.scala @@ -31,8 +31,8 @@ class ArrayTests extends JimpleCode2CpgFixture { def m = cpg.method(".*foo.*") val List(placeholderArg: Identifier, arrayInit: Call) = - m.assignment.codeExact("$stack2 = new int[3]").argument.l: @unchecked - placeholderArg.code shouldBe "$stack2" + m.assignment.codeExact("x = new int[3]").argument.l: @unchecked + placeholderArg.code shouldBe "x" placeholderArg.typeFullName shouldBe "int[]" arrayInit.code shouldBe "new int[3]" @@ -44,37 +44,37 @@ class ArrayTests extends JimpleCode2CpgFixture { case None => Failed("arrayInitializer should have a literal with the value of 3") } - val List(stackAt0: Call, arg0: Literal) = m.assignment.codeExact("$stack2[0] = 0").argument.l: @unchecked + val List(stackAt0: Call, arg0: Literal) = m.assignment.codeExact("x[0] = 0").argument.l: @unchecked arg0.code shouldBe "0" arg0.typeFullName shouldBe "int" - stackAt0.code shouldBe "$stack2[0]" + stackAt0.code shouldBe "x[0]" stackAt0.methodFullName shouldBe Operators.indexAccess val List(stackPointerAt0: Identifier, zero: Literal) = stackAt0.astChildren.l: @unchecked - stackPointerAt0.code shouldBe "$stack2" + stackPointerAt0.code shouldBe "x" zero.code shouldBe "0" - val List(stackAt1: Call, arg1: Literal) = m.assignment.codeExact("$stack2[1] = 1").argument.l: @unchecked + val List(stackAt1: Call, arg1: Literal) = m.assignment.codeExact("x[1] = 1").argument.l: @unchecked arg1.code shouldBe "1" arg1.typeFullName shouldBe "int" - stackAt1.code shouldBe "$stack2[1]" + stackAt1.code shouldBe "x[1]" stackAt1.methodFullName shouldBe Operators.indexAccess val List(stackPointerAt1: Identifier, one: Literal) = stackAt1.astChildren.l: @unchecked - stackPointerAt1.code shouldBe "$stack2" + stackPointerAt1.code shouldBe "x" one.code shouldBe "1" - val List(stackAt2: Call, arg2: Literal) = m.assignment.codeExact("$stack2[2] = 2").argument.l: @unchecked + val List(stackAt2: Call, arg2: Literal) = m.assignment.codeExact("x[2] = 2").argument.l: @unchecked arg2.code shouldBe "2" arg2.typeFullName shouldBe "int" - stackAt2.code shouldBe "$stack2[2]" + stackAt2.code shouldBe "x[2]" stackAt2.methodFullName shouldBe Operators.indexAccess val List(stackPointerAt2: Identifier, two: Literal) = stackAt2.astChildren.l: @unchecked - stackPointerAt2.code shouldBe "$stack2" + stackPointerAt2.code shouldBe "x" two.code shouldBe "2" } @@ -95,7 +95,7 @@ class ArrayTests extends JimpleCode2CpgFixture { "should handle arrayIndexAccesses correctly (3-address code form)" in { def m = cpg.method(".*baz.*") - val List(indexAccess: Call, rhsStub: Identifier) = m.assignment.codeExact("x[1] = $stack3").argument.l: @unchecked + val List(indexAccess: Call, rhsStub: Identifier) = m.assignment.codeExact("x[1] = $stack2").argument.l: @unchecked indexAccess.name shouldBe Operators.indexAccess indexAccess.methodFullName shouldBe Operators.indexAccess @@ -108,9 +108,9 @@ class ArrayTests extends JimpleCode2CpgFixture { } withClue("placeholder in expr on RHS of assignment") { - rhsStub.name shouldBe "$stack3" + rhsStub.name shouldBe "$stack2" rhsStub.typeFullName shouldBe "int" - rhsStub.code shouldBe "$stack3" + rhsStub.code shouldBe "$stack2" } } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ConstructorInvocationTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ConstructorInvocationTests.scala index e22798f108bf..985c79efd472 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ConstructorInvocationTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ConstructorInvocationTests.scala @@ -97,7 +97,7 @@ class ConstructorInvocationTests extends JimpleCode2CpgFixture { "it should create joint `alloc` and `init` calls for a constructor invocation in a vardecl" in { cpg.typeDecl.name("Bar").method.name("test1").l match { case List(method) => - val List(_: Local, _: Local, assign: Call, init: Call, _: Call, _: Return) = + val List(_: Local, assign: Call, init: Call, _: Return) = method.astChildren.isBlock.astChildren.l: @unchecked assign.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH.toString @@ -134,7 +134,7 @@ class ConstructorInvocationTests extends JimpleCode2CpgFixture { "it should create joint `alloc` and `init` calls for a constructor invocation in an assignment" in { cpg.typeDecl.name("Bar").method.name("test2").l match { case List(method) => - val List(_: Local, _: Local, assign: Call, init: Call, _: Call, _: Return) = + val List(assign: Call, init: Call, _: Return) = method.astChildren.isBlock.astChildren.l: @unchecked assign.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH.toString @@ -153,14 +153,14 @@ class ConstructorInvocationTests extends JimpleCode2CpgFixture { init.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH.toString init.typeFullName shouldBe "void" init.signature shouldBe "void(int,int)" - init.code shouldBe "$stack1.Bar(4, 2)" + init.code shouldBe "b.Bar(4, 2)" init.argument.size shouldBe 3 val List(obj: Identifier, initArg1: Literal, initArg2: Literal) = init.argument.l: @unchecked obj.argumentIndex shouldBe 0 - obj.name shouldBe "$stack1" + obj.name shouldBe "b" obj.typeFullName shouldBe "Bar" - obj.code shouldBe "$stack1" + obj.code shouldBe "b" initArg1.code shouldBe "4" initArg2.code shouldBe "2" @@ -219,7 +219,7 @@ class ConstructorInvocationTests extends JimpleCode2CpgFixture { init.signature shouldBe "void(int)" val List(temp: Identifier, add: Call) = assignAddition.argument.l: @unchecked - temp.name shouldBe "$stack3" + temp.name shouldBe "x" temp.argumentIndex shouldBe 1 temp.typeFullName shouldBe "int" @@ -230,7 +230,7 @@ class ConstructorInvocationTests extends JimpleCode2CpgFixture { obj.argumentIndex shouldBe 0 obj.typeFullName shouldBe "Bar" - additionResultPointer.code shouldBe "$stack3" + additionResultPointer.code shouldBe "x" case res => fail(s"Expected Bar constructor but found $res") } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/FieldAccessTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/FieldAccessTests.scala index 233c5a3a65e9..cb4653b8eeed 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/FieldAccessTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/FieldAccessTests.scala @@ -60,7 +60,7 @@ class FieldAccessTests extends JimpleCode2CpgFixture { } "should handle object field accesses on RHS of assignments" in { - val List(_: Call, _: Call, assign: Call) = cpg.method(".*bar.*").call(".*assignment").l + val List(_: Call, assign: Call) = cpg.method(".*bar.*").call(".*assignment").l assign.code shouldBe "y = f.value" val List(access: Call) = cpg.method(".*bar.*").call(".*fieldAccess").l val List(identifier: Identifier, fieldIdentifier: FieldIdentifier) = access.argument.l: @unchecked @@ -70,7 +70,7 @@ class FieldAccessTests extends JimpleCode2CpgFixture { } "should handle object field accesses on LHS of assignments" in { - val List(_: Call, _: Call, assign: Call) = cpg.method(".*baz.*").call(".*assignment").l + val List(_: Call, assign: Call) = cpg.method(".*baz.*").call(".*assignment").l assign.code shouldBe "g.value = 66" val List(access: Call) = cpg.method(".*baz.*").call(".*fieldAccess").l val List(identifier: Identifier, fieldIdentifier: FieldIdentifier) = access.argument.l: @unchecked diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/IfGotoTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/IfGotoTests.scala index 20cc5fe5c232..8016362efd6b 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/IfGotoTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/IfGotoTests.scala @@ -40,7 +40,7 @@ class IfGotoTests extends JimpleCode2CpgFixture { x.cfgOut.size > 1 } .code - .toSetMutable shouldBe Set("i < 11", "$stack6 >= x", "x <= y", "i >= 10") + .toSetMutable shouldBe Set("i#3 < 11", "$stack4 >= x", "x > y", "i >= 10") } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/LocalTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/LocalTests.scala index 8762d9b2f9ee..1be8eee0d3b2 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/LocalTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/LocalTests.scala @@ -16,32 +16,32 @@ class LocalTests extends JimpleCode2CpgFixture { | Integer y = null; | x = 1; | y = new Integer(x); - | return y; + | return x + y; | } | } |""".stripMargin).cpg "should contain locals `x` and `y` with correct fields set" in { - val List(x: Local) = cpg.local("\\$stack3").l + val List(x: Local) = cpg.local("\\$stack4").l val List(y: Local) = cpg.local("y").l - x.name shouldBe "$stack3" - x.code shouldBe "java.lang.Integer $stack3" - x.typeFullName shouldBe "java.lang.Integer" - x.order shouldBe 1 + x.name shouldBe "$stack4" + x.code shouldBe "int $stack4" + x.typeFullName shouldBe "int" + x.order shouldBe 2 y.name shouldBe "y" y.code shouldBe "java.lang.Integer y" y.typeFullName shouldBe "java.lang.Integer" - y.order shouldBe 2 + y.order shouldBe 1 } "should allow traversing from local to identifier" in { val ys = cpg.local.nameExact("y").referencingIdentifiers.l - ys.size shouldBe 2 + ys.size shouldBe 5 ys.head.name shouldBe "y" - val xs = cpg.local.nameExact("$stack3").referencingIdentifiers.l - xs.size shouldBe 3 - xs.head.name shouldBe "$stack3" + val xs = cpg.local.nameExact("$stack4").referencingIdentifiers.l + xs.size shouldBe 4 + xs.head.name shouldBe "$stack4" } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ReflectionTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ReflectionTests.scala index 5b50d987c98c..6c3870c6172e 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ReflectionTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/ReflectionTests.scala @@ -35,6 +35,6 @@ class ReflectionTests extends JimpleCode2CpgFixture { } case None => fail("Should be the child of an .assignment call") } - fooMethod.typeFullName shouldBe "java.lang.reflect.Method" + fooMethod.typeFullName shouldBe "java.lang.Class" } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/StaticCallGraphTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/StaticCallGraphTests.scala index b32fee649550..69373837ea61 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/StaticCallGraphTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/StaticCallGraphTests.scala @@ -35,9 +35,9 @@ class StaticCallGraphTests extends JimpleCode2CpgFixture { cpg.method.name("main").call.code.toSetMutable shouldBe Set( "add(3, 3)", - "$stack2.println($stack3)", + "$stack2.println(argc)", "$stack2 = java.lang.System.out", - "$stack3 = add(3, 3)", + "argc = add(3, 3)", "java.lang.System.out" ) } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/SynchronizedTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/SynchronizedTests.scala index ce4481faaee6..b667041a4aea 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/SynchronizedTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/SynchronizedTests.scala @@ -43,12 +43,12 @@ class SynchronizedTests extends JimpleCode2CpgFixture { enterThis.lineNumber shouldBe Some(8) enterThis.columnNumber shouldBe None - exit1.code shouldBe "exitmonitor l2" + exit1.code shouldBe "exitmonitor this" exit1.lineNumber shouldBe Some(10) exit1.columnNumber shouldBe None - exit2.code shouldBe "exitmonitor l2" - exit2.lineNumber shouldBe Some(11) + exit2.code shouldBe "exitmonitor this" + exit2.lineNumber shouldBe Some(10) exit2.columnNumber shouldBe None } } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ArrayTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ArrayTests.scala index a375017251e0..3844596a7a0d 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ArrayTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ArrayTests.scala @@ -98,12 +98,6 @@ class ArrayTests extends JimpleDataFlowCodeToCpgSuite { sink.reachableBy(source).size shouldBe 1 } - "find a path if an entry in the `MALICIOUS` array is printed (approximation)" in { - // The reason this false positive occurs is because Jimple makes an alias in here unlike in test4 - val (source, sink) = getConstSourceSink("test2") - sink.reachableBy(source).size shouldBe 1 - } - "find a path for alternative array initializer syntax" in { val (source, sink) = getConstSourceSink("test3") sink.reachableBy(source).size shouldBe 1 @@ -124,12 +118,6 @@ class ArrayTests extends JimpleDataFlowCodeToCpgSuite { sink.reachableBy(source).size shouldBe 1 } - "find a path if the `MALICIOUS` array element is overwritten (approximation)" in { - // Similarly to test2, this false positive occurs because Jimple makes an alias - val (source, sink) = getConstSourceSink("test7") - sink.reachableBy(source).size shouldBe 1 - } - "find a path if sink is in a `FOR` loop" in { val (source, sink) = getConstSourceSink("test8") sink.reachableBy(source).size shouldBe 1 diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/IfTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/IfTests.scala index 6d6a502879ee..bb3b9feb7099 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/IfTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/IfTests.scala @@ -53,6 +53,7 @@ class IfTests extends JimpleDataFlowCodeToCpgSuite { | | public void test5(boolean b) { | String s = "MALICIOUS"; + | System.out.printf("do not optimize away: %s", s); | | if (b) { | s = "SAFE"; diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/LoopTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/LoopTests.scala index 7e0082be7805..a07accc5eced 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/LoopTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/LoopTests.scala @@ -86,6 +86,7 @@ class LoopTests extends JimpleDataFlowCodeToCpgSuite { | | public void test12(boolean b) { | String s = "MALICIOUS"; + | System.out.printf("do not optimize away: %s", s); | do { | s = "SAFE"; | b = !b; diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ObjectTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ObjectTests.scala index 8a77e7b4a680..8ef79e68c1e4 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ObjectTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/ObjectTests.scala @@ -180,8 +180,7 @@ class ObjectTests extends JimpleDataFlowCodeToCpgSuite { "find a path if a field is reassigned to `MALICIOUS` via an alias" in { val (source, sink) = getConstSourceSink("test10") - // TODO: The data flow appears to be alias insensitive and taints the whole object instance - sink.reachableBy(source).size shouldBe 0 + sink.reachableBy(source).size shouldBe 1 } // TODO this isn't supported yet diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/OperatorTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/OperatorTests.scala index fb3991ee7946..6ea72766dda4 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/OperatorTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/OperatorTests.scala @@ -49,6 +49,7 @@ class OperatorTests extends JimpleDataFlowCodeToCpgSuite { | | public void test6() { | String s = "MALICIOUS"; + | System.out.printf("do not optimize away: %s", s); | s = "SAFE"; | System.out.println(s); | } diff --git a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/TryTests.scala b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/TryTests.scala index a74fd9e3c5fb..b855a48237ae 100644 --- a/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/TryTests.scala +++ b/joern-cli/frontends/jimple2cpg/src/test/scala/io/joern/jimple2cpg/querying/dataflow/TryTests.scala @@ -102,6 +102,7 @@ class TryTests extends JimpleDataFlowCodeToCpgSuite { | | public void test8() { | String s = "MALICIOUS"; + | System.out.printf("don't optimize away: %s", s); | | try { | s = "SAFE"; @@ -115,6 +116,7 @@ class TryTests extends JimpleDataFlowCodeToCpgSuite { | | public void test9() { | String s = "MALICIOUS"; + | System.out.printf("don't optimize away: %s", s); | | try { | s = "MALICIOUS"; @@ -129,6 +131,7 @@ class TryTests extends JimpleDataFlowCodeToCpgSuite { | | public void test10() { | String s = "MALICIOUS"; + | System.out.printf("don't optimize away: %s", s); | | try { | spooky(); @@ -175,7 +178,6 @@ class TryTests extends JimpleDataFlowCodeToCpgSuite { } "find a path if the sink is in a `FINALLY`" in { - // Jimple's flat AST evaluates multiple paths that will end at the FINALLY sink but it is a conservative result val (source, sink) = getConstSourceSink("test3") sink.reachableBy(source).size shouldBe 3 } @@ -197,9 +199,8 @@ class TryTests extends JimpleDataFlowCodeToCpgSuite { } "find a path if `MALICIOUS` is assigned in `FINALLY`" in { - // Jimple's flat AST evaluates multiple paths that will end at the FINALLY sink but it is a conservative result val (source, sink) = getConstSourceSink("test7") - sink.reachableBy(source).size shouldBe 3 + sink.reachableBy(source).size shouldBe 1 } "not find a path if `MALICIOUS` is reassigned in both TRY/CATCH" in { diff --git a/project/Versions.scala b/project/Versions.scala index 695c5c92465e..ce2a59317492 100644 --- a/project/Versions.scala +++ b/project/Versions.scala @@ -35,7 +35,7 @@ object Versions { val scalatest = "3.2.18" val scopt = "4.1.0" val semverParser = "0.0.6" - val soot = "4.6.0" + val soot = "4.7.1" val slf4j = "2.0.7" val log4j = "2.20.0" val upickle = "4.0.2"