Skip to content

Commit

Permalink
[ruby] Fixed Call/Method Dataflow Tests (#4532)
Browse files Browse the repository at this point in the history
* Fixed parameter alignment issue where some methods didn't have a 0th parameter
* Fixed `require` regex to account for local relative file imports
* Fixed up other tests not expecting `0th` param
  • Loading branch information
DavidBakerEffendi authored May 6, 2024
1 parent 85e8ec6 commit bd4ed84
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,15 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
if (methodName == XDefines.ConstructorMethodName) scope.pushNewScope(ConstructorScope(fullName))
else scope.pushNewScope(MethodScope(fullName, procParamGen.fresh))

val parameterAsts = astForParameters(node.parameters)
val thisParameterAst = Ast(
newThisParameterNode(
code = Defines.This,
typeFullName = scope.surroundingTypeFullName.getOrElse(Defines.Any),
line = method.lineNumber,
column = method.columnNumber
)
)
val parameterAsts = thisParameterAst :: astForParameters(node.parameters)

val optionalStatementList = statementListForOptionalParams(node.parameters)

Expand Down Expand Up @@ -281,9 +289,9 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
scope.tryResolveTypeReference(baseType) match {
case Some(typ) =>
(Option(NodeTypes.TYPE_DECL), Option(typ.name), baseType, true)
case None => (None, None, "", false)
case None => (None, None, Defines.This, false)
}
case None => (None, None, "", false)
case None => (None, None, Defines.This, false)
}
}

Expand All @@ -308,7 +316,7 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th
)
)

val parameterAsts = astForParameters(node.parameters, true)
val parameterAsts = astForParameters(node.parameters)
val optionalStatementList = statementListForOptionalParams(node.parameters)
val stmtBlockAst = astForMethodBody(node.body, optionalStatementList)

Expand Down Expand Up @@ -342,9 +350,9 @@ trait AstForFunctionsCreator(implicit withSchemaValidation: ValidationMode) { th

}

