@@ -6,267 +6,10 @@ private import ruby
6
6
private import codeql.ruby.Concepts
7
7
private import codeql.ruby.ApiGraphs
8
8
private import codeql.ruby.DataFlow
9
- private import codeql.ruby.frameworks.Core
10
9
private import codeql.ruby.dataflow.FlowSummary
11
-
12
- private DataFlow:: Node ioInstanceInstantiation ( ) {
13
- result = API:: getTopLevelMember ( "IO" ) .getAnInstantiation ( ) or
14
- result = API:: getTopLevelMember ( "IO" ) .getAMethodCall ( [ "for_fd" , "open" , "try_convert" ] )
15
- }
16
-
17
- private DataFlow:: Node ioInstance ( ) {
18
- result = ioInstanceInstantiation ( )
19
- or
20
- exists ( DataFlow:: Node inst |
21
- inst = ioInstance ( ) and
22
- inst .( DataFlow:: LocalSourceNode ) .flowsTo ( result )
23
- )
24
- }
25
-
26
- // Match some simple cases where a path argument specifies a shell command to
27
- // be executed. For example, the `"|date"` argument in `IO.read("|date")`, which
28
- // will execute a shell command and read its output rather than reading from the
29
- // filesystem.
30
- private predicate pathArgSpawnsSubprocess ( Expr arg ) {
31
- arg .getConstantValue ( ) .getStringlikeValue ( ) .charAt ( 0 ) = "|"
32
- }
33
-
34
- private DataFlow:: Node fileInstanceInstantiation ( ) {
35
- result = API:: getTopLevelMember ( "File" ) .getAnInstantiation ( )
36
- or
37
- result = API:: getTopLevelMember ( "File" ) .getAMethodCall ( [ "open" , "try_convert" ] )
38
- or
39
- // Calls to `Kernel.open` can yield `File` instances
40
- result .( KernelMethodCall ) .getMethodName ( ) = "open" and
41
- // Assume that calls that don't invoke shell commands will instead open
42
- // a file.
43
- not pathArgSpawnsSubprocess ( result .( KernelMethodCall ) .getArgument ( 0 ) .asExpr ( ) .getExpr ( ) )
44
- }
45
-
46
- private DataFlow:: Node fileInstance ( ) {
47
- result = fileInstanceInstantiation ( )
48
- or
49
- exists ( DataFlow:: Node inst |
50
- inst = fileInstance ( ) and
51
- inst .( DataFlow:: LocalSourceNode ) .flowsTo ( result )
52
- )
53
- }
54
-
55
- abstract private class IOOrFileMethodCall extends DataFlow:: CallNode {
56
- // TODO: Currently this only handles class method calls.
57
- // Can we infer a path argument for instance method calls?
58
- // e.g. by tracing back to the instantiation of that instance
59
- DataFlow:: Node getAPathArgumentImpl ( ) {
60
- result = this .getArgument ( 0 ) and this .getReceiverKind ( ) = "class"
61
- }
62
-
63
- /**
64
- * Holds if this call appears to read/write from/to a spawned subprocess,
65
- * rather than to/from a file.
66
- */
67
- predicate spawnsSubprocess ( ) {
68
- pathArgSpawnsSubprocess ( this .getAPathArgumentImpl ( ) .asExpr ( ) .getExpr ( ) )
69
- }
70
-
71
- /** Gets the API used to perform this call, either "IO" or "File" */
72
- abstract string getApi ( ) ;
73
-
74
- /** DEPRECATED: Alias for getApi */
75
- deprecated string getAPI ( ) { result = this .getApi ( ) }
76
-
77
- /** Gets a node representing the data read or written by this call */
78
- abstract DataFlow:: Node getADataNodeImpl ( ) ;
79
-
80
- /** Gets a string representation of the receiver kind, either "class" or "instance". */
81
- abstract string getReceiverKind ( ) ;
82
- }
83
-
84
- /**
85
- * A method call that performs a read using either the `IO` or `File` classes.
86
- */
87
- private class IOOrFileReadMethodCall extends IOOrFileMethodCall {
88
- private string api ;
89
- private string receiverKind ;
90
-
91
- IOOrFileReadMethodCall ( ) {
92
- exists ( string methodName | methodName = this .getMethodName ( ) |
93
- // e.g. `{IO,File}.readlines("foo.txt")`
94
- receiverKind = "class" and
95
- methodName = [ "binread" , "foreach" , "read" , "readlines" ] and
96
- api = [ "IO" , "File" ] and
97
- this = API:: getTopLevelMember ( api ) .getAMethodCall ( methodName )
98
- or
99
- // e.g. `{IO,File}.new("foo.txt", "r").getc`
100
- receiverKind = "interface" and
101
- (
102
- methodName =
103
- [
104
- "getbyte" , "getc" , "gets" , "pread" , "read" , "read_nonblock" , "readbyte" , "readchar" ,
105
- "readline" , "readlines" , "readpartial" , "sysread"
106
- ] and
107
- (
108
- this .getReceiver ( ) = ioInstance ( ) and api = "IO"
109
- or
110
- this .getReceiver ( ) = fileInstance ( ) and api = "File"
111
- )
112
- )
113
- )
114
- }
115
-
116
- override string getApi ( ) { result = api }
117
-
118
- /** DEPRECATED: Alias for getApi */
119
- deprecated override string getAPI ( ) { result = this .getApi ( ) }
120
-
121
- override DataFlow:: Node getADataNodeImpl ( ) { result = this }
122
-
123
- override string getReceiverKind ( ) { result = receiverKind }
124
- }
125
-
126
- /**
127
- * A method call that performs a write using either the `IO` or `File` classes.
128
- */
129
- private class IOOrFileWriteMethodCall extends IOOrFileMethodCall {
130
- private string api ;
131
- private string receiverKind ;
132
- private DataFlow:: Node dataNode ;
133
-
134
- IOOrFileWriteMethodCall ( ) {
135
- exists ( string methodName | methodName = this .getMethodName ( ) |
136
- // e.g. `{IO,File}.write("foo.txt", "hello\n")`
137
- receiverKind = "class" and
138
- api = [ "IO" , "File" ] and
139
- this = API:: getTopLevelMember ( api ) .getAMethodCall ( methodName ) and
140
- methodName = [ "binwrite" , "write" ] and
141
- dataNode = this .getArgument ( 1 )
142
- or
143
- // e.g. `{IO,File}.new("foo.txt", "a+).puts("hello")`
144
- receiverKind = "interface" and
145
- (
146
- this .getReceiver ( ) = ioInstance ( ) and api = "IO"
147
- or
148
- this .getReceiver ( ) = fileInstance ( ) and api = "File"
149
- ) and
150
- (
151
- methodName = [ "<<" , "print" , "putc" , "puts" , "syswrite" , "pwrite" , "write_nonblock" ] and
152
- dataNode = this .getArgument ( 0 )
153
- or
154
- // Any argument to these methods may be written as data
155
- methodName = [ "printf" , "write" ] and dataNode = this .getArgument ( _)
156
- )
157
- )
158
- }
159
-
160
- override string getApi ( ) { result = api }
161
-
162
- /** DEPRECATED: Alias for getApi */
163
- deprecated override string getAPI ( ) { result = this .getApi ( ) }
164
-
165
- override DataFlow:: Node getADataNodeImpl ( ) { result = dataNode }
166
-
167
- override string getReceiverKind ( ) { result = receiverKind }
168
- }
169
-
170
- /**
171
- * Classes and predicates for modeling the core `IO` module.
172
- */
173
- module IO {
174
- /**
175
- * An instance of the `IO` class, for example in
176
- *
177
- * ```rb
178
- * rand = IO.new(IO.sysopen("/dev/random", "r"), "r")
179
- * rand_data = rand.read(32)
180
- * ```
181
- *
182
- * there are 3 `IOInstance`s - the call to `IO.new`, the assignment
183
- * `rand = ...`, and the read access to `rand` on the second line.
184
- */
185
- class IOInstance extends DataFlow:: Node {
186
- IOInstance ( ) {
187
- this = ioInstance ( ) or
188
- this = fileInstance ( )
189
- }
190
- }
191
-
192
- /**
193
- * A `DataFlow::CallNode` that reads data using the `IO` class. For example,
194
- * the `read` and `readline` calls in:
195
- *
196
- * ```rb
197
- * # invokes the `date` shell command as a subprocess, returning its output as a string
198
- * IO.read("|date")
199
- *
200
- * # reads from the file `foo.txt`, returning its first line as a string
201
- * IO.new(IO.sysopen("foo.txt")).readline
202
- * ```
203
- *
204
- * This class includes only reads that use the `IO` class directly, not those
205
- * that use a subclass of `IO` such as `File`.
206
- */
207
- class IOReader extends IOOrFileReadMethodCall {
208
- IOReader ( ) { this .getApi ( ) = "IO" }
209
- }
210
-
211
- /**
212
- * A `DataFlow::CallNode` that writes data using the `IO` class. For example,
213
- * the `write` and `puts` calls in:
214
- *
215
- * ```rb
216
- * # writes the string `hello world` to the file `foo.txt`
217
- * IO.write("foo.txt", "hello world")
218
- *
219
- * # appends the string `hello again\n` to the file `foo.txt`
220
- * IO.new(IO.sysopen("foo.txt", "a")).puts("hello again")
221
- * ```
222
- *
223
- * This class includes only writes that use the `IO` class directly, not those
224
- * that use a subclass of `IO` such as `File`.
225
- */
226
- class IOWriter extends IOOrFileWriteMethodCall {
227
- IOWriter ( ) { this .getApi ( ) = "IO" }
228
- }
229
-
230
- /**
231
- * A `DataFlow::CallNode` that reads data to the filesystem using the `IO`
232
- * or `File` classes. For example, the `IO.read` and `File#readline` calls in:
233
- *
234
- * ```rb
235
- * # reads the file `foo.txt` and returns its contents as a string.
236
- * IO.read("foo.txt")
237
- *
238
- * # reads from the file `foo.txt`, returning its first line as a string
239
- * File.new("foo.txt").readline
240
- * ```
241
- */
242
- class FileReader extends IOOrFileReadMethodCall , FileSystemReadAccess:: Range {
243
- FileReader ( ) { not this .spawnsSubprocess ( ) }
244
-
245
- override DataFlow:: Node getADataNode ( ) { result = this .getADataNodeImpl ( ) }
246
-
247
- override DataFlow:: Node getAPathArgument ( ) { result = this .getAPathArgumentImpl ( ) }
248
- }
249
-
250
- /**
251
- * A `DataFlow::CallNode` that reads data from the filesystem using the `IO`
252
- * or `File` classes. For example, the `write` and `puts` calls in:
253
- *
254
- * ```rb
255
- * # writes the string `hello world` to the file `foo.txt`
256
- * IO.write("foo.txt", "hello world")
257
- *
258
- * # appends the string `hello again\n` to the file `foo.txt`
259
- * File.new("foo.txt", "a").puts("hello again")
260
- * ```
261
- */
262
- class FileWriter extends IOOrFileWriteMethodCall , FileSystemWriteAccess:: Range {
263
- FileWriter ( ) { not this .spawnsSubprocess ( ) }
264
-
265
- override DataFlow:: Node getADataNode ( ) { result = this .getADataNodeImpl ( ) }
266
-
267
- override DataFlow:: Node getAPathArgument ( ) { result = this .getAPathArgumentImpl ( ) }
268
- }
269
- }
10
+ private import core.IO
11
+ private import core.Kernel:: Kernel
12
+ private import core.internal.IOOrFile
270
13
271
14
/**
272
15
* Classes and predicates for modeling the core `File` module.
@@ -330,7 +73,7 @@ module File {
330
73
] )
331
74
or
332
75
// Instance methods
333
- exists ( FileInstance fi |
76
+ exists ( File :: FileInstance fi |
334
77
this .getReceiver ( ) = fi and
335
78
this .getMethodName ( ) = [ "path" , "to_path" ]
336
79
)
0 commit comments