Skip to content

Commit 03d0f66

Browse files
committed
Ruby: add flow summaries for Pathname class
1 parent 381bcf7 commit 03d0f66

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import core.Hash
1414
import core.String
1515
import core.Regexp
1616
import core.IO
17+
import core.Pathname
1718

1819
/**
1920
* A system command executed via subshell literal syntax.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/** Modeling of the `Pathname` class from the Ruby standard library. */
2+
3+
private import codeql.ruby.AST
4+
private import codeql.ruby.ApiGraphs
5+
private import codeql.ruby.DataFlow
6+
private import codeql.ruby.dataflow.FlowSummary
7+
private import codeql.ruby.dataflow.internal.DataFlowDispatch
8+
private import codeql.ruby.controlflow.CfgNodes
9+
10+
/**
11+
* Modeling of the `Pathname` class from the Ruby standard library.
12+
*
13+
* https://docs.ruby-lang.org/en/3.1/Pathname.html
14+
*/
15+
module Pathname {
16+
/// Flow summary for `Pathname.new`.
17+
private class NewSummary extends SummarizedCallable {
18+
NewSummary() { this = "Pathname.new" }
19+
20+
override MethodCall getACall() {
21+
result = API::getTopLevelMember("Pathname").getAnInstantiation().getExprNode().getExpr()
22+
}
23+
24+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
25+
input = "Argument[0]" and
26+
output = "ReturnValue" and
27+
preservesValue = false
28+
}
29+
}
30+
31+
/// Flow summary for `Pathname#dirname`.
32+
private class DirnameSummary extends SimpleSummarizedCallable {
33+
DirnameSummary() { this = "dirname" }
34+
35+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
36+
input = "Argument[self]" and
37+
output = "ReturnValue" and
38+
preservesValue = false
39+
}
40+
}
41+
42+
/// Flow summary for `Pathname#each_filename`.
43+
private class EachFilenameSummary extends SimpleSummarizedCallable {
44+
EachFilenameSummary() { this = "each_filename" }
45+
46+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
47+
input = "Argument[self]" and
48+
output = "Argument[block].Parameter[0]" and
49+
preservesValue = false
50+
}
51+
}
52+
53+
/// Flow summary for `Pathname#expand_path`.
54+
private class ExpandPathSummary extends SimpleSummarizedCallable {
55+
ExpandPathSummary() { this = "expand_path" }
56+
57+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
58+
input = "Argument[self]" and
59+
output = "ReturnValue" and
60+
preservesValue = false
61+
}
62+
}
63+
64+
/// Flow summary for `Pathname#join`.
65+
private class JoinSummary extends SimpleSummarizedCallable {
66+
JoinSummary() { this = "join" }
67+
68+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
69+
input = ["Argument[self]", "Argument[any]"] and
70+
output = "ReturnValue" and
71+
preservesValue = false
72+
}
73+
}
74+
75+
/// Flow summary for `Pathname#parent`.
76+
private class ParentSummary extends SimpleSummarizedCallable {
77+
ParentSummary() { this = "parent" }
78+
79+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
80+
input = "Argument[self]" and
81+
output = "ReturnValue" and
82+
preservesValue = false
83+
}
84+
}
85+
86+
/// Flow summary for `Pathname#realpath`.
87+
private class RealpathSummary extends SimpleSummarizedCallable {
88+
RealpathSummary() { this = "realpath" }
89+
90+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
91+
input = "Argument[self]" and
92+
output = "ReturnValue" and
93+
preservesValue = false
94+
}
95+
}
96+
97+
/// Flow summary for `Pathname#relative_path_from`.
98+
private class RelativePathFromSummary extends SimpleSummarizedCallable {
99+
RelativePathFromSummary() { this = "relative_path_from" }
100+
101+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
102+
input = "Argument[self]" and
103+
output = "ReturnValue" and
104+
preservesValue = false
105+
}
106+
}
107+
108+
/// Flow summary for `Pathname#to_path`.
109+
private class ToPathSummary extends SimpleSummarizedCallable {
110+
ToPathSummary() { this = "to_path" }
111+
112+
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
113+
input = "Argument[self]" and
114+
output = "ReturnValue" and
115+
preservesValue = false
116+
}
117+
}
118+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
failures
2+
edges
3+
| pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn |
4+
| pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : |
5+
| pathname_flow.rb:9:6:9:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... |
6+
| pathname_flow.rb:9:19:9:28 | call to source : | pathname_flow.rb:9:6:9:29 | call to new : |
7+
| pathname_flow.rb:10:6:10:29 | call to new : | pathname_flow.rb:11:7:11:11 | ... + ... |
8+
| pathname_flow.rb:10:19:10:28 | call to source : | pathname_flow.rb:10:6:10:29 | call to new : |
9+
| pathname_flow.rb:15:7:15:30 | call to new : | pathname_flow.rb:16:7:16:8 | pn : |
10+
| pathname_flow.rb:15:20:15:29 | call to source : | pathname_flow.rb:15:7:15:30 | call to new : |
11+
| pathname_flow.rb:16:7:16:8 | pn : | pathname_flow.rb:16:7:16:16 | call to dirname |
12+
| pathname_flow.rb:20:6:20:29 | call to new : | pathname_flow.rb:21:2:21:2 | a : |
13+
| pathname_flow.rb:20:19:20:28 | call to source : | pathname_flow.rb:20:6:20:29 | call to new : |
14+
| pathname_flow.rb:21:2:21:2 | a : | pathname_flow.rb:21:22:21:22 | x : |
15+
| pathname_flow.rb:21:22:21:22 | x : | pathname_flow.rb:22:8:22:8 | x |
16+
| pathname_flow.rb:27:6:27:29 | call to new : | pathname_flow.rb:28:7:28:7 | a : |
17+
| pathname_flow.rb:27:19:27:28 | call to source : | pathname_flow.rb:27:6:27:29 | call to new : |
18+
| pathname_flow.rb:28:7:28:7 | a : | pathname_flow.rb:28:7:28:21 | call to expand_path |
19+
| pathname_flow.rb:32:6:32:29 | call to new : | pathname_flow.rb:35:7:35:7 | a : |
20+
| pathname_flow.rb:32:19:32:28 | call to source : | pathname_flow.rb:32:6:32:29 | call to new : |
21+
| pathname_flow.rb:34:6:34:29 | call to new : | pathname_flow.rb:35:17:35:17 | c : |
22+
| pathname_flow.rb:34:19:34:28 | call to source : | pathname_flow.rb:34:6:34:29 | call to new : |
23+
| pathname_flow.rb:35:7:35:7 | a : | pathname_flow.rb:35:7:35:18 | call to join |
24+
| pathname_flow.rb:35:17:35:17 | c : | pathname_flow.rb:35:7:35:18 | call to join |
25+
| pathname_flow.rb:39:6:39:29 | call to new : | pathname_flow.rb:40:7:40:7 | a : |
26+
| pathname_flow.rb:39:19:39:28 | call to source : | pathname_flow.rb:39:6:39:29 | call to new : |
27+
| pathname_flow.rb:40:7:40:7 | a : | pathname_flow.rb:40:7:40:16 | call to parent |
28+
| pathname_flow.rb:44:6:44:29 | call to new : | pathname_flow.rb:45:7:45:7 | a : |
29+
| pathname_flow.rb:44:19:44:28 | call to source : | pathname_flow.rb:44:6:44:29 | call to new : |
30+
| pathname_flow.rb:45:7:45:7 | a : | pathname_flow.rb:45:7:45:18 | call to realpath |
31+
| pathname_flow.rb:49:6:49:29 | call to new : | pathname_flow.rb:50:7:50:7 | a : |
32+
| pathname_flow.rb:49:19:49:28 | call to source : | pathname_flow.rb:49:6:49:29 | call to new : |
33+
| pathname_flow.rb:50:7:50:7 | a : | pathname_flow.rb:50:7:50:38 | call to relative_path_from |
34+
| pathname_flow.rb:54:6:54:29 | call to new : | pathname_flow.rb:55:7:55:7 | a : |
35+
| pathname_flow.rb:54:19:54:28 | call to source : | pathname_flow.rb:54:6:54:29 | call to new : |
36+
| pathname_flow.rb:55:7:55:7 | a : | pathname_flow.rb:55:7:55:15 | call to to_path |
37+
| pathname_flow.rb:59:6:59:29 | call to new : | pathname_flow.rb:60:7:60:7 | a : |
38+
| pathname_flow.rb:59:19:59:28 | call to source : | pathname_flow.rb:59:6:59:29 | call to new : |
39+
| pathname_flow.rb:60:7:60:7 | a : | pathname_flow.rb:60:7:60:12 | call to to_s |
40+
nodes
41+
| pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : |
42+
| pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : |
43+
| pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn |
44+
| pathname_flow.rb:9:6:9:29 | call to new : | semmle.label | call to new : |
45+
| pathname_flow.rb:9:19:9:28 | call to source : | semmle.label | call to source : |
46+
| pathname_flow.rb:10:6:10:29 | call to new : | semmle.label | call to new : |
47+
| pathname_flow.rb:10:19:10:28 | call to source : | semmle.label | call to source : |
48+
| pathname_flow.rb:11:7:11:11 | ... + ... | semmle.label | ... + ... |
49+
| pathname_flow.rb:15:7:15:30 | call to new : | semmle.label | call to new : |
50+
| pathname_flow.rb:15:20:15:29 | call to source : | semmle.label | call to source : |
51+
| pathname_flow.rb:16:7:16:8 | pn : | semmle.label | pn : |
52+
| pathname_flow.rb:16:7:16:16 | call to dirname | semmle.label | call to dirname |
53+
| pathname_flow.rb:20:6:20:29 | call to new : | semmle.label | call to new : |
54+
| pathname_flow.rb:20:19:20:28 | call to source : | semmle.label | call to source : |
55+
| pathname_flow.rb:21:2:21:2 | a : | semmle.label | a : |
56+
| pathname_flow.rb:21:22:21:22 | x : | semmle.label | x : |
57+
| pathname_flow.rb:22:8:22:8 | x | semmle.label | x |
58+
| pathname_flow.rb:27:6:27:29 | call to new : | semmle.label | call to new : |
59+
| pathname_flow.rb:27:19:27:28 | call to source : | semmle.label | call to source : |
60+
| pathname_flow.rb:28:7:28:7 | a : | semmle.label | a : |
61+
| pathname_flow.rb:28:7:28:21 | call to expand_path | semmle.label | call to expand_path |
62+
| pathname_flow.rb:32:6:32:29 | call to new : | semmle.label | call to new : |
63+
| pathname_flow.rb:32:19:32:28 | call to source : | semmle.label | call to source : |
64+
| pathname_flow.rb:34:6:34:29 | call to new : | semmle.label | call to new : |
65+
| pathname_flow.rb:34:19:34:28 | call to source : | semmle.label | call to source : |
66+
| pathname_flow.rb:35:7:35:7 | a : | semmle.label | a : |
67+
| pathname_flow.rb:35:7:35:18 | call to join | semmle.label | call to join |
68+
| pathname_flow.rb:35:17:35:17 | c : | semmle.label | c : |
69+
| pathname_flow.rb:39:6:39:29 | call to new : | semmle.label | call to new : |
70+
| pathname_flow.rb:39:19:39:28 | call to source : | semmle.label | call to source : |
71+
| pathname_flow.rb:40:7:40:7 | a : | semmle.label | a : |
72+
| pathname_flow.rb:40:7:40:16 | call to parent | semmle.label | call to parent |
73+
| pathname_flow.rb:44:6:44:29 | call to new : | semmle.label | call to new : |
74+
| pathname_flow.rb:44:19:44:28 | call to source : | semmle.label | call to source : |
75+
| pathname_flow.rb:45:7:45:7 | a : | semmle.label | a : |
76+
| pathname_flow.rb:45:7:45:18 | call to realpath | semmle.label | call to realpath |
77+
| pathname_flow.rb:49:6:49:29 | call to new : | semmle.label | call to new : |
78+
| pathname_flow.rb:49:19:49:28 | call to source : | semmle.label | call to source : |
79+
| pathname_flow.rb:50:7:50:7 | a : | semmle.label | a : |
80+
| pathname_flow.rb:50:7:50:38 | call to relative_path_from | semmle.label | call to relative_path_from |
81+
| pathname_flow.rb:54:6:54:29 | call to new : | semmle.label | call to new : |
82+
| pathname_flow.rb:54:19:54:28 | call to source : | semmle.label | call to source : |
83+
| pathname_flow.rb:55:7:55:7 | a : | semmle.label | a : |
84+
| pathname_flow.rb:55:7:55:15 | call to to_path | semmle.label | call to to_path |
85+
| pathname_flow.rb:59:6:59:29 | call to new : | semmle.label | call to new : |
86+
| pathname_flow.rb:59:19:59:28 | call to source : | semmle.label | call to source : |
87+
| pathname_flow.rb:60:7:60:7 | a : | semmle.label | a : |
88+
| pathname_flow.rb:60:7:60:12 | call to to_s | semmle.label | call to to_s |
89+
subpaths
90+
#select
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @kind path-problem
3+
*/
4+
5+
import ruby
6+
import TestUtilities.InlineFlowTest
7+
import PathGraph
8+
9+
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
10+
where conf.hasFlowPath(source, sink)
11+
select sink, source, sink, "$@", source, source.toString()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
require 'pathname'
2+
3+
def m_new
4+
pn = Pathname.new(source 'a')
5+
sink pn # $ hasTaintFlow=a
6+
end
7+
8+
def m_plus
9+
a = Pathname.new(source 'a')
10+
b = Pathname.new(source 'b')
11+
sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b
12+
end
13+
14+
def m_dirname
15+
pn = Pathname.new(source 'a')
16+
sink pn.dirname # $ hasTaintFlow=a
17+
end
18+
19+
def m_each_filename
20+
a = Pathname.new(source 'a')
21+
a.each_filename do |x|
22+
sink x # $ hasTaintFlow=a
23+
end
24+
end
25+
26+
def m_expand_path
27+
a = Pathname.new(source 'a')
28+
sink a.expand_path() # $ hasTaintFlow=a
29+
end
30+
31+
def m_join
32+
a = Pathname.new(source 'a')
33+
b = Pathname.new('foo')
34+
c = Pathname.new(source 'c')
35+
sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c
36+
end
37+
38+
def m_parent
39+
a = Pathname.new(source 'a')
40+
sink a.parent() # $ hasTaintFlow=a
41+
end
42+
43+
def m_realpath
44+
a = Pathname.new(source 'a')
45+
sink a.realpath() # $ hasTaintFlow=a
46+
end
47+
48+
def m_relative_path_from
49+
a = Pathname.new(source 'a')
50+
sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a
51+
end
52+
53+
def m_to_path
54+
a = Pathname.new(source 'a')
55+
sink a.to_path # $ hasTaintFlow=a
56+
end
57+
58+
def m_to_s
59+
a = Pathname.new(source 'a')
60+
sink a.to_s # $ hasTaintFlow=a
61+
end

0 commit comments

Comments
 (0)