private def astForParameters(parameters: List[RubyNode], plusOne: Boolean = false): List[Ast] = {
private def astForParameters(parameters: List[RubyNode]): List[Ast] = {
parameters.zipWithIndex.map { case (parameterNode, index) =>
astForParameter(parameterNode, if (plusOne) index + 1 else index)
astForParameter(parameterNode, index + 1)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ object RubyIntermediateAst {
def isString: Boolean = text.startsWith("\"") || text.startsWith("'")

def innerText: String = {
val strRegex = ":?['\"]([\\w\\d_-]+)['\"]".r
val strRegex = "[./:]?['\"]([\\w\\d_-]+)(?:\\.rb)?['\"]".r
text match {
case s":'$content'" => content
case s":$symbol" => symbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,67 +6,63 @@ import io.shiftleft.semanticcpg.language.*

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

// Works on deprecated
"Flow through call" ignore {
"Flow through call" in {
val cpg = code("""
|def print(content)
|puts content
|def foo(content)
| puts content
|end
|
|def main
|n = 1
|print( n )
| n = 1
| foo( n )
|end
|""".stripMargin)

val src = cpg.method.name("print").parameter.where(_.index(1)).argument.l
val src = cpg.method.name("foo").parameter.where(_.index(1)).argument.l
val sink = cpg.method.name("puts").callIn.argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

// Works on deprecated
"Explicit return via call with initialization" ignore {
"Explicit return via call with initialization" in {
val cpg = code("""
|def add(p)
|q = 5
|q = p
|return q
| q = 5
| q = p
| return q
|end
|
|n = 1
|ret = add(n)
|puts ret
|""".stripMargin)

val src = cpg.identifier.name("n").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(src).l.size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

// Works on deprecated
"Implicit return via call with initialization" ignore {
"Implicit return via call with initialization" in {
val cpg = code("""
|def add(p)
|q = 5
|q = p
|q
| q = 5
| q = p
| q
|end
|
|n = 1
|ret = add(n)
|puts ret
|""".stripMargin)

val src = cpg.identifier.name("n").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(src).l.size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

// Works in deprecated
"Data flow through grouping expression with negation" ignore {
"Data flow through grouping expression with negation" in {
val cpg = code("""
|def foo(arg)
|return arg
| return arg
|end
|
|x = false
Expand All @@ -75,30 +71,28 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 2
val source = cpg.literal.code("false").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(source).size shouldBe 1
}

// Works in deprecated
"Data flow through variable params" ignore {
val cpg = code("""
|def foo(*args)
| return args
|end
|
|x = 1
|y = foo(x, "another param")
|y = foo("another param", x)
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 2
val source = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(source).size shouldBe 1
}

// Works in deprecated
"Data flow through optional params" ignore {
"Data flow through optional params" in {
val cpg = code("""
|def foo(arg=10)
| return arg + 10
Expand All @@ -109,13 +103,12 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 2
val source = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(source).size shouldBe 1
}

// Works in deprecated
"Data flow across files" ignore {
"Data flow across files" in {
val cpg = code(
"""
|def my_func(x)
Expand All @@ -138,8 +131,7 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
sink.reachableByFlows(source).size shouldBe 1
}

// Works in deprecated
"Across the file data flow test" ignore {
"Across the file data flow test" should {
val cpg = code(
"""
|def foo(arg)
Expand All @@ -164,22 +156,23 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
"bar.rb"
)

val source = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).lineNumber(3).l
sink.reachableByFlows(source).size shouldBe 1
val src = cpg.identifier("x").lineNumber(3).l
sink.reachableByFlows(src).size shouldBe 1

// // TODO: Need to be fixed.
// "be found for sink in nested block" ignore {
// val src = cpg.identifier("x").lineNumber(3).l
// val sink = cpg.call.name("puts").argument(1).lineNumber(7).l
// sink.reachableByFlows(src).size shouldBe 1
// }
"be found for sink in outer block" in {
val source = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).lineNumber(3).l
sink.reachableByFlows(source).size shouldBe 1
val src = cpg.identifier("x").lineNumber(3).l
sink.reachableByFlows(src).size shouldBe 1
}

"be found for sink in nested block" in {
val src = cpg.identifier("x").lineNumber(3).l
val sink = cpg.call.name("puts").argument(1).lineNumber(7).l
sink.reachableByFlows(src).size shouldBe 1
}
}

// Works in deprecated - does not parse on new frontend
"Data flow through invocation or command with EMARK" ignore {
// TODO: Generates warnings
"Data flow through invocation or command with EMARK" in {
val cpg = code("""
|x=12
|def woo(x)
Expand All @@ -193,13 +186,12 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|end
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 3
val source = cpg.literal.code("12").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(source).size shouldBe 1
}

// Works in deprecated
"Flow for nested puts calls" ignore {
"Flow for nested puts calls" in {
val cpg = code("""
|x=10
|def put_name(x)
Expand All @@ -214,9 +206,9 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|double_nested_put(x)
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 5
val source = cpg.literal.code("10").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(source).size shouldBe 1
}

"Data flow through a keyword? named method usage" in {
Expand All @@ -226,29 +218,27 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val src = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(src).size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

// Works in deprecated
"Data flow through a keyword inside a association" ignore {
"Data flow through a keyword inside a association" in {
val cpg = code("""
|def foo(arg)
|puts arg
| puts arg
|end
|
|x = 1
|foo if: x.nil?
|""".stripMargin)

val src = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(src).size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

// Works in deprecated
"flow through a method call with safe navigation operator with parantheses" ignore {
"flow through a method call with safe navigation operator with parentheses" in {
val cpg = code("""
|class Foo
| def bar(arg)
Expand All @@ -261,13 +251,13 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
src.reachableByFlows(src).size shouldBe 1
}

// Works in deprecated
"flow through a method call with safe navigation operator without parantheses" ignore {
// TODO: This does not create a call node, as AMPDOT is not recognized by the parser without parentheses
"flow through a method call with safe navigation operator without parentheses" ignore {
val cpg = code("""
|class Foo
| def bar(arg)
Expand All @@ -280,9 +270,9 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 2
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 1
}

"flow through a method call present in next line, with the second line starting with `.`" in {
Expand All @@ -300,9 +290,9 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 1
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 2
}

"flow through a method call present in next line, with the first line ending with `.`" in {
Expand All @@ -320,8 +310,8 @@ class CallTests extends RubyCode2CpgFixture(withPostProcessing = true, withDataF
|puts y
|""".stripMargin)

val source = cpg.identifier.name("x").l
val sink = cpg.call.name("puts").l
sink.reachableByFlows(source).size shouldBe 1
val src = cpg.literal.code("1").l
val sink = cpg.call.name("puts").argument(1).l
sink.reachableByFlows(src).size shouldBe 2
}
}
Loading

0 comments on commit bd4ed84

Please sign in to comment.