Skip to content

Commit 5ff98cd

Browse files
authored
Merge pull request github#10888 from erik-krogh/glob
Ruby: add model for Dir.glob and other Dir methods
2 parents bcfe4ec + 07d90b3 commit 5ff98cd

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,39 @@ module FileUtils {
182182
override DataFlow::Node getAPermissionNode() { result = permissionArg }
183183
}
184184
}
185+
186+
/**
187+
* Classes and predicates for modeling the core `Dir` module.
188+
*/
189+
module Dir {
190+
/**
191+
* A call to a method on `Dir` that operates on a path as its first argument, and produces file-names.
192+
* Considered as a `FileNameSource` and a `FileSystemAccess`.
193+
*/
194+
class DirGlob extends FileSystemAccess::Range, FileNameSource instanceof DataFlow::CallNode {
195+
DirGlob() {
196+
this =
197+
API::getTopLevelMember("Dir")
198+
.getAMethodCall(["glob", "[]", "children", "each_child", "entries", "foreach"])
199+
}
200+
201+
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
202+
}
203+
204+
/**
205+
* A call to a method on `Dir` that operates on a path as its first argument, considered as a `FileSystemAccess`.
206+
*/
207+
class DirPathAccess extends FileSystemAccess::Range instanceof DataFlow::CallNode {
208+
DirPathAccess() {
209+
this =
210+
API::getTopLevelMember("Dir")
211+
.getAMethodCall([
212+
"chdir", "chroot", "delete", "empty?", "exist?", "exists?", "mkdir", "new", "open",
213+
"rmdir", "unlink"
214+
])
215+
}
216+
217+
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
218+
}
219+
// TODO: Model that `(Dir.new "foo").each { |f| ... }` yields a filename (and some other public methods)
220+
}

ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ edges
3939
| tainted_path.rb:71:12:71:53 | call to new : | tainted_path.rb:72:15:72:18 | path |
4040
| tainted_path.rb:71:40:71:45 | call to params : | tainted_path.rb:71:40:71:52 | ...[...] : |
4141
| tainted_path.rb:71:40:71:52 | ...[...] : | tainted_path.rb:71:12:71:53 | call to new : |
42+
| tainted_path.rb:77:12:77:53 | call to new : | tainted_path.rb:78:19:78:22 | path |
43+
| tainted_path.rb:77:12:77:53 | call to new : | tainted_path.rb:79:14:79:17 | path |
44+
| tainted_path.rb:77:40:77:45 | call to params : | tainted_path.rb:77:40:77:52 | ...[...] : |
45+
| tainted_path.rb:77:40:77:52 | ...[...] : | tainted_path.rb:77:12:77:53 | call to new : |
4246
nodes
4347
| ArchiveApiPathTraversal.rb:5:26:5:31 | call to params : | semmle.label | call to params : |
4448
| ArchiveApiPathTraversal.rb:5:26:5:42 | ...[...] : | semmle.label | ...[...] : |
@@ -93,6 +97,11 @@ nodes
9397
| tainted_path.rb:71:40:71:45 | call to params : | semmle.label | call to params : |
9498
| tainted_path.rb:71:40:71:52 | ...[...] : | semmle.label | ...[...] : |
9599
| tainted_path.rb:72:15:72:18 | path | semmle.label | path |
100+
| tainted_path.rb:77:12:77:53 | call to new : | semmle.label | call to new : |
101+
| tainted_path.rb:77:40:77:45 | call to params : | semmle.label | call to params : |
102+
| tainted_path.rb:77:40:77:52 | ...[...] : | semmle.label | ...[...] : |
103+
| tainted_path.rb:78:19:78:22 | path | semmle.label | path |
104+
| tainted_path.rb:79:14:79:17 | path | semmle.label | path |
96105
subpaths
97106
#select
98107
| ArchiveApiPathTraversal.rb:59:21:59:36 | destination_file | ArchiveApiPathTraversal.rb:5:26:5:31 | call to params : | ArchiveApiPathTraversal.rb:59:21:59:36 | destination_file | This path depends on a $@. | ArchiveApiPathTraversal.rb:5:26:5:31 | call to params | user-provided value |
@@ -108,3 +117,5 @@ subpaths
108117
| tainted_path.rb:48:26:48:29 | path | tainted_path.rb:47:43:47:48 | call to params : | tainted_path.rb:48:26:48:29 | path | This path depends on a $@. | tainted_path.rb:47:43:47:48 | call to params | user-provided value |
109118
| tainted_path.rb:60:26:60:29 | path | tainted_path.rb:59:40:59:45 | call to params : | tainted_path.rb:60:26:60:29 | path | This path depends on a $@. | tainted_path.rb:59:40:59:45 | call to params | user-provided value |
110119
| tainted_path.rb:72:15:72:18 | path | tainted_path.rb:71:40:71:45 | call to params : | tainted_path.rb:72:15:72:18 | path | This path depends on a $@. | tainted_path.rb:71:40:71:45 | call to params | user-provided value |
120+
| tainted_path.rb:78:19:78:22 | path | tainted_path.rb:77:40:77:45 | call to params : | tainted_path.rb:78:19:78:22 | path | This path depends on a $@. | tainted_path.rb:77:40:77:45 | call to params | user-provided value |
121+
| tainted_path.rb:79:14:79:17 | path | tainted_path.rb:77:40:77:45 | call to params : | tainted_path.rb:79:14:79:17 | path | This path depends on a $@. | tainted_path.rb:77:40:77:45 | call to params | user-provided value |

ruby/ql/test/query-tests/security/cwe-022/tainted_path.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,11 @@ def route11
7171
path = ActiveStorage::Filename.new(params[:path])
7272
send_file path
7373
end
74+
75+
# BAD
76+
def route12
77+
path = ActiveStorage::Filename.new(params[:path])
78+
bla (Dir.glob path)
79+
bla (Dir[path])
80+
end
7481
end

0 commit comments

Comments
 (0)