Skip to content

Commit

Permalink
[rubysrc2cpg] dataflow tests & start measuring the gap against the pr…
Browse files Browse the repository at this point in the history
…evious frontend (#3798)
  • Loading branch information
xavierpinho authored Nov 6, 2023
1 parent 3d67ca3 commit 4d90eef
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.joern.rubysrc2cpg.dataflow

import io.joern.dataflowengineoss.language.*
import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.semanticcpg.language.*

class ConditionalTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataFlow = true) {

"flow through both branches of a ternary `.. ? .. : ..` operator" in {
val cpg = code("""
|x = 1
|y = 2
|z = foo ? x : y
|puts z
|""".stripMargin)
val source = cpg.literal
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(
List(("x = 1", 2), ("x", 2), ("foo ? x : y", 4), ("z", 4), ("puts z", 5)),
List(("y = 2", 3), ("y", 3), ("foo ? x : y", 4), ("z", 4), ("puts z", 5))
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package io.joern.rubysrc2cpg.dataflow

import io.joern.dataflowengineoss.language.*
import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.semanticcpg.language.*

class ControlStructureTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataFlow = true) {

"flow through body of a `while-end` statement" in {
val cpg = code("""
|x = 10
|while x > 0 do
| x = x - 1
|end
|puts x
|""".stripMargin)
val source = cpg.literal("10")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("x = 10", 2), ("x", 2), ("x - 1", 4), ("x = x - 1", 4), ("x > 0", 3), ("puts x", 6)))
}

"flow through body of an `until-end` statement" in {
val cpg = code("""
|x = 10
|until x <= 0 do
| x = x - 1
|end
|puts x
|""".stripMargin)
val source = cpg.literal("10")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("x = 10", 2), ("x", 2), ("x - 1", 4), ("x = x - 1", 4), ("x <= 0", 3), ("puts x", 6)))
}

"flow through the 1st branch of an `if-end` statement" in {
val cpg = code("""
|t = 100
|if true
| t = t + 1
|end
|puts t
|""".stripMargin)
val source = cpg.literal("100")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("t = 100", 2), ("t", 2), ("t + 1", 4), ("t = t + 1", 4), ("puts t", 6)))
}

"flow through the 2nd branch of an `if-else-end` statement" in {
val cpg = code("""
|t = 100
|if false
| foo
|else
| t = t - 1
|end
|puts t
|""".stripMargin)
val source = cpg.literal("100")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("t = 100", 2), ("t", 2), ("t - 1", 6), ("t = t - 1", 6), ("puts t", 8)))
}

"flow through the 2nd branch of an `if-elsif-end` statement" in {
val cpg = code("""
|t = 100
|if false
| foo
|elsif true
| t = t * 2
|end
|puts t
|""".stripMargin)
val source = cpg.literal("100")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("t = 100", 2), ("t", 2), ("t * 2", 6), ("t = t * 2", 6), ("puts t", 8)))
}

"flow through both branches of an `if-else-end` statement" in {
val cpg = code("""
|t = 100
|if false
| puts t + 1
|else
| puts t + 2
|end
|""".stripMargin)
val source = cpg.literal("100")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("t = 100", 2), ("t", 2), ("t + 2", 6)), List(("t = 100", 2), ("t", 2), ("t + 1", 4)))
}

