mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
Data flow through constants
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
private import codeql_ruby.AST
|
||||
private import internal.AST
|
||||
private import internal.Module
|
||||
private import internal.Variable
|
||||
private import internal.TreeSitter
|
||||
|
||||
@@ -90,6 +91,32 @@ class ConstantReadAccess extends ConstantAccess {
|
||||
this = any(AssignOperation a).getLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value being read, if any. For example, in
|
||||
*
|
||||
* ```rb
|
||||
* module M
|
||||
* CONST = "const"
|
||||
* end
|
||||
*
|
||||
* puts M::CONST
|
||||
* ```
|
||||
*
|
||||
* the value being read at `M::CONST` is `"const"`.
|
||||
*/
|
||||
Expr getValue() {
|
||||
not exists(this.getScopeExpr()) and
|
||||
result = lookupConst(this.getEnclosingModule+().getModule(), this.getName()) and
|
||||
// For now, we restrict the scope of top-level declarations to their file.
|
||||
// This may remove some plausible targets, but also removes a lot of
|
||||
// implausible targets
|
||||
if result.getEnclosingModule() instanceof Toplevel
|
||||
then result.getFile() = this.getFile()
|
||||
else any()
|
||||
or
|
||||
result = lookupConst(resolveScopeExpr(this.getScopeExpr()), this.getName())
|
||||
}
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ConstantReadAccess" }
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,26 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
|
||||
result = this.getAModule() and result.getName() = name
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the constant named `name`, if any.
|
||||
*
|
||||
* For example, the value of `CONST` is `"const"` in
|
||||
* ```rb
|
||||
* module M
|
||||
* CONST = "const"
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
Expr getConstant(string name) {
|
||||
exists(AssignExpr ae, ConstantWriteAccess w |
|
||||
ae = this.getAStmt() and
|
||||
w = ae.getLeftOperand() and
|
||||
w.getName() = name and
|
||||
not exists(w.getScopeExpr()) and
|
||||
result = ae.getRightOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the representation of the run-time value of this module or class. */
|
||||
Module getModule() { none() }
|
||||
}
|
||||
|
||||
@@ -130,6 +130,12 @@ private module Cached {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Method lookupMethod(Module m, string name) { TMethod(result) = lookupMethodOrConst(m, name) }
|
||||
|
||||
cached
|
||||
Expr lookupConst(Module m, string name) { TExpr(result) = lookupMethodOrConst(m, name) }
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -340,24 +346,39 @@ private Module getAncestors(Module m) {
|
||||
result = getAncestors(m.getAPrependedModule())
|
||||
}
|
||||
|
||||
Method getMethod(TModule owner, string name) {
|
||||
exists(ModuleBase m | m.getModule() = owner and result = m.getMethod(name))
|
||||
}
|
||||
private newtype TMethodOrExpr =
|
||||
TMethod(Method m) or
|
||||
TExpr(Expr e)
|
||||
|
||||
private Method lookupMethod0(Module m, string name) {
|
||||
result = lookupMethod0(m.getAPrependedModule(), name)
|
||||
or
|
||||
not exists(getMethod(getAncestors(m.getAPrependedModule()), name)) and
|
||||
(
|
||||
result = getMethod(m, name)
|
||||
private TMethodOrExpr getMethodOrConst(TModule owner, string name) {
|
||||
exists(ModuleBase m | m.getModule() = owner |
|
||||
result = TMethod(m.getMethod(name))
|
||||
or
|
||||
not exists(getMethod(m, name)) and result = lookupMethod0(m.getAnIncludedModule(), name)
|
||||
result = TExpr(m.getConstant(name))
|
||||
)
|
||||
}
|
||||
|
||||
Method lookupMethod(Module m, string name) {
|
||||
result = lookupMethod0(m, name)
|
||||
or
|
||||
not exists(lookupMethod0(m, name)) and
|
||||
result = lookupMethod(m.getSuperClass(), name)
|
||||
module ExposedForTestingOnly {
|
||||
Method getMethod(TModule owner, string name) { TMethod(result) = getMethodOrConst(owner, name) }
|
||||
|
||||
Expr getConst(TModule owner, string name) { TExpr(result) = getMethodOrConst(owner, name) }
|
||||
}
|
||||
|
||||
private TMethodOrExpr lookupMethodOrConst0(Module m, string name) {
|
||||
result = lookupMethodOrConst0(m.getAPrependedModule(), name)
|
||||
or
|
||||
not exists(getMethodOrConst(getAncestors(m.getAPrependedModule()), name)) and
|
||||
(
|
||||
result = getMethodOrConst(m, name)
|
||||
or
|
||||
not exists(getMethodOrConst(m, name)) and
|
||||
result = lookupMethodOrConst0(m.getAnIncludedModule(), name)
|
||||
)
|
||||
}
|
||||
|
||||
private TMethodOrExpr lookupMethodOrConst(Module m, string name) {
|
||||
result = lookupMethodOrConst0(m, name)
|
||||
or
|
||||
not exists(lookupMethodOrConst0(m, name)) and
|
||||
result = lookupMethodOrConst(m.getSuperClass(), name)
|
||||
}
|
||||
|
||||
@@ -455,6 +455,8 @@ predicate jumpStep(Node pred, Node succ) {
|
||||
m = s.getEnclosingMethod() and
|
||||
m != s.getEnclosingCallable()
|
||||
)
|
||||
or
|
||||
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
|
||||
}
|
||||
|
||||
predicate storeStep(Node node1, Content c, Node node2) { none() }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
constantAccess
|
||||
| constants.rb:1:1:15:3 | ModuleA | write | ModuleA | ModuleDeclaration |
|
||||
| constants.rb:2:5:4:7 | ClassA | write | ClassA | ClassDeclaration |
|
||||
| constants.rb:3:9:3:15 | CONST_A | write | CONST_A | ConstantAssignment |
|
||||
@@ -27,3 +28,28 @@
|
||||
| constants.rb:37:1:37:7 | ModuleA | read | ModuleA | ConstantReadAccess |
|
||||
| constants.rb:37:1:37:16 | ModuleB | read | ModuleB | ConstantReadAccess |
|
||||
| constants.rb:37:1:37:26 | MAX_SIZE | write | MAX_SIZE | ConstantAssignment |
|
||||
getConst
|
||||
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
|
||||
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
|
||||
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
lookupConst
|
||||
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
|
||||
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
|
||||
| constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| constants.rb:9:9:10:11 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| constants.rb:31:1:32:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" |
|
||||
| constants.rb:31:1:32:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Array | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Class | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Complex | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | FalseClass | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Float | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Hash | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Integer | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Module | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | NilClass | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Numeric | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | Rational | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
| file://:0:0:0:0 | TrueClass | GREETING | constants.rb:17:12:17:64 | ... + ... |
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import ruby
|
||||
import codeql_ruby.ast.internal.Module as M
|
||||
|
||||
from ConstantAccess a, string kind
|
||||
where
|
||||
a instanceof ConstantReadAccess and kind = "read"
|
||||
or
|
||||
a instanceof ConstantWriteAccess and kind = "write"
|
||||
select a, kind, a.getName(), a.getAPrimaryQlClass()
|
||||
query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) {
|
||||
(
|
||||
a instanceof ConstantReadAccess and kind = "read"
|
||||
or
|
||||
a instanceof ConstantWriteAccess and kind = "write"
|
||||
) and
|
||||
name = a.getName() and
|
||||
cls = a.getAPrimaryQlClass()
|
||||
}
|
||||
|
||||
query Expr getConst(Module m, string name) { result = M::ExposedForTestingOnly::getConst(m, name) }
|
||||
|
||||
query Expr lookupConst(Module m, string name) { result = M::lookupConst(m, name) }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ruby
|
||||
import codeql_ruby.ast.internal.Module as M
|
||||
|
||||
query MethodBase getMethod(Module m, string name) { result = M::getMethod(m, name) }
|
||||
query MethodBase getMethod(Module m, string name) {
|
||||
result = M::ExposedForTestingOnly::getMethod(m, name)
|
||||
}
|
||||
|
||||
query MethodBase lookupMethod(Module m, string name) { result = M::lookupMethod(m, name) }
|
||||
|
||||
Reference in New Issue
Block a user