mirror of
https://github.com/github/codeql.git
synced 2026-05-04 13:15:21 +02:00
Revert "Ruby: overhaul API graphs"
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
classMethodCalls
|
||||
| test1.rb:58:1:58:8 | ForwardNode(call to m) |
|
||||
| test1.rb:59:1:59:8 | ForwardNode(call to m) |
|
||||
| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() |
|
||||
| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() |
|
||||
instanceMethodCalls
|
||||
| test1.rb:61:1:61:12 | ForwardNode(call to m) |
|
||||
| test1.rb:62:1:62:12 | ForwardNode(call to m) |
|
||||
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
flowThroughArray
|
||||
| test1.rb:73:1:73:10 | call to m |
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import ruby
|
||||
import codeql.ruby.ast.internal.TreeSitter
|
||||
import codeql.ruby.dataflow.internal.AccessPathSyntax
|
||||
import codeql.ruby.frameworks.data.internal.ApiGraphModels
|
||||
import codeql.ruby.ApiGraphs
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class AccessPathFromExpectation extends AccessPath::Range {
|
||||
AccessPathFromExpectation() { hasExpectationWithValue(_, this) }
|
||||
}
|
||||
|
||||
API::Node evaluatePath(AccessPath path, int n) {
|
||||
path instanceof AccessPathFromExpectation and
|
||||
n = 1 and
|
||||
exists(AccessPathToken token | token = path.getToken(0) |
|
||||
token.getName() = "Member" and
|
||||
result = API::getTopLevelMember(token.getAnArgument())
|
||||
or
|
||||
token.getName() = "Method" and
|
||||
result = API::getTopLevelCall(token.getAnArgument())
|
||||
or
|
||||
token.getName() = "EntryPoint" and
|
||||
result = token.getAnArgument().(API::EntryPoint).getANode()
|
||||
)
|
||||
or
|
||||
result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1))
|
||||
or
|
||||
// TODO this is a workaround, support parsing of Method['[]'] instead
|
||||
path.getToken(n - 1).getName() = "MethodBracket" and
|
||||
result = evaluatePath(path, n - 1).getMethod("[]")
|
||||
}
|
||||
|
||||
API::Node evaluatePath(AccessPath path) { result = evaluatePath(path, path.getNumToken()) }
|
||||
|
||||
module ApiUseTest implements TestSig {
|
||||
string getARelevantTag() { result = ["source", "sink", "call", "reachableFromSource"] }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
// All results are considered optional
|
||||
none()
|
||||
}
|
||||
|
||||
predicate hasOptionalResult(Location location, string element, string tag, string value) {
|
||||
exists(API::Node apiNode, DataFlow::Node dataflowNode |
|
||||
apiNode = evaluatePath(value) and
|
||||
(
|
||||
tag = "source" and dataflowNode = apiNode.asSource()
|
||||
or
|
||||
tag = "reachableFromSource" and dataflowNode = apiNode.getAValueReachableFromSource()
|
||||
or
|
||||
tag = "sink" and dataflowNode = apiNode.asSink()
|
||||
or
|
||||
tag = "call" and dataflowNode = apiNode.asCall()
|
||||
) and
|
||||
location = dataflowNode.getLocation() and
|
||||
element = dataflowNode.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<ApiUseTest>
|
||||
|
||||
class CustomEntryPointCall extends API::EntryPoint {
|
||||
CustomEntryPointCall() { this = "CustomEntryPointCall" }
|
||||
|
||||
override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" }
|
||||
}
|
||||
|
||||
class CustomEntryPointUse extends API::EntryPoint {
|
||||
CustomEntryPointUse() { this = "CustomEntryPointUse" }
|
||||
|
||||
override DataFlow::LocalSourceNode getASource() {
|
||||
result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse"
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,39 @@
|
||||
Something.foo.withCallback do |a, b| #$ source=Member[Something].Method[foo].ReturnValue
|
||||
a.something #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[0].Method[something].ReturnValue
|
||||
b.somethingElse #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[1].Method[somethingElse].ReturnValue
|
||||
end #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].ReturnValue
|
||||
Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn()
|
||||
end #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn()
|
||||
|
||||
Something.withNamedArg do |a:, b: nil| #$ source=Member[Something]
|
||||
a.something #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[a:].Method[something].ReturnValue
|
||||
b.somethingElse #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[b:].Method[somethingElse].ReturnValue
|
||||
end #$ source=Member[Something].Method[withNamedArg].ReturnValue
|
||||
Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn()
|
||||
end #$ use=getMember("Something").getMethod("withNamedArg").getReturn()
|
||||
|
||||
Something.withLambda ->(a, b) { #$ source=Member[Something]
|
||||
a.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[0].Method[something].ReturnValue
|
||||
b.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[1].Method[something].ReturnValue
|
||||
} #$ source=Member[Something].Method[withLambda].ReturnValue
|
||||
Something.withLambda ->(a, b) { #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn()
|
||||
} #$ use=getMember("Something").getMethod("withLambda").getReturn()
|
||||
|
||||
Something.namedCallback( #$ source=Member[Something]
|
||||
Something.namedCallback( #$ use=getMember("Something")
|
||||
onEvent: ->(a, b) {
|
||||
a.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[0].Method[something].ReturnValue
|
||||
b.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[1].Method[something].ReturnValue
|
||||
a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn()
|
||||
}
|
||||
) #$ source=Member[Something].Method[namedCallback].ReturnValue
|
||||
) #$ use=getMember("Something").getMethod("namedCallback").getReturn()
|
||||
|
||||
Something.nestedCall1 do |a| #$ source=Member[Something]
|
||||
a.nestedCall2 do |b:| #$ reachableFromSource=Member[Something].Method[nestedCall1].Argument[block].Parameter[0]
|
||||
b.something #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].Argument[block].Parameter[b:].Method[something].ReturnValue
|
||||
end #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].ReturnValue
|
||||
end #$ source=Member[Something].Method[nestedCall1].ReturnValue
|
||||
Something.nestedCall1 do |a| #$ use=getMember("Something")
|
||||
a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0)
|
||||
b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn()
|
||||
end #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn()
|
||||
end #$ use=getMember("Something").getMethod("nestedCall1").getReturn()
|
||||
|
||||
def getCallback()
|
||||
->(x) {
|
||||
x.something #$ source=Member[Something].Method[indirectCallback].Argument[0].Parameter[0].Method[something].ReturnValue
|
||||
x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn()
|
||||
}
|
||||
end
|
||||
Something.indirectCallback(getCallback()) #$ source=Member[Something].Method[indirectCallback].ReturnValue
|
||||
Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn()
|
||||
|
||||
Something.withMixed do |a, *args, b| #$ source=Member[Something]
|
||||
a.something #$ source=Member[Something].Method[withMixed].Argument[block].Parameter[0].Method[something].ReturnValue
|
||||
Something.withMixed do |a, *args, b| #$ use=getMember("Something")
|
||||
a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
# b.something # not currently handled correctly
|
||||
end #$ source=Member[Something].Method[withMixed].ReturnValue
|
||||
end #$ use=getMember("Something").getMethod("withMixed").getReturn()
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
def chained_access1
|
||||
Something.foo [[[
|
||||
'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0]
|
||||
]]]
|
||||
end
|
||||
|
||||
def chained_access2
|
||||
array = []
|
||||
array[0] = [[
|
||||
'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0]
|
||||
]]
|
||||
Something.foo array
|
||||
end
|
||||
|
||||
def chained_access3
|
||||
array = [[]]
|
||||
array[0][0] = [
|
||||
'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0]
|
||||
]
|
||||
Something.foo array
|
||||
end
|
||||
|
||||
def chained_access4
|
||||
Something.foo {
|
||||
:one => {
|
||||
:two => {
|
||||
:three => 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[:one].Element[:two].Element[:three]
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
@@ -1,64 +0,0 @@
|
||||
class BaseClass
|
||||
def inheritedInstanceMethod
|
||||
yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[inheritedInstanceMethod].Parameter[block].Argument[0]
|
||||
end
|
||||
|
||||
def self.inheritedSingletonMethod
|
||||
yield "taint" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[inheritedSingletonMethod].Parameter[block].Argument[0]
|
||||
end
|
||||
end
|
||||
|
||||
class ClassWithCallbacks < BaseClass
|
||||
def instanceMethod
|
||||
yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[instanceMethod].Parameter[block].Argument[0]
|
||||
end
|
||||
|
||||
def self.singletonMethod
|
||||
yield "bar" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[singletonMethod].Parameter[block].Argument[0]
|
||||
end
|
||||
|
||||
def escapeSelf
|
||||
Something.baz { self }
|
||||
end
|
||||
|
||||
def self.escapeSingletonSelf
|
||||
Something.baz { self }
|
||||
end
|
||||
|
||||
def self.foo x
|
||||
x # $ reachableFromSource=Member[BaseClass].Method[foo].Parameter[0]
|
||||
x # $ reachableFromSource=Member[ClassWithCallbacks].Method[foo].Parameter[0]
|
||||
x # $ reachableFromSource=Member[Subclass].Method[foo].Parameter[0]
|
||||
end
|
||||
|
||||
def bar x
|
||||
x # $ reachableFromSource=Member[BaseClass].Instance.Method[bar].Parameter[0]
|
||||
x # $ reachableFromSource=Member[ClassWithCallbacks].Instance.Method[bar].Parameter[0]
|
||||
x # $ reachableFromSource=Member[Subclass].Instance.Method[bar].Parameter[0]
|
||||
end
|
||||
end
|
||||
|
||||
class Subclass < ClassWithCallbacks
|
||||
def instanceMethodInSubclass
|
||||
yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[instanceMethodInSubclass].Parameter[block].Argument[0]
|
||||
end
|
||||
|
||||
def self.singletonMethodInSubclass
|
||||
yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[singletonMethodInSubclass].Parameter[block].Argument[0]
|
||||
end
|
||||
end
|
||||
|
||||
Something.foo { ClassWithCallbacks.new }
|
||||
Something.bar { ClassWithCallbacks }
|
||||
|
||||
class ClassWithCallMethod
|
||||
def call x
|
||||
x # $ reachableFromSource=Method[topLevelMethod].Argument[0].Parameter[0]
|
||||
"bar" # $ sink=Method[topLevelMethod].Argument[0].ReturnValue
|
||||
end
|
||||
end
|
||||
|
||||
topLevelMethod ClassWithCallMethod.new
|
||||
|
||||
blah = topLevelMethod
|
||||
blah # $ reachableFromSource=Method[topLevelMethod].ReturnValue
|
||||
@@ -1,10 +0,0 @@
|
||||
module SelfDotClass
|
||||
module Mixin
|
||||
def foo
|
||||
self.class.bar # $ call=Member[Foo].Method[bar]
|
||||
end
|
||||
end
|
||||
class Subclass < Foo
|
||||
include Mixin
|
||||
end
|
||||
end
|
||||
@@ -1,34 +1,34 @@
|
||||
MyModule #$ source=Member[MyModule]
|
||||
print MyModule.foo #$ source=Member[MyModule].Method[foo].ReturnValue
|
||||
Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue sink=Member[Kernel].Method[print].Argument[0]
|
||||
Object::Kernel #$ source=Member[Kernel]
|
||||
Object::Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue
|
||||
MyModule #$ use=getMember("MyModule")
|
||||
print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn()
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0)
|
||||
Object::Kernel #$ use=getMember("Kernel")
|
||||
Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
|
||||
begin
|
||||
print MyModule.bar #$ source=Member[MyModule].Method[bar].ReturnValue
|
||||
raise AttributeError #$ source=Member[AttributeError]
|
||||
rescue AttributeError => e #$ source=Member[AttributeError]
|
||||
Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue
|
||||
print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn()
|
||||
raise AttributeError #$ use=getMember("AttributeError")
|
||||
rescue AttributeError => e #$ use=getMember("AttributeError")
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
|
||||
end
|
||||
Unknown.new.run #$ source=Member[Unknown].Method[new].ReturnValue.Method[run].ReturnValue
|
||||
Foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz]
|
||||
Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn()
|
||||
Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
Const = [1, 2, 3] #$ source=Member[Array].MethodBracket.ReturnValue
|
||||
Const.each do |c| #$ source=Member[Const]
|
||||
puts c #$ reachableFromSource=Member[Const].Method[each].Argument[block].Parameter[0] reachableFromSource=Member[Const].Element[any]
|
||||
end #$ source=Member[Const].Method[each].ReturnValue sink=Member[Const].Method[each].Argument[block]
|
||||
Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
Const.each do |c| #$ use=getMember("Const")
|
||||
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element)
|
||||
end #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
|
||||
|
||||
foo = Foo #$ source=Member[Foo]
|
||||
foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz]
|
||||
foo = Foo #$ use=getMember("Foo")
|
||||
foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
FooAlias = Foo #$ source=Member[Foo]
|
||||
FooAlias::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] source=Member[FooAlias].Member[Bar].Member[Baz]
|
||||
FooAlias = Foo #$ use=getMember("Foo")
|
||||
FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
module Outer
|
||||
module Inner
|
||||
end
|
||||
end
|
||||
|
||||
Outer::Inner.foo #$ source=Member[Outer].Member[Inner].Method[foo].ReturnValue
|
||||
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn()
|
||||
|
||||
module M1
|
||||
class C1
|
||||
@@ -40,36 +40,36 @@ module M1
|
||||
end
|
||||
end
|
||||
|
||||
class C2 < M1::C1 #$ source=Member[M1].Member[C1]
|
||||
class C2 < M1::C1 #$ use=getMember("M1").getMember("C1")
|
||||
end
|
||||
|
||||
module M2
|
||||
class C3 < M1::C1 #$ source=Member[M1].Member[C1]
|
||||
class C3 < M1::C1 #$ use=getMember("M1").getMember("C1")
|
||||
end
|
||||
|
||||
class C4 < C2 #$ source=Member[C2]
|
||||
class C4 < C2 #$ use=getMember("C2")
|
||||
end
|
||||
end
|
||||
|
||||
C2 #$ source=Member[C2] reachableFromSource=Member[M1].Member[C1]
|
||||
M2::C3 #$ source=Member[M2].Member[C3] reachableFromSource=Member[M1].Member[C1]
|
||||
M2::C4 #$ source=Member[M2].Member[C4] reachableFromSource=Member[C2] reachableFromSource=Member[M1].Member[C1]
|
||||
C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass()
|
||||
M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass()
|
||||
M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass()
|
||||
|
||||
M1::C1.m #$ source=Member[M1].Member[C1].Method[m].ReturnValue
|
||||
M2::C3.m #$ source=Member[M2].Member[C3].Method[m].ReturnValue source=Member[M1].Member[C1].Method[m].ReturnValue
|
||||
M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn()
|
||||
M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn()
|
||||
|
||||
M1::C1.new.m #$ source=Member[M1].Member[C1].Method[new].ReturnValue.Method[m].ReturnValue
|
||||
M2::C3.new.m #$ source=Member[M2].Member[C3].Method[new].ReturnValue.Method[m].ReturnValue
|
||||
M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn()
|
||||
M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn()
|
||||
|
||||
Foo.foo(a,b:c) #$ source=Member[Foo].Method[foo].ReturnValue sink=Member[Foo].Method[foo].Argument[0] sink=Member[Foo].Method[foo].Argument[b:]
|
||||
Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b")
|
||||
|
||||
def userDefinedFunction(x, y)
|
||||
x.noApiGraph(y)
|
||||
x.customEntryPointCall(y) #$ call=EntryPoint[CustomEntryPointCall] source=EntryPoint[CustomEntryPointCall].ReturnValue sink=EntryPoint[CustomEntryPointCall].Parameter[0]
|
||||
x.customEntryPointUse(y) #$ source=EntryPoint[CustomEntryPointUse]
|
||||
x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0)
|
||||
x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse")
|
||||
end
|
||||
|
||||
array = [A::B::C] #$ source=Member[Array].MethodBracket.ReturnValue
|
||||
array[0].m #$ source=Member[A].Member[B].Member[C].Method[m].ReturnValue source=Member[Array].MethodBracket.ReturnValue.Element[0].Method[m].ReturnValue
|
||||
array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn()
|
||||
|
||||
A::B::C[0] #$ source=Member[A].Member[B].Member[C].Element[0]
|
||||
A::B::C[0] #$ use=getMember("A").getMember("B").getMember("C").getContent(element_0)
|
||||
|
||||
88
ruby/ql/test/library-tests/dataflow/api-graphs/use.ql
Normal file
88
ruby/ql/test/library-tests/dataflow/api-graphs/use.ql
Normal file
@@ -0,0 +1,88 @@
|
||||
import codeql.ruby.AST
|
||||
import codeql.ruby.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import codeql.ruby.ApiGraphs
|
||||
|
||||
class CustomEntryPointCall extends API::EntryPoint {
|
||||
CustomEntryPointCall() { this = "CustomEntryPointCall" }
|
||||
|
||||
override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" }
|
||||
}
|
||||
|
||||
class CustomEntryPointUse extends API::EntryPoint {
|
||||
CustomEntryPointUse() { this = "CustomEntryPointUse" }
|
||||
|
||||
override DataFlow::LocalSourceNode getASource() {
|
||||
result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse"
|
||||
}
|
||||
}
|
||||
|
||||
module ApiUseTest implements TestSig {
|
||||
string getARelevantTag() { result = ["use", "def", "call"] }
|
||||
|
||||
private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) {
|
||||
l = n.getLocation() and
|
||||
(
|
||||
tag = "use" and
|
||||
n = a.getAValueReachableFromSource()
|
||||
or
|
||||
tag = "def" and
|
||||
n = a.asSink()
|
||||
or
|
||||
tag = "call" and
|
||||
n = a.(API::MethodAccessNode).getCallNode()
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "use" and // def tags are always optional
|
||||
exists(DataFlow::Node n | relevantNode(_, n, location, tag) |
|
||||
// Only report the longest path on this line:
|
||||
value =
|
||||
max(API::Node a2, Location l2, DataFlow::Node n2 |
|
||||
relevantNode(a2, n2, l2, tag) and
|
||||
l2.getFile() = location.getFile() and
|
||||
l2.getEndLine() = location.getEndLine()
|
||||
|
|
||||
a2.getPath()
|
||||
order by
|
||||
size(n2.asExpr().getExpr()), a2.getPath().length() desc, a2.getPath() desc
|
||||
) and
|
||||
element = n.toString()
|
||||
)
|
||||
}
|
||||
|
||||
// We also permit optional annotations for any other path on the line.
|
||||
// This is used to test subclass paths, which typically have a shorter canonical path.
|
||||
predicate hasOptionalResult(Location location, string element, string tag, string value) {
|
||||
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
|
||||
element = n.toString() and
|
||||
value = getAPath(a, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<ApiUseTest>
|
||||
|
||||
private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) }
|
||||
|
||||
/**
|
||||
* Gets a path of the given `length` from the root to the given node.
|
||||
* This is a copy of `API::getAPath()` without the restriction on path length,
|
||||
* which would otherwise rule out paths involving `getASubclass()`.
|
||||
*/
|
||||
string getAPath(API::Node node, int length) {
|
||||
node instanceof API::Root and
|
||||
length = 0 and
|
||||
result = ""
|
||||
or
|
||||
exists(API::Node pred, API::Label::ApiLabel lbl, string predpath |
|
||||
pred.getASuccessor(lbl) = node and
|
||||
predpath = getAPath(pred, length - 1) and
|
||||
exists(string dot | if length = 1 then dot = "" else dot = "." |
|
||||
result = predpath + dot + lbl and
|
||||
// avoid producing strings longer than 1MB
|
||||
result.length() < 1000 * 1000
|
||||
)
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user