66private import codeql.ruby.AST
77private import codeql.ruby.Concepts
88private import codeql.ruby.ApiGraphs
9+ private import codeql.ruby.DataFlow
10+ private import codeql.ruby.dataflow.RemoteFlowSources
911private 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}
0 commit comments