-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[javascript] Object Property Call Linker (#4634)
A common pattern in JavaScript is something like ```javascript var foo = {}; foo.bar = {}; foo.bar.someFunc = function someFunc() {}; foo.bar.someFunc(); ``` This PR adds a post-processing pass to find instances where the definition and the call live in the same file and to link them. This "common file" limitation aims to reduce false linking.
- Loading branch information
1 parent
b93288e
commit a23d9fc
Showing
3 changed files
with
67 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
...ontends/jssrc2cpg/src/main/scala/io/joern/jssrc2cpg/passes/ObjectPropertyCallLinker.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package io.joern.jssrc2cpg.passes | ||
|
||
import io.shiftleft.codepropertygraph.generated.nodes.{Call, MethodRef} | ||
import io.shiftleft.codepropertygraph.generated.{Cpg, PropertyNames} | ||
import io.shiftleft.passes.CpgPass | ||
import overflowdb.BatchedUpdate | ||
import io.shiftleft.semanticcpg.language.* | ||
|
||
/** Perform a simple analysis to find a common pattern in JavaScript where objects are dynamically assigned function | ||
* pointers. To keep this precise, this will only match objects defined within the scope of the same file. | ||
* | ||
* This relies on JavaScriptTypeHintCallLinker. | ||
*/ | ||
class ObjectPropertyCallLinker(cpg: Cpg) extends CpgPass(cpg) { | ||
|
||
override def run(builder: BatchedUpdate.DiffGraphBuilder): Unit = { | ||
|
||
def propertyCallRegexPattern(withMatchingGroup: Boolean): String = | ||
"^(?:\\{.*\\}|.*<returnValue>):<member>\\(" + (if withMatchingGroup then "(.*)" else ".*") + "\\):.*$" | ||
|
||
val propertyCallRegex = propertyCallRegexPattern(true).r | ||
val objectCalls = cpg.call.methodFullName(propertyCallRegexPattern(false)).l | ||
val propertyAccessToCalls = objectCalls | ||
.flatMap { call => | ||
call.methodFullName match { | ||
case propertyCallRegex(baseProperty) => Option(s"$baseProperty.${call.name}" -> call) | ||
case _ => None | ||
} | ||
} | ||
.groupBy(_._1) | ||
.map { case (k, vs) => k -> vs.map(_._2) } | ||
cpg.assignment | ||
.and(_.source.isMethodRef, _.target.isCall.fieldAccess) | ||
.map { a => a.target.asInstanceOf[Call] -> a.source.asInstanceOf[MethodRef].referencedMethod.fullName } | ||
.foreach { (functionTarget, calleeFn) => | ||
propertyAccessToCalls | ||
.filter { case (propertyAccess, _) => functionTarget.code.endsWith(propertyAccess) } | ||
.foreach { case (_, calls) => | ||
calls.where(_.file.nameExact(functionTarget.file.name.toSeq*)).foreach { c => | ||
builder.setNodeProperty(c, PropertyNames.METHOD_FULL_NAME, calleeFn) | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters