From 5a355d7f3508e788300317d082d3410fed491987 Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Thu, 24 Jul 2025 16:31:22 +0200 Subject: [PATCH 1/2] Populate static receiver field for php calls --- build.sbt | 2 +- .../io/joern/php2cpg/astcreation/AstCreatorHelper.scala | 2 +- .../php2cpg/astcreation/AstForExpressionsCreator.scala | 9 ++++++++- .../test/scala/io/joern/php2cpg/querying/CallTests.scala | 4 +++- .../src/main/scala/io/joern/x2cpg/AstNodeBuilder.scala | 4 +++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/build.sbt b/build.sbt index ac2565db82b5..ceb11723abaa 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := "joern" ThisBuild / organization := "io.joern" ThisBuild / scalaVersion := "3.6.4" -val cpgVersion = "1.7.43" +val cpgVersion = "1.7.45" lazy val joerncli = Projects.joerncli lazy val querydb = Projects.querydb diff --git a/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstCreatorHelper.scala b/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstCreatorHelper.scala index d84928245a83..d35fa654ceee 100644 --- a/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstCreatorHelper.scala +++ b/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstCreatorHelper.scala @@ -92,7 +92,7 @@ trait AstCreatorHelper(disableFileContent: Boolean)(implicit withSchemaValidatio } } - private def getTypeDeclPrefix: Option[String] = + protected def getTypeDeclPrefix: Option[String] = scope.getEnclosingTypeDeclTypeName.filterNot(_ == NamespaceTraversal.globalNamespaceName) protected def codeForMethodCall(call: PhpCallExpr, targetAst: Ast, name: String): String = { diff --git a/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForExpressionsCreator.scala b/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForExpressionsCreator.scala index e3fbbe36d2a5..aac35ebddcc8 100644 --- a/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForExpressionsCreator.scala +++ b/joern-cli/frontends/php2cpg/src/main/scala/io/joern/php2cpg/astcreation/AstForExpressionsCreator.scala @@ -130,7 +130,14 @@ trait AstForExpressionsCreator(implicit withSchemaValidation: ValidationMode) { case _ => getMfn(call, name) } - val callRoot = callNode(call, code, name, fullName, dispatchType, None, Some(Defines.Any)) + val staticReceiver = call.target.collect { + case nameExpr: PhpNameExpr if nameExpr.name == NameConstants.Self => + getTypeDeclPrefix.map(name => s"$name$MetaTypeDeclExtension") + case nameExpr: PhpNameExpr => + Option(s"${nameExpr.name}$MetaTypeDeclExtension") + }.flatten + + val callRoot = callNode(call, code, name, fullName, dispatchType, None, Some(Defines.Any), staticReceiver) callAst(callRoot, arguments) } diff --git a/joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/CallTests.scala b/joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/CallTests.scala index c1297081d1bc..e9ff3a939731 100644 --- a/joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/CallTests.scala +++ b/joern-cli/frontends/php2cpg/src/test/scala/io/joern/php2cpg/querying/CallTests.scala @@ -143,6 +143,7 @@ class CallTests extends PhpCode2CpgFixture { barCall.receiver.isEmpty shouldBe true barCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH barCall.code shouldBe "self::bar($x)" + barCall.staticReceiver shouldBe Some("ClassA") } } @@ -337,6 +338,7 @@ class CallTests extends PhpCode2CpgFixture { test1.name shouldBe "test1" test1.methodFullName shouldBe "Foo\\Bar\\baz.test1" test1.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH + test1.staticReceiver shouldBe Some("Foo\\Bar\\baz") } "be unknown in the case of dynamic calls" in { @@ -344,6 +346,7 @@ class CallTests extends PhpCode2CpgFixture { test2.name shouldBe "test2" test2.methodFullName shouldBe s"${Defines.UnresolvedNamespace}.test2" test2.dispatchType shouldBe DispatchTypes.DYNAMIC_DISPATCH + test2.staticReceiver shouldBe None } } @@ -384,5 +387,4 @@ class CallTests extends PhpCode2CpgFixture { construct.typeFullName shouldBe Defines.Any construct.dynamicTypeHintFullName shouldBe Seq.empty } - } 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 f522d9217eb5..25651fe80d50 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 @@ -216,7 +216,8 @@ trait AstNodeBuilder[Node, NodeProcessor] { this: NodeProcessor => methodFullName: String, dispatchType: String, signature: Option[String], - typeFullName: Option[String] + typeFullName: Option[String], + staticReceiver: Option[String] = None ): NewCall = { val node_ = NewCall() @@ -224,6 +225,7 @@ trait AstNodeBuilder[Node, NodeProcessor] { this: NodeProcessor => .name(name) .methodFullName(methodFullName) .dispatchType(dispatchType) + .staticReceiver(staticReceiver) .lineNumber(line(node)) .columnNumber(column(node)) signature.foreach { s => node_.signature(s) } From 50453693030e222650e3526969b3bf714469444d Mon Sep 17 00:00:00 2001 From: Johannes Coetzee Date: Fri, 25 Jul 2025 14:26:23 +0200 Subject: [PATCH 2/2] Populate static receiver field for javasrc2cpg --- .../AstForCallExpressionsCreator.scala | 2 + .../javasrc2cpg/querying/CallTests.scala | 51 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala index de638990c248..ce015dba5587 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForCallExpressionsCreator.scala @@ -82,6 +82,7 @@ trait AstForCallExpressionsCreator { this: AstCreator => } val receiverType = scopeAsts.rootType.filter(_ != TypeConstants.Any).orElse(receiverTypeOption) + val staticReceiver = Option.when(dispatchType == DispatchTypes.STATIC_DISPATCH)(receiverType).flatten val argumentsCode = getArgumentCodeString(call.getArguments) val codePrefix = scopeAsts.headOption @@ -103,6 +104,7 @@ trait AstForCallExpressionsCreator { this: AstCreator => .lineNumber(line(call)) .columnNumber(column(call)) .typeFullName(expressionTypeFullName.getOrElse(defaultTypeFallback())) + .staticReceiver(staticReceiver) callAst(callRoot, argumentAsts, scopeAsts.headOption) } diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala index 8e9727a4d251..f6376a7b704a 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/CallTests.scala @@ -62,7 +62,10 @@ class NewCallTests extends JavaSrcCode2CpgFixture { | |""".stripMargin) - cpg.call.name("valueOf").methodFullName.l shouldBe List("java.lang.String.valueOf:java.lang.String(boolean)") + inside(cpg.call.name("valueOf").l) { case List(valueOfCall) => + valueOfCall.methodFullName shouldBe "java.lang.String.valueOf:java.lang.String(boolean)" + valueOfCall.staticReceiver shouldBe Some("java.lang.String") + } } "they are instance methods imported from java.lang.* should be resolved" in { @@ -131,7 +134,51 @@ class NewCallTests extends JavaSrcCode2CpgFixture { |} |""".stripMargin) - cpg.call.name("foo").methodFullName.l shouldBe List("foo.Foo.foo:java.lang.String()") + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.methodFullName shouldBe "foo.Foo.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("foo.Foo") + } + } + + "calls to inherited static methods" should { + val cpg = code( + """ + |package foo; + | + |class Foo { + | public static String foo() { + | return "FOO"; + | } + |} + |""".stripMargin) + .moreCode( + """ + |package bar; + | + |import foo.Foo; + | + |class Bar extends Foo { } + |""".stripMargin) + .moreCode( + """ + |package baz; + | + |import bar.Bar; + | + |class Baz { + | void test() { + | Bar.foo(); + | } + |} + |""".stripMargin + ) + + "have the correct staticReceiver set" in { + inside(cpg.call.name("foo").l) { case List(fooCall) => + fooCall.methodFullName shouldBe "bar.Bar.foo:java.lang.String()" + fooCall.staticReceiver shouldBe Some("bar.Bar") + } + } } "calls with unresolved receivers should have the correct fullnames" in {