Skip to content

Commit 5afd3c7

Browse files
authored
Merge pull request #213 from github/aibaars/api-graphs2
First version of ApiGraphs
2 parents 703e9e7 + efde1f8 commit 5afd3c7

10 files changed

Lines changed: 875 additions & 29 deletions

File tree

ql/src/codeql_ruby/ApiGraphs.qll

Lines changed: 422 additions & 0 deletions
Large diffs are not rendered by default.

ql/src/codeql_ruby/controlflow/CfgNodes.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ module ExprNodes {
240240
/** Gets the `n`th argument of this call. */
241241
final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) }
242242

243+
/** Gets the the keyword argument whose key is `keyword` of this call. */
244+
final ExprCfgNode getKeywordArgument(string keyword) {
245+
e.hasCfgChild(e.getKeywordArgument(keyword), this, result)
246+
}
247+
243248
/** Gets the number of arguments of this call. */
244249
final int getNumberOfArguments() { result = e.getNumberOfArguments() }
245250

@@ -296,6 +301,20 @@ module ExprNodes {
296301
final ExprCfgNode getBranch(boolean cond) { e.hasCfgChild(e.getBranch(cond), this, result) }
297302
}
298303

304+
private class ConstantAccessChildMapping extends ExprChildMapping, ConstantAccess {
305+
override predicate relevantChild(Expr e) { e = this.getScopeExpr() }
306+
}
307+
308+
/** A control-flow node that wraps a `ConditionalExpr` AST expression. */
309+
class ConstantAccessCfgNode extends ExprCfgNode {
310+
override ConstantAccessChildMapping e;
311+
312+
final override ConstantAccess getExpr() { result = super.getExpr() }
313+
314+
/** Gets the scope expression. */
315+
final ExprCfgNode getScopeExpr() { e.hasCfgChild(e.getScopeExpr(), this, result) }
316+
}
317+
299318
private class StmtSequenceChildMapping extends ExprChildMapping, StmtSequence {
300319
override predicate relevantChild(Expr e) { e = this.getLastStmt() }
301320
}

ql/src/codeql_ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ class Node extends TNode {
3939
}
4040
}
4141

42+
/** A data-flow node corresponding to a call in the control-flow graph. */
43+
class CallNode extends LocalSourceNode {
44+
private CfgNodes::ExprNodes::CallCfgNode node;
45+
46+
CallNode() { node = this.asExpr() }
47+
48+
/** Gets the data-flow node corresponding to the receiver of the call corresponding to this data-flow node */
49+
Node getReceiver() { result.asExpr() = node.getReceiver() }
50+
51+
/** Gets the data-flow node corresponding to the `n`th argument of the call corresponding to this data-flow node */
52+
Node getArgument(int n) { result.asExpr() = node.getArgument(n) }
53+
54+
/** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */
55+
Node getKeywordArgument(string name) { result.asExpr() = node.getKeywordArgument(name) }
56+
}
57+
4258
/**
4359
* An expression, viewed as a node in a data flow graph.
4460
*

ql/src/queries/security/cwe-732/WeakFilePermissions.ql

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,7 @@
1212
import ruby
1313
import codeql_ruby.DataFlow
1414
import DataFlow::PathGraph
15-
private import codeql_ruby.dataflow.SSA
16-
17-
// TODO: account for flows through tuple assignments
18-
/** An expression referencing the File or FileUtils module */
19-
class FileModuleAccess extends Expr {
20-
FileModuleAccess() {
21-
this.(ConstantAccess).getName() = "File"
22-
or
23-
this.(ConstantAccess).getName() = "FileUtils"
24-
or
25-
exists(FileModuleAccess fma, Ssa::WriteDefinition def |
26-
def.getARead() = this.getAControlFlowNode() and
27-
def.getWriteAccess().getParent().(Assignment).getRightOperand() = fma
28-
)
29-
}
30-
}
15+
import codeql_ruby.ApiGraphs
3116

3217
bindingset[p]
3318
int world_permission(int p) { result = p.bitAnd(7) }
@@ -58,14 +43,13 @@ class PermissivePermissionsExpr extends Expr {
5843
}
5944

6045
/** A permissions argument of a call to a File/FileUtils method that may modify file permissions */
61-
class PermissionArgument extends Expr {
62-
private MethodCall call;
63-
private string methodName;
46+
class PermissionArgument extends DataFlow::Node {
47+
private DataFlow::CallNode call;
6448

6549
PermissionArgument() {
66-
call.getReceiver() instanceof FileModuleAccess and
67-
call.getMethodName() = methodName and
68-
(
50+
exists(string methodName |
51+
call = API::moduleImport(["File", "FileUtils"]).getAMethodCall(methodName)
52+
|
6953
methodName in ["chmod", "chmod_R", "lchmod"] and this = call.getArgument(0)
7054
or
7155
methodName = "mkfifo" and this = call.getArgument(1)
@@ -78,7 +62,7 @@ class PermissionArgument extends Expr {
7862
)
7963
}
8064

81-
MethodCall getCall() { result = call }
65+
MethodCall getCall() { result = call.asExpr().getExpr() }
8266
}
8367

8468
class PermissivePermissionsConfig extends DataFlow::Configuration {
@@ -88,14 +72,12 @@ class PermissivePermissionsConfig extends DataFlow::Configuration {
8872
exists(PermissivePermissionsExpr ppe | source.asExpr().getExpr() = ppe)
8973
}
9074

91-
override predicate isSink(DataFlow::Node sink) {
92-
exists(PermissionArgument arg | sink.asExpr().getExpr() = arg)
93-
}
75+
override predicate isSink(DataFlow::Node sink) { sink instanceof PermissionArgument }
9476
}
9577

9678
from
9779
DataFlow::PathNode source, DataFlow::PathNode sink, PermissivePermissionsConfig conf,
9880
PermissionArgument arg
99-
where conf.hasFlowPath(source, sink) and arg = sink.getNode().asExpr().getExpr()
81+
where conf.hasFlowPath(source, sink) and arg = sink.getNode()
10082
select source.getNode(), source, sink, "Overly permissive mask in $@ sets file to $@.",
10183
arg.getCall(), arg.getCall().toString(), source.getNode(), source.getNode().toString()

0 commit comments

Comments
 (0)