Skip to content

Commit e3ebf1c

Browse files
authored
Merge pull request github#11187 from github/nickrolfe/actioncable
Ruby: add ActionCable channel RPC params as remote flow sources
2 parents 176405c + 20f76e5 commit e3ebf1c

File tree

6 files changed

+63
-0
lines changed

6 files changed

+63
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Arguments to RPC endpoints (public methods) on subclasses of `ActionCable::Channel::Base` are now recognized as sources of remote user input.

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,15 @@ class MethodNode extends CallableNode {
10581058

10591059
/** Gets the name of this method. */
10601060
string getMethodName() { result = this.asCallableAstNode().getName() }
1061+
1062+
/** Holds if this method is public. */
1063+
predicate isPublic() { this.asCallableAstNode().isPublic() }
1064+
1065+
/** Holds if this method is private. */
1066+
predicate isPrivate() { this.asCallableAstNode().isPrivate() }
1067+
1068+
/** Holds if this method is protected. */
1069+
predicate isProtected() { this.asCallableAstNode().isProtected() }
10611070
}
10621071

10631072
/**

ruby/ql/lib/codeql/ruby/frameworks/ActionCable.qll

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
private import codeql.ruby.AST
77
private import codeql.ruby.Concepts
88
private import codeql.ruby.ApiGraphs
9+
private import codeql.ruby.DataFlow
10+
private import codeql.ruby.dataflow.RemoteFlowSources
911
private import codeql.ruby.frameworks.stdlib.Logger::Logger as StdlibLogger
1012

1113
/**
@@ -26,4 +28,36 @@ module ActionCable {
2628
}
2729
}
2830
}
31+
32+
private DataFlow::ConstRef getActionCableChannelBase() {
33+
result = DataFlow::getConstant("ActionCable").getConstant("Channel").getConstant("Base")
34+
}
35+
36+
/**
37+
* The data argument in an RPC endpoint method on a subclass of
38+
* `ActionCable::Channel::Base`, considered as a remote flow source.
39+
*/
40+
class ActionCableChannelRpcParam extends RemoteFlowSource::Range {
41+
ActionCableChannelRpcParam() {
42+
exists(DataFlow::MethodNode m |
43+
// Any method on a subclass of `ActionCable::Channel::Base`
44+
// automatically becomes an RPC endpoint
45+
m = getActionCableChannelBase().getADescendentModule().getAnInstanceMethod() and
46+
// as long as it's not an instance method of
47+
// `ActionCable::Channel::Base` itself, which might exist in the
48+
// database
49+
not m = getActionCableChannelBase().asModule().getAnInstanceMethod() and
50+
// and as long as it's public
51+
m.isPublic() and
52+
// and is not called `subscribed` or `unsubscribed`.
53+
not m.getMethodName() = ["subscribed", "unsubscribed"]
54+
|
55+
// If the method takes a parameter, it contains data from the remote
56+
// request.
57+
this = m.getParameter(0)
58+
)
59+
}
60+
61+
override string getSourceType() { result = "ActionCable channel RPC data" }
62+
}
2963
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1+
loggerInstantiations
12
| action_cable.rb:1:1:1:54 | call to new |
3+
remoteFlowSources
4+
| action_cable.rb:9:10:9:13 | data |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import codeql.ruby.frameworks.ActionCable
22
import codeql.ruby.frameworks.stdlib.Logger
3+
import codeql.ruby.dataflow.RemoteFlowSources
34

45
query predicate loggerInstantiations(Logger::LoggerInstantiation l) { any() }
6+
7+
query predicate remoteFlowSources(RemoteFlowSource s) { any() }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
11
ActionCable::Connection::TaggedLoggerProxy.new(logger)
2+
3+
class MyChannel < ActionCable::Channel::Base
4+
# An RPC endpoint without the optional data parm, so no remote flow source.
5+
def foo
6+
end
7+
8+
# An RPC endpoint including the optional data parm, which is a remote flow source.
9+
def bar(data)
10+
end
11+
end

0 commit comments

Comments
 (0)