"flow through an `unless-end` statement" in {
val cpg = code("""
|x = 1
|unless __LINE__ == 0 then
| x = x * 2
|end
|puts x
|""".stripMargin)
val source = cpg.literal("1")
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("x = 1", 2), ("x", 2), ("x * 2", 4), ("x = x * 2", 4), ("puts x", 6)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,17 @@ class MethodReturnTests extends RubyCode2CpgFixture(withPostProcessing = true, w
Set(List(("f(x)", 2), ("x", 2), ("RET", 2)))
}

"flow from method parameter to implicit return via assignment to temporary variable" in {
val cpg = code("""
|def f(x)
| y = x
|end
|""".stripMargin)
val source = cpg.method.name("f").parameter
val sink = cpg.method.name("f").methodReturn
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(List(("f(x)", 2), ("y = x", 3), ("y", 3), ("y = x", 3), ("RET", 2)))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.joern.rubysrc2cpg.dataflow

import io.joern.dataflowengineoss.language.*
import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.semanticcpg.language.*

class SingleAssignmentTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataFlow = true) {

"flow through two inline assignments `z = x = y = 1`" in {
val cpg = code("""
|z = x = y = 1
|puts y
|puts x
|puts z
|""".stripMargin)
val source = cpg.literal
val sink = cpg.method.name("puts").callIn.argument
val flows = sink.reachableByFlows(source)
flows.map(flowToResultPairs).toSet shouldBe
Set(
List(("y = 1", 2), ("y", 2), ("y = 1", 2), ("x", 2), ("x = y = 1", 2), ("z", 2), ("puts z", 5)),
List(("y = 1", 2), ("y", 2), ("y = 1", 2), ("x", 2), ("puts x", 4)),
List(("y = 1", 2), ("y", 2), ("puts y", 3))
)
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
package io.joern.rubysrc2cpg.deprecated.passes.ast

import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.joern.rubysrc2cpg.testfixtures.{DifferentInNewFrontend, RubyCode2CpgFixture, SameInNewFrontend}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators, nodes}
import io.shiftleft.semanticcpg.language.*
import org.scalatest.Tag

class AssignCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {

"single target assign" should {
val cpg = code("""x = 2""".stripMargin)

"test local and identifier nodes" in {
"test local and identifier nodes" taggedAs SameInNewFrontend in {
val localX = cpg.local.head
localX.name shouldBe "x"
val List(idX) = localX.referencingIdentifiers.l: @unchecked
idX.name shouldBe "x"
}

"test assignment node properties" in {
"test assignment node properties" taggedAs SameInNewFrontend in {
val assignCall = cpg.call.methodFullName(Operators.assignment).head
assignCall.code shouldBe "x = 2"
assignCall.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH
assignCall.lineNumber shouldBe Some(1)
assignCall.columnNumber shouldBe Some(2)
}

"test assignment node ast children" in {
"test assignment node ast children" taggedAs SameInNewFrontend in {
cpg.call
.methodFullName(Operators.assignment)
.astChildren
Expand All @@ -41,7 +42,7 @@ class AssignCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
.code shouldBe "2"
}

"test assignment node arguments" in {
"test assignment node arguments" taggedAs SameInNewFrontend in {
cpg.call
.methodFullName(Operators.assignment)
.argument
Expand Down Expand Up @@ -150,13 +151,13 @@ class AssignCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
cpg.all.collect { case block: nodes.Block if block.code != "" => block }.head
}

"test block exists" in {
"test block exists" taggedAs DifferentInNewFrontend in {
// Throws if block does not exist.
getSurroundingBlock
}

// TODO: Fix the code property of the Block node
"test block node properties" ignore {
"test block node properties" taggedAs DifferentInNewFrontend ignore {
val block = getSurroundingBlock
block.code shouldBe
"""tmp0 = list
Expand All @@ -166,12 +167,12 @@ class AssignCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
}

// TODO: Need to fix the local variables
"test local node" ignore {
"test local node" taggedAs DifferentInNewFrontend ignore {
cpg.method.name("Test0.rb::program").local.name("tmp0").headOption should not be empty
}

// TODO: Need to fix the code property
"test tmp variable assignment" ignore {
"test tmp variable assignment" taggedAs DifferentInNewFrontend ignore {
val block = getSurroundingBlock
val tmpAssignNode = block.astChildren.isCall.sortBy(_.order).head
tmpAssignNode.code shouldBe "tmp0 = list"
Expand All @@ -183,7 +184,7 @@ class AssignCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
"empty array assignment" should {
val cpg = code("""x.y = []""".stripMargin)

"have an empty assignment" in {
"have an empty assignment" taggedAs DifferentInNewFrontend in {
val List(assignment) = cpg.call.name(Operators.assignment).l
assignment.argument.where(_.argumentIndex(2)).isCall.name.l shouldBe List(Operators.arrayInitializer)
assignment.argument.where(_.argumentIndex(2)).isCall.argument.l shouldBe List()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package io.joern.rubysrc2cpg.deprecated.passes.ast

import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes}
import io.joern.rubysrc2cpg.testfixtures.{RubyCode2CpgFixture, SameInNewFrontend}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators}
import io.shiftleft.semanticcpg.language.*
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal

class AttributeCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
val cpg = code("""x.y""".stripMargin)

// TODO: Class Modeling testcase
"test field access call node properties" ignore {
"test field access call node properties" taggedAs SameInNewFrontend ignore {
val callNode = cpg.call.methodFullName(Operators.fieldAccess).head
callNode.code shouldBe "x.y"
callNode.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package io.joern.rubysrc2cpg.deprecated.passes.ast

import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.shiftleft.codepropertygraph.generated.{EvaluationStrategies, NodeTypes, DispatchTypes, Operators, nodes}
import io.joern.rubysrc2cpg.testfixtures.{DifferentInNewFrontend, RubyCode2CpgFixture, SameInNewFrontend}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, Operators}
import io.shiftleft.semanticcpg.language.*
import io.shiftleft.semanticcpg.language.types.structure.NamespaceTraversal

class BoolOpCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
val cpg = code("""x or y or z""".stripMargin)

"test boolOp 'or' call node properties" in {
"test boolOp 'or' call node properties" taggedAs SameInNewFrontend in {
val orCall = cpg.call.head
// val orCall = cpg.call.methodFullName(Operators.logicalOr).head
orCall.code shouldBe "x or y or z"
Expand All @@ -18,7 +17,7 @@ class BoolOpCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
}

// TODO: Fix this multi logicalOr operation
"test boolOp 'or' ast children" ignore {
"test boolOp 'or' ast children" taggedAs DifferentInNewFrontend ignore {
cpg.call
.methodFullName(Operators.logicalOr)
.astChildren
Expand All @@ -43,7 +42,7 @@ class BoolOpCpgTests extends RubyCode2CpgFixture(useDeprecatedFrontend = true) {
}

// TODO: Fix this multi logicalOr operation arguments
"test boolOp 'or' arguments" ignore {
"test boolOp 'or' arguments" taggedAs DifferentInNewFrontend ignore {
cpg.call
.methodFullName(Operators.logicalOr)
.argument
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.joern.rubysrc2cpg.deprecated.passes.ast

import io.joern.rubysrc2cpg.deprecated.passes.Defines
import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.joern.rubysrc2cpg.testfixtures.{RubyCode2CpgFixture, SameInNewFrontend}
import io.shiftleft.codepropertygraph.generated.nodes.{Identifier, MethodRef}
import io.shiftleft.codepropertygraph.generated.{DispatchTypes, nodes}
import io.shiftleft.semanticcpg.language.*
Expand All @@ -10,15 +10,15 @@ class CallCpgTests extends RubyCode2CpgFixture(withPostProcessing = true, useDep
"simple call method" should {
val cpg = code("""foo("a", b)""".stripMargin)

"test call node properties" in {
"test call node properties" taggedAs SameInNewFrontend in {
val callNode = cpg.call.name("foo").head
callNode.code shouldBe """foo("a", b)"""
callNode.signature shouldBe ""
callNode.dispatchType shouldBe DispatchTypes.STATIC_DISPATCH
callNode.lineNumber shouldBe Some(1)
}

"test call arguments" in {
"test call arguments" taggedAs SameInNewFrontend in {
val callNode = cpg.call.name("foo").head
val arg1 = callNode.argument(1)
arg1.code shouldBe "\"a\""
Expand All @@ -27,7 +27,7 @@ class CallCpgTests extends RubyCode2CpgFixture(withPostProcessing = true, useDep
arg2.code shouldBe "b"
}

"test astChildren" in {
"test astChildren" taggedAs SameInNewFrontend in {
val callNode = cpg.call.name("foo").head
val children = callNode.astChildren
children.size shouldBe 2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.joern.rubysrc2cpg.deprecated.passes.ast

import io.joern.rubysrc2cpg.testfixtures.RubyCode2CpgFixture
import io.joern.rubysrc2cpg.testfixtures.{DifferentInNewFrontend, RubyCode2CpgFixture}
import io.shiftleft.codepropertygraph.generated.nodes.{Identifier, MethodRef, TypeRef}
import io.shiftleft.semanticcpg.language.*

Expand All @@ -10,7 +10,7 @@ class CustomAssignmentTests extends RubyCode2CpgFixture(withPostProcessing = tru
val cpg = code("""
|puts "This is ruby"
|""".stripMargin)
"be created for builtin presence" in {
"be created for builtin presence" taggedAs DifferentInNewFrontend in {
val List(putsAssignmentCall, _) = cpg.call.l
putsAssignmentCall.name shouldBe "<operator>.assignment"

Expand Down
Loading

0 comments on commit 4d90eef

Please sign in to comment.