mirror of
https://github.com/github/codeql.git
synced 2026-05-03 20:58:03 +02:00
Ruby: Add def-nodes to API graphs
This commit is contained in:
@@ -43,6 +43,11 @@ module API {
|
||||
*/
|
||||
DataFlow::LocalSourceNode getAnImmediateUse() { Impl::use(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node corresponding the value flowing into this API component.
|
||||
*/
|
||||
DataFlow::Node getARhs() { Impl::def(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a call to a method on the receiver represented by this API component.
|
||||
*/
|
||||
@@ -97,6 +102,30 @@ module API {
|
||||
result = this.getASubclass().getASuccessor(Label::return(method))
|
||||
}
|
||||
|
||||
private predicate hasParameterIndex(int n) {
|
||||
exists(string str |
|
||||
exists(this.getASuccessor(Label::parameterByStr(str))) and
|
||||
n = str.toInt()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an API node representing the `n`th positional parameter. */
|
||||
Node getParameter(int n) {
|
||||
result = this.getASuccessor(Label::parameter(n)) and this.hasParameterIndex(n)
|
||||
}
|
||||
|
||||
private predicate hasKeywordParameter(string name) {
|
||||
exists(this.getASuccessor(Label::keywordParameter(name)))
|
||||
}
|
||||
|
||||
/** Gets an API node representing the given keyword parameter. */
|
||||
Node getKeywordParameter(string name) {
|
||||
result = this.getASuccessor(Label::keywordParameter(name)) and this.hasKeywordParameter(name)
|
||||
}
|
||||
|
||||
/** Gets an API node representing the block parameter. */
|
||||
Node getBlock() { result = this.getASuccessor(Label::blockParameter()) }
|
||||
|
||||
/**
|
||||
* Gets a `new` call to the function represented by this API component.
|
||||
*/
|
||||
@@ -260,7 +289,9 @@ module API {
|
||||
/** The root of the API graph. */
|
||||
MkRoot() or
|
||||
/** A use of an API member at the node `nd`. */
|
||||
MkUse(DataFlow::Node nd) { isUse(nd) }
|
||||
MkUse(DataFlow::Node nd) { isUse(nd) } or
|
||||
/** A value that escapes into an API at the node `nd` */
|
||||
MkDef(DataFlow::Node nd) { isDef(nd) }
|
||||
|
||||
private string resolveTopLevel(ConstantReadAccess read) {
|
||||
TResolved(result) = resolveConstantReadAccess(read) and
|
||||
@@ -319,6 +350,8 @@ module API {
|
||||
useCandFwd().flowsTo(node) and
|
||||
useStep(_, node, nd)
|
||||
)
|
||||
or
|
||||
parameterStep(_, defCand(), nd)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -327,6 +360,13 @@ module API {
|
||||
cached
|
||||
predicate use(TApiNode nd, DataFlow::Node ref) { nd = MkUse(ref) }
|
||||
|
||||
/**
|
||||
* Holds if `ref` is a RHS of node `nd`.
|
||||
*/
|
||||
cached
|
||||
predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) }
|
||||
|
||||
/** Gets a node reachable from a use-node. */
|
||||
private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) {
|
||||
t.start() and
|
||||
isUse(result)
|
||||
@@ -334,6 +374,7 @@ module API {
|
||||
exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a node reachable from a use-node. */
|
||||
private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) }
|
||||
|
||||
private DataFlow::Node useCandRev(TypeBackTracker tb) {
|
||||
@@ -353,6 +394,73 @@ module API {
|
||||
isUse(result)
|
||||
}
|
||||
|
||||
private predicate isDef(DataFlow::Node rhs) {
|
||||
exists(DataFlow::Node use |
|
||||
useCandFwd().flowsTo(use) and
|
||||
argumentStep(_, use, rhs)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a data flow node that flows to the RHS of a def-node. */
|
||||
private DataFlow::LocalSourceNode defCand(TypeBackTracker t) {
|
||||
t.start() and
|
||||
exists(DataFlow::Node rhs |
|
||||
isDef(rhs) and
|
||||
result = rhs.getALocalSource()
|
||||
)
|
||||
or
|
||||
exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that flows to the RHS of a def-node. */
|
||||
private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) }
|
||||
|
||||
/**
|
||||
* Holds if there should be a `lbl`-edge from the given call to an argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate argumentStep(string lbl, DataFlow::CallNode call, DataFlow::Node argument) {
|
||||
exists(int n |
|
||||
argument = call.getArgument(n) and
|
||||
lbl = Label::parameter(n)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
argument = call.getKeywordArgument(name) and
|
||||
lbl = Label::keywordParameter(name)
|
||||
)
|
||||
or
|
||||
argument = call.getBlock() and
|
||||
lbl = Label::blockParameter()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there should be a `lbl`-edge from the given callable to a parameter.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate parameterStep(string lbl, DataFlow::Node callable, DataFlow::Node paramNode) {
|
||||
exists(Parameter param |
|
||||
paramNode.asParameter() = param and
|
||||
callable.asExpr().getExpr().(Callable).getAParameter() = param and
|
||||
lbl = getLabelFromParameter(param)
|
||||
)
|
||||
}
|
||||
|
||||
private string getLabelFromParameter(Parameter param) {
|
||||
result = Label::keywordParameter(param.(KeywordParameter).getName())
|
||||
or
|
||||
param instanceof BlockParameter and
|
||||
result = Label::blockParameter()
|
||||
or
|
||||
// TODO: the index can be offset by preceding non-positional parameters; translate correctly
|
||||
(
|
||||
param instanceof SimpleParameter
|
||||
or
|
||||
param instanceof OptionalParameter
|
||||
) and
|
||||
result = Label::parameter(param.getPosition())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
|
||||
*
|
||||
@@ -381,6 +489,21 @@ module API {
|
||||
result = trackUseNode(src, TypeTracker::end())
|
||||
}
|
||||
|
||||
/** Gets a data flow node reaching the RHS of the given def node. */
|
||||
private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) {
|
||||
t.start() and
|
||||
isDef(rhs) and
|
||||
result = rhs.getALocalSource()
|
||||
or
|
||||
exists(TypeBackTracker t2 | result = trackDefNode(rhs, t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node reaching the RHS of the given def node. */
|
||||
cached
|
||||
DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) {
|
||||
result = trackDefNode(rhs, TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
|
||||
*/
|
||||
@@ -408,6 +531,17 @@ module API {
|
||||
c.getSuperclassExpr() = a.asExpr().getExpr() and
|
||||
lbl = Label::subclass()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node use, DataFlow::Node base, DataFlow::Node rhs |
|
||||
pred = MkUse(use) and
|
||||
trackUseNode(use).flowsTo(base) and
|
||||
argumentStep(lbl, base, rhs) and
|
||||
succ = MkDef(rhs)
|
||||
or
|
||||
pred = MkDef(rhs) and
|
||||
parameterStep(lbl, trackDefNode(rhs), use) and
|
||||
succ = MkUse(use)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,4 +570,21 @@ private module Label {
|
||||
string return(string m) { result = "getReturn(\"" + m + "\")" }
|
||||
|
||||
string subclass() { result = "getASubclass()" }
|
||||
|
||||
/** Gets the label representing the given keword argument/parameter. */
|
||||
bindingset[name]
|
||||
bindingset[result]
|
||||
string keywordParameter(string name) { result = "getKeywordParameter(\"" + name + "\")" }
|
||||
|
||||
/** Gets the label representing the `n`th positional argument/parameter. */
|
||||
bindingset[n]
|
||||
string parameter(int n) { result = parameterByStr(n.toString()) }
|
||||
|
||||
/** Gets the label representing the `n`th positional argument/parameter. */
|
||||
bindingset[n]
|
||||
bindingset[result]
|
||||
string parameterByStr(string n) { result = "getParameter(" + n + ")" }
|
||||
|
||||
/** Gets the label representing the block argument/parameter. */
|
||||
string blockParameter() { result = "getBlock()" }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
Const = [1, 2, 3] #$ use=getMember("Array").getReturn("[]")
|
||||
Const.each do |c| #$ use=getMember("Const").getReturn("each")
|
||||
puts c
|
||||
puts c #$ use=getMember("Const").getReturn("each").getBlock().getParameter(0)
|
||||
end
|
||||
|
||||
foo = Foo #$ use=getMember("Foo")
|
||||
@@ -58,5 +58,5 @@ M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass()
|
||||
M1::C1.m #$ use=getMember("M1").getMember("C1").getReturn("m")
|
||||
M2::C3.m #$ use=getMember("M2").getMember("C3").getReturn("m") use=getMember("M1").getMember("C1").getASubclass().getReturn("m")
|
||||
|
||||
M1::C1.new.m #$ use=getMember("M1").getMember("C1").instance.getReturn("m")
|
||||
M2::C3.new.m #$ use=getMember("M2").getMember("C3").instance.getReturn("m")
|
||||
M1::C1.new.m #$ use=getMember("M1").getMember("C1").getReturn("new").getReturn("m")
|
||||
M2::C3.new.m #$ use=getMember("M2").getMember("C3").getReturn("new").getReturn("m")
|
||||
|
||||
Reference in New Issue
Block a user