Repository: joernio/joern (scala)

Issue:
[Bug] jssrc: missing REF edges for locals for destructured parameters
from https://github.com/puppeteer/puppeteer/blob/14b236965000c2821aece8deae72fb927ab33930/experimental/puppeteer-firefox/lib/Page.js#L737-L740

```javascript
function _onConsole({type, args, executionContextId, location}) {
  const context = this._frameManager.executionContextById(executionContextId);
  this.emit(Events.Page.Console, new ConsoleMessage(type, args.map(arg => createHandle(context, arg)), location));
}
```

```
joern> cpg.identifier.name("location").not(_.refsTo).size 
val res1: Int = 1
joern> cpg.identifier.name("location").not(_.refsTo).astParent.code.l
val res4: List[String] = List("location = param1_0.location")
```

cc @DavidBakerEffendi since you recently changed some code around that area (though I'm not sure that broke it) and @max-leuthaeuser as regular jssrc maintainer

Pull request:
Fixed instance where identifiers generated by destructured parameters were not linked to their respective locals.

Resolves #3024

Patch:
diff --git a/joern-cli/frontends/jssrc2cpg/src/main/scala/io/joern/jssrc2cpg/astcreation/AstForFunctionsCreator.scala b/joern-cli/frontends/jssrc2cpg/src/main/scala/io/joern/jssrc2cpg/astcreation/AstForFunctionsCreator.scala
index 954d2c09e179..5efe01c03000 100644
--- a/joern-cli/frontends/jssrc2cpg/src/main/scala/io/joern/jssrc2cpg/astcreation/AstForFunctionsCreator.scala
+++ b/joern-cli/frontends/jssrc2cpg/src/main/scala/io/joern/jssrc2cpg/astcreation/AstForFunctionsCreator.scala
@@ -1,12 +1,12 @@
 package io.joern.jssrc2cpg.astcreation
 
 import io.joern.jssrc2cpg.datastructures.{BlockScope, MethodScope}
-import io.joern.jssrc2cpg.parser.BabelAst._
+import io.joern.jssrc2cpg.parser.BabelAst.*
 import io.joern.jssrc2cpg.parser.BabelNodeInfo
 import io.joern.x2cpg.Ast
-import io.joern.x2cpg.datastructures.Stack._
+import io.joern.x2cpg.datastructures.Stack.*
 import io.joern.x2cpg.utils.NodeBuilders.{newBindingNode, newLocalNode}
-import io.shiftleft.codepropertygraph.generated.nodes.{Identifier => _, _}
+import io.shiftleft.codepropertygraph.generated.nodes.{Identifier as _, *}
 import io.shiftleft.codepropertygraph.generated.{DispatchTypes, EdgeTypes, EvaluationStrategies, ModifierTypes}
 import ujson.Value
 
@@ -189,13 +189,18 @@ trait AstForFunctionsCreator { this: AstCreator =>
                   createFieldIdentifierNode(elemName, elementNodeInfo.lineNumber, elementNodeInfo.columnNumber)
                 val accessAst =
                   createFieldAccessCallAst(paramNode, keyNode, elementNodeInfo.lineNumber, elementNodeInfo.columnNumber)
-                createAssignmentCallAst(
+                val assignmentCallAst = createAssignmentCallAst(
                   Ast(localParamNode),
                   accessAst,
                   s"$elemName = ${codeOf(accessAst.nodes.head)}",
                   elementNodeInfo.lineNumber,
                   elementNodeInfo.columnNumber
                 )
+                // Handle identifiers referring to locals created by destructured parameters
+                assignmentCallAst.nodes
+                  .collect { case i: NewIdentifier if localNode.name == i.name => i }
+                  .map { i => assignmentCallAst.withRefEdge(i, localNode) }
+                  .reduce(_ merge _)
               case RestElement => handleRestInParameters(elementNodeInfo, nodeInfo, paramName)
               case _           => astForNodeWithFunctionReference(elementNodeInfo.json)
             }


Test patch:
diff --git a/joern-cli/frontends/jssrc2cpg/src/test/scala/io/joern/jssrc2cpg/passes/ast/TsClassesAstCreationPassTest.scala b/joern-cli/frontends/jssrc2cpg/src/test/scala/io/joern/jssrc2cpg/passes/ast/TsClassesAstCreationPassTest.scala
index 4a87aeddf50b..ba9373b9e947 100644
--- a/joern-cli/frontends/jssrc2cpg/src/test/scala/io/joern/jssrc2cpg/passes/ast/TsClassesAstCreationPassTest.scala
+++ b/joern-cli/frontends/jssrc2cpg/src/test/scala/io/joern/jssrc2cpg/passes/ast/TsClassesAstCreationPassTest.scala
@@ -2,8 +2,8 @@ package io.joern.jssrc2cpg.passes.ast
 
 import io.joern.jssrc2cpg.passes.{AbstractPassTest, Defines}
 import io.shiftleft.codepropertygraph.generated.ModifierTypes
-import io.shiftleft.codepropertygraph.generated.nodes.{Call, CfgNode}
-import io.shiftleft.semanticcpg.language._
+import io.shiftleft.codepropertygraph.generated.nodes.{Call, CfgNode, Declaration, Identifier}
+import io.shiftleft.semanticcpg.language.*
 
 class TsClassesAstCreationPassTest extends AbstractPassTest {
 
@@ -312,6 +312,8 @@ class TsClassesAstCreationPassTest extends AbstractPassTest {
       credentialsParam.typeFullName shouldBe "code.ts::program:Test:run:_anon_cdecl"
       // should not produce dangling nodes that are meant to be inside procedures
       cpg.all.collectAll[CfgNode].whereNot(_._astIn).size shouldBe 0
+      cpg.identifier.count(_.refsTo.size > 1) shouldBe 0
+      cpg.identifier.whereNot(_.refsTo).size shouldBe 0
     }
 
     "AST generation for destructured type in a parameter" in TsAstFixture("""
@@ -327,6 +329,8 @@ class TsClassesAstCreationPassTest extends AbstractPassTest {
       credentialsParam.typeFullName shouldBe "code.ts::program:apiCall:_anon_cdecl"
       // should not produce dangling nodes that are meant to be inside procedures
       cpg.all.collectAll[CfgNode].whereNot(_._astIn).size shouldBe 0
+      cpg.identifier.count(_.refsTo.size > 1) shouldBe 0
+      cpg.identifier.whereNot(_.refsTo).size shouldBe 0
     }
 
